ランキングは毎日更新します。
が同じ値の場合は投稿日時の新しいものが上位としています。
| 1位 |
|
|||
|
23:58:21 |
|
|
Markdown記法のチートシートです。
本ページではQiitaで使用可能なMarkdownのみ掲載しているため、一部原文と異なります。 Markdownの原文については、[Daring Fireball: Markdown Syntax Documentation] (http://daringfireball.net/projects/markdown/syntax.php)をご覧下さい。 また、コードに関する記法は[GitHub Flavored Markdown](http://github.github.com/github-flavored-markdown/)に準拠しています。 Qiitaでシンタックスハイライト可能な言語一覧については、 [シンタックスハイライト可能な言語](http://qiita.com/Qiita/items/e84f5aad7757afce82ba) をご覧下さい。 ## Code - コードの挿入 たとえば、Rubyで記述したコードをファイル名「qiita.rb」として投稿したいときは、 **バッククオート** を使用して以下のように投稿するとシンタックスハイライトが適用されます。 **コードブロック上下に空行を挿入しないと正しく表示されないことがあります。** > (空行) > \`\`\`ruby:qiita.rb > puts 'The best way to log and share programmers knowledge.' > \`\`\` > (空行) **結果** ```ruby:qiita.rb puts 'The best way to log and share programmers knowledge.' ``` また、コードをインライン表示することも可能です。 > \` puts 'Qiita'` はプログラマのための技術情報共有サービスです。 **結果** ` puts 'Qiita'` はプログラマのための技術情報共有サービスです。 インラインコードがn個連続するバッククオートを含む場合、n+1連続のバッククオートで囲みます。 > \`\` \`バッククオート\` \`\` や \`\`\` \`\`2連続バッククオート\`\` \`\`\` も記述できます。 **結果** `` `バッククオート` `` や ``` ``2連続バッククオート`` ``` も記述できます。 インラインコードの中身が [CSS の `<color>` 型](https://developer.mozilla.org/ja/docs/Web/CSS/color_value)の RGB 16進数表記, `rgb()`, `rgba()`, `hsl()`, `hsla()` 場合横にその色が表示されます。 > \`#ffce44\` > \`rgb(255,0,0)\` > \`rgba(0,255,0,0.4)\` > \`hsl(100, 10%, 10%)\` > \`hsla(100, 24%, 40%, 0.5)\` **結果** `#ffce44` `rgb(255,0,0)` `rgba(0,255,0,0.4)` `hsl(100, 10%, 10%)` `hsla(100, 24%, 40%, 0.5)` ### Gist連携について ##### GitHubアカウントでQiitaにログインされている場合 投稿時、Octocatアイコンにチェックを入れていただくと連携を行います。 コードを含むアイテムを投稿するとコード部分を抽出し、同じ内容がGistにも投稿される仕組みになっています。 Gistとの連携は、 * コードの投稿 * Qiita側でのコードの編集 の2点について連携しています。 Gist側でコードを編集されても、 **Qiitaには反映されません** のでご注意下さい。 ## Format Text - テキストの装飾 ### Headers - 見出し * \# これはH1タグです * \## これはH2タグです * \###### これはH6タグです ### Emphasis - 強調・強勢 ```markdown _ か * で囲むとHTMLのemタグになります。Qiitaでは*イタリック体*になります。 __ か ** で囲むとHTMLのstrongタグになります。Qiitaでは**太字**になります。 ``` _ か * で囲むとHTMLのemタグになります。Qiitaでは*イタリック体*になります。 __ か ** で囲むとHTMLのstrongタグになります。Qiitaでは**太字**になります。 ### Strikethrough - 打ち消し線 ```markdown 打ち消し線を使うには ~~ で囲みます。 ~~打ち消し~~ ``` 打ち消し線を使うには ~~ で囲みます。 ~~打ち消し~~ イタリックや太文字と同様に前後に **半角スペース** か **改行文字** が必要です。 ### Details - 折りたたみ ```markdown 追加情報としたい内容を、detailsタグで囲みます。そして、要約として表示したい文章をsummaryタグで記載します。 Qiitaとは <details><summary>Qiita(キータ)は、プログラマのための技術情報共有サービスです。</summary>プログラミングに関することをどんどん投稿して、知識を記録、共有しましょう。 Qiitaに投稿すると、自分のコードやノウハウを見やすい形で残すことができます。 技術情報はテキストファイルへのメモではなく、タグを付けた文章、シンタックスハイライトされたコードで保存することで初めて再利用可能な知識になる、そうQiitaでは考えています。</details> ``` 追加情報としたい内容を、detailsタグで囲みます。そして、要約として表示したい文章をsummaryタグで記載します。 Qiitaとは <details><summary>Qiita(キータ)は、プログラマのための技術情報共有サービスです。</summary> プログラミングに関することをどんどん投稿して、知識を記録、共有しましょう。 Qiitaに投稿すると、自分のコードやノウハウを見やすい形で残すことができます。 技術情報はテキストファイルへのメモではなく、タグを付けた文章、シンタックスハイライトされたコードで保存することで初めて再利用可能な知識になる、そうQiitaでは考えています。</details> ## Lists - リスト ### Disc型 * 文頭に「*」「+」「-」のいずれかを入れるとDisc型リストになります * 要点をまとめる際に便利です * リストを挿入する際は、 **リストの上下に空行がないと正しく表示されません。また「*」「+」「-」の後にはスペースが必要です** ### Decimal型 1. 文頭に「数字.」を入れるとDecimal型リストになります 2. 後からの挿入/移動を考慮して、1. 2. 3. と順番にするのではなく、1. 1. 1. という風に同じ数字にしておくといい具合です。 3. リストを挿入する際は、 **リストの上下に空行がないと正しく表示されません。また「数字.」の後にはスペースが必要です** ### Definition型 HTMLの`<dl>`タグをそのまま使うことで実現できます。 ```html <dl> <dt>リンゴ</dt> <dd>赤いフルーツ</dd> <dt>オレンジ</dt> <dd>橙色のフルーツ</dd> </dl> ``` 次のようになります。 <dl> <dt>リンゴ</dt> <dd>赤いフルーツ</dd> <dt>オレンジ</dt> <dd>橙色フルーツ</dd> </dl> 注意するべきは、Definition型のリスト内ではMarkdown記法が使えないということです。例えば以下のように書いてはなりません。 ```html <dl> <dt>リンゴ</dt> <dd> とても **赤い** フルーツ </dd> </dl> ``` 次のようになってしまいます。 <dl> <dt>リンゴ</dt> <dd> とても **赤い** フルーツ </dd> </dl> Definition型リスト内ではMarkdown記法ではなくて、HTMLタグを使って修飾しなければならないので、正しくは次のようになります。 ```html <dl> <dt>リンゴ</dt> <dd> とても<strong>赤い</strong>フルーツ </dd> </dl> ``` <dl> <dt>リンゴ</dt> <dd> とても<strong>赤い</strong>フルーツ</dd> </dl> Markdown記法とHTMLタグの対応は次のようになっています。 | 修飾 | Markdown | HTML | |:----------:|:---------------:|:------------------------:| | ボールド | `** **` | `<strong></strong>` | | イタリック | `_ _` | `<em></em>` | | コード | <code>``</code> | `<code></code>` | | リンク | `[text](url)` | `<a href="url">text</a>` | ### Checkbox型 Disc型の記述の後ろに[ ]を入れるとチェックボックスが生成されます。 チェックが入った状態のボックスを生成する場合は[x]を入力します。 **前後にスペースが必要です。** ```md - [ ] タスク1 - [x] タスク2 ``` - [ ] タスク1 - [x] タスク2 ## Blockquotes - 引用 > \> 文頭に>を置くことで引用になります。 > \> 複数行にまたがる場合、改行のたびにこの記号を置く必要があります。 > \> **引用の上下にはリストと同じく空行がないと正しく表示されません** > \> 引用の中に別のMarkdownを使用することも可能です。 > > これはネストされた引用です。 ## Horizontal rules - 水平線 下記は全て水平線として表示されます > \* * * > \*** > \***** > \- - - > \--------------------------------------- ## Links - リンク * \[リンクテキスト](URL "タイトル") * タイトル付きのリンクを投稿できます。 **例** > *Markdown:* \[Qiita]\(http://qiita.com "Qiita") > *結果:* [Qiita](http://qiita.com "Qiita") * \[リンクテキスト](URL) * こちらはタイトル無しのリンクになります。 **例** > *Markdown:* \[Qiita]\(http://qiita.com) > *結果:* [Qiita](http://qiita.com) - \[リンクテキスト]\[名前] - \[名前]:URL - 同じURLへのリンクを複数箇所に設定することができます **例** > *Markdown:* \[ここ]\[link-1] と \[この]\[link-1] リンクは同じになります。 \[link-1][\] も可能です。 \[link-1]:http://qiita.com/drafts/c686397e4a0f4f11683d > *結果:* [ここ][link-1] と [この][link-1] リンクは同じになります。 [link-1][] も可能です。 [link-1]:http://qiita.com/drafts/c686397e4a0f4f11683d ## Images - 画像埋め込み * \![代替テキスト]\(画像のURL) * タイトル無しの画像を埋め込む * \![代替テキスト]\(画像のURL "画像タイトル") * タイトル有りの画像を埋め込む **例** > *Markdown:* \![Qiita]\(https://qiita-image-store.s3.amazonaws.com/0/45617/015bd058-7ea0-e6a5-b9cb-36a4fb38e59c.png "Qiita") > *結果:* >  ## テーブル記法 ### 入力補完を利用する場合  ### 手動で入力する場合 ``` | Left align | Right align | Center align | |:-----------|------------:|:------------:| | This | This | This | | column | column | column | | will | will | will | | be | be | be | | left | right | center | | aligned | aligned | aligned | ``` 上記のように書くと,以下のように表示されます. | Left align | Right align | Center align | |:-----------|------------:|:------------:| | This | This | This | | column | column | column | | will | will | will | | be | be | be | | left | right | center | | aligned | aligned | aligned | ## 数式の挿入 コードブロックの言語指定に "math" を指定することでTeX記法を用いて数式を記述することができます。 > \`\`\`math > \left( \sum_{k=1}^n a_k b_k \right)^{\!\!2} \leq > \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) > \`\`\` ```math \left( \sum_{k=1}^n a_k b_k \right)^{\!\!2} \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) ``` `$2^3$` のように数式を "$" で挟むと行中に数式を埋め込むこともできます。 > x^2 + y^2 = 1 をインライン表示すると $x^2 + y^2 = 1$ になります。 ただしインライン数式の中でコントロールシンボル(`\{`のような、バックスラッシュの後に記号が続くもの)を使うと、後述のバックスラッシュによるMarkdownのエスケープと衝突してしまいます。 ``` $a = \{1, 2, 3\}$ ``` > $a = \{1, 2, 3\}$ なので次のように二つのバックスラッシュを使います。 ``` $a = \\{1, 2, 3\\}$ ``` > $a = \\{1, 2, 3\\}$ ## 目次(TOC)の自動挿入 目次は記事内の見出しを元に自動生成し、右上に自動挿入されます。詳細は[目次機能の紹介記事](http://blog.qiita.com/post/77055935852/qiita-toc)をご覧ください。 ## 注釈 本文中に`[^1]`や`[^example]`のように文字列を記述することで、脚注へのリンクを表現できます。注釈内容は、同じく本文中に `[^1]: ...` というように記述します[^1]。 [^1]: 注釈内容を記述する位置は、本文の途中でも末尾でも構いません。 ## 絵文字 厳密には Markdown 記法の外ですが、`:` で囲って、絵文字を埋め込めます。 **例** ``` \:kissing_closed_eyes: chu☆ ``` > \:kissing_closed_eyes: chu☆ 絵文字チートシート http://www.emoji-cheat-sheet.com/ ## その他 バックスラッシュ[\\]をMarkdownの前に挿入することで、Markdownをエスケープ(無効化)することができます。 **例** > \# H1 > エスケープされています また本文では一部のHTMLタグも利用可能です。 |
|
| 2位 |
|
|||
|
23:42:02 |
(ShouldBee 所属) |
|
[これ知らないプログラマって損してんなって思う汎用的なツール](http://qiita.com/puriketu99/items/f75d42f1ff669b924ec1)のコメントに寄せられたツールを分類分けしてみました。
* 解説は、ほぼコメントに寄せられた内容のコピペです。 * URLのみの記述は公式サイト(か、ほぼ公式サイトと化しているサイト) * 公式サイトとは別に、ページタイトルだけでツールを説明しきっているページへのリンクも付けておきました。類似ページが複数ある場合は、はてブのブックマーク数が多いものを選びました。 * 知らないツールもあるので、分類がいいかげんなところもあると思います。何か気づいたらコメントください。 * 解説が不十分なツールについても、補足(コピペで本文に取り込める体裁だとありがたい)を頂けると助かります! * 元ネタの投稿は現在進行形なので、随時取り込んでいきたいと思います。 --- # エディタ ### vim 自分はVimmerなんで、Vimを推しておきます。Vimがあれば大抵の事が出来ると思ってます。 が、Vimは習得に時間がかかるという難点があります。 エディタプラグイン * neocomplcache.vim * quickrun.vim * vimproc + quickrun * unite.vim ### MacVim ### Emacs VIM, Emacs は別格って感じですね・・・ エディタプラグイン * anything.el ### CocoaEmacs ### Mou Markdownを書くためのエディタ。 * http://mouapp.com/ * [ リアルタイムプレビューができるMarkdownエディタ『Mou』 | Macの手書き説明書](http://veadardiary.blog29.fc2.com/blog-entry-3492.html) ### Sublime Text 3 https://sublimetext.com/3 ### CotEditor フリー。超軽量なエディター。 簡単な文章修正とかに向いてる。 ### Coda https://panic.com/jp/coda/ シェア。エディター+IDE的な感じ。デザインを確認しながら編集できる。FTPやSSHとかSVNとかも同梱されていてデザイナーはこれで十分。 ### TextWrangler https://itunes.apple.com/jp/app/textwrangler/id404010395 ### Espresso http://espressoapp.com/ Espresso はリアルタイムでCSSをプレビューしながら書けたりするエディタ。 ### JSON Editor サクっとJSONを整形してくれる --- # IDE(統合開発環境) ### Xcode iPhoneアプリを開発する気がなくても入れておくと、差分を簡単に扱えるFileMergeアプリやopendiffコマンドなどおまけが付いてくるのでオススメ * https://developer.apple.com/xcode/ ### Eclipse Eclipse とか NetBeans とかもとりあえず使ってみる派。で、どっちがいいというのを決めるより「これは何が優れているか」をみて、シーンによって起動するのを変えてしまう。 * http://www.eclipse.org/ ### NetBeans Eclipse とか NetBeans とかもとりあえず使ってみる派。で、どっちがいいというのを決めるより「これは何が優れているか」をみて、シーンによって起動するのを変えてしまう。 * http://ja.netbeans.org/ ### PhpStrom PHPやJavaScriptをはじめとしたウェブ向けのIDE。 * http://www.jetbrains.com/phpstorm/ * [最強のJavaScript IDE「WebStorm」の姉妹品「PhpStorm」はPHP IDEとして最高だった ::ハブろぐ](http://havelog.ayumusato.com/computer/software/e173-phpstorm-best-ide.html) ### Cloud9 まだ荒削りだけど、ブラウザで実行できるIDE。GitHubやBitbucketなんかと連動。 * http://c9.io/ ### WebStorm 現行のIDEの中ではJavaScript回りの環境が最強。姉妹品のPHPStormも評判良いです。 リファクタとか超簡単に出来たりします。が、有料です。その価値はあると思いますけど…。 * http://www.jetbrains.com/webstorm/index.html * [最強のJavaScript IDE 「WebStorm」を使ってみた](http://efcl.info/2010/1027/res2023/) ### Nide node.jsのIDE * http://coreh.github.com/nide/ * [Node.js向けのWebベースIDE「Nide 0.2」がリリース - SourceForge.JP Magazine : オープンソースの話題満載](http://sourceforge.jp/magazine/12/01/17/080233) --- # SQL, DB ### Sequel Pro MySQLクライアント。phpMyAdminがいらなくなりました。 * http://www.sequelpro.com/ * [高機能なMySQLフロントエンド「Sequel Pro」 - MOONGIFT](http://www.moongift.jp/2008/06/sequel_pro/) ### Navicat MySQLクライアント。 * http://www.navicat.jp/ * [便利なGUIのMySQLフロントエンド「Navicat for MySQL Lite」 - MOONGIFT](http://www.moongift.jp/2008/02/navicat_for_mysql_lite/) ### MySQLWorkbench MySQLのクライアントは MySQLWorkbench を使っています。 EER図も描けるからデータベースの設計にも使える。 * http://www-jp.mysql.com/products/workbench/ ### HeidiSQL Windows向けMySQLのクライアントですが、HeidiSQL JDBC/ODBC等なしにこれ単体で接続でき、スプレッドシートを順次開いていくかたちでのブラウジングができる。 そのため、「実際にDBのデータを見ながらデータをいじる」というのが直感的にでき、非常に便利。 SSHトンネル接続や、サーバ間のデータダンプ&リストアもできるため、運用作業はだいたいこれで完結できる。 ただ、若干不安定なので、ミッションクリティカルな作業には向いてないかも。 (そういうのはサーバ上でやれよ、と言われそうですが…) * http://www.heidisql.com/ ### adminer phpMyAdminと同じくブラウザベースのDBクライアント。 1ファイルでphpが動く環境ならOK、なにより軽いから重宝してる。 MySQL,PostgreSQL, SQLite, MS SQL,Oracleと意外に相手を問わないのになぜかマイナーなんだよな~。 * http://www.adminer.org/ ### SQLEditor ER図作成ツール。生SQLやActiveRecord Modelを書き出せる ### Base.app SQlite用GUI管理ツール ### ERMaster DB管理ツール。 EclipseのプラグインですがDB管理+ドキュメント化が非常に楽になります。 ### The SQLite Sorcerer SQLite用のクライアント。AIR製。 * http://afoucal.free.fr/index.php/applications/sqlite-sorcerer/ --- # PHP ### MAMP * http://www.mamp.info/en/index.html ### xhprof php で書かれたWebアプリケーションのボトルネック調査に必須 ### xdebug ### PHPUnit 使えるテストフレームワークがこれしかない… ### Phing php のビルドツール,テスト環境や本番などの環境ごとのブートストラップ指定や,結合テストの設定に --- # Ruby ### travisCI 継続インテグレーション ### bundler アプリケーション毎に必要な Gem を管理できる.デプロイ先の構築に便利 ### Capistrano デプロイツール,PHP とか他の言語で書かれたアプリケーションも設定次第でこれでデプロイできる ### Pry irb から乗り換えた ### watchr ### guard ### gist --- # Java JavaDecompiler --- # JavaScript ### node.js JavaScriptの対話実行環境 とにかく速くJSの動作をチェックしたい時に便利 * http://nodejs.org/ * [サーバサイドJavaScriptの本命「Node.js」の基礎知識(1/3)- @IT](http://www.atmarkit.co.jp/fwcr/rensai2/nodejs01/01.html) ### SpiderMonkey JavaScriptの対話実行環境 とにかく速くJSの動作をチェックしたい時に便利 * https://developer.mozilla.org/ja/SpiderMonkey ### ringo.js JavaScriptの対話実行環境 とにかく速くJSの動作をチェックしたい時に便利 * http://ringojs.org/ ### jsPerf JavaScriptのベンチマーク取れるサイト。jsのベンチマークをとって公開するのに便利。 * http://jsperf.com/ ### jsFiddle ブラウザ内でJavaScriptの実行・HTML/CSSのマークアップなんかが試せる * http://jsfiddle.net/ ### Google Closure Compiler JavaScriptをミニファイすることがあれば、Google Closure Compiler * http://code.google.com/p/closure-compiler/ * [Google製JavaScript最適化ツール「Closure Compiler」 - MOONGIFT](http://www.moongift.jp/2009/11/closure-compiler/) ### YUI Compressor JavaScriptもCSSもミニファイすることがあれば、YUI Compressor * http://developer.yahoo.com/yui/compressor/ * [YUI Compressorで簡単にjavascriptとCSSを圧縮 - Affirmative Way](http://d.hatena.ne.jp/cos31/20080116/1200504657) ### impress.js * http://bartaz.github.com/impress.js/ * [impress.jsライクなダイナミックなプレゼンテーションを作成するjQuery依存タイプのライブラリ・jmpress.js - かちびと. net](http://kachibito.net/web-design/jmpress-js.html) ### enchant.js * http://enchantjs.com/ja/?s=ja * [Monaca + enchant.js でお手軽スマフォゲームアプリ開発 : アシアルブログ](http://blog.asial.co.jp/858) * gl.enchant.js * [wise9 › 3D野郎は寄ってたかれ!WebGLでグリグリ遊べるgl.enchant.jsがついにβ公開!](http://wise9.jp/archives/6245) ### npm ### forever ### coffee-script ### rvm ### nvm ### nave ---- # Python ### pypy pythonで書いたプログラムがちょっと遅いなと思ったらとりあえず一回使ってみる(djangoが動くので関係してますよね?)。 * http://pypy.org/ ### virtualenv, virtualenvwrapper, pythonbrew pythonだとvirtualenv, virtualenvwrapper。最近では代わりにpythonbrewは絶対入れる。 * http://pypi.python.org/pypi/virtualenv * http://pypi.python.org/pypi/virtualenvwrapper * https://github.com/utahta/pythonbrew * [virtualenv, virtualenvwrapper, pip を使う方法 - Ian Lewis](http://www.ianlewis.org/jp/virtualenv-pip-fabric) * [Pythonのバージョンを管理するツール、pythonbrewを作ってみた | ninxit.blog](http://www.ninxit.com/blog/2010/10/04/python%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%82%92%E7%AE%A1%E7%90%86%E3%81%99%E3%82%8B%E3%83%84%E3%83%BC%E3%83%AB%E3%80%81pythonbrew%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF/) ### SimpleHTTPServer Pythonだと、SimpleHTTPServerが便利だと思います。 * [Pythonを使って、1行でファイル共有Webサーバを立ち上げる - モジログ](http://mojix.org/2009/03/05/python_one_line_fileserver) ### pip ### ipython ### scipy ### werkzeug --- # CSS, HTML, LESS ### Less.app LESSを書くことがあれば LESS.app も便利です。 * http://incident57.com/less/ * [Sassよりラクチン,LESS.appで簡単CSSコンパイル生活!(Mac限定) ::ハブろぐ](http://havelog.ayumusato.com/computer/software/e218-less_better_css.html) ### Twitter Bootstrap 自分はプログラムに集中したい、でも見た目もそれなりになってたほうがいい、というときにはTwitter Bootstrapでテンプレートをサクっと作っておくといいかもです。 * http://twitter.github.com/bootstrap/ * [Twitter Bootstrapが大幅バージョンアップ!して凄まじいことに・・・ | IDEA*IDEA](http://www.ideaxidea.com/archives/2012/02/twitter_bootstrap_v2.html) ### zen-coding HTML/CSSの入力支援。IDEがサポートしてたり、エディタにプラグインで組み込めたりする。 * https://github.com/sergeche/zen-coding --- # テスト ### xUnit 系 [XUnitTestPatterns](http://xunitpatterns.com/) とか読んでおくとどの言語の xUnit 系テストフレームワークへの理解が進む ### xSpec 系 JBehave とか RSpec とか. PHP には無い.[Wikipedia英語版のBDDのページ](http://en.wikipedia.org/wiki/Behavior_Driven_Development)がわかりやすい ### QuickCheck 系 プログラム仕様そのものを記述し,ランダムな引数を与えて検証するテストフレームワーク. オリジナルは Haskell からだが,[メジャーな言語ではけっこう既に実装されている](http://en.wikipedia.org/wiki/QuickCheck). Haskell 以外の言語で意欲的に実装を開発している[YelloSoftのページ](http://www.yellosoft.us/quickcheck) がわかりやすいかと --- # ネットワーク ### Hoster hostsファイルをGUIで設定するアプリ。 * http://www.redwinder.com/macapp/hoster/ ### wireshark あと wireshark, tshark は使い方覚えると超強力ですよ。 * http://www.wireshark.org/ * [パケットキャプチャツール「Wireshark」 - Wiresharkって? - ネットワークエンジニアを目指して](http://www.itbook.info/study/wireshark1.html) * [Tshark(キャプチャツール)](http://homepage2.nifty.com/protocol/tshark/index.html) ### HTTP Client * http://ditchnet.org/httpclient/ * [Mac OSXのWeb開発のお供に「HTTP Client」 - MOONGIFT](http://www.moongift.jp/2008/12/http_client/) ### MockSMTP 仮想ローカルSMTP。ネットに繋がっていなくてもSMTPの挙動がチェックできる --- # FTP, SSH, SCP ### ~/.ssh/config: SSHまわりの設定をまとめておくと大変便利 * [~/.ssh/config で簡単に複数ホストへのSSH接続を管理する - すぱぶらの日記](http://d.hatena.ne.jp/superbrothers/20090730/1248971671) ### PuTTY WindowsのSSHは PuTTY 派なんですが、人にすすめるのは Poderosa ですね。 * http://yebisuya.dip.jp/Software/PuTTY/ ### Poderosa WindowsのSSHは PuTTY 派なんですが、人にすすめるのは Poderosa ですね。 * http://ja.poderosa.org/ ### ClusterSSH * http://sourceforge.net/projects/clusterssh/ * [しげふみメモ : Cluster SSHで複数ホストでコマンド同時実行](http://blog.livedoor.jp/hakin/archives/51564707.html) ### Transmit リモート上のファイルをダブルクリックでローカルに保存→開く、ファイル保存→自動的にアップロード なんてのが出来て、ちょっとした開発には結構便利。あと、接続先のディレクトリをFinderにマウント出来る恐怖の機能とかも。もちろんssh等にも対応。 --- # grep ### rak Ruby系のツールの rak コマンド。grep検索ツールです。 * [コードスニペットを検索するのにとても便利な「rak」のご紹介 - Seasons.NET](http://d.hatena.ne.jp/Seasons/20090615/1245091432) ### ack better than grep * http://betterthangrep.com/ ### jvgrep 手前味噌ですがgrepでjvgrep。go言語で書かれているのでlinuxでもwindowsでも同じ動作。 日本語のエンコーディングはだいたいサポートしてるので、いろんなエンコーディングのファイルが混じった時のgrepや、通称駄目文字を含んだ正規表現でも正しく動きます。 ### Tips `export GREP_OPTIONS='--color=auto'` でfgrep/egrep/grepしたときにマッチしたとき単語がカラフルにハイライト表示されるようになる grep/fgrep/egrepの-oオプション。マッチした単語のみ抽出出来る、これはワンライナー書くときに使える。egrepは拡張正規表現なのでPerlとかRubyみたいに便利な正規表現使える。fgrepは固定文字列検索するときに早い 詳細: [単語抽出するちょっとしたテクニック - 技術メモ帳](http://d.hatena.ne.jp/lurker/20070131/1170201200) --- # 統計処理 数万件程度のちょっとしたログをさくっと処理したい時に便利 中央値、標準偏差なんかも一発で出せる。データを元にヒストグラム、3Dのグラフ描画とかも ### R * http://www.r-project.org/ ### Octave * http://www.gnu.org/software/octave/ --- # ターミナル ### TotalTerminal Macのターミナルをショートカットキーですぐに呼び出せます。 * http://totalterminal.binaryage.com/ * [ホットキーでターミナルを呼び出す『TotalTerminal』 | Macの手書き説明書](http://veadardiary.blog29.fc2.com/blog-entry-3377.html) ### iTerm2 * http://www.iterm2.com/#/section/home * [米LH編集部のイチオシのMacのターミナルエミュレータは『iTerm2』 : ライフハッカー[日本版]](http://www.lifehacker.jp/2011/11/111116maciterm2.html) ### percol percol がターミナルで使える汎用的で便利なツールです。どう使うかでこちらの知識/センスが試されている気分になります。 * https://github.com/mooz/percol --- # Subversion ### Cornerstone SVNクライアント。 ### Versions SVNクライアント。 --- # git ### SmartGit (Windows) Git クライアント * http://www.syntevo.com/smartgit/index.html ### SourceTree Git クライアント * http://www.sourcetreeapp.com/ ### GitHub 自分の書いたソースコードを公開。 * https://github.com/ * [とっても優しい github の使い方 - ¬¬日常日記](http://d.hatena.ne.jp/keita_yamaguchi/20080409/1207752188) ### tig git使うならtigが無いと生きていけない。 * [コンソールから使える git ブラウザ、tig が超便利 - #生存戦略 、それは - subtech](http://subtech.g.hatena.ne.jp/secondlife/20101114/1289736508) ### GitX * http://gitx.frim.nl/ * [Git リポジトリを視覚化するツール「GitX」 - kなんとかの日記](http://d.hatena.ne.jp/kwatch/20081021/1224608591) ### Tower * http://www.git-tower.com/ ### github for mac * http://mac.github.com/ --- # ブラウザ ### SRWare Iron UserAgentが変えられるChrome的な使い方をしてます --- # パッケージ管理 ### MacPorts フリー。パッケージ管理ツール。コンパイルに時間かかるけど昔から使ってるから手放せないツール。 ### Homebrew UNIXのコマンドなどをサクっと入れられます。 * http://mxcl.github.com/homebrew/ * [MacPortsより使いやすい!?パッケージ管理システムHomebrewの使い方 | Macとかの雑記帳](http://tukaikta.blog135.fc2.com/blog-entry-183.html) --- # その他開発関係ツール ### codekit CodeKit automatically compiles Less, Sass, Stylus, CoffeeScript & Haml files. * http://incident57.com/codekit/ * [H2O Blog., 新世代Web構築には必須になりそうな『CodeKit』](http://blog.h2o-space.com/post/12365214600) ### pandoc 書き溜めたmarkdownメモを ドキュメントつくるときにpandoc使ってhtml化してる * http://johnmacfarlane.net/pandoc/ * [Markdown/HTML/LaTexを相互変換「Pandoc」 - MOONGIFT](http://www.moongift.jp/2007/12/pandoc/) ### Integrity 高速リンクチェッカー。デプロイ前の必須アイテム * http://peacockmedia.co.uk/integrity/ * [MacでWebサイトのリンク切れをチェックできるアプリケーション『Integrity』 | kotaログ](http://www.kotalog.net/archives/2566) ### Omnigraffle シェア。ドキュメント作成ツール。これで仕様書を作るとめちゃくちゃ捗る。 ### osx-gcc-installer Macで「xcodeは要らないけど、gccだけ欲しいわー」ってなったときに死ぬほど有り難いツール。とりあえずWatchかブクマ。 * https://github.com/kennethreitz/osx-gcc-installer ### VMware + Gentoo (or Ubuntu, Debian, etc) ### ImageOptim 画像ファイルを軽量化 ### HTMLサーバとしてのDropbox HTML+CSS+JSをグローバルアクセスからデバッグしようと思ったら、結局DropboxのPublicフォルダが一番簡単なことに気がつきました(スマフォ向けサイトとかね)。 --- # その他作業効率化関係ツール ### Caffeine * http://lightheadsw.com/caffeine/ * [Mac OSXのスリープ防止に「Caffeine」 - MOONGIFT](http://moongift.jp/2007/10/caffeine/) ### BetterTouchTool * http://blog.boastr.net/ * [Macのトラックパッド操作を快適にするBetterTouchToolの設定 - #RyoAnnaBlog](http://d.hatena.ne.jp/RyoAnna/20110731/1312100591) ### ClipMenu * http://www.clipmenu.com/ja/ * [クリップボードを履歴管理する「ClipMenu」 - MOONGIFT](http://www.moongift.jp/2010/01/clipmenu/) ### Limechat for mac osx Preferences -> Log -> Show image links in inlineを有効にすると ユーザー名に対応した画像をTwitterから引っ張ってきてインラインで展開してくれる。 もちろん間違ってるプロフィール画像が展開されることもあるけど、有名人多いIRCとかだとだいたいあってる。 * http://limechat.net/mac/ja.html * [軽くてシンプルなIRCクライアント『LimeChat』 | Macの手書き説明書](http://veadardiary.blog29.fc2.com/blog-entry-2672.html) ### QuickSilver Quicksilverは、アプリケーションの立ち上げ、コンピューター・ファイルの操作、e-mailの下書きと送信といった作業を、キーボードにより、手早く処理できる。 * http://www.blacktree.com/ * http://ja.wikipedia.org/wiki/Quicksilver ### Quix quix便利やけど使ってる人見たことない * http://www.quixapp.com/ * [ライフハッカーお勧めの多機能ブックマークレットQuixを試したら本当に便利だった - かちびと. net](http://kachibito.net/web-service/quix.html) ### Skitch * http://skitch.com/jp/ * [Macのキャプチャ・編集ソフト「Skitch」が大変便利 - カイ士伝](http://bloggingfrom.tv/wp/2010/05/05/3668) ### Lingon フリーorシェア。自動起動の管理ツール。OSXの自動起動プログラムを視覚的に編集したりできる。 * http://www.peterborgapps.com/lingon/ * [launchdの設定をGUIで編集「Lingon」 - MOONGIFT](http://www.moongift.jp/2008/04/lingon/) ### Growl 基本的なところでMacだとGrowl ### Alfred QuickSilverみたいなfuzzyな文字列による、アプリケーション検索 & 実行ランチャ。QuickSilverよりも良いのは、マッチするアプリケーションがなかったときはGoogleで検索してくれること。 ### Reeder RSSリーダーは Reeder がおすすめ。 * http://reederapp.com/mac/ ### Cinch Macで簡単に画面2分割(Win7のあれ)をやってくれるツール。同僚に教えてもらってアディクトしてしまった。 * http://www.irradiatedsoftware.com/cinch/ ### Fluid Webアプリを簡単にローカルアプリ化出来るヤツ。Webアプリで簡単に開発して、クライアントをブラウザにしなくなかったり、常駐させたいときに便利。 ### DIVVY 画面をショートカットキー1発で分割できる ### A Better Finder Rename バッチリネームが超絶簡単 ### Skim 軽量PDFリーダ。Adobe Acrobatよりも気持ちいい ### Bean Microsoft Wordよりも快適 --- # Unix/Linux/Macコマンド * gdb * lv * xargs * iotop * htop * iperf * htop * zsh * tree * make * bison * awk ------ * screen * byobu * tmux > これはサーバで長時間かかる作業を不安定な回線などで行うときに特に役立ちます。 > screenは手軽にプロセスを長生きさせるのに便利です。 sshで接続して何かしらの時間がかかるプロセスを実行しても、接続切ったらプロセスもカーネルに殺されてしまう。 そこでscreenの中でプロセスを起動して、screenをデタッチしとくと、そのプロセスを生き長らえさせることができます。 > 最初にありがたいと思ったのはサーバー上で長い事作業する時 通信がぶちっと切れてもscreenのプロセス自体は生き残るので、再度サーバーに接続して 残ってるscreenのプロセスにアタッチすれば作業が即再開できる。 vimの作業状態なんかもそのまま残っている byobuというのはscreenがいい感じにカスタマイズされた奴 > screenは最近tmuxに置き換えました。まだ使いこなしてないけど。 > screen/tmux がとっつきにくいという向けにはbyobu がお勧めです. とりあえず困らない設定になっています. > 後は以下に沢山のtips が "ターミナルマルチプレクサ Advent Calendar 2011 : ATND" http://atnd.org/events/22320 > ネイティブのバイナリ関連なら、od,file,nm,strings,strace,varglind > あと個人的にはvim/zsh/screen。この3つはどんな環境でも必ず入れる > find ./ -name *.php | xargs grep hoge とかはよく使いますね。Windowsは秀丸のgrepが強力です。 ### git, よく使うコマンドとオプション, コマンド小ネタ, シェル小ネタ http://qiita.com/items/2047#comment-2182 ### 寒い日にCPUが発熱してあったかくなるコマンド 寒い日は充電中のMacBook上で```ruby -r digest/sha1 -e 'loop{Digest::SHA1.hexdigest(Time.now.to_f.to_s)}'``` とかしてしばらく放置すると勝手にCPUが発熱してあったかくなる --- # ウェブサービス ### Qiita ここ * http://qiita.com/ # 未分類 ### mscgen シーケンス図を書くのであればmscgenをお勧めします。 日本語もフォントを指定すれば問題なくうごきます。 * http://www.mcternan.me.uk/mscgen/ ### cheat * https://github.com/defunkt/cheat ### yUML UMLを描いて出力できるWebサービスです。ユニークなURLが発行されるのでブログに貼り付けたり出来ます クラス図 / アクティビティ図 / ユースケース図 * http://yuml.me/ ### WebSequenceDiagrams.com UMLを描いて出力できるWebサービスです。ユニークなURLが発行されるのでブログに貼り付けたり出来ます シーケンス図 http://www.websequencediagrams.com/ ### Cacoo ### AppleK for VMware Macで仮想マシン上のWindowsを使う場合にキー操作がMacと同じになるように設定してくれるアプリ。有料だけどVM上での作業効率が大きく上がるのでおすすめ。 - VMWare用 http://www.trinityworks.co.jp/software/AppleKforVMware3/index.php - Parallels用 http://www.trinityworks.co.jp/software/AppleKforParallels3/index.php ### KeyRemap4Macbook なぜかまだ挙げられていないKeyRemap4Macbook。キー割り当てを自在にカスタマイズできる他、キーリピートやキーウェイトを標準仕様より高速化できる。Mac使いのプログラマにとっては定番のアプリ http://pqrs.org/macosx/keyremap4macbook/index.html.ja ### BitNami 様々なOSSプラットフォーム/アプリをインストーラを使って簡単に導入し試すことの出来るソフト http://bitnami.org/stacks ### AMPPS 様々なOSSプラットフォーム/アプリをインストーラを使って簡単に導入し試すことの出来るソフト http://www.ampps.com/ ### TEKICO 日本語・欧文のダミーテキストを生成するAIR製ソフト。Webサイトのプロトタイプ作成時に活躍。 * http://www.uniteair.co.jp/app/air/pid_001.html ------ 作業メモ 2012/02/08 http://qiita.com/items/2047#comment-2159 まで書き写した 2012/02/09 http://qiita.com/items/2047#comment-2215 まで書き写した \#comment-2219 まで取り込んだ Googleとかでタイトルの後ろがtruncateされて本家と区別つかないのでタイトル変更「これ知らないプログラマって損してんなって思う汎用的なツール 100超 まとめ」→「【まとめ】これ知らないプログラマって損してんなって思う汎用的なツール 100超」 2012/02/10 http://qiita.com/items/2047#comment-2295 まで書き写した 未分類だったものをある程度分類した |
|
| 3位 |
|
|||
|
15:59:24 |
(esa, LLC 所属) |
|
英語コミットコメントに使えそうなオシャレフレーズを聞いたので、これを使ってドヤ顔コミットをしたくてやれるチャンスを虎視眈々と狙う毎日です :shipit:
v, x, g, z とかこのへんが入ってる単語だとなんかカッコ良さ増す。 tweak とかデザイナーにはだいぶ便利。 |単語|意味| ----|---- |undesirable|好ましくない| |avoid, circumvent|回避する| |retrieve| 取りに行く(fetch / 「受け取る」よりもっと能動的に「取りに行く」ニュアンスを示す)| |revise|= fix| |tweak|微調整する| |in the sight of 〜| 〜の視点で (point of view をわざと仰々しく言った感じ)| |refer|参照する| |disambiguate|曖昧さをなくす| |quite|とても(veryのかわりにつかえる| |literally|文字通り| |exactly|まさに、確かに、イグザクトリー| |resurrect|よみがえらせる| |in favor of|〜に賛成して, 何かに沿って| |get rid of|好ましくないものを取り除く| |kick out| get rid of の強いバージョン、腹立たしさある| |introduce|導入する| |extract|抽出する| |no longer used|もはや使われてない| |R.I.P.|安らかに眠れ(もう使ってないコードを消す時などに。「Removeでは語りきれない思いがあるんだ」 by @idesaku )| |defeat|倒す、打ち負かす| |:put_litter_in_its_place:```:put_litter_in_its_place:```|ゴミはゴミ箱へ(不要なコメントやwhitespaceを消したときなど)| |cosmetic change, cosme|インデントを美しくした時など| |fruitful|みのりの多い〜, fruitful discussion など| |:golf:```:golf:```|コードをより短くした(code golf)| |Take a cup of coffee|JS を CoffeeScript 化した, またはCoffeeをよりCoffeeらしく書いた| |kick in|効きはじめる, 適用される など(DHHが使っていたらしい。)| |A in B out|Bの変わりにAを導入(サッカー的な感じ。Gemの差し替えなどに使う)| |enable to work|動くようにした| |wipe out|拭い去る(get rid of, kick out の亜種)| |stop rendering 〜|〜を表示しない| |aesthetic|cosmetic の亜種(多分)| |Ooops! :scream: |しょうもないミスした時などに。 :scream_cat: ```:scream_cat:``` 使うとかわいみでる。| 随時追加しています :baby_chick: あと、ぷるりくとかもお待ちしてます :bird: ### あわせて読みたい コミットコメントに対する心得がわかりやすく書いてありました。当記事の補完としてお読みいただけるとイイカンジ。 [初心者から一歩抜け出すためのGitの業 Vol.3](http://qiita.com/tbaba/items/77224ad2046c918d061b) |
|
| 4位 |
|
|||
|
08:05:13 |
(SonicGarden Inc. 所属) |
|
## はじめに: 遠回りせずに「近道」を探す
RubyやRailsを始めたばかりの人は、もっと短く書く方法や便利な標準ライブラリの存在を知らずに遠回りした書き方をしてしまいがちです。 そこで、RubyやRails初心者の人によく見かける「遠回り(または車輪の再発明)」と、それを回避する「近道」をいろいろ集めてみました。 ### 2013.11.06 追記 この投稿を書くに至った経緯などを自分のブログに書きました。 こちらも合わせてどうぞ! - [昨日Qiitaに投稿した記事は普段のコードレビューの副産物 - give IT a try](http://blog.jnito.com/entry/2013/11/06/072831) ## Ruby編 以下はRubyの標準機能を使ったイディオムやメソッドです。 Railsプロジェクトでもそれ以外でも使えます。(Ruby 1.9以上を想定) ### 後置ifで行数を減らす ````ruby if user.active? send_mail_to(user) end ```` ````ruby send_mail_to(user) if user.active? ```` ### if + notではなく、unlessを使う ````ruby user.destroy if !user.active? ```` ````ruby user.destroy unless user.active? ```` ただし、unlessの条件がandやorでつながっていたり、否定形の条件が入っていたりすると、読み手の脳に負担がかかるので、複雑な条件はifを使う方が良いです。 ````ruby # こんなunlessは理解するのに時間がかかるのでNG user.destroy unless (user.active? || user.admin?) && !user.spam? ```` ### 三項演算子を使って行数を減らす ````ruby if user.admin? "I appreciate for that." else "Thanks." end ```` ````ruby user.admin? ? "I appreciate for that." : "Thanks" ```` ただし、三項演算子をネストさせたりすると極めて読みにくくなるのでやめておきましょう。 ````ruby # 三項演算子のネストは読みづらい user.admin? ? user.active? ? "I appreciate for that." : "Are you OK?" : "Thanks." ```` ### 代入してからifで存在を確認、をまとめて書く ````ruby user = find_user if user send_mail_to(user) end ```` ````ruby if user = find_user send_mail_to(user) end ```` ただし、このイディオムは「`==`と`=`を書き間違えたんじゃないか?」と読み手に勘違いされる恐れもあるので、好き嫌いが分かれるのも事実です。 ### 子どものオブジェクトが存在する場合にのみ、そのプロパティやメソッドを呼び出して条件を確認する、をひとつのifで書く 以下のコードは、`parent.children`が`nil`になっている可能性があるので、`children`が存在するときだけ`children.singleton?`を呼び出したい、というようなケースです。 ````ruby if parent.children if parent.children.singleton? singleton = parent.children.first send_mail_to(singleton) end end ```` ````ruby if parent.children && parent.children.singleton? singleton = parent.children.first send_mail_to(singleton) end ```` Ruby 2.3では safe navigation operator という新しい演算子(`&.`)が追加されました。 これを使うと `nil` かもしれないオブジェクトにメソッド呼び出しを試すことができます。 もしオブジェクトが `nil` であれば戻り値も `nil` になります。 ````ruby # Ruby 2.3以降(childrenがnilでもエラーにならず、nilが返る) if parent.children&.singleton? singleton = parent.children.first send_mail_to(singleton) end ```` その他、Ruby 2.3の新機能についてはこちらの記事をご覧ください。 [サンプルコードでわかる!Ruby 2.3の主な新機能 - Qiita](http://qiita.com/jnchito/items/0faac073cb77417d61c7) ### メソッドの戻り値を返すときにreturnを使わない 他の言語からやってきた人はついつい`return`を使いたくなりますが、`return`を使わない書き方の方がRubyっぽいです。 ````ruby def build_message(user) message = 'hello' message += '!!' if user.admin? return message end ```` ````ruby def build_message(user) message = 'hello' message += '!!' if user.admin? message end ```` ### 「初期化、プロパティセット、戻り値として返す」の代わりにObject#tapを使う tapを使わなくても行数は同じですが、ローカル変数の宣言や値を返却するためだけに書く最後の行がいらなくなります。 ````ruby def build_user user = User.new user.email = "hoge@hoge.com" user.name = "Taro Yamada" user end ```` ````ruby def build_user User.new.tap do |user| user.email = "hoge@hoge.com" user.name = "Taro Yamada" end end ```` ### "+”ではなく"#{ }"で文字列を連結する ````ruby "Hello, " + user.name + "!" ```` ````ruby "Hello, #{user.name}!" ```` ### 複数行にわたる文字列はヒアドキュメントを使う ```rb text = "Hello, world!\nGood-bye, world!" ``` ```rb text = <<-TEXT Hello, world! Good-bye, world! TEXT ``` Rubyのヒアドキュメントは高機能なので、もっと詳しく知りたい方は公式ドキュメントやネット上の情報を参考にしてください。 [ヒアドキュメント (行指向文字列リテラル)](http://docs.ruby-lang.org/ja/2.3.0/doc/spec=2fliteral.html#here) ### 定数はfreezeさせる 文字列であれ、配列であれ、ハッシュであれ、定数宣言した値は`freeze`しておく方が無難です。万一変更されると困るので。 #### 文字列の場合 ````ruby CONTACT_PHONE_NUMBER = "03-1234-5678" CONTACT_PHONE_NUMBER << "@#$%^" puts CONTACT_PHONE_NUMBER # => 03-1234-5678@#$%^ ```` ````ruby CONTACT_PHONE_NUMBER = "03-1234-5678".freeze CONTACT_PHONE_NUMBER << "@#$%^" # => RuntimeError: can't modify frozen String ```` #### 配列の場合 ````ruby ADMIN_NAMES = ["Tom", "Alice"] ADMIN_NAMES << "Taro" ADMIN_NAMES[0].downcase! puts ADMIN_NAMES # => ["tom", "Alice"] ```` ````ruby ADMIN_NAMES = ["Tom", "Alice"].freeze.each(&:freeze) ADMIN_NAMES << "Taro" # => RuntimeError: can't modify frozen Array ADMIN_NAMES[0].downcase! # => RuntimeError: can't modify frozen String ```` #### 整数の場合 整数(`FixNum`)は変更不能なので`freeze`しなくても問題ありません。 ````ruby # エラーにはならないが、あまり意味が無い ITEM_LIMIT = 500.freeze ```` ### 配列やハッシュを初期化する際、最後の要素をあえてカンマ付きで書いておく 配列やハッシュを初期化する場合は、カンマで要素を区切ります。 ```rb countries = [ :japan, :italy, :uk ] capitals = { japan: 'Tokyo', italy: 'Rome', uk: 'London' } ``` Rubyでは次のように、最後の要素にカンマを付けても文法上エラーになりません。 ```rb countries = [ :japan, :italy, :uk, ] capitals = { japan: 'Tokyo', italy: 'Rome', uk: 'London', } ``` 将来的に要素が追加される可能性が高い配列やハッシュであれば、1つ前の行を修正せずに新しい要素を追加できるので、プログラム修正の手間が少し省けます。 ```rb countries = [ :japan, :italy, :uk, :india, # <= 1つ前の行を修正せずに追加 ] capitals = { japan: 'Tokyo', italy: 'Rome', uk: 'London', india: 'New Delhi', # <= 1つ前の行を修正せずに追加 } ``` また、すべての要素にカンマを付けておけば、要素の順番を入れ替えたいときも行単位の単純なカット&ペーストで修正できますね。 ### 配列を作るとき、[ ]の代わりに%w( )、%i( )を使う 文字列だけの配列を作りたい場合は`%w( )`を使うと少し短く書けます。 ````ruby actions = ['index', 'new', 'create'] ```` ````ruby actions = %w(index new create) # => ['index', 'new', 'create'] ```` Ruby 2.0なら`%i( )`でシンボルの配列も作れます。 ````ruby actions = %i(index new create) # => [:index, :new, :create] ```` ### 配列を順番に処理するとき、"object.method"の代わりに"&:method"を使う ````ruby names = users.map{|user| user.name } ```` ````ruby names = users.map(&:name) ```` `map`に限らず、`each`や`select`などブロックで配列の中身を受け取るようなメソッドは同じように`&:method`で処理できます。 ### nilか配列かを区別せず、Array( )で処理してしまう 基本的に配列だが、nilが渡される場合もある変数を処理する場合、`Array()`(`Kernel#Array`)を使うと条件分岐を無くせます。 ```rb # usersはnilが渡される場合もあるので分岐する if users users.each{|user| send_direct_mail(user) } end ``` ```rb # Array()を使うと、nilの場合は空の配列([])が、それ以外は元の配列が返されるので分岐が不要 Array(users).each{|user| send_direct_mail(user) } ``` ### 大きな数値を宣言する場合、"_"を入れて読みやすくする ````ruby ITEM_LIMIT = 1000000000 ```` ````ruby ITEM_LIMIT = 1_000_000_000 ```` ### 単純なgetterメソッドを定義する代わりに、attr_readerを使う ````ruby class Person def initialize @name = "No name" end def name @name end end ```` ````ruby class Person attr_reader :name def initialize @name = "No name" end # いらない # def name # @name # end end ```` ### 要素の順番に意味がある配列は、同時に別々の変数で受け取る `変数 = 配列`のように書くと変数には配列が格納されますが、`変数, 変数 = 配列`のように書くと配列の各要素を別々の変数に格納できます。 ````ruby ans_array = 14.divmod(3) puts "商は#{ans_array[0]}" # => 商は4 puts "あまりは#{ans_array[1]}" # => あまりは2 ```` ````ruby quotient, remainder = 14.divmod(3) puts "商は#{quotient}" # => 商は4 puts "あまりは#{remainder}" # => あまりは2 ```` ハッシュを`each`で回したときに、ブロックが受け取る引数も同じですね。 ````ruby # keyとvalueを配列として受け取る {name: 'Tom', email: 'hoge@hoge.com'}.each do |key_and_value| puts "key: #{key_and_value[0]}" puts "value: #{key_and_value[1]}" end ```` ````ruby # keyとvalueを別々の変数で受け取る {name: 'Tom', email: 'hoge@hoge.com'}.each do |key, value| puts "key: #{key}" puts "value: #{value}" end ```` ### 配列を連結するのに+ではなく、*(splat)を使う ````ruby numbers = [1, 2, 3] numbers_with_zero_and_100 = [0] + numbers + [100] # => [0, 1, 2, 3, 100] ```` ````ruby numbers = [1, 2, 3] numbers_with_zero_and_100 = [0, *numbers, 100] # => [0, 1, 2, 3, 100] ```` ちなみに`*`がないと、こうなります。(配列が展開されない) ````ruby [0, numbers, 100] # => [0, [1, 2, 3], 100] ```` ### nilだったら初期化、の代わりに ||= を使う いわゆる遅延初期化のイディオムですね。 ````ruby def twitter_client @twitter_client = Twitter::REST::Client.new if @twitter_client.nil? @twitter_client end ```` ````ruby def twitter_client @twitter_client ||= Twitter::REST::Client.new end ```` 初期化の処理が複数行にわたる場合は`begin/end`を使うことができます。([参考](http://blog.honeybadger.io/rubyist_guide_to_memoization/)) ````ruby def twitter_client return @twitter_client if @twitter_client @twitter_client = Twitter::REST::Client.new # 初期化に必要な処理 # ... @twitter_client end ```` ````ruby def twitter_client @twitter_client ||= begin client = Twitter::REST::Client.new # 初期化に必要な処理 # ... client end end ```` ### ハッシュのキーには文字列ではなくシンボルを使う ハッシュに値をセットする場合、キーには文字列よりもシンボルを使う方がベターです。 ```rb # キーに文字列を使う currencies = { 'japan' => 'yen', 'america' => 'dollar', 'italy' => 'euro' } currencies['japan'] # => 'yen' ``` ```rb # キーにシンボルを使う currencies = { japan: 'yen', america: 'dollar', italy: 'euro' } currencies[:japan] # => 'yen' ``` シンボルを使うと以下のようなメリットがあります。 - `{ key: value }` のように簡潔なリテラルで書ける。 - 文字列よりも速い。 - 文字列よりもメモリの使用効率が良い。 参考: [Why use symbols as hash keys in Ruby? - Stack Overflow](http://stackoverflow.com/questions/8189416/why-use-symbols-as-hash-keys-in-ruby) ### メソッド全体rescueの対象にするときはbegin/endを省く ````ruby def process_user(user) begin send_to_mail(user) rescue # 例外処理 end end ```` ````ruby def process_user(user) send_to_mail(user) rescue # 例外処理 end ```` ### Exceptionをrescueするのではなく、StandardErrorをrescueする JavaやC#をやっていた人は「すべての例外を捕捉したい = Exceptionを捕捉する」と考えがちです。 しかし、Rubyで`Exception`を捕捉すると、`NoMemoryError`等の致命的な例外も捕捉してしまいます。 実行時エラーを表すRubyの例外クラスは`Exception`のサブクラスである`StandardError`です。 rescueでデフォルトで捕捉するのは`StandardError`とそのサブクラスなので、すべての実行時エラーを捕捉したい場合は`rescue`節に具体的な例外クラス名を書く必要はありません。 ````ruby def process_user(user) send_to_mail(user) rescue Exception => ex # NoMemoryError等の致命的な例外まで捕捉してしまうので良くない end ```` ````ruby def process_user(user) send_to_mail(user) rescue => ex # すべての実行時エラー(= StandardErrorとそのサブクラス)が捕捉される end ```` - 参考: http://qiita.com/jnchito/items/a6046733dd5683ff35b7 ### 一度rescueした例外をもう一度再raiseする 「ある特定の例外クラス」だけでなく、「例外メッセージの中身」も確認して条件に合致すればrescue、そうでなければ対象外のエラーなのでそのままシステムエラーにしたい、というケースがたまにあります。 その場合はrescue節の中で`raise`を呼ぶと元のエラーを再raiseできます。 ````ruby def process_user(user) send_to_mail(user) rescue ArgumentError => ex if ex.message =~ /blah blah blah/ # ArgumentErrorかつ、メッセージも条件に合致すれば # 別の処理を実行してそのまま続行する send_to_admin(user, ex) else # メッセージが条件に合致しなかった場合は対処不能なエラーとして # 元のエラーを再度raiseする raise end end ```` ### private 以下の行にクラスメソッドを定義して、privateなクラスメソッドを作ったと勘違いしない 以下のようにprivateの下にインスタンスメソッドとクラスメソッドを定義したとします。 ```rb class User private def secret_name # 外部から呼ばれたくないインスタンスメソッド "secret!" end def self.secret_data # 外部から呼ばれたくないクラスメソッド!? "secret!!" end end ``` 上のコードを実行すると、`secret_name`は呼び出せませんが、`self.secret_data`呼び出すことが可能です。 ```rb user = User.new # 呼び出せない user.secret_name # => NoMethodError: private method `secret_name' called for #<User:0x007fa90c1e7cd0> # 呼び出せる User.secret_data # => "secret!!" ``` 以下のように`private_class_method`を付けると、外部から呼び出せなくなります。 ```rb class User private def secret_name # 外部から呼ばれたくないインスタンスメソッド "secret!" end def self.secret_data # 外部から呼ばれたくないクラスメソッド "secret!!" end # secret_dataをprivateなクラスメソッドにする private_class_method :secret_data end ``` ```rb User.secret_data # => NoMethodError: private method `secret_data' called for User:Class ``` ですが、privateなインスタンスメソッドを作るときに比べると手間がかかるので、「どうしても」という場合以外は「クラスメソッドはpublicのままにしておく」という選択肢もアリかもしれません。 (そもそもクラスメソッドを多用しすぎている場合は、クラス設計に何か問題がある可能性が高いです) 参考: [クラスメソッドのprivate化 - @tmtms のメモ](http://tmtms.hatenablog.com/entry/20100629/ruby_private_class_method) ### privateメソッドはそのクラスでしか呼び出せない、と勘違いしない 以下のようにprivateなインスタンスメソッド(`secret_price`)を定義したとします。 ```rb class Item private def secret_price 1000 end end ``` さらに、上の`Item`クラスを継承したクラスを定義します。この中で`secret_price`メソッドを呼び出すようにします。 ```rb class Book < Item def public_price secret_price end end ``` すると、`Book`クラスのインスタンスから問題なく(`public_price`メソッド経由で)`secret_price`メソッドを呼び出すことができます。 ```rb book = Book.new book.public_price # => 1000 ``` Javaや.NETの経験が長いと、privateメソッドはそのクラスでしか呼び出せないと思ってしまいがちですが、Rubyでは継承先でprivateメソッドが呼ばれる可能性があります。 そのクラスの中で呼び出されていないからといって安易にprivateメソッドを削除すると、継承先のクラスで呼び出されたときにエラーが発生するかもしれないので気をつけてください。 参考:[JavaやC#の常識が通用しないRubyのprivateメソッド - give IT a try](http://blog.jnito.com/entry/20120315/1331754912) ### size - 1ではなく、マイナスのインデックスで最後の文字や要素を指定する ````ruby numbers = [1, 2, 3, 4, 5] name = 'Taro Yamada' numbers[numbers.size - 1] # => 5 name[name.size - 1] # => 'a' numbers[1..numbers.size - 2] # => [2, 3, 4] name[1..name.size - 2] # => "aro Yamad" ```` ````ruby numbers = [1, 2, 3, 4, 5] name = 'Taro Yamada' numbers[-1] # => 5 name[-1] # => 'a' numbers[1..-2] # => [2, 3, 4] name[1..-2] # => "aro Yamad" ```` ### 配列の便利なメソッドいろいろ #### find: 最初に見つかったものを返す ````ruby def find_admin(users) users.each do |user| return user if user.admin? end nil end ```` ````ruby def find_admin(users) users.find(&:admin?) end ```` 最初の見つかった要素のインデックスを返す場合は`find_index`。 #### select: 条件に合うものすべてを返す ````ruby def find_admins(users) admins = [] users.each do |user| admins << user if user.admin? end admins end ```` ````ruby def find_admins(users) users.select(&:admin?) end ```` `select`とは反対で`false`になる要素だけを集める場合は`reject`。 #### count: 条件に合う要素の数を返す ````ruby def count_admin(users) count = 0 users.each do |user| count += 1 if user.admin? end count end ```` ````ruby def count_admin(users) users.count(&:admin?) end ```` #### map: ある配列から別の配列を作る ````ruby def user_names(users) names = [] users.each do |user| names << user.name end names end ```` ````ruby def user_names(users) users.map(&:name) end ```` #### flat_map: mapの結果をネストしないフラットな配列として受け取る ```ruby nested_array = [[1, 2, 3], [4, 5, 6]] mapped_array = nested_array.map {|array| array.map {|n| n * 10 } } # => [[10, 20, 30], [40, 50, 60]] flat_array = mapped_array.flatten # => [10, 20, 30, 40, 50, 60] ``` ```ruby nested_array = [[1, 2, 3], [4, 5, 6]] flat_array = nested_array.flat_map {|array| array.map {|n| n * 10 } } # => [10, 20, 30, 40, 50, 60] ``` #### compact: nil以外の要素を集める ````ruby numbmers_and_nil = [1, 2, 3, nil, nil, 6] only_numbers = numbmers_and_nil.reject(&:nil?) # => [1, 2, 3, 6] ```` ````ruby numbers_and_nil = [1, 2, 3, nil, nil, 6] only_numbers = numbers_and_nil.compact # => [1, 2, 3, 6] ```` #### any?: 最低でも1つ条件に合う要素があればtrueを返す ````ruby def contains_nil?(users) users.each do |user| return true if user.nil? end false end ```` ````ruby def contains_nil?(users) users.any?(&:nil?) end ```` すべての要素が条件に合っている場合に`true`を返す場合は`all?`。 #### empty?: 1件もなければtrueを返す ````ruby puts "empty!" if users.size == 0 ```` ````ruby puts "empty!" if users.empty? ```` #### first/last: 最初と最後の要素を返す ````ruby first_user = users[0] last_user = users[users.size - 1] ```` ````ruby first_user = users.first last_user = users.last ```` #### sample: 任意の要素を返す ````ruby users[rand(users.size)] ```` ````ruby users.sample ```` #### each_with_index: eachでループしつつ、カウンタも同時に取得する ````ruby counter = 0 users.each do |user| puts ", " if counter > 0 puts user.name counter += 1 end ```` ````ruby users.each_with_index |user, counter| puts ", " if counter > 0 puts user.name end ```` #### ループ処理系のメソッド + with_index: カウンタ付きで元のループ処理を実行する ````ruby counter = 1 users_with_index = users.map do |user| [counter, user] counter += 1 end ```` ````ruby users_with_index = users.map.with_index do |user, counter| [counter + 1, user] end ```` `with_index`はカウンタの初期値を指定できます。(デフォルトはゼロ) なので、上のコードは次のように書いても同じです。 ````ruby users_with_index = users.map.with_index(1) do |user, counter| [counter, user] end ```` #### join: 配列を1つの文字列として返す ````ruby def numbers_text(numbers) text = '' numbers.each_with_index do |number, i| text += ', ' if i > 0 text += number.to_s end text end ```` ````ruby def numbers_text(numbers) numbers.join(', ') # [1, 2, 3] => "1, 2, 3" end ```` #### max/max_by: 最大の要素を返す ````ruby def oldest_user(users) oldest = nil users.each do |user| oldest = user if oldest.nil? || user.age > oldest.age end oldet end ```` ````ruby def oldest_user(users) users.max_by(&:age) end ```` 単純な数値や文字列の配列なら`numbers.max`だけでもOK。 最小の要素を返す場合は`min`や`min_by`を使う。 #### each_with_object: ループを回しつつ、別のオブジェクトを組み立ててそれを返す ````ruby def admin_names(users) ret = [] users.each do |user| ret << user.name if user.admin? end ret end ```` ````ruby def admin_names(users) users.each_with_object([]) do |user, names| names << user.name if user.admin? end end ```` まあ、上のサンプルのような場合は`users.select(&:admin?).map(&:name)`って書けばいいんですけどね。 #### その他の情報源 全部挙げていくとキリがないので、配列を操作するロジックを書く前にまず、`Array`や`Enumerable`のAPIドキュメントを読んで「車輪の再発明」をしていないかチェックしてください。 - http://ruby-doc.org/core-2.0.0/Array.html - http://ruby-doc.org/core-2.0.0/Enumerable.html ### 英語の文法や品詞を意識する ケースバイケースで原則から外れる場合は十分ありえますが、文法や品詞の使いわけに明らかな逸脱(間違い)があると読み手が混乱します。 #### 配列の変数名や配列を返すメソッド名は原則複数形にする ````ruby # number = [1, 2, 3] numbers = [1, 2, 3] ```` ````ruby # def find_even_number(numbers) # numbers.select(&:even?) # end def find_even_numbers(numbers) numbers.select(&:even?) end ```` #### プロパティや変数名、クラス名は原則名詞や形容詞に、何かを操作するメソッドは原則動詞にする ````ruby # reserve=予約する(動詞)、reserved=予約済みである(形容詞) # chair.reserve? chair.reserved? # => false # chair.reserved('Tom') chair.reserve('Tom') chair.reserved? # => true ```` その他、英語の使い方については以下の記事も参考になると思うので、あわせて読んでみてください。 [モデルやメソッドに名前を付けるときは英語の品詞に気をつけよう](http://qiita.com/jnchito/items/459d58ba652bf4763820) ## Rails編 以下はRails開発時にのみ使えるイディオムやメソッドです。 標準のRubyでは用意されていないので使えません。 ただし、大半のメソッドはActiveSupportのGemを導入することで使えるようになります。 ```` $ gem install active_support ```` ````ruby require 'active_support/all' nil.blank? # => true ```` ### nilチェックの代わりにObject#try(:method_name)を使う ````ruby if parent.children && parent.children.singleton? singleton = parent.children.first send_mail_to(singleton) end ```` ````ruby # childrenがnilならtry(:singleton?)はnilを返す # nilでなければ、children.singleton?が普通に呼ばれる if parent.children.try(:singleton?) singleton = parent.children.first send_mail_to(singleton) end ```` ### 「nil、もしくは空っぽい値」のチェックにblank?/present?を使う ````ruby # String name = nil name.blank? # => true name = "" name.blank? # => true name = " " name.blank? # => true name = "Tom" name.blank? # => false # Array numbers = nil numbers.blank? # => true numbers = [] numbers.blank? # => true numbers = [1, 2, 3] numbers.blank? # => false # Hash params = nil params.blank? # => true params = {} params.blank? # => true params = { name: "Tom", email: "hoge@hoge.com" } params.blank? # => false ```` `present?`は`blank?`の反対で、「空ではない値」のときに`true`を返します。 ````ruby # String name = "" name.present? # => false name = "Tom" name.present? # => true ```` ### 「空なら別の値を代入」の代わりにpresenceを使う ````ruby if user.name.blank? name = "What's your name?" else name = user.name end ```` ````ruby name = user.name.presence || "What's your name?" ```` `"".presence`や`[].presence`は`nil`を返すので注意してください。(`blank?`かどうかを判別しているため) ````ruby name = "" puts name.presence || "What's your name?" # => What's your name? ```` **2014.11.12 追記** `presence` を使うと便利なイディオムがありました。 ````ruby # Newsが1件でも存在すればメール送信&ツイート発信 good_news = company.good_news if good_news.count > 0 send_mail(good_news) tweet(good_news) end ```` 上のようなコードはpresenceを使うと一行で代入と条件判断ができます。 ````ruby if good_news = company.good_news.presence send_mail(good_news) tweet(good_news) end ```` `company.good_news.presence` は `company.good_news` が0件だと `nil` が返るので `false` 扱いになり、if文の中が実行されません。 同様に、「文字列に何かしらの値が入っている場合」を分岐させるケースでも役立ちます。 ```ruby # nameがnilや空文字列("")だったらメッセージを表示したくない name = blog.user.name if name.present? show_message("Hello, #{name}!") end ``` ```ruby if name = blog.user.name.presence show_message("Hello, #{name}!") end ``` ### 存在の有無を確認する場合はblank?/present?を積極的に使う Rubyの標準APIでは`nil?`や`empty?`のように「無い」を表すメソッドしかないので、「もしあるなら」をコードで表現するとぎこちなくなります。 ````ruby if user # userがいれば、何かを実行する # =>「もしuserがいれば」ではなく「もしuserなら」と読めてしまう end unless users.empty? # usersが空ではないなら、何かを実行する # => empty?の逆がないので、否定形で条件を書かざるを得ない end ```` Railsであれば、`present?`を使って「もしあれば」を明示的に書くことができます。 ````ruby if user.present? # userがいれば、何かを実行する # =>「もしuserがいれば」と明示的に読める end if users.present? # usersに1つ以上の要素があれば、何かを実行する # => 肯定形で条件が書ける end ```` ### 文字列の存在チェックはnil?ではなく、blank?を積極的に使う 「文字列に値が入っていない」状態は`nil`と`""`の区別をしないことが多いと思います。 (`nil`は空白だが`""`は空白ではない、とわざわざ区別することはほとんどないはず) `nil?`を使うと`""`は入力済みであるというような条件文を書いてしまう恐れがあります。 なのでRailsでは`nil?`の代わりに`blank?`を使う癖を付けておく方が良いです。 ````ruby if email.nil? # => emailが "" なら入力済みとして扱われてしまい、コールされない puts "Please input email!" end ```` ````ruby if email.blank? # => emailが "" や " " の場合でも未入力と扱われるので、コールされる puts "Please input email!" end ```` 同じ理由でModelの`validates`で指定するオプションも、特別な理由が無い限り`allow_nil: true`ではなく、`allow_blank: true`を使うようにしましょう。 - 参考: http://edgeguides.rubyonrails.org/active_record_validations.html#common-validation-options ### ロジックではなく、クエリでフィルタリングする Ruby編で配列の便利なメソッドをいろいろ紹介しましたが、RailsのModelをフィルタリングしたい場合は配列を操作するのではなく、データベース(SQL)上でフィルタリングした方が効率的です。 ````ruby def admin_users User.all.select(&:admin?) end ```` ````ruby def admin_users User.where(admin: true) end ```` ### mapではなく、pluckを使う `pluck`を使うと必要なカラムだけをデータベースから取得するので処理効率が良くなります。 ````ruby def admin_user_ids User.where(admin: true).map(&:id) end ```` ````ruby def admin_user_ids User.where(admin: true).pluck(:id) end ```` ### tap の代わりに new + ブロックを使う ActiveRecordであれば `new` に直接ブロックを渡して、プロパティをセットすることができます。 ( @sachin21 さん、コメントありがとうございます! ) ```rb def build_user User.new.tap do |user| user.email = "hoge@hoge.com" user.name = "Taro Yamada" end end ``` ```rb def build_user User.new do |user| user.email = "hoge@hoge.com" user.name = "Taro Yamada" end end ``` ### Railsにおけるタイムゾーンの扱いを理解する Railsの場合、application.rbに設定したタイムゾーンが使われる場合と、環境変数 TZ に設定されたタイムゾーンが使われる場合の2パターンがあります。 両者のタイムゾーン設定が異なる場合、予期せぬ不具合が生まれる恐れがあります。 そうした不具合を防ぐため、コード内ではapplication.rbに設定されたタイムゾーンを使うように統一することが望ましいです。 具体的には、`Date.today`ではなく`Date.current`を、`Time.now`ではなく`Time.current`(または`Time.zone.now`)を使うようにしてください。 詳しい内容はこちらの記事にまとめてあるので読んでみてください。 [RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い](http://qiita.com/jnchito/items/cae89ee43c30f5d6fa2c) ### 日付や日時の便利メソッドを活用する #### システム日付から見た昨日/明日を求める ````ruby Date.current # => Tue, 05 Nov 2013 Date.yesterday # => Tue, 04 Nov 2013 Date.tomorrow # => # => Tue, 06 Nov 2013 ```` #### システム日時から見たxx年前/後、xxヶ月前/後、xx週間前/後、xx日前/後、etcを求める ````ruby Date.current # => 2013-11-05 2.years.ago # => 2011-11-05 06:21:40 +0900 2.years.since # => 2015-11-05 06:21:40 +0900 2.months.ago # => 2013-09-05 06:21:40 +0900 2.months.since # => 2014-01-05 06:21:40 +0900 ```` weeks, days, hours, minutes, secondsでも同じように使えます。 #### 特定の日付/日時から見た昨日/明日、先週/来週、xx日前/後、etcを求める 結果を求める方法は一つだけでなく、いろいろな書き方があります。 ````ruby date = Date.current # => 2013-11-05 date.yesterday # => 2013-11-04 date.tomoroow # => 2013-11-06 date.prev_day # => 2013-11-04 date.next_day # => 2013-11-06 date.prev_day(2) # => 2013-11-03 date.next_day(2) # => 2013-11-07 date - 2.days # => 2013-11-03 date + 2.days # => 2013-11-07 date.ago(2.days) # => 2013-11-03 date.since(2.days) # => 2013-11-07 date.prev_month # => 2013-10-05 date.next_month # => 2013-12-05 date.prev_month(2) # => 2013-09-05 date.next_month(2) # => 2014-01-05 date - 2.months # => 2013-09-05 date + 2.months # => 2014-01-05 date.months_ago(2) # => 2013-09-05 date.months_since(2) # => 2014-01-05 date.ago(2.months) # => 2013-09-05 date.since(2.months) # => 2014-01-05 ```` week, year等でも考え方は同じです。 Time型でも使えます。 #### ある日付/日時から見た始まりと終わりの日付/日時を求める ````ruby date = Date.current # => 2013-11-05 date.beginning_of_month # => 2013-11-01 date.end_of_month # => 2013-11-30 date.beginning_of_day # => 2013-11-05 00:00:00 +0900 date.end_of_day # => 2013-11-05 23:59:59 +0900 datetime = Time.current # => 2013-11-05T06:43:53+09:00 datetime.beginning_of_hour # => 2013-11-05T06:00:00+09:00 datetime.end_of_hour # => 2013-11-05T06:59:59+09:00 ```` week, year等でも考え方は同じです。 Time型でも使えます。 #### 1日の初めから終わりまで、月の初めから終わりまで、といった範囲を求める `all_xxx` メソッドを使うと、「xxxの始めから終わりまで」をRangeオブジェクトとして取得できます。 ```rb time = Time.current # => Mon, 23 Nov 2015 16:45:23 JST +09:00 # その日の始まりと終わりの日時を取得 time.all_day # => Mon, 23 Nov 2015 00:00:00 JST +09:00..Mon, 23 Nov 2015 23:59:59 JST +09:00 # その日から見た月の始まりと、月の終わりの日時を取得する time.all_month # => Sun, 01 Nov 2015 00:00:00 JST +09:00..Mon, 30 Nov 2015 23:59:59 JST +09:00 # その日から見た年の始まりと、年の終わりの日時を取得する time.all_year # => Thu, 01 Jan 2015 00:00:00 JST +09:00..Thu, 31 Dec 2015 23:59:59 JST +09:00 ``` 他にも`all_week`や`all_quarter `といったメソッドが定義されています。 #### ある日付から見た先週/来週のxx曜日を求める ````ruby date = Date.current # => 2013-11-05 date.tuesday? # => true date.prev_week(:monday) # => 2013-10-28 date.next_week(:monday) # => 2013-11-11 ```` 他の曜日でも考え方は同じです。 #### その他の情報源 日付、日時の操作例を挙げ始めるとキリがないので、詳しくはActiveSupportのAPIを参照してください。 - http://guides.rubyonrails.org/active_support_core_extensions.html ### 語形やフォーマットを変える (2013.11.14追記) ````ruby # キャメルケースにする "my_book".camelize # => "MyBook" # アンダースコア区切り(スネークケース)にする "MyBook".underscore # => "my_book" # ダッシュ(ハイフン)区切りにする "my_book".dasherize # => "my-book" # 複数形にする "book".pluralize # => "books" "person".pluralize # => "people" "fish".pluralize # => "fish" "book_and_person".pluralize # => "book_and_people" "book and person".pluralize # => "book and people" "BookAndPerson".pluralize # => "BookAndPeople" # 単数形にする "books".singularize # => "book" "people".singularize # => "person" "books_and_people".singularize # => "books_and_person" "books and people".singularize # => "books and person" "BooksAndPeople".singularize # => "BooksAndPerson" # 人間が読みやすくする(一文字目は大文字 + スペース区切り) "my_books".humanize # => "My books" # タイトル形式にする(各単語の一文字目が大文字 + スペース区切り) "my_books".titleize # => "My Books" # クラス名にする(キャメルケース + 単数形) "my_book".classify # => "MyBook" "my_books".classify # => "MyBook" # テーブル名にする(アンダースコア区切り + 複数形) "my_book".tableize # => "my_books" "MyBook".tableize # => "my_books" ```` #### その他の情報源 `constantize`や`demodulize`など、個人的に使用頻度が低そうだなと思ったメソッドは紹介しませんでした。 他のメソッドも気になる方はRailsのAPIドキュメントを参照して下さい。 - http://apidock.com/rails/String ### 余分なスペースや改行を取り除く (2013.11.14追記) ````ruby " My \r\n \t \n books ".squish # => "My books" ```` ## まとめ: 良いコードを書くために ### 魚ではなく、「魚の釣り方」を覚える 「遠回り」や「車輪の再発明」をする前に、もっと良い書き方はないか、すでに同じメソッドが用意されていないか、書籍やAPIドキュメントをしっかり読みましょう。 - [プログラミング言語 Ruby](http://goo.gl/ItJeDc) - オライリージャパン - [Rails3レシピブック 190の技](http://goo.gl/iu1Tc4) - ソフトバンククリエイティブ - [ruby-doc.org](http://ruby-doc.org) - [String](http://ruby-doc.org/core-2.0.0/String.html) - [Array](http://ruby-doc.org/core-2.0.0/Array.html) - [Hash](http://ruby-doc.org/core-2.0.0/Hash.html) - [Enumerable](http://ruby-doc.org/core-2.0.0/Enumerable.html) - [RailsGuides](http://guides.rubyonrails.org/) - [Active Support Core Extensions](http://guides.rubyonrails.org/active_support_core_extensions.html) - [Active Record Query Interface](http://guides.rubyonrails.org/active_record_querying.html) ### チーム内でコードレビューをする 書籍やAPIドキュメントだけでなく、同僚の知見も有効な情報源です。 チーム内で定期的にコードレビューを行い、お互いの知見を交換しあいましょう。 また、コードの短さや簡潔さに凝りすぎると、独りよがりでわかりにくいコードを書いてしまう恐れもあるため、コードレビューを通じてチーム内で「わかりやすいコード」「わかりにくいコード」の判断基準を共有しておくことも重要です。 ## おまけ: トリビア的なテクニック こういう書き方もあるけど無理して使わなくても良いかも、と思うような記法も紹介しておきます。 ### 「1文字」を表すのに「?+文字」を使う ````ruby "index,new,create".split(',') # => ["index", "new", "create"] ```` ````ruby "index,new,create".split(?,) # => ["index", "new", "create"] ```` タイプ量は減りますが、直感的なわかりやすさに関しては微妙かも・・・。 ### 配列を順番に処理するとき、直接メソッドを呼ぶ代わりに"&method(:name)"を使う 普通にブロックを書く方が一般的ですが、`&method(:name)`みたいな引数を渡すこともできます。 ````ruby def process_users users.each do |user| process_user(user) end end def process_user(user) send_mail_to(user) user.mail_sent_at = Time.now user.save end ```` ````ruby def process_users users.each(&method(:process_user)) end def process_user(user) send_mail_to(user) user.mail_sent_at = Time.now user.save end ```` ### 配列を順番に処理するとき、ブロックを直接書く代わりにlambdaを使う 複雑な条件式とかをlambdaにして明示的な名前を付けておくと多少読みやすくなるかも、です。 ````ruby def destroy_aged_admins(users, limit_age) users.select{|user| user.admin? && user.age > limit_age }.each(&:destroy) end ```` ````ruby def destroy_aged_admins(users, limit_age) aged_admin = ->(user){ user.admin? && user.age > limit_age } users.select(&aged_admin).each(&:destroy) end ```` でも上のサンプルだとこう書いた方がわかりやすいかな。。。 ````ruby def destroy_aged_admins(users, limit_age) aged_admins = users.select{|user| user.admin? && user.age > limit_age } aged_admins.each(&:destroy) end ```` ### 何が何でもtrueかfalseで条件分岐させたいとき、!!を使う Rubyでは`false`と`nil`が`false`、それ以外の値がすべて`true`と評価されます。 JavaやC#を長くやっていたので最初は僕も違和感がありましたが、使っているうちにこれはこれで良くできてるなと感じてきました。 しかし、世の中には「わしゃtrueかfalseで条件分岐させなきゃイヤなんぢゃ!!」というプログラマもいるかもしれません。 そんな人は「イヤなんぢゃ!!」の`!!`を値の前に付ければ、Rubyが必ず`true`か`false`を返してくれます。 ````ruby !!nil # => false !!false # => false !!0 # => true !![] # => true !!true # => true ```` 実は`BasicObject`クラスには`!`というメソッドが用意されているので、こんな書き方もできます。 ```rb nil.!.! # => false false.!.! # => false 0.!.! # => true [].!.! # => true true.!.! # => true ``` このメソッドを使えば、`nil?`や`empty?`の逆も作れます。 ```rb puts "User exists!" if user.nil?.! puts "Some users exist!" if users.empty?.! ``` ・・・が、何もそこまでがんばる必要はないんじゃないかと僕は思います ^^; |
|
| 5位 |
|
|||
|
16:18:11 |
|
|
#課題
サイトをを立ち上げるときに当然のごとくSSL証明書をベンダーから購入して設置していたが、いざセキュリティ診断等でチェックしてもらうとSSLについての指摘を何件か受けてみた。なんでだろうと思いながらも、さらに最適なSSL設定は?と聞かれてそういえばあまり昔から手を入れたことなかったなと思い調べてみた #SSL通信が確立するまでの概要フロー SSL通信について再度おさらい  #Nginxを元にしたSSLの設定 nginxの[HTTPS サーバの設定](http://nginx.org/ja/docs/http/configuring_https_servers.html)を参考に、たった2行だけどSSLを考えてみる。書き方は違えどもapacheも概念は一緒のはず。 ```Nginx ssl_protocols SSLv3 TLSv1; ssl_ciphers HIGH:!ADH:!MD5; ``` #SSLプロトコルの種類を確認 [wiki](http://ja.wikipedia.org/wiki/Transport_Layer_Security)よりプロトコルの種類を確認してみる。 |略称|正式名称|開発元| |---|---|---| |SSL|Secure Socket Layer|ネットスケープコミュニケーション| |TLS|Transport Layer Security|IETF| バージョン毎の概要を確認してみる。 |バージョン|安全性|概要| |---|:---:|---| |SSL1.0|✕|最初のSSLとして設計したが、設計レビューの時点でプロトコルの脆弱性が発見されたため破棄されている。| |[SSL2.0](https://tools.ietf.org/html/rfc6176)|✕|SSL1.0の問題を修正して設計後、1994年にSSL2.0として発表。その後、いくつか脆弱性が発見されてSSL3.0が登場するが、未使用でもSSL2.0が有効な状態の場合に提示する最弱のアルゴリズムを使用させるダウングレード攻撃などを受ける可能性があるので、明示的に無効にする必要がある。| |[SSL3.0](https://tools.ietf.org/html/rfc7568)|✕|SSL2.0の問題を修正して機能追加も行い1995年にSSL3.0として発表。ただ、古くなってきている。CVE-2014-3566で脆弱性が発生したため明示的に無効にする必要がある。| |[TLS1.0](https://tools.ietf.org/html/rfc2246)|△|SSL3.0とTLS1.0の両者間には正確な互換性はないがほぼ同じ。CVE-2011-3389(BEAST)による一部脆弱性を含む。| |[TLS1.1](https://tools.ietf.org/html/rfc4346)|◯|TLS 1.0からの変更点は、新しく発見された攻撃手法に対する耐性の強化が中心である。| |[TLS1.2](https://tools.ietf.org/html/rfc5246)|◎|ハッシュのアルゴリズムにSHA-256が追加されたほか、ブロック暗号について、従来のCBCモードだけではなく、GCM、CCMといった認証付き暗号が利用可能となった。| |[TLS1.3](https://tlswg.github.io/tls13-spec/)|?|[ドラフト]インターネット環境の変化とTLS1.2までの暗号化強度不足を改善するため2016年中に仕様化完了を目指している(らしい)| そのため、SSLv1 / SSLv2 / SSLv3 は脆弱性があるので、 **現時点で使用できるものはTLSv1以上** #SSL_Ciphersについて調べてみる... `HIGH:!ADH:!MD5;`と書いてはあるものの、「HIGHグループでADHとMD5を使わない」という読み方であってるのか?調べてみると[OpenSSL](https://www.openssl.org/docs/apps/ciphers.html)で確認出来るらしい `$ openssl ciphers -v 'HIGH:!ADH:!MD5;'` ```bash:result DHE-RSA-AES256-SHA SSLv3 Kx=DH Au=RSA Enc=AES(256) Mac=SHA1 DHE-DSS-AES256-SHA SSLv3 Kx=DH Au=DSS Enc=AES(256) Mac=SHA1 DHE-RSA-CAMELLIA256-SHA SSLv3 Kx=DH Au=RSA Enc=Camellia(256) Mac=SHA1 DHE-DSS-CAMELLIA256-SHA SSLv3 Kx=DH Au=DSS Enc=Camellia(256) Mac=SHA1 AES256-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(256) Mac=SHA1 CAMELLIA256-SHA SSLv3 Kx=RSA Au=RSA Enc=Camellia(256) Mac=SHA1 PSK-AES256-CBC-SHA SSLv3 Kx=PSK Au=PSK Enc=AES(256) Mac=SHA1 EDH-RSA-DES-CBC3-SHA SSLv3 Kx=DH Au=RSA Enc=3DES(168) Mac=SHA1 EDH-DSS-DES-CBC3-SHA SSLv3 Kx=DH Au=DSS Enc=3DES(168) Mac=SHA1 DES-CBC3-SHA SSLv3 Kx=RSA Au=RSA Enc=3DES(168) Mac=SHA1 PSK-3DES-EDE-CBC-SHA SSLv3 Kx=PSK Au=PSK Enc=3DES(168) Mac=SHA1 KRB5-DES-CBC3-SHA SSLv3 Kx=KRB5 Au=KRB5 Enc=3DES(168) Mac=SHA1 DHE-RSA-AES128-SHA SSLv3 Kx=DH Au=RSA Enc=AES(128) Mac=SHA1 DHE-DSS-AES128-SHA SSLv3 Kx=DH Au=DSS Enc=AES(128) Mac=SHA1 DHE-RSA-CAMELLIA128-SHA SSLv3 Kx=DH Au=RSA Enc=Camellia(128) Mac=SHA1 DHE-DSS-CAMELLIA128-SHA SSLv3 Kx=DH Au=DSS Enc=Camellia(128) Mac=SHA1 AES128-SHA SSLv3 Kx=RSA Au=RSA Enc=AES(128) Mac=SHA1 CAMELLIA128-SHA SSLv3 Kx=RSA Au=RSA Enc=Camellia(128) Mac=SHA1 PSK-AES128-CBC-SHA SSLv3 Kx=PSK Au=PSK Enc=AES(128) Mac=SHA1 ``` なんかいっぱい出てきた #暗号方法の内容を1つ1つ確認 まずは1行目の `DHE-RSA-AES256-SHA SSLv3 Kx=DH Au=RSA Enc=AES(256) Mac=SHA1` を読み解いてみる。[このサイト](http://nzbget.net/Choosing_a_cipher)によると下記のようだ。 |Column|内容|原文| |---|---|---| |DHE-RSA-AES256-SHA|暗号化スイート|cipher suite name| |kx|鍵交換をする時のアルゴリズム|the algorithm used for key exchange| |Au|鍵認証のためのアルゴリズム|the algorithm for authentication| |Enc|暗号化通信に使われる一括暗号化アルゴリズム|the bulk encryption algorithm| |Mac|メッセージ認証符号|the message authentication code (MAC) algorithm| いろいろアルゴリズムを使ってるのはわかったもののどこで使ってるんだ? #それぞれどこで使ってるの?  #主な暗号化アルゴリズム 種類がたくさんありますが、一部集めてみました。 |アルゴリズム|用途|ブロック長|鍵長|概要| |---|---|---|---|---| |[RSA](http://ja.wikipedia.org/wiki/RSA%E6%9A%97%E5%8F%B7)|Au:公開鍵暗号[署名]||1024-4096 bit|[主流] 桁数が大きい合成数の素因数分解問題が困難であることを安全性の根拠とした公開鍵暗号の一つである| |[DSA](http://ja.wikipedia.org/wiki/Digital_Signature_Algorithm)|Au:公開鍵暗号[署名]||1024bit|[まだ少ない?] OpensslでみるとDSSと表記されている。有限体上の離散対数問題の困難性を安全性の根拠としており、現仕様においては有効な攻撃方法が示されたことはないものの、理論上はいまだ署名としての最も高い安全性を満たすことが証明されていない。| |[ECC](http://ja.wikipedia.org/wiki/%E6%A5%95%E5%86%86%E6%9B%B2%E7%B7%9A%E6%9A%97%E5%8F%B7)|Au:公開鍵暗号[署名]|||[注目株] EC-DLPを解く準指数関数時間アルゴリズムがまだ見つかっていないため、それが見つかるまでの間は、[ベリサイン](http://www.atmarkit.co.jp/ait/articles/1303/21/news005.html)によると12分の1の鍵長で、現在主流のRSA 2048の1.5倍の強度を実現できるため同レベルの安全性をより短い鍵で実現でき、処理速度も速いことをメリットとして、ポストRSA暗号として注目されている。| |[DH](https://www.ipa.go.jp/security/rfc/RFC2631JA.html)|Kx:公開鍵暗号[鍵交換]|||[主流] DHパラメータは証明書に書かれている物を使う。よって、static DHとも呼ばれる。| |[ECDH](http://en.wikipedia.org/wiki/Diffie-Hellman_key_exchange)|Kx:公開鍵暗号[鍵交換]|||[注目株] Elliptic curve Diffie–Hellmanの略。楕円曲線上でDiffie-Hellman(DH)鍵共有を⾏う楕円DH(ECDH)⽅式。PFS(perfect forward secrecy)をサポートしていない。 | |[ECDSA](http://en.wikipedia.org/wiki/Elliptic_Curve_DSA)|Kx:公開鍵暗号[鍵交換]|||[注目株] Elliptic Curve Digital Signature Algorithmの略。楕円曲線上でDigital Signature Algorithm(DSA)を実現する楕円DSA(ECDSA)⽅式。PFS(perfect forward secrecy)をサポートしていない。| |[ECDHE](http://vincent.bernat.im/en/blog/2011-ssl-perfect-forward-secrecy.html)|Kx:公開鍵暗号[鍵交換]|||[注目株] Ephemeral Elliptic curve Diffie–Hellmanの略。DHパラメータを通信時に動的に作成する。楕円曲線上でDiffie-Hellman(DH)鍵共有を⾏う楕円DH(ECDH)⽅式。EはEphemeral(一時的)を意味している。PFS(perfect forward secrecy)をサポートしている。 | |[DES](http://en.wikipedia.org/wiki/Data_Encryption_Standard)|Enc:共通鍵暗号|64bit|56bit|[強度弱] 現代においては十分な強度とは言えなくなっています。並列処理可能なDES破り専用プロセッサも存在し、 資金さえ投入すれば数時間~数日というレベルで解読することができます。| |[AES](http://en.wikipedia.org/wiki/Advanced_Encryption_Standard)|Enc:共通鍵暗号|128bit|128/192/256bit|[注目株] DESに代わる次世代の暗号標準。暗号強度、高速性に優れてます。| |[RC4](http://en.wikipedia.org/wiki/RC4)|Enc:共通鍵暗号|可変長|ストリーム暗号|[脆弱性あり] RC4はストリーム暗号としては大変広く使われていますが、同時にいろんな脆弱性があることでも知られてきている。WEPやSSL/TLSで広く使われてますが、WEPが脆弱であることが広く知られているのに比べ、SSL/TLSでのRC4はそれほど恐れられていませんでした。| |[MD5](http://en.wikipedia.org/wiki/MD5)|Mac:ハッシュ関数|||[脆弱性あり] 任意長メッセージから128ビットの一方向ハッシュ値を生成します。SLOTH攻撃(CVE-2015-7575)による影響があります。| |[SHA-1](http://ja.wikipedia.org/wiki/SHA-1)|Mac:ハッシュ関数|||[脆弱性あり] 160ビットメッセージダイジェストアルゴリズム。SLOTH攻撃(CVE-2015-7575)による影響があります。| |[SHA-2](https://jp.globalsign.com/sha256/)|Mac:ハッシュ関数|||[主流] 224ビット・256ビット・384ビット・512ビットのメッセージダイジェストアルゴリズム| |[SHA-3](https://ja.wikipedia.org/wiki/SHA-3)|Mac:ハッシュ関数|||[次世代?]米国の国立標準技術研究所(NIST)は,2012年10月2日に次世代の暗号学的ハッシュ関数の標準を決めるSHA-3として Keccak を選定した。しかし、その後、SHA-2への有望な攻撃法は2015年8月現在発表されていないため結果としてSHA-2の代替の用意が重要ではなくなるなど、状況が変化している| #暗号化利用モード [暗号化利用モード](http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation)とは、ブロック長よりも長いメッセージを暗号化するための方式。 |略称|名称|利用モード|概要| |---|---|---|---| |ECB|Electronic CodeBook|秘匿用|[強度弱]各ブロックを1つずつ暗号化処理をする。最もシンプルな方式 |[CBC](http://j-net21.smrj.go.jp/develop/digital/entry/001-20120229-01.html)|Cipher Block Chaining|秘匿用|[脆弱性あり]各平文のブロックは暗号化される前に、前の暗号化ブロックでXORすることを繰り返す。最初のブロックは暗号と無関係なIV(Initialization Vector)を使用して暗号化する。BEAST攻撃によりデータが復号化される可能性あり。 |CTR|CounTeR|秘匿用|[主流]IVではなくカウンターの値をインクリメントしつつブロック暗号処理したものに平文ブロックをXORする。| |[CCM](http://en.wikipedia.org/wiki/CCM_mode)|Counter with CBC-MAC|認証用|TLS1.2で使用可能。カウンター (CTR)モードとCBC-MACモードを組み合わせたものであり、前者が暗号化に、後者が認証に用いられる。同一の鍵を暗号化と認証の双方に用いることができることができ、暗号化に用いられたカウンター値が認証で用いられる初期化ベクトルと衝突することがない| |[GCM](http://en.wikipedia.org/wiki/Galois/Counter_Mode)|Galois/Counter Mode|認証用|[主流]TLS1.2で使用可能。GCMは認証付き暗号の一つであり、データ保護と認証(完全性確認)の双方に利用できる。遅延、オーバーヘッドが少ないことから、パケット化されたデータの保護に適している。| #PFS(Perfect Forward Secrecy)について [SSL/TLS & Perfect Forward Secrecy](http://vincent.bernat.im/en/blog/2011-ssl-perfect-forward-secrecy.html) |名称|略称|概要|Kx|例| |---|---|---|---|---| |Without forward secrecy|-|認証と同じ公開暗号を使用した場合に、攻撃者が暗号化した通信を全部記録していたとすると、後に認証に使用していた秘密鍵が漏洩してしまうと復号化が出来てしまう。|FS・PFS以外|AES128-SHA| |Forward Secrecy|FS|認証用の鍵と鍵交換用の鍵が異なるので、通信の漏洩の可能性が低くなります。RSA 2048bitと比べるとサーバへのCPU負荷が3.1倍程度に増加するようです。|DH / ECDH|DH-RSA-AES128-SHA| |Perfect Forward Secrecy|PFS|FSに比べて、一定時間後に定期的に異なる鍵交換を行うため、より漏洩の可能性を低く出来ます。また、RSA 2048bitと比べてもサーバへのCPU負荷は微増のようです。|DHE / ECDHE|DHE-RSA-AES128-SHA| #暗号化方法はたくさんあるけどナニを選べばいいの? [SSL/TLS Deployment Best Practices](https://www.ssllabs.com/projects/best-practices/index.html) というのがある > Use Secure Protocols > > * SSL v2 is insecure and must not be used. > > * SSL v3 is very old and obsolete. Because it lacks some key features and because virtually all clients support TLS 1.0 and better, you should not support SSL v3 unless you have a very good reason. > > * TLS v1.0 is largely still secure; we do not know of major security flaws when they are used for protocols other than HTTP. When used with HTTP, it can almost be made secure with careful configuration. > * TLS v1.1 and v1.2 are without known security issues. ***SSLプロトコル*** * SSLv2は使用しない * SSLv3は非常に古く廃止されています。いくつかの主要機能が不足しており、事実上すべてのクライアントがTLS1.0をサポートしている。そのため、よっぽど良い理由がない限りはSSLv3をサポートするべきではない * TLS v1.0の大部分はまだ安全です。HTTPを使用するときに、キチンと設定をすればセキュアに出来る * TLS v1.1 とTLS v1.2はセキュリティに関する問題が発見されていない > Use Secure Cipher Suites > > * Anonymous Diffie-Hellman (ADH) suites do not provide authentication. > > * NULL cipher suites provide no encryption. > > * Export key exchange suites use authentication that can easily be broken. > > * Suites with weak ciphers (typically of 40 and 56 bits) use encryption that can easily be broken. > > * RC4 is weaker than previously thought. You should remove support for this cipher in the near future. > > * 3DES provides only 108 bits of security (or 112, depending on the source), which is below the recommended minimum of 128 bits. You should remove support for this cipher in the near future. ***暗号化スイート*** * ADH(Anonymous Diffie-Hellman)は認証機能を提供していない * Null暗号化スイートは暗号化しない * Export鍵交換は簡単に解除される認証機能を使っている * 特に40, 56bitの弱い暗号スイートは簡単に暗号化を解除される * RC4は以前考えられてたよりも弱い。近い将来、サポート対象外にするべき。 * 3DESは108または112bitsしか提供していない。最低でも128bits以上が推奨されるので、近い将来サポート対象外にするべき。 #セキュリティに余念のない有名企業はどうしているのか? [Netcraft](http://news.netcraft.com/archives/2013/06/25/ssl-intercepted-today-decrypted-tomorrow.html)が2013年9月に調査した情報によると下記のような対応状況です。2014年3月にwww.facebook.comを確認したところ、ECDHE-RSA-AES128-GCM-SHAになっていたので各社変わっている可能性はありそうです。  #最近発生した主なSSLの脆弱性 |名称|CVE|発生日|概要|対応概要| |---|---|---|---|---| |[Beast](http://blog.zoller.lu/2011/09/beast-summary-tls-cbc-countermeasures.html)|[CVE-2011-3389](http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2011-3389)|2011/9/6|CBC mode脆弱性|| |[TLS heartbleed](http://d.hatena.ne.jp/Kango/20140410/1397139257)|[CVE-2014-0160](https://www.openssl.org/news/secadv_20140407.txt)|2014/4/7|TLS脆弱性|opensslをupdate| |[CCS injection](http://d.hatena.ne.jp/Kango/touch/20140605/1401978480)|[CVE-2014-0224](https://www.openssl.org/news/secadv_20140605.txt)|2014/6/5|CCS脆弱性|opensslをupdate| |[POODLE](http://googleonlinesecurity.blogspot.jp/2014/10/this-poodle-bites-exploiting-ssl-30.html)|[CVE-2014-3566](https://access.redhat.com/security/cve/CVE-2014-3566)|2014/10/14|SSLv3脆弱性|SSLv3を無効にする| |[複数脆弱性](https://jvn.jp/vu/JVNVU95877131/index.html)|[CVE-2015-0291等(全14個)](https://www.openssl.org/news/secadv_20150319.txt)|2015/3/19|複数脆弱性対応|opensslをupdate| |[Altチェイン証明書偽造](http://d.hatena.ne.jp/jovi0608/20150710/1436521488)|[CVE-2015-1793](https://mta.openssl.org/pipermail/openssl-announce/2015-July/000037.html)|2015/7/6|証明書偽造|opensslをupdate| |[SLOTH攻撃](http://d.hatena.ne.jp/jovi0608/20160113/1452649563)|[CVE-2015-7575](https://access.redhat.com/articles/2112261)|2016/1/8|TLS脆弱性(ハッシュ衝突)|[MD5とSHA-1を使用しない](https://nakedsecurity.sophos.com/2016/01/08/the-sloth-attacks-why-laziness-about-cryptography-puts-security-at-risk/)| |[DROWN](http://d.hatena.ne.jp/Kango/20160301/1456849603)|[CVE-2016-0800](https://mta.openssl.org/pipermail/openssl-announce/2016-March/000066.html)|2016/3/1|クロスプロトコル攻撃|SSLv2を無効にする| |[SWEET32](https://sweet32.info/)|[CVE-2016-2183](https://access.redhat.com/security/cve/cve-2016-2183)|2016/8/24|[誕生日攻撃](https://ja.wikipedia.org/wiki/%E8%AA%95%E7%94%9F%E6%97%A5%E6%94%BB%E6%92%83)|3DESなど64bitsブロック暗号の停止| |[SHAttered](https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html)|[CVE-2005-4900](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2005-4900)|2017/2/23|[ハッシュ衝突攻撃](https://shattered.io/)|SHA-256, SHA-3への移行| #結局今のところどのような設定が良さそうか 最初に記載したNginxの公式にサイトに書いてあったよりも、脆弱性があるまたは弱い暗号化を除いて、明示的に使用する暗号化スイートを設定するのが良さそう。 ***あくまで例なのでサイトに合ったものを設定してください。*** * 最新版のopenssl [CVE-2014-0160 / CVE-2014-0224] * 128bit以上 * Enc: AES * Au: RSA * Kx: DH/ECDH/ECDHE (FS以上) * 暗号化利用モード: GCM * SSLv3以下を無効 [CVE-2014-3566] ```Nginx ssl on; ssl_prefer_server_ciphers on; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE+RSAGCM:ECDH+AESGCM: DH+AESGCM:ECDH+AES256:DH+AES256: ECDH+AES128:DH+AES: !EXPORT:!DES:!3DES:!MD5:!DSS; ``` ###対応暗号スイートを確認 ``` $openssl ciphers -v 'ECDHE+RSAGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!EXPORT:!DES:!3DES:!MD5:!DSS' ``` ローカルだとこのような感じで出てきた ```bash:result ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD ECDH-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AESGCM(256) Mac=AEAD ECDH-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AESGCM(256) Mac=AEAD ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD ECDH-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AESGCM(128) Mac=AEAD ECDH-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AESGCM(128) Mac=AEAD DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH Au=RSA Enc=AESGCM(256) Mac=AEAD DHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AESGCM(128) Mac=AEAD ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384 ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384 ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1 ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1 ECDH-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AES(256) Mac=SHA384 ECDH-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AES(256) Mac=SHA384 ECDH-RSA-AES256-SHA SSLv3 Kx=ECDH/RSA Au=ECDH Enc=AES(256) Mac=SHA1 ECDH-ECDSA-AES256-SHA SSLv3 Kx=ECDH/ECDSA Au=ECDH Enc=AES(256) Mac=SHA1 DHE-RSA-AES256-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AES(256) Mac=SHA256 DHE-RSA-AES256-SHA SSLv3 Kx=DH Au=RSA Enc=AES(256) Mac=SHA1 ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA256 ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256 ECDHE-RSA-AES128-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(128) Mac=SHA1 ECDHE-ECDSA-AES128-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1 ECDH-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH/RSA Au=ECDH Enc=AES(128) Mac=SHA256 ECDH-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH/ECDSA Au=ECDH Enc=AES(128) Mac=SHA256 ECDH-RSA-AES128-SHA SSLv3 Kx=ECDH/RSA Au=ECDH Enc=AES(128) Mac=SHA1 ECDH-ECDSA-AES128-SHA SSLv3 Kx=ECDH/ECDSA Au=ECDH Enc=AES(128) Mac=SHA1 DHE-RSA-AES128-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AES(128) Mac=SHA256 DHE-RSA-AES128-SHA SSLv3 Kx=DH Au=RSA Enc=AES(128) Mac=SHA1 ``` Apacheの場合はこのような感じになると思う(Apache 2.4以降ではSSLv2が除外) ```Apache SSLProtocol ALL -SSLv3 SSLHonorCipherOrder On SSLCipherSuite ECDHE+RSAGCM:ECDH+AESGCM: DH+AESGCM:ECDH+AES256:DH+AES256: ECDH+AES128:DH+AES: !EXPORT:!DES:!3DES:!MD5:!DSS ``` ### 設定を生成してくれるサービスもある! * [Mozilla SSL Configuration Generator](https://mozilla.github.io/server-side-tls/ssl-config-generator/) #SSLがキチンと設定されているかテストするには [SSL Server Test](https://www.ssllabs.com/ssltest/)または[GlobalSign SSL チェック](https://sslcheck.globalsign.com/ja)で暗号化強度やブラウザのサポート具合などもチェックできます。また、コンソールでチェックするのに[testssl.sh: Testing TLS/SSL encryption](https://testssl.sh/)も便利です。 簡易的には下記でもSSLの状態が確認出来る ``` $ openssl s_client -host [URL/HOST NAME] -port 443 -ssl3 ``` #まとめ たかが2行設定するのにここまで色々あるとは...まだ関連する技術がありそうなので日々精進が必要そうです。ただ、今回わかったことは単にコピペで設定すると、かなりのセキュリティホールになりそうなのでちゃんとテストして設定しようと。 #参考資料 > 電子政府における調達のために参照すべき暗号のリスト(CRYPTREC暗号リスト) http://www.cryptrec.go.jp/list.html http://search.e-gov.go.jp/servlet/PcmFileDownload?seqNo=0000097652 > Hardening Your Web Server’s SSL Ciphers https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ > IE使うならVista以降が無難、SSLの暗号強度調査結果から http://internet.watch.impress.co.jp/docs/event/iw2009/20091126_331592.html > SSL/TLSでBEASTを恐れてRC4を優先するのは危ない http://tetsutalow.hateblo.jp/entry/2013/04/02/053927 |
|
| 6位 |
|
|||
|
15:21:53 |
(Increments 所属) |
|
コメント/編集リクエスト歓迎します。載っていない語があればぜひ追加してください。
|略語|元の語|意味| |---|---|---| |AFAICT|As far as I can tell|分かる限りでは○○| |AFAIK|As far as I know|知る限りでは○○| |AKA, aka, a.k.a.|Also known as|○○としても知られる| |ASAP|As soon as possible|できる限り早く| |BTW|By the way|ところで| |c.f., cf.|ラテン語 confer / 英語 compare|○○を参照| |e.g., E.g.|ラテン語 exempli gratia / 英語 for example|たとえば| |ETA|Estimated time of arrival|(作業の)完了予定時刻 (用例は[コメントを参照](http://qiita.com/items/86c3a09d17792ab62dfe#comment-54f3d7872ba953941e7d))| |FTW|For the win|〔何か良い案を挙げて〕これで決まり;これでよし| |FWIW|For what it's worth|役に立つかどうかは分からないが| |FYA|For your action|要対応;要返信| |FYI|For your information|参考;ご参考までに| |HTH|Happy to help|(用例は[コメントを参照](http://qiita.com/items/86c3a09d17792ab62dfe#comment-54f3d7872ba953941e7d))| |i.e., I.e.|ラテン語 id est / 英語 that is|すなわち| |IIRC|If I remember [recall] correctly|記憶が正しければ○○| |IIUC|If I understand correctly|〔相手の発言を受けて〕私の理解が正しければ| |IMO, IMHO|In my (humble) opinion|〔ていねいに〕○○だと思います| |LGTM|Looks good to me|〔提案に対して〕いいと思う;問題ないと思う;〔コードレビュアーが、問題ないコードに対して〕レビュー終了;(コードの)承認| |LOL, lol|Laugh out loud| 〔くだけて〕www;(笑)| |NA, N/A|Not applicable; not available|〔表などで、項目の組み合わせが無効な欄に記載して〕該当なし;〔表などで、まだ値がない欄に記載して〕該当値なし| |NB, N.B.|ラテン語 nota bene / 英語 note well|〔通例コメントの先頭で〕特に注意せよ| |NP, np|No problem|〔依頼・謝罪などに対してくだけて〕(問題ないから)気にしないで;大丈夫〔感謝に対してくだけて〕どういたしまして| |OTOH|On the other hand|もう一方では、これに反して| |PTAL|Please take another look|〔再レビューの要求などで〕再度ご確認ください| |PR|Pull request|プルリクエスト| |RFC|Request for comments|〔おもに issue のタグとして〕意見募集| |ROFL, rofl|Rolling on the floor laughing|〔くだけて〕www;(笑)| |RTFM|Read the fucking manual|〔乱暴に〕マニュアルを読め| |TBA|To be advised|あとで連絡する| |TBA|To be announced|あとで発表する| |TBD|To be determined|あとで決める| |TLDR, tldr, TL;DR, tl;dr, TL/DR, tl/dr|Too long, didn't read|長すぎるので読んでいない;(長い文を読みたくない人向けの)要約| |UTSL|Use the source, Luke|〔くだけて〕ソースを読め(スター・ウォーズのセリフとかけた言葉遊び)| |w/|with|(with の省略形)| |w/o|without|(without の省略形)| |WIP|Work in progress|作業中| |WTF, wtf|What the fuck|〔想定外の事・ものにくだけて〕なんてこった;どうすりゃいいんだ;マジかよ| |XD|顔文字:90度右に回すと、目をつぶって大きく口を開けた笑顔に見える|〔くだけたコメントで〕(≧▽≦)| # おまけ ソフトウェアのバージョン: |略語|元の語|意味| |---|---|---| |GA|General availability|一般向け提供;正式リリース版| |GM|Golden master|(CD プレスの)原盤;正式リリース版| |RC|Release candidate|リリース候補版| |RTM|Ready to manufacturing|量産可能;正式リリース版| 慣用表現: |語|直訳|意味| |---|---|---| |Bikeshed, Bikeshedding|自転車置き場|重要でない議論に時間を取られること(家のペンキを塗り終える前に自転車置き場を何色で塗るか揉めることから)| |Yak shaving|ヤクの毛刈り|ある大きな問題を解決するために、 一見無関係な小さい問題をいくつも片付けること。転じて、大きな問題を解決するつもりが、一見関係しているように見えて実は無用な小さい問題に気を取られること(長く固いヤクの毛を刈ってウールにするには多くの下準備が必要なことから)| 略語関連サイト: - [Abbreviations.com - Computing Abbreviations](http://www.abbreviations.com/category/COMPUTING) - [Acronym Finder](http://www.acronymfinder.com) |
|
| 7位 |
|
|||
|
13:58:37 |
(フリーランス 所属)
|
|
by @mixiappwchr
iOSでこんな感じアプリを作ろうと思うんだけど、こんなの作る場合何をみればいいの?というときの、 最初の一歩がわからない場合や、みんながはまりそうなポイントなどを中心に、皆さんのアプリ開発がこれでスタートダッシュ決めれれば幸いです。 Androidの方はこちらをどうぞ [iOSとの比較つき!Androidでこんなアプリ,こんな機能を作りたかったらこれを見ろ!作りたいアプリに対応するクラス、ライブラリのまとめ!] (http://qiita.com/appwatcher/items/7d270de99d63bb9f2be4)  #Twitter,Facebookみたいなタイムラインのアプリを作りたい!  まずは一番作りたいものとして思い浮かぶのがTwitterやFacebookみたいなタイムライン、フィードなどを閲覧するアプリでしょう。 ##ここがポイント iOS開発で一番使うと言っても過言でないUITableViewControllerの使い方でしょう。 ###一行一行の高さを変えたいんだけれども? (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPathを実装する ```objc:tableviewの高さを動的に - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return //高さを返す; } ``` ###セクションの見た目を思いっきり変えたいんだけれども? - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)sectionで自由にカスタムしたUIViewを返すとokです。 ```objc:tableviewの高さを動的に - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section // コードで生成したり とかXib ,Storyboard内から生成とか return //UIViewを返す; } ``` ###画像を表示しようとしたら遅いよ! 外部データを取ってきて表示するときにまずはまるのが画像の処理だと思います。非同期で必ず処理しましょう。 画像の非同期ロードはライブラリを使った方が早いと思います。 ###ヘッダーやフッタをつけたいよ! tableView.tableHeaderView tableView.tableFooterView に突っ込む方法を書いてあるのが多いんですが、今回はもっと簡単な方法としてStoryboardなどでこれらのビューを直接突っ込んで管理できるのでおすすめ。 テーブルビューの上や下にUIViewをドラッグすると  UIViewをIB上で直接追加できます。  後はIBでレイアウトをくめばok  ##使用するクラス、フレームワーク、ライブラリ ###ビュー UITableViewController https://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html ###画像非同期読み込み AFNetworking https://github.com/AFNetworking/AFNetworking AFNetworkingにもUIImageViewのカテゴリがあります。cacheの実装上version2を使うのが吉。 SDWebImage https://github.com/rs/SDWebImage ###キャッシュ NSCache https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSCache_Class/Reference/Reference.html TMCache https://github.com/tumblr/TMCache tumblr製のキャッシュ. ほかでも使えそうです。 ##UI参考 http://pttrns.com/categories/1-activity-feeds #Instagramみたいなカメラアプリを作りたいんだ!  アプリを作り始めるとやっぱりカメラや写真などを扱うアプリを作りたくなるはずです。 ##ここがポイント ###写真の向きが何かおかしいよ! 写真の向きは、保存されている向きと表示されてる向きは違うため、処理する場合にそこを意識する場面が多々あります。 ###かっこいいフィルターをつけたり、デコったりしたいよ! 自前実装は難しいので簡単にやるならライブラリを使いましょう フィルター処理ならGPUImageを使うのと、 https://github.com/BradLarson/GPUImage フル機能の編集機能を簡単に組み込みたい場合はAviaryを入れるとよいでしょう http://developers.aviary.com/ パフォーマンスはGPUImageの方がいいですが、CoreImageも場面によっては使えそうです。 ###自分でカメラロールのデータを処理したいんだけど! AssetsLibaryを使いましょう。扱うのがちょっと難しいかもしれませんが、必須です。 取得した写真をきれいに並べたい場合はUICollectionViewControllerで表示すればいいですね ##使用するクラス、フレームワーク、ライブラリ ###ビュー UIImagePickerViewController https://developer.apple.com/library/ios/documentation/uikit/reference/UIImagePickerController_Class/UIImagePickerController/UIImagePickerController.html UICollectionViewController https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewController_clas/Reference/Reference.html ##画像処理 AssetsLibrary https://developer.apple.com/library/ios/documentation/AssetsLibrary/Reference/AssetsLibraryFramework/_index.html GPUImage https://github.com/BradLarson/GPUImage Aviary http://developers.aviary.com/ CoreImage https://developer.apple.com/library/ios/documentation/graphicsimaging/reference/CoreImageFilterReference/Reference/reference.html ##UI参考 http://pttrns.com/categories/7-capture http://pttrns.com/categories/5-photos #ページをめくったりする電子書籍アプリ作りたいんだけど!  例えば本を読むようなアプリを作りたい!といった場合,iOSでは実現は簡単にできます。 ##ここがポイント ##ページの遷移のアニメーションを変えたいよ! iOS6.0以降よりページめくり以外に通常のスクロールも増えているので活用できる場面がちょっとだけふえました。 UIPageViewControllerTransitionStylePageCurl UIPageViewControllerTransitionStyleScroll ##使用するクラス、フレームワーク、ライブラリ UIPageViewController https://developer.apple.com/library/ios/documentation/uikit/reference/UIPageViewControllerClassReferenceClassRef/UIPageViewControllerClassReference.html #vineみたいな動画のアプリを作ってみたいんだけど  写真アプリの次のフェーズとして動画のアプリがここ最近増えてます。今がその流れに乗り時です! ##ここがポイント ###動画を撮影したい! 動画を加工したい! AVFoundationフレームワークを使うと色々できます https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVFoundationFramework/_index.html またまたGPUImageにお世話になるのが簡単です。撮影の機能もあり、フィルターもかけれます。 https://github.com/BradLarson/GPUImage ###動画の向きが何かおかしいよ! 動画も写真と同じく表示の向きと保存の向きが違うので注意が必要です。 ###カメラロールから動画だけとりたいんだけど UIImagePickerControllerならmediaTypeにpublic.movieをセット ```objc _imagePickerController.mediaTypes = [NSArray arrayWithObjects:@"public.movie", nil]; ``` AssetsLibraryで処理する場合はfilterにallvidesoをセット ```objc [assetsGroup setAssetsFilter:[ALAssetsFilter allVideos]]; ``` ##使用するクラス、フレームワーク、ライブラリ AVFoundation https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVFoundationFramework/_index.html AssetsLibrary https://developer.apple.com/library/ios/documentation/AssetsLibrary/Reference/AssetsLibraryFramework/_index.html GPUImage https://github.com/BradLarson/GPUImage #ミュージックアプリを作りたいよ!  音楽プレーヤーとしても便利なiPhoneだからこそ、音楽との連携はまだまだ可能性がありますね。 ##ここがポイント ###iPhoneに入っている音楽を取得したいんだけど 組み込みのPickerを使いたい場合はMPMediaPickerControllerをつかえばok ```objc // 呼び出し MPMediaPickerController *mediaPickerController = [[MPMediaPickerController alloc] init]; mediaPickerController.delegate = self; mediaPickerController.allowsPickingMultipleItems = NO; [self presentModalViewController:_mediaPickerController animated:YES]; // delegate - (void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker{ [self dismissModalViewControllerAnimated:YES]; } - (void) mediaPicker: (MPMediaPickerController *) mediaPicker didPickMediaItems: (MPMediaItemCollection *) collection { // 取得したメディアアイテム MPMediaItem *item = [collection representativeItem]; // media itemにたいして プロパティ取得 NSString *title = [_item valueForProperty: MPMediaItemPropertyTitle]; NSString *artistName = [_item valueForProperty: MPMediaItemPropertyArtist]; NSString *albumName = [_item valueForProperty: MPMediaItemPropertyAlbumTitle]; NSString *totalPlaybackTime =[_item valueForProperty:MPMediaItemPropertyPlaybackDuration]; MPMediaItemArtwork *artwork = [_item valueForProperty: MPMediaItemPropertyArtwork]; } ``` ###アプリを閉じても音楽を再生させたい! 再生するときは MPMusicPlayerControllerを使うのですが、注意点としてアプリをとじでも再生したままにしたい場合はiPodMusicPlayerのinstanceを取得します。 ```objc MPMusicPlayerController *player = [MPMusicPlayerController iPodMusicPlayer]; [player setNowPlayingItem:item]; [player play]; ``` ###今聞いてる音楽を取得したいんだけど NSNotificationCenterでMPMusicPlayerControllerNowPlayingItemDidChangeNotificationをobserveします。 ```objc MPMusicPlayerController *player = [MPMusicPlayerController iPodMusicPlayer]; NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter]; // 再生曲が変化した時の通知先を登録 [noteCenter addObserver:self selector:@selector(didItemChanged:) name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification object:_player]; - (void)didItemChanged:(id)object { // playerからitemを取得 MPMediaItem *currentItem = [player nowPlayingItem]; } ``` ###音楽の情報を引っ張ってきたいなあ MPMediaQueryで操作すればOK ```objc // 例:アルバム取得 MPMediaQuery *query = [MPMediaQuery albumsQuery]; NSMutableArray *list = [[NSMutableArray alloc] init ]; for(MPMediaItemCollection *collection in [query collections] ){ [list addObject: [NSMutableDictionary dictionaryWithObjectsAndKeys: [[collection representativeItem] valueForProperty:MPMediaItemPropertyAlbumTitle],@"title", [[collection representativeItem] valueForProperty:MPMediaItemPropertyAlbumPersistentID],@"id", nil] ]; } ``` ###使用するクラス、フレームワーク、ライブラリ MediaPlayer https://developer.apple.com/library/ios/documentation/MediaPlayer/Reference/MediaPlayer_Framework/_index.html ざっくりと説明をしたので、ここもっと深堀って欲しいとかあれば別で書きたいと思います。 # 最近の記事 [限界までUXを向上させる! Facebook paperのようなアプリを最新UIライブラリAsyncDisplayKitでヌルヌルな操作感を実現しよう!](http://qiita.com/appwatcher/items/bc6d97a80c847206ba8e) [あらゆる「モノ」との連携するIoT時代に向けて。iOS と AndroidのBluetooth LE対応状況を確認しておこう](http://qiita.com/appwatcher/items/c6e7e5767ba26aa6558e) [作ったアプリをブラウザ即見せでモチベーションアップ!!TestFlightよりお手軽なアプリ配布サービスappetize.io](http://qiita.com/appwatcher/items/da77b66cce3c60e50d22) ##### appwchr post ---- [次世代のスーパーエンジニアたちも学ぶ!マインクラフトで触れる技術たちその1](http://qiita.com/appwatcher/items/05513e170b42b09ecd9a) [あんなすごいエンジニアともであえるかも??Qiitaユーザーが集まるSlack Teamを作ってみたよ!](http://qiita.com/appwatcher/items/00faadbf02f691186a54) [goからiOSまで一人でアプリ開発をしてたらいつの間にかマインクラフトエンジニアになった話](http://qiita.com/appwatcher/items/6c0280cda9c4c8b3c65f) [API開発の効率化の架け橋!APIのStubサーバーを導入して,API開発に効率化、スピード化、柔軟性を手に入れよう!](http://qiita.com/appwatcher/items/49807e72d9600db7196d) [アプリエンジニアから見てAPI設計において気をつけてもらえるとうれしいこと](http://qiita.com/appwatcher/items/4a0dd09c393cc70d961a) [Goodbye... Jenkins... Jenkinsを卒業してお手軽CI! iOSもAndroidもCircle CIでアプリのCIを回そう](http://qiita.com/appwatcher/items/4cdf39804d6e46ab7af5) [まだTestFlight使ってたの?急げ!終了目前のTestFlightから,今すぐにiOSもDeployGateに移行しよう!移行パターンも紹介するよ。](http://qiita.com/appwatcher/items/632460e15fbdb81b7a71) [Swiftを使ってみて直面した闇。現時点で現場でSwiftを採用すべきかどうかの判断材料](http://qiita.com/appwatcher/items/50295e82ab0095902aaa) [iOSの開発をする上で絶対に使うべき!外せない!webサービス、開発ツール集【完全版】](http://qiita.com/appwatcher/items/07a3babcb9b6cefb307e) [注目のiBeacomなどの波に乗り遅れないために!iOSのBluetooth開発を容易にするライブラリを書きました。] (http://qiita.com/appwatcher/items/7491beffd7260b713542) [まだまだあった!iOSの開発を劇的に改善する最新のwebサービス、開発ツール集1] (http://qiita.com/appwatcher/items/f0024fe2ac34da345f04) [さらに快適なアプリ開発を!iOSの開発をもっと劇的に改善する最新のwebサービス、開発ツール集2] (http://qiita.com/appwatcher/items/c15d7311e71b4c2b77f1) [スパゲッティから脱出!iOS開発における遷移の問題をすっきり解決する便利ルーティングライブラリをご紹介] (http://qiita.com/appwatcher/items/259e8d13fff0547e90af) |
|
| 8位 |
|
|||
|
21:52:59 |
(シナプス株式会社 所属) |
|
# あらかじめ読んでおきたい記事
- [Qiita - 【PHP超入門】クラス~例外処理~PDOの基礎](http://qiita.com/7968/items/6f089fec8dde676abb5b) by @7968 # 初心者がやりがちなミス 以下のどれかに1つでも当てはまるコードは見直す必要があります.付録にリンクを貼っておきましたので,「該当するかも?」という人はクリックして飛んで読んでください.太字にしてあるものは<ins>脆弱性に直結する危険度の高いもの</ins>です. - [`mysql_query` などの非推奨関数を利用している](#mysql_query-%E3%81%AA%E3%81%A9%E3%81%AE%E9%9D%9E%E6%8E%A8%E5%A5%A8%E9%96%A2%E6%95%B0%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B) - [`SET NAMES` あるいは `SET CHARACTER SET` などで文字コードを指定している<br>そもそもデータベースで使用する文字コードの指定をしていない](#set-names-%E3%81%82%E3%82%8B%E3%81%84%E3%81%AF-set-character-set-%E3%81%AA%E3%81%A9%E3%81%A7%E6%96%87%E5%AD%97%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%81%9D%E3%82%82%E3%81%9D%E3%82%82%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%A7%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E6%96%87%E5%AD%97%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E6%8C%87%E5%AE%9A%E3%82%92%E3%81%97%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84) - **[`"SELECT * FROM users WHERE id = '$id'"` のように変数展開を使ってSQL文を組み立てている](#select--from-users-where-id--id-%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E5%A4%89%E6%95%B0%E5%B1%95%E9%96%8B%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6sql%E6%96%87%E3%82%92%E7%B5%84%E3%81%BF%E7%AB%8B%E3%81%A6%E3%81%A6%E3%81%84%E3%82%8B)** - [`$_POST['id']` などの外部入力からきた変数が定義されているかどうか確認していない<br>それらが文字列であるかどうかの確認をしていない<br>**それらを出力する際に `htmlspecialchars` 関数を利用していない**](#_postid-%E3%81%AA%E3%81%A9%E3%81%AE%E5%A4%96%E9%83%A8%E5%85%A5%E5%8A%9B%E3%81%8B%E3%82%89%E3%81%8D%E3%81%9F%E5%A4%89%E6%95%B0%E3%81%8C%E5%AE%9A%E7%BE%A9%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%81%8B%E3%81%A9%E3%81%86%E3%81%8B%E7%A2%BA%E8%AA%8D%E3%81%97%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E3%81%9D%E3%82%8C%E3%82%89%E3%81%8C%E6%96%87%E5%AD%97%E5%88%97%E3%81%A7%E3%81%82%E3%82%8B%E3%81%8B%E3%81%A9%E3%81%86%E3%81%8B%E3%81%AE%E7%A2%BA%E8%AA%8D%E3%82%92%E3%81%97%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E3%81%9D%E3%82%8C%E3%82%89%E3%82%92%E5%87%BA%E5%8A%9B%E3%81%99%E3%82%8B%E9%9A%9B%E3%81%AB-htmlspecialchars-%E9%96%A2%E6%95%B0%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84) - [SQLの `LIKE` 演算子を使っているのに `%` `_` `\` のエスケープをしていない](#sql%E3%81%AE-like-%E6%BC%94%E7%AE%97%E5%AD%90%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%AB--_--%E3%81%AE%E3%82%A8%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%97%E3%82%92%E3%81%97%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84) - [HTMLの `<body></body>` の中にデータベース接続処理を書いている<br>`echo()` や `print()` をベタ書きしている<br>Content-Typeを `text/plain` に変更せずに `exit()` や `die()` で強制終了処理を記述している](#html%E3%81%AE-bodybody-%E3%81%AE%E4%B8%AD%E3%81%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E6%8E%A5%E7%B6%9A%E5%87%A6%E7%90%86%E3%82%92%E6%9B%B8%E3%81%84%E3%81%A6%E3%81%84%E3%82%8Becho-%E3%82%84-print-%E3%82%92%E3%83%99%E3%82%BF%E6%9B%B8%E3%81%8D%E3%81%97%E3%81%A6%E3%81%84%E3%82%8Bcontent-type%E3%82%92-textplain-%E3%81%AB%E5%A4%89%E6%9B%B4%E3%81%9B%E3%81%9A%E3%81%AB-exit-%E3%82%84-die-%E3%81%A7%E5%BC%B7%E5%88%B6%E7%B5%82%E4%BA%86%E5%87%A6%E7%90%86%E3%82%92%E8%A8%98%E8%BF%B0%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B) # PDOの基本 PHPでデータベースに接続するためには,現在はPDOを使う方法が主流であると言えるでしょう.PDOの基本的な使い方を確認していきます.内容によって,「前でさらっと書いてあることの定義が後で出てくる」など,どうしても多少前後してしまう部分はありますが,特に気にせずに読み流してください. ## データベースに接続 [PDO::__construct] メソッドを使用してインスタンスを生成します.コンストラクタなので,実際には直接これを呼び出すコードは書かずに, **new演算子** を用います. ```php $pdo = new PDO($dsn, $username, $password, $driver_options); ``` 引数は以下の通りです. ### `$dsn` データベースに接続するために必要な情報です. (Data Source Name) 以下に各データベース製品に応じたDSNの書き方が掲載されています. - [PHP Manual - PDOクラスのデータベース別DSN一覧](http://www.php.net/manual/ja/pdo.drivers.php) MySQLの基本的な書き方を例に挙げます. **`mysql:dbname=test;host=localhost;charset=utf8mb4`** - 頭にデータベースの種類を指定して `:` で区切る. - 各項目は `項目名=値` とし, `;` で区切る. #### <font color="silver">dbname</font> データベース名を指定します.基本的には必須です.但し,データベースを後で `USE test` のようにSQL文で選択する場合,省略することができます. #### <font color="silver">host</font> ホスト名またはIPアドレスを指定します.ローカル環境で動かす場合,省略しても問題ない場合が多いです.`localhost` は自分自身のホスト名, `127.0.0.1` は自分自身のIPアドレスを指します.どちらを用いても問題が発生する可能性があるので,適宜問題のない方を選択してください. - Linux環境はホスト名推奨 - [upsilonさん: MySQLではlocalhostと127.0.0.1で接続方法が変わるため注意が必要](http://qiita.com/mpyw/items/b00b72c5c95aac573b71#comment-e9db50fff9bffa1dd6f8) - Windows環境はIPアドレス推奨 - [AH-2 - Windowsでlocalhostへの接続が遅い(解決方法)](http://www.ah-2.com/2012/01/28/win_localhost_slow.html) #### <font color="silver">charset</font> 文字セットを指定します.`SET NAMES`は避けて,ここで指定するべきです.`UTF-8`ではなく`utf8`であることに注意してください.ハイフンは入りません. **【2016/12/17 追記】** **MySQL5.5.3以降の場合は,4バイトからなる絵文字なども正常に取り扱える`utf8mb4`を使用することを強く推奨します。** ### `$username` ユーザー名.ルート権限を使う場合,デフォルトでは `root` です. ### `$password` パスワード.ルート権限を使う場合,デフォルトでは空白です. ### `$driver_options` 接続時のオプションを連想配列で渡します. - キーはあらかじめ用意されている定数を取る. - 値はあらかじめ用意されている定数以外に,論理値・文字列などの一般的な値も取り得る. ```php [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false, ] ``` オプションの内容については後で触れます. ## 接続後にオプションを指定 [PDO::setAttribute] メソッドを使用します.多くのオプションはコンストラクタの `$driver_options` で指定してもこちらで指定しても大差はありませんが, `PDO::MYSQL_ATTR_INIT_COMMAND` `PDO::ATTR_PERSISTENT` など,一部コンストラクタ専用のものがあります. ```php $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); ``` ## よく使われるドライバオプションとその値 ### `PDO::ATTR_ERRMODE` SQL実行でエラーが起こった際にどう処理するかを指定します.デフォルトは `PDO::ERRMODE_SILENT` です. - **`PDO::ERRMODE_EXCEPTION`** を設定すると例外をスローしてくれる.これを選択しておくのが一番無難. - `PDO::ERRMODE_WARNING` はSQLで発生したエラーをPHPのWarningとして報告する.<br><ins>[PDOStatement::execute] メソッドの返り値が `false` でないかを毎回のように確認する必要がある</ins>. - `PDO::ERRMODE_SILENT` は何も報告しない.<br><ins>[PDOStatement::execute] メソッドの返り値が `false` でないかを毎回のように確認する必要がある</ins>. ### `PDO::ATTR_DEFAULT_FETCH_MODE` [PDOStatement::fetch] メソッドや [PDOStatement::fetchAll] メソッドで引数が省略された場合や,ステートメントがforeach文に直接かけられた場合のフェッチスタイルを設定します.デフォルトは`PDO::FETCH_BOTH`です. - `PDO::FETCH_BOTH`<br> カラム番号とカラム名の両方をキーとする連想配列で取得する. - `PDO::FETCH_NUM`<br> カラム番号をキーとする配列で取得する. - **`PDO::FETCH_ASSOC`**<br> カラム名をキーとする連想配列で取得する.これが一番ポピュラーな設定. - `PDO::FETCH_OBJ`<br> カラム名をプロパティとする基本オブジェクトで取得する. ### `PDO::ATTR_EMULATE_PREPARES` データベース側が持つ「プリペアドステートメント」という機能のエミュレーションをPDO側で行うかどうかを設定します. - PHP5.1のデフォルトは `false` - <ins>PHP5.2以降のデフォルトは `true`</ins> この設定で,いくつかPDOの挙動に違いが表れます. - プリペアドステートメントのためにデータベースサーバと通信する必要が無くなるため,エミュレーションを行ったほうがパフォーマンスは向上する. - 存在しないテーブル名やカラム名をSQL文に持つプリペアドステートメントを発行したとき,エミュレーションOFFの場合はすぐにエラーが発生するが,エミュレーションONの場合は実際にクエリを実行するまでエラーが発生するかどうかわからない. - エミュレーションがONの場合のみ, `;` 区切りで複数のSQL文を1つのクエリで実行することができる. その他,どちらにも利点と欠点があるので,違いは追って見ていき,最後に表にまとめることにします. ```php:PDOクラスのエミュレーションを無効にする $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); ``` ### `PDO::ATTR_PERSISTENT` (コンストラクタ専用) `true` を設定すると,スクリプトが終了してもデータベースへの接続を維持し,次回に再利用します.規模が大きくなってくると設定する恩恵が大きくなりますが,ほそぼそと練習用サイトを作っているうちは役に立たないでしょう. ### `PDO::MYSQL_ATTR_USE_BUFFERED_QUERY` (MySQL専用) `true` のとき, バッファクエリを使用します.デフォルト値はバージョンによって異なるようで,不明です.PHP5.3の時代には既にデフォルトが `true` で今も変わっていない…?少なくとも,私が確認できる限りの最近バージョンではデフォルトは `true` です. - **バッファクエリ**:<br>全ての情報をデータベースサーバから取得してきておいて,PHPに1件ずつ取り出させる - **非バッファクエリ**:<br>1件ごとにデータベースサーバと通信を行って,PHPに取り出させる 取得してくる情報がメモリに収まりきらない莫大な量である,といった非常に特殊なケースを除けば,バッファクエリを選択しておく方が無難です.サーバ負荷も軽減され,途中までフェッチしたところで突然例外が発生するような事態も避けられます.また,非バッファクエリには複数同時にクエリを実行できないなどの大きな欠点もあります.基本的にこれは,データベースから取得したデータでHTMLを表示する用途ではなく,<ins>コマンドラインからバッチ処理を実行する</ins>用途で使われます. ### `PDO::MYSQL_ATTR_INIT_COMMAND` (MySQL専用) (コンストラクタ専用) 接続した直後に実行されるクエリをここに書きます. ## 基本コーディング 書き方のテンプレです. ```html+php <?php try { /* リクエストから得たスーパーグローバル変数をチェックするなどの処理 */ // データベースに接続 $pdo = new PDO( 'mysql:dbname=testdb;host=localhost;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); /* データベースから値を取ってきたり, データを挿入したりする処理 */ } catch (PDOException $e) { // エラーが発生した場合は「500 Internal Server Error」でテキストとして表示して終了する // - もし手抜きしたくない場合は普通にHTMLの表示を継続する // - ここではエラー内容を表示しているが, 実際の商用環境ではログファイルに記録して, Webブラウザには出さないほうが望ましい header('Content-Type: text/plain; charset=UTF-8', true, 500); exit($e->getMessage()); } // Webブラウザにこれから表示するものがUTF-8で書かれたHTMLであることを伝える // (これか <meta charset="utf-8"> の最低限どちらか1つがあればいい. 両方あっても良い.) header('Content-Type: text/html; charset=utf-8'); ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Example</title> </head> <body> <!-- ここではHTMLを書く以外のことは一切しない --> </body> </html> ``` ### 参考: コンストラクタでのエラーモードってどうなるの? PDOのコンストラクタは `PDO::ERRMODE_EXCEPTION` の有無に関わらず **PDOException** をスローしますが, <ins>ホストに接続できなかった</ins>場合などに **Warning** を発生します.普通は気に留める必要はありませんが,ホストを動的に指定させるようなプログラムを書いているならば, [set_error_handler](http://www.php.net/manual/ja/function.set-error-handler.php) 関数を使って **ErrorException** に変換しなければ,例外処理を行うことができません. ## [PDO::query] メソッドで直接クエリを実行する ユーザー入力を伴わないクエリに関しては単に [PDO::query] メソッドを実行すればいいだけです.返り値は **PDOStatement** となります. ```php:全員を取得する $stmt = $pdo->query('SELECT * FROM users'); ``` ## [PDO::exec](http://www.php.net/manual/ja/pdo.exec.php) メソッドで直接クエリを実行する ユーザー入力を伴わないクエリで, **INSERT** や **UPDATE** 等で作用した件数を直接返り値に欲しい場合は [PDO::exec] メソッドを代わりに使います.特に結果を必要としない場合においてもこちらを使用すべきです.後に登場する [PDOStatement::execute] と紛らわしいので注意してください. ```php:全員の年齢を+1し,その対象となった人数を返り値として取得する $count = $pdo->exec('UPDATE users SET age = age + 1'); ``` ## [PDO::prepare] → [PDOStatement::bindValue] → [PDOStatement::execute] の3ステップでクエリを実行する ユーザー入力を受け取ってSQL文を動的に生成する場合は **プリペアドステートメント** と **プレースホルダ** を使わなければなりません. - **プレースホルダ**:<br>直訳すると「場所取り」.何かユーザ入力を当てはめる場所としてあらかじめ確保しておくもの. - **プリペアドステートメント**:<br>直訳すると「予約文」.文を予約したもの.通常,「予約文」は「場所取り」を使うために作られる.もし「場所取り」が無ければ普通に [PDO::query] などで実行するだけで十分なためである. プレースホルダには2種類あり,**疑問符プレースホルダ** を使う方法と, **名前付きプレースホルダ** を使う方法があります.もしこれらが混ざってしまうと ``` SQLSTATE[HY093]: Invalid parameter number: mixed named and positional parameters ``` が発生するので,どちらか一方のみを選択してください. ### 疑問符プレースホルダ - `?` の「番目」は **1** から始まる. - `PDO::PARAM_STR` は省略することが出来る. - エミュレーションがONの場合には正しくキャストしてくれないバグのようなものが仕様として存在するため,<ins>文字列以外のものを扱う際に明示的なキャストが必要</ins>. - `NULL` 値に関しては `PDO::PARAM_NULL` が暗黙的に使用される. ```php:エミュレーションがONの場合はこうする必要がある $stmt = $pdo->prepare('SELECT * FROM users WHERE gender = ? AND age = ?'); $stmt->bindValue(1, $gender); $stmt->bindValue(2, (int)$age, PDO::PARAM_INT); $stmt->execute(); ``` ```php:エミュレーションがOFFの場合はこれでもOK $stmt = $pdo->prepare('SELECT * FROM users WHERE gender = ? AND age = ?'); $stmt->bindValue(1, $gender); $stmt->bindValue(2, $age, PDO::PARAM_INT); $stmt->execute(); ``` - 番目で整数以外を指定した場合,適当な変換が行われる. (但し,こんなコードは書くべきではない) ```php:良い例 $stmt->bindValue('2', $age, PDO::PARAM_INT); $stmt->bindValue('02', $age, PDO::PARAM_INT); $stmt->bindValue('+02', $age, PDO::PARAM_INT); $stmt->bindValue('2e0', $age, PDO::PARAM_INT); $stmt->bindValue('00002.9999', $age, PDO::PARAM_INT); ``` ```php:悪い例 // SQLSTATE[HY093]: Invalid parameter number: Columns/Parameters are 1-based $stmt->bindValue(-2, $age, PDO::PARAM_INT); // オーバーフローして3になる // SQLSTATE[HY093]: Invalid parameter number: parameter was not defined $stmt->bindValue('00002.999999999999999999999999999', $age, PDO::PARAM_INT); // Notice: A non well formed numeric value encountered $stmt->bindValue('2a', $age, PDO::PARAM_INT); ``` ### 名前付きプレースホルダ - `:` を頭につけ,半角英数字とアンダースコアにて構成する. - バインド時の頭の `:` は省略することが出来る. ```php:こちらも同様にエミュレーションがONならば明示的なキャストを忘れない $stmt = $pdo->prepare('SELECT * FROM users WHERE age = :age AND gender = :gender'); $stmt->bindValue(':age', (int)$age, PDO::PARAM_INT); $stmt->bindValue(':gender', $gender); ``` ```php:頭のコロンを省略したケース $stmt->bindValue('gender', $gender); ``` - <ins>エミュレーションがONの場合のみ</ins>,同名のプレースホルダを複数使うことが出来る. ```php:IDが20で年齢も20歳の人を取得 $n = 20; $stmt = $pdo->prepare('SELECT * FROM users WHERE age = :n AND id = :n'); $stmt->bindValue(':n', (int)$age, PDO::PARAM_INT); $stmt->execute(); ``` なお, 値を即時にバインドするのではなく,変数を参照的にバインドしておき,実際に値をバインドするのは実行時になる [PDOStatement::bindParam] メソッドもありますが,原則的にこちらを使う必要はありません.<ins>エミュレーションがONの場合</ins>,実行後にバインドした変数が文字列型にされる仕様もあるので注意してください. - [Qiita - 最近PDOを使っていてハマったこと2つ.](http://qiita.com/_dozen_/items/e3c00a0a581378a4cc70) ## [PDO::prepare] → [PDOStatement::execute] の2ステップでクエリを実行する [PDOStatement::execute] メソッドの引数に配列を渡すと,それらを全てバインドしたあとそのままSQLを実行してくれます.但し,以下の条件に注意してください. - <ins>`NULL` 値以外は全て `PDO::PARAM_STR` 扱いになる</ins> .もし間違った型でバインドをしても,MySQL/SQLiteはデータベース側で自動的にキャストし直してくれるが,パフォーマンスの低下やバグの原因になるので,可能な限り避けたほうがいい.<ins>PostgreSQLの場合はエラーになる</ins>. - また, <ins>既に [PDOStatement::bindValue] メソッドで値がバインドされていた場合でも,それらは全て無視される</ins>.これを用いる場合,全てのバインドをこの引数で行わなければならない. ### 疑問符プレースホルダ [PDOStatement::bindValue] メソッドを用いたときと異なり,`?` の「番目」が **0** から始まることに注意してください. ```php $stmt = $pdo->prepare('SELECT * FROM users WHERE city = ? AND gender = ?'); $stmt->execute([$city, $gender]); ``` ```php:キーを適切に設定すれば順番を変えて指定することも可能 $stmt->execute([1 => $gender, 0 => $city]); ``` ### 名前付きプレースホルダ ```php $stmt = $pdo->prepare('SELECT * FROM users WHERE city = :city AND gender = :gender'); $stmt->execute([':city' => $city, ':gender' => $gender]); ``` ```php:頭のコロンは省略することが出来る $stmt->execute(['city' => $city, 'gender' => $gender]); ``` ```php:compact関数を活用する例 $stmt->execute(compact('city', 'gender')); ``` ## `SELECT` の結果を取得する ### 結果の型 #### <font color="silver">MySQL</font> | データベース | PHP<br />(エミュレーション無しmysqlnd) | PHP<br />(libmysqlclient)<br />(エミュレーション有りmysqlnd)<br /> | |:--------:|:--------:|:--------:| | NULL | NULL | NULL | | 文字列 | String | String | | 日付 | String | String | | タイムスタンプ | Integer | String | | 論理値 | **Integer** | String | | PHPで扱える<ins>値の</ins>整数 | Integer | String | | PHPで扱えない<ins>値の</ins>整数 | String | String | | 浮動小数点数 | Float | String | エミュレーションに関するオプションは `PDO::ATTR_EMULATE_PREPARES` という命名ではありますが,<ins>プリペアドステートメントを使用しない場合にも影響が及ぶ</ins>ことに注意してください. #### <font color="silver">PostgreSQL</font> | データベース | PHP | |:--------:|:--------:| | NULL | NULL | | 文字列 | String | | 日付 | String | | タイムスタンプ | Integer | | 論理値 | Boolean | | PHPで扱える<ins>型の</ins>整数 | Integer | | PHPで扱えない<ins>型の</ins>整数 | String | | 浮動小数点数 | **String** | #### <font color="silver">SQLite</font> | データベース | PHP | |:--------:|:--------:| | NULL | NULL | | その他 | String | ### [PDO::setAttribute](http://www.php.net/manual/ja/pdo.setattribute.php) で取得する型を変更できるケース #### `PDO::ATTR_ORACLE_NULLS` | データベース上 |**`PDO::NULL_EMPTY_STRING`**|**`PDO::NULL_TO_STRING`**| |:--------:|:--------:|:--------:| | NULL | NULL | "" | | "" | NULL | "" | オプション名に `ORACLE` という名前が入っていますが,Oracle以外のデータベースでも利用することができます. #### `PDO::ATTR_STRINGIFY_FETCHES` `true` に設定すると,エミュレーションがOFFの場合に数値が文字列化されます.<ins>エミュレーションがONの場合は,この設定に関わらず常に文字列化されます</ins>. ### [PDOStatement::fetch] カーソルをずらしながら,指定したフェッチモードで1行ずつ取得していきます. - 引数を省略した場合はデフォルトフェッチモードが使用される. - 全ての取得が終わると常に `false` を返す. デフォルトフェッチモードを `PDO::FETCH_ASSOC` に設定した際の例を示します. ```php:一番基本的な方法 while ($row = $stmt->fetch()) { printf("%s lives in %s<br />\n", $row['name'], $row['city']); } ``` ```php:vprintf()関数を使った応用 while ($row = $stmt->fetch()) { vprintf("%s lives in %s<br />\n", $row); } ``` PDOStatementクラスはTraversableインターフェースを実装しているため,デフォルトフェッチモードを使う場合,while文の代わりにforeach文でもっとスマートに書くことができます.但し,<ins>HTML表示のために変数を準備する場合には,多くは配列として持っておく方が都合がいいので,後に紹介する [PDOStatement::fetchAll] メソッドの使用を検討してください</ins>. ```php foreach ($stmt as $row) { printf("%s lives in %s<br />\n", $row['name'], $row['city']); } ``` ```php:0から始まるオフセットを取り出すことも出来る foreach ($stmt as $i => $row) { printf("[%d] %s lives in %s<br />\n", $i, $row['name'], $row['city']); } ``` ### [PDOStatement::fetchObject](http://www.php.net/manual/ja/pdostatement.fetchobject.php) 連想配列の代わりにstdClassオブジェクトとして取得します.[PDOStatement::fetch](http://www.php.net/manual/ja/pdostatement.fetch.php) で `PDO::FETCH_OBJ` を指定するケースと等価ですが,こちらの方が短く書くことができます. ```php while ($row = $stmt->fetchObject()) { printf("%s lives in %s<br />\n", $row->name, $row->city); } ``` ### [PDOStatement::fetchColumn](http://www.php.net/manual/ja/pdostatement.fetchcolumn.php) 特定の1カラムのみを文字列として取得します.[PDOStatement::fetch] で `PDO::FETCH_COLUMN` を指定するケースと等価ですが,こちらの方が短く書くことができます. - 先頭から数えてそのカラムが何番目にあるかを第1引数として渡す.「番目」は **0** から始まる.省略した場合は 0 を指定したとみなされる. - 値に `0` が含まれる可能性がある場合は, **`false !==`** の条件判定をしなければならない. ```php while (false !== $value = $stmt->fetchColumn()) { echo "{$value}<br />\n"; } ``` ### [PDOStatement::fetchAll] 一気に全件取得して2次元配列とします. - 引数を省略した場合はデフォルトフェッチモードが使用される. ```php $rows = $stmt->fetchAll(); var_dump($rows); ``` - 特定のカラムだけ一気に全件取得して1次元配列としたい場合,第1引数に **`PDO::FETCH_COLUMN`** を指定し,第2引数にカラムの「番目」を渡す.「番目」は **0** から始まる.省略した場合は 0 を指定したとみなされる. ```php $values = $stmt->fetchAll(PDO::FETCH_COLUMN); var_dump($values); ``` ### [PDOStatement::setFetchMode](http://www.php.net/manual/ja/pdostatement.setfetchmode.php) PDOオブジェクト自体にデフォルトフェッチモードを指定する方法を紹介しましたが,このメソッドを利用すれば個別に発行されたPDOStatementオブジェクトに対して後からフェッチモードを指定することができます.引数の渡し方がモードによって異なるので,詳しくはマニュアルを参照してください. ```php:0番目のカラムをforeachで取得していくケース $stmt->setFetchMode(PDO::FETCH_COLUMN, 0); foreach ($stmt as $i => $name) { ... } ``` 実は… [PDO::query] を用いる場合にも同じ形式でフェッチモードを指定することができます. ```php:0番目のカラムをforeachで取得していくケース foreach ($pdo->query($sql, PDO::FETCH_COLUMN, 0) as $i => $name) { ... } ``` ## `UPDATE`, `INSERT` で作用した行数を取得する こちらの結果に対してフェッチしようとした場合,`SQLSTATE[HY000]: General error` が発生します.こちらに対して用いることができるのは [PDOStatement::rowCount](http://www.php.net/manual/ja/pdostatement.rowcount.php) メソッドのみです. ```php printf("%d行に作用しました<br >\n", $stmt->rowCount()); ``` ## `SELECT` で該当した行数を取得する 基本的に,以下の方法に従います. - 件数だけでなく結果セットも一緒に欲しい場合, [PDOStatement::fetchAll] メソッドで一気に配列として取得し,それに対してPHPの [count] 関数を使う. - 件数だけが欲しい場合は, `SELECT COUNT(*) WHERE ...` といったクエリを発行し,その結果を [PDOStatement::fetchColumn] メソッドで得る. これらの方法が最も推奨されますが,他の方法が無いわけではありません.以下に補足説明を示します.但しこれらはMySQLやPostgreSQLについてのみ当てはまり,<ins>SQLiteには当てはまりません</ins>. ### バッファクエリ使用時 - **SELECT** に対しても常に [PDOStatement::rowCount] メソッドを使うことができる. ### 非バッファクエリ使用時 - [PDOStatement::fetchAll] メソッドを実行した後,つまり<ins>全てのフェッチが終わった後</ins>であれば **SELECT** に対しても [PDOStatement::rowCount] メソッドを使うことができる. - 結果のフェッチを途中で中断したまま次のクエリ実行に移行するときは, [PDOStatement::closeCursor] メソッドを使ってカーソルを閉じる必要がある.1つ目の結果セットをPDOStatementに保持したまま2つ目のSQLを実行することはできないので,その場合はあらかじめ [PDOStatement::fetchAll] メソッドでデータを退避させておく必要がある. ## データベース接続の切断 データベース処理の最後に ```php $pdo = null; ``` と書いているコードが散見されますが,ほとんどの場合ではこの記述は不要です.必要になるのは,非バッファクエリを使うようなバッチ処理のシーンのみです. # エミュレーションに関するまとめ これまでに何度か登場した,エミュレーションの有無に関する差異を表にまとめます. | 項目 | エミュレーションON | エミュレーションOFF | |:----|:---------:|:----:| | パフォーマンス | ○ | △ | | `SET NAMES` による安全性 | △ | ○ | | NULL値をそのままの型で受け取る | ○ | ○ | | **数値をそのままの型で受け取る**<br />( **mysqlnd** のみが対象) | × | ○ | | **複数の同名プレースホルダ** | ○ | × | | **`PDO::PARAM_*` 定数による正しいキャスト** | × | ○ | | **[PDOStatement::bindParam]** メソッドによる副作用問題 | × | ○ | | **複文の実行** | ○ | ✕ | # PDOの応用 ## PDOStatementのデータ取得形式を極める - [Qiita - PDOフェッチパターン大全](http://qiita.com/mpyw/items/d52351bd1a8068344cc2) ## MySQLの暗黙的な型変換を防止する 既に述べたとおり,MySQLは暗黙的な型変換を行います. ```php:もしageが整数型でも,MySQLはエラーを発生しない $stmt = $pdo->prepare('SELECT * FROM users WHERE age = ?'); $stmt->execute(['20']); ``` もしPostgreSQLのように厳密さを与えたいならば,あらかじめ以下のクエリを実行しておきます. ```php $pdo->exec("SET SESSION sql_mode='TRADITIONAL'"); ``` - [Qiita - MySQLの自動変換が厄介な時の設定](http://qiita.com/yukix/items/c9ad1e38bb98b7c924f3) ## LIKE検索 部分一致のあいまいな検索を行いたい場合,LIKE演算子を使用します.LIKE検索で使われる特殊文字には - `%` … 任意の0文字以上の文字列 - `_` … 任意の1文字 があるため,これらの文字を普通に検索したい場合にはエスケープが必要となります.MySQLの場合はエスケープ用の文字を省略して `\` にすることが出来ますが,SQLiteなど他のデータベースとの互換性を考慮する場合,エスケープに使用する文字を明示すべきです.例えば `!` を用いる場合は `ESCAPE '!'` とします.なお,エスケープ用の文字が文字列中に含まれているケースにも対応するために,そのエスケープ用の文字自体のエスケープも必要です. ```php:どんなデータベースにも広く使える方法 $stmt = $pdo->prepare("SELECT * FROM users WHERE name LIKE ? ESCAPE '!'"); $stmt->bindValue(1, '%' . preg_replace('/(?=[!_%])/', '!', $name) . '%', PDO::PARAM_STR); ``` ```php:MySQL専用の方法 $stmt = $pdo->prepare('SELECT * FROM users WHERE name LIKE ?'); $stmt->bindValue(1, '%' . addcslashes($name, '\_%') . '%', PDO::PARAM_STR); ``` ## ブール全文検索 MySQLでブール全文検索を行いたい場合,IN BOOLEAN MODE修飾子を使用します.全文検索関数で使われる特殊文字には - `+` … 各行に存在しなければならない - `-` … 各行に存在してはならない - `@` … 距離 - `<`, `>` … 単語の貢献度 - `(`, `)` … グループ化 - `~` … 否定演算子 - `*` … 切り捨てまたはワイルドカード - `"` … フレーズ検索 があるため,これらの文字を普通に検索したい場合にはエスケープが必要となります. ```php:MySQL専用の方法 $stmt = $pdo->prepare('SELECT * FROM users WHERE MATCH (description) AGAINST (? IN BOOLEAN MODE)'); $stmt->bindValue(1, addcslashes($name, '\+-@<>()~*"'), PDO::PARAM_STR); ``` ## 可変長プレースホルダ ### 固定長プレースホルダではどうなるか 例えば,検索キーワードを複数受け取って特定のカラムがそれらすべてに部分一致するかどうかの検索を行う場合を考えましょう.キーワードの数が2個の場合,実行の流れは次のようになります. ```php $keywords = ['foo', 'bar']; foreach ($keywords as $keyword) { // LIKE検索のために「%キーワード%」の形式にする $values[] = '%' . preg_replace('/(?=[!_%])/', '!', $keyword) . '%'; } $sql = "SELECT * FROM books WHERE ((summary LIKE ? ESCAPE '!') AND (summary LIKE ? ESCAPE '!'))"; $stmt = $pdo->prepare($sql); $stmt->execute($values); ``` ### 可変長プレースホルダの導入 ところが,実際は検索キーワードの数が不定個数となるのが一般的です.このような場合にはどうすればいいか?そこで必要になってくるのが,動的にプレースホルダを生成することです.具体例を下に示します. ```php if ($keywords) { /* キーワードが1つ以上のときだけ実行 */ foreach ($keywords as $keyword) { // プレースホルダのLIKE部分を用意 $holders[] = "(summary LIKE ? ESCAPE '!')"; // LIKE検索のために「%キーワード%」の形式にする $values[] = '%' . preg_replace('/(?=[!_%])/', '!', $keyword) . '%'; } // AND条件で結合する $sql = 'SELECT * FROM books WHERE (' . implode(' AND ', $holders) . ')'; // 実行 $stmt = $pdo->prepare($sql); $stmt->execute($values); } ``` 検索対象のカラムが2つあり, *summary1* と *summary2* の<ins>どちらかに</ins>一致すればOKと見なす場合は次のようにします.OR条件とAND条件を両方用いていることに注意してください. ```php if ($keywords) { /* キーワードが1つ以上のときだけ実行 */ foreach ($keywords as $keyword) { // プレースホルダのLIKE部分を用意 $holders[] = "((summary1 LIKE ? ESCAPE '!') OR (summary2 LIKE ? ESCAPE '!'))"; // LIKE検索のために「%キーワード%」の形式にする $values[] = $values[] = '%' . preg_replace('/(?=[!_%])/', '!', $keyword) . '%'; } // AND条件で結合する $sql = 'SELECT * FROM books WHERE (' . implode(' AND ', $holders) . ')'; // 実行 $stmt = $pdo->prepare($sql); $stmt->execute($values); } ``` ### 検索キーワードを単語単位に分解する 実際に検索システムを実装するときに,どのようにして検索キーワードを `$keywords` に配列でセットするかどうかが問題となりますが,これは半角スペースで分割させることが一般的であると考えられます.しかし,ここでは半角スペースだけでなく… - **全てのASCII制御文字** - 全角スペース - ハードスペース(&nbsp;) 正規表現を活用して,これらすべてを対象にしてみます. ```php $q = (string)filter_input(INPUT_GET, 'q'); $regex = "/[\\x0-\x20\x7f\xc2\xa0\xe3\x80\x80]++/u"; $keywords = preg_split($regex, $q, -1, PREG_SPLIT_NO_EMPTY); ``` **`PREG_SPLIT_NO_EMPTY`** フラグを用いることで,空文字列の要素を自動的に除外できるのも [preg_split](http://www.php.net/manual/ja/function.preg-split.php) 関数の強みです. ## トランザクション処理 連続的なSQL実行に一貫性・信頼性を持たせたい場合,「トランザクション」という機能を利用します. - [Wikipedia - トランザクション処理](http://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%87%A6%E7%90%86) ### トランザクションの基本的な利用法 #### 使用されるメソッド 処理の実体はデータベースごとに固有の実装が為されていますが,インタフェースはPDOのメソッドとして抽象化されており,SQL文をデータベースごとに書き分けたりする必要はなくなります. - [PDO::beginTransaction](http://www.php.net/manual/ja/pdo.begintransaction.php) - [PDO::commit](http://www.php.net/manual/ja/pdo.commit.php) - [PDO::rollBack](http://www.php.net/manual/ja/pdo.rollback.php) - [PDO::lastInsertId](http://www.php.net/manual/ja/pdo.lastinsertid.php) (必要に応じて) #### ポイント - Tryブロックを **2重** に設け,例外発生時にはロールバックを実行し,捕捉した例外を必要に応じて<ins>外側のTryブロックに向けてスロー</ins>する. - 同じ形式のプリペアドステートメントの生成は1回だけにして,それを使いまわすようにした方がパフォーマンスは向上する. ```php:例 try { // データベースに接続 $pdo = new PDO( 'mysql:dbname=testdb;host=localhost;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); // パラメータ $from = 'A'; $to = 'B'; $transfer_amount = 12000; // プリペアドステートメントを用意 $stmt = $pdo->prepare('UPDATE account SET credit = credit + (?) WHERE id = ?'); // トランザクション処理を開始 $pdo->beginTransaction(); try { // Aの預金残高を減らす $stmt->bindValue(1, -1 * $transfer_amount, PDO::PARAM_INT); $stmt->bindValue(2, $from); $stmt->execute(); // Bの預金残高を増やす $stmt->bindValue(1, +1 * $transfer_amount, PDO::PARAM_INT); $stmt->bindValue(2, $to); $stmt->execute(); // コミット $pdo->commit(); } catch (PDOException $e) { // ロールバック $pdo->rollBack(); // 外側のTryブロックに対してスロー throw $e; } } catch (PDOException $e) { // 例外メッセージを表示 header('Content-Type: text/plain; charset=UTF-8', true, 500); exit($e->getMessage()); } ``` ### トランザクションの種類 秀逸な記事は太字にして紹介します. - **[tree-tips - MySQLのトランザクション分離レベル](http://tree-tips.appspot.com/mysql/transaction_isoration/)** - **[TECHSCORE - トランザクションの定義](http://www.techscore.com/tech/sql/SQL11/11_03.html)** - **[主に言語とシステム開発に関して - DBの「トランザクション分離レベル」が必要な理由](http://d.hatena.ne.jp/language_and_engineering/20110104/p1)** - [Wikipedia - トランザクション分離レベル](http://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%88%86%E9%9B%A2%E3%83%AC%E3%83%99%E3%83%AB) - [CUBE SUGAR STORAGE - MySQL を使ってトランザクション分離レベルの違いを試す](http://momijiame.tumblr.com/post/41512778756/mysql) - [ソフト開発お仕事メモ - \[DB\]H2, Derby, SQLiteの仕様の調査メモ(ロック周り)](http://d.hatena.ne.jp/sekom/20081115/p1) #### 推奨場面まとめ  #### 各データベース製品のデフォルト - **MySQL** のInnoDBのデフォルトは **`REPEATABLE READ`** である. - **PostgreSQL** のデフォルトは **`READ COMMITTED`** である. - **SQLite** のデフォルトは **`DEFERRED`** であり,これは `READ COMMITTED` に相当する. #### 分離レベルを設定するSQLの実行 ```php:MySQLでの例 $pdo->exec('SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE'); ``` ```php:PostgreSQLでの例 $pdo->exec('SET SESSION CHARACTERISTICS AS TRANSACTION SERIALIZABLE'); ``` ## テーブル名・カラム名のエスケープ処理 設計としてはあまり宜しくありませんが, **「ユーザーに任意のテーブル名やカラム名を指定させたい」** というケースも考えられなくはないです.PDOクラスにこういったものをエスケープする機能は含まれていないので,自前でエスケープを行わなければなりません. - NULLバイトを除外する. - バッククオートを<ins>2つ連ねて</ins>エスケープさせる. - 最終的にバッククオートでくくる. ```php:テーブル名をエスケープして動的に指定する例 $sql = sprintf( "CREATE TABLE `%s`(id int, name text)", str_replace(["\0", "`"], ["", "``"], $table_name) ); ``` ## PDOクラスの継承 - [Qiita - PDOでオブジェクトをフェッチ&JSONとCSVファイル出力](http://qiita.com/k-holy/items/ce829a60cafe48bd1f81) - [Architect Note - PDOの真の力を開放する - PHPでデータベースを扱う(3)](http://blog.tojiru.net/article/277021312.html) ----- ----- # 付録 ## 初心者がやりがちなミスの解説 ### `mysql_query` などの非推奨関数を利用している ```php $link = mysql_connect('localhost', 'root', 'password'); mysql_select_db('test'); $result = mysql_query('SELECT * FROM users'); $rows = []; while ($row = mysql_fetch_assoc($result)) { $rows[] = $row; } ``` あなたが参考にしている情報は古すぎます. `mysql_` で始まる関数は,PHP5.5で非推奨になり,PHP7.0で完全に削除されて使えなくなっています.この記事で解説しているように,PDOを使う方法を覚えてください. ### `SET NAMES` あるいは `SET CHARACTER SET` などで文字コードを指定している<br>そもそもデータベースで使用する文字コードの指定をしていない 文字コードの指定が無いのは論外として,`SET NAMES`などを用いるのは可能な限り避けるべきです.このクエリは,データベース側の文字セットを操作するだけで,PDOドライバの文字セットにはノータッチです.入力側と出力側で文字セットが異なると,脆弱性が発生する原因になります. - 文字コードの性質上, `SET NAMES sjis` や `SET CHARACTER SET sjis` とした場合,顕著に脆弱性が現れる. - `SET NAMES utf8` や `SET CHARACTER SET utf8` は基本的には安全だが,例外もあるので注意. MySQLにおいては,libmysqlclientのコンパイルオプションに `--with-charset=cp932` や `--with-charset=sjis` を指定している場合が該当. mysqlndを利用している場合は問題ない. - [へぼい日記 - libmysqlclientを使うプログラムはset namesをutf8であっても使ってはいけない](http://blog.everqueue.com/chiba/2009/02/05/129/) PDOクラスでの正しい指定方法は,以下のようになります. - コンストラクタの [DSN(Data Source Name)](http://www.php.net/manual/ja/ref.pdo-mysql.connection.php) で指定する. ```php $pdo = new PDO('mysql:dbname=test;host=localhost;charset=utf8'); ``` - PHP5.3.5以前のPDOではDSNで文字セット指定が出来ないため, `SET NAMES` や `SET CHARACTER SET` に頼らざるを得ない.但しLinux版では,cnfファイルを利用する選択肢がある. - [Qiita - PHP 5.3.6より前のバージョンの PDO MySQL で charset を指定する](http://qiita.com/ngyuki/items/d88a4df860abb51eb714) - PHP5.3.6Windows版のPDOではDSNで文字セット指定が出来るが,バグがあるため, `SET NAMES utf8` や `SET CHARACTER SET utf8` を使う場合と同じ結果になる. - やむを得ず `SET NAMES` を使うにしても, `SET CHARACTER SET` は避けるべき. - [MySQLマニュアル - SET NAMES と SET CHARACTER SET の違い](http://dev.mysql.com/doc/refman/4.1/ja/charset-connection.html) MySQL以外では以下のようになります. - PostgreSQLでは,DSNに `options='--client_encoding=UTF8'"` を含める. - SQLiteでは特に指定する必要が無く,暗黙的にUTF-8が使用される. ### **`"SELECT * FROM users WHERE id = '$id'"` のように変数展開を使ってSQL文を組み立てている** Wikipediaを読むのがてっとり早いです. - [Wikipedia - SQLインジェクション](http://ja.wikipedia.org/wiki/SQL%E3%82%A4%E3%83%B3%E3%82%B8%E3%82%A7%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3) ```php $id = $_POST['id']; $sql = "SELECT * FROM users WHERE id = '$id'"; ``` このようなコードは, **SQLインジェクション攻撃** の格好の標的になってしまうどころか,それを意図せずとも `'` を含めたクエリで検索を行おうとしたときにも正常な処理が行えなくなってしまいます.これを防ぐために,SQL文中で特別な意味を持つ`'`をエスケープし,`\'`に変換する必要があります. [mysql_query] 関数を使っていた頃は,以下のような手段でSQL文を組み立てるのが主流でした. 1. [mysql_real_escape_string] 関数で文字列をあらかじめエスケープする 2. 変数展開して埋め込む ```php $id = mysql_real_escape_string($_POST['id']); $sql = "SELECT * FROM users WHERE id = '$id'"; ``` 但し,もしこの2行の間にたくさんのコードが入っていたらどうでしょうか? - あなたは `$id` がエスケープ済みかそうでないか, `$sql` を書くところまでしっかり覚えていられますか? - 「エスケープ忘れ」「多重エスケープ」という人為的なミスを生んでしまうリスクはないでしょうか? また,手動エスケープにはこんな罠もあります. - [htmlspecialchars] 関数は **「HTMLの特殊文字」** をエスケープする関数であり, **「SQLの特殊文字」** をエスケープする関数ではない.全く目的が異なる上に,これを通してしまうとHTML特殊文字をデータベースにそのまま格納できないというバグも発生してしまう. - [addslashes] 関数は一応それっぽくSQL文に対してエスケープが行えるが,本当にそれがデータベースに合わせて正しくエスケープされているかどうかの保証は無い.マニュアルにもそういった注意書きがある.**例えばSQLiteは`'`を`\'`ではなく`''`にエスケープしなければならない.** PDOを使う方法では,このようなリスクから開放されます.PDOには,**「プリペアドステートメント」**および**「プレースホルダ」**という仕組みが取り入れられています.詳細については本文中で紹介しているので,付録から戻って読んでみてください. ```php $id = $_POST['id']; $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmt->bindValue(1, $id, PDO::PARAM_STR); ``` ### `$_POST['id']` などの外部入力からきた変数が定義されているかどうか確認していない<br>それらが文字列であるかどうかの確認をしていない<br>**それらを出力する際に `htmlspecialchars` 関数を利用していない** 以下の記事をお読みください. - [Qiita - $_GET, $_POSTなどを受け取る際の処理](http://qiita.com/mpyw/items/2f9955db1c02eeef43ea) 別の場所でも指摘していますが,ただ思考停止して [htmlspecialchars] 関数を使えばいいというものでもありません.「<ins>出力する際に</ins>」というのがネックです.例えば以下のコードは間違いです. ```php:データベースに挿入する前にhtmlspecialchars関数を適用している間違った例 $stmt = $pdo->prepare('INSERT INTO users(name) VALUES(?)'); $stmt->bindValue(1, htmlspecialchars($name), PDO::PARAM_STR); ``` ### SQLの `LIKE` 演算子を使っているのに `%` `_` `\` のエスケープをしていない 以下のようなコードを書いていたらアウトです. ```php $stmt = $pdo->prepare("SELECT * FROM users WHERE name LIKE ?"); $stmt->bindValue(1, "%{$name}%", PDO::PARAM_STR); ``` プレースホルダを使っているので脆弱性こそありませんが,LIKE演算子による正しい検索が保証されません.正しい実装方法は本文中に記載しています. ### HTMLの `<body></body>` の中にデータベース接続処理を書いている<br>`echo()` や `print()` をベタ書きしている<br>Content-Typeを `text/plain` に変更せずに `exit()` や `die()` で強制終了処理を記述している 以下のようなコードを書いていたらアウトです. ```html+php <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Example</title> </head> <body> <ul> <?php try { $pdo = new PDO('mysql: ... ', 'root', 'password', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, ]); $stmt = $pdo->query('SELECT * FROM users'); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { echo '<li>' . htmlspecialchars($row['name']) . '</li>'; } } catch (PDOException $e) { exit($e->getMessage()); } ?> </ul> </body> </html> ``` もし,PDOに接続するところで失敗してしまった場合にどうなるでしょうか?きっと,出力されるHTMLはこんな感じになるはずです. ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Example</title> </head> <body> <ul>No such file or directory ``` もし,あなたがこんな壊れたHTMLをWebサイト利用者に見られて恥も何も感じないのであれば私は止めませんが,多くの人は気にすると思います.…というよりも,まずこのコードは非常に読みにくいので**「ロジックとテンプレートの分離」**は行いましょう. - 頭のほうですべてのロジックを書き,そこを過ぎたら<ins>用意された変数を使ってHTMLを出力する以外のことは行ってはいけません</ins>. (理想を言えば,ファイルも分けるべき) - 広々と確保した `<?php ... ?>` 中で [echo] や [print] を連呼してHTMLを生成するのは,初心者が陥りやすい典型的な間違ったPHPの使い方です.PHPはこれでも**テンプレートエンジン**的な役割を持つ言語なので,上記で指摘したロジックとテンプレートの分離を行った後は,<ins>PHPの中にHTMLを埋め込むのではなく,**HTMLの中にPHPを埋め込む**感覚で書きましょう</ins>. 上記2点に関して,以下に修正した例を示します. ```html+php <?php try { $pdo = new PDO('mysql: ... ', 'root', 'password', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, ]); $rows = $pdo->query('SELECT * FROM users')->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { exit($e->getMessage()); } function h($str) { return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); } ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Example</title> </head> <body> <ul> <?php foreach ($rows as $row): ?> <li><?=h($row)?></li> <?php endforeach; ?> </ul> </body> </html> ``` **[echo]短縮構文** および **[制御構造に関する別の構文](http://php.net/manual/ja/control-structures.alternative-syntax.php)** を用いてみました.この2つはHTMLを出力する際に適用するとコードが非常に読みやすくなります.これで,エラー時に出力されるHTMLは以下のようになります. ```html No such file or directory ``` ただ,これってもはやHTMLではないですよね?もしエラー時にも丁寧にエラー画面用のHTMLを用意するのであればそれで構わないのですが, [exit] や [die] で手抜きしたい場合はWebブラウザに**「これはただのテキストです」**と伝えるようにしましょう. [exit] の前にこの1行を入れてください. ```php header('Content-Type: text/plain; charset=UTF-8', true, 500); ``` 500は「500 Internal Server Error」の意味で,サーバ側が原因でエラーになってしまったことを表します. ## PDOとmysqliの比較 ### [mysqliクラス (またはmysqli関数)](http://www.php.net/manual/ja/book.mysqli.php) 手続き型(関数)とオブジェクト指向型(クラス)を両方ともサポートしています. #### <font color="silver">メリット</font> - 動作が **非常に** 速い. - (手続き型の場合は) mysql関数からの書き換えが容易. - プリペアドステートメントで型を指定しながら複数のバインドが同時に **できる** . - **マルチクエリ** を使用できる.1回の関数コールで複数のクエリが実行できるため,普通に2回コールする場合に比べてパフォーマンスは向上する. - エラーを例外としてスローさせることができる.2種類の設定方法があるが,これに関しては<ins>他の記述がオブジェクト指向型でも手続き型を採用すべき</ins>であると言える.明らかに手続き型の方がスッキリと書ける. ```php:オブジェクト指向型 $driver = new mysqli_driver(); $driver->report_mode = MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT; ``` ```php:手続き型 mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); ``` - 不適切なインデックスを含むクエリに対しても,例外をスローさせることができる.開発段階でデータベースに負荷をかけてしまうクエリを潰すことが出来る利点がある.これを有効化するためには,下記のようにする. `MYSQLI_REPORT_ALL` でまとめる手もある. ```php mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT | MYSQLI_REPORT_INDEX); ``` ```php mysqli_report(MYSQLI_REPORT_ALL); ``` - [久保清隆の成長ノート - MySQLパフォーマンスチューニングのためのインデックスの基礎知識](http://d.hatena.ne.jp/kiyo560808/20101117/1289952549) #### <font color="silver">デメリット</font> - プリペアドステートメントで名前付きプレースホルダが **使えない** . - **「プリペアドステートメント ≠ 結果セット」** であり,SQL実行後にプリペアドステートメントから結果セットを取り出す必要がある.この点は次に紹介するPDOクラスと大きく異なる. - その他の点でも多機能すぎる為,初心者が混乱しやすい. ### [PDOクラス](http://php.net/manual/ja/book.pdo.php) オブジェクト指向型のみをサポートしています. #### <font color="silver">メリット</font> - 動作が速い. - エラーを例外としてスローさせることが出来る.但し,一部の特例を除き,<ins>コンストラクタは常に例外をスローする</ins>. ```php:PDOクラスで例外をスローさせる $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); ``` - **「プリペアドステートメント = 結果セット」** であり,mysqliクラスに比べると初心者にも分かりやすい. - プリペアドステートメントで名前付きプレースホルダが **使える** . - プリペアドステートメントのエミュレーションを行う場合は **同名の名前付きプレースホルダを複数回使える** .但し,エミュレーションを設定していないと `SQLSTATE[HY093]: Invalid parameter number` が発生する. - 他のデータベースへ乗り換える際,書き換えが容易.PDOクラスの使い方だけを覚えておけばドライバが存在するあらゆるデータベースに対応が出来るようになる. #### <font color="silver">デメリット</font> - プリペアドステートメントで型を指定しながら複数のバインドが同時に **できない** . - `SET NAMES sjis` や `SET CHARACTER SET sjis` を設定している場合,プリペアドステートメントのエミュレーションを無効にしておかないと脆弱性が発生する. - プリペアドステートメントのエミュレーションを行う場合は [PDOStatement::bindValue], [PDOStatement::bindParam] メソッドで,<ins>指定した型と実際の型が一致しない場合,全て文字列としてバインドされる</ins>. [PEAR::DB](http://pear.php.net/manual/ja/package.database.db.php) や [PEAR::MDB2](http://pear.php.net/manual/ja/package.database.mdb2.php) に関しては,PDOが安定してきたPHP5.4系以降で使う必要性は皆無でしょう.PHPで実装された抽象化レイヤは,C言語で実装されたPDOよりも明らかにパフォーマンスが悪くなります. - [\[Z\]ZAPAブロ~グ2.0 - PDO,PEAR::DB,Mysql関数の速度比較](http://zapanet.info/blog/item/972) [mysql_query]: http://php.net/manual/ja/function.mysql-query.php [mysql_real_escape_string]: http://php.net/manual/ja/function.mysql-real-escape-string.php [htmlspecialchars]: http://www.php.net/manual/ja/function.htmlspecialchars.php [addslashes]: http://www.php.net/manual/ja/function.addslashes.php [exit]: http://www.php.net/manual/ja/function.exit.php [die]: http://www.php.net/manual/ja/function.die.php [PDO::__construct]: http://php.net/manual/ja/pdo.construct.php [PDO::setAttribute]: http://www.php.net/manual/ja/pdo.setattribute.php [PDOStatement::execute]: http://www.php.net/manual/ja/pdostatement.execute.php [PDOStatement::fetch]: http://www.php.net/manual/ja/pdostatement.fetch.php [PDOStatement::fetchAll]: http://www.php.net/manual/ja/pdostatement.fetchall.php [PDOStatement::bindValue]: http://www.php.net/manual/ja/pdostatement.bindvalue.php [PDOStatement::bindParam]: http://www.php.net/manual/ja/pdostatement.bindparam.php [PDO::query]: http://www.php.net/manual/ja/pdo.query.php [PDOStatement::rowCount]: http://www.php.net/manual/ja/pdostatement.rowcount.php [PDO::exec]: http://www.php.net/manual/ja/pdo.exec.php [PDO::prepare]: http://www.php.net/manual/ja/pdo.prepare.php [PDOStatement::bindParam]: http://www.php.net/manual/ja/pdostatement.bindparam.php [count]: http://www.php.net/manual/ja/function.count.php [PDOStatement::fetchColumn]: http://www.php.net/manual/ja/pdostatement.fetchcolumn.php [PDOStatement::closeCursor]: http://www.php.net/manual/ja/pdostatement.closecursor.php [echo]: http://www.php.net/manual/ja/function.echo.php [print]: http://www.php.net/manual/ja/function.print.php |
|
| 9位 |
|
|||
|
01:12:52 |
(Zonu.me 所属) |
|
<del datetime="2017-05-11T20:45:00+09:00">この記事は[CC BY 3.0](http://creativecommons.org/licenses/by/3.0/)に基いて公開されてゐるWebサイト[Choosing an OSS license doesn’t need to be scary - ChooseALicense.com](http://choosealicense.com/)のコンテンツ各ページを翻訳し、単一記事として再構成、訳者による補足を追加したものです</del>。
<div class="alert alert-warning"><ins datetime="2017-05-09T20:45:00+09:00"><time datetime="2017-05-11T01:27:00+09:00">2017年5月9日</time>に<a href="http://blog.qiita.com/post/160423117339/communityguideline">開示</a>された<a href="http://help.qiita.com/ja/articles/qiita-community-guideline">コミュニティガイドライン</a>に伴って、本記事の翻訳部分につきましては削除いたしました。 (この記事が削除または非公開化されない限り、<a href="http://qiita.com/tadsan/items/99d816e78ca429093b75/revisions">編集履歴</a>からお読みいただくことは可能です。)</ins></div> <div class="alert alert-info">日本語での自由ソフトウェア/オープンソースライセンスについての紹介については、<a href="http://www.catch.jp/">可知豊</a>(@<a href="https://twitter.com/y_catch">y_catch</a>)先生の<a href="http://www.catch.jp/oss-license/2013/09/10/github/">Githubによる、オープンソースライセンスの選び方 | オープンソース・ライセンスの談話室</a>をオススメします。</div> *(訳註: この「はじめに」及び末尾の「訳者による補足」の章は原文にはなく、翻訳者(@tadsan)によるものです。記事の著作権表示及び元Webサイトの利用規約、免責事項、そしてこの記事についての訳者の見解について記します)* <a rel="license" href="http://creativecommons.org/licenses/by/3.0/"><img alt="Creative Commons Attribution-ShareAlike 3.0 Unported License" style="border-width:0" src="https://qiita-image-store.s3.amazonaws.com/0/4121/b660c4a4-3d55-e6d6-4108-0a6939ea7aee.png" /></a> (この記事の一部または全て ——ただしコメント欄は含まれない—— はCC BY-SA 3.0に則る限り、自由に利用することができます。<del datetime="2017-05-11T20:25:00+09:00">翻訳と「はじめに」「訳註」については独自の著作権は主張しません。</del> 改竄しても二次利用は独自の責任にて行ってください。) > Please consult a legal expert before adopting a software license for your project. > This site is licensed under the Creative Commons Attribution 3.0 Unported License. > > Demystified with <3 by GitHub, Inc. 元サイトには利用規約として[Terms of Service - ChooseALicense.com](http://choosealicense.com/terms-of-service/)が存在します。この利用規約の有効性及び有効範囲について、訳者(@tadsan)は **言及しません**。参考として、見出しのみを抜萃し、()内に訳者による参考訳を補ひました。 > ### [Terms of Service - ChooseALicense.com](http://choosealicense.com/terms-of-service/) > 1. Introduction (はじめに) > 2. Agreement to the Terms (規約に同意すること) > 3. No legal advice is being provided (法的な助言をするものではない) > 4. DISCLAIMER OF WARRANTIES (保証の抛棄) > 5. LIMITATION OF LIABILITY (責任の制限) > 6. Indemnification for breach of the Terms (規約違反の補償) > 7. Termination of this Agreement (契約の終了) > 8. Miscellaneous Terms (雑則) この記事において「ライセンス」とは、特に断りのない限りはフリーソフトウェア(自由ソフトウェア)・オープンソースライセンスと呼ばれる形態のライセンスのことです([FLOSS - Wikipedia](https://ja.wikipedia.org/wiki/FLOSS))。 もとのWebサイト([creativecommons.org](http://creativecommons.org))及びこの翻訳記事は、あなたに **法的な助言** を与へるものではありません。ライセンス問題及び各ライセンスが日本及び外国の法律・条約・条例に基いて有効・適法な契約であるかについては、必ず弁護士などの法律専門家の判断を仰いでください。上記の "Term of Service" の解釈についても同様とします。 翻訳は直訳よりは日本語として解釈しやすいように意訳してあります。原文との見解の相違がないように注意して訳しますが、英語及びライセンスへの認識の差により、原文と矛盾した意味になってしまってゐるリスクもあります。 *(訳註:* から始まる文章は、訳者(@tadsan)が **独自の見解により** 本文の内容を註釈・補足するものです。 筆者は翻訳及び訳註により原文と異った意味に改変することを望まないので、もし誤訳及びライセンス見解の誤り、その他ミスなどを発見した場合には訳者(@tadsan)にコメントなどでフィードバックをいただけると幸ひです。 翻訳にあたって法律の専門家や法律智識を持った翻訳家の助言を受けたものではありません。また、この記事はあくまでライセンス原文を解釈し、要約を試みたものなので、優先されるべきは常に原文です。 *(本記事のみならず、ほかの翻訳・ライセンス解説についても同様に解釈するべきです)* 繰り返しますが、この記事は専門家による法律判断ではありません。法律問題への対処には専門家によるアドバイスを受けてください。元サイト及び本記事を参考にしたことで受けた全ての利益・損害について、原著者及び翻訳者は一切の権利・責任を負ひません。 *(以下より記事本文です)* ----- [Choose an open source license (choosealicense.com)](https://choosealicense.com/)をお読みください。 [](https://qiita-image-store.s3.amazonaws.com/0/4121/2aa5eda8-c273-9d9f-df07-b2b2500bc78a.png) <ins datetime="2017-05-09T20:45:00+09:00">(日本語訳文は削除されました。原文または「<a href="http://www.catch.jp/oss-license/2013/09/10/github/">Githubによる、オープンソースライセンスの選び方</a>」をお読みください。</ins> ----- *(翻訳による内容は、ここまで)* 訳者による補足 -------------- ### 補足1. ライセンスを宣言する方法 最善の方法はソースコードすべてにライセンスを明示することです。 そして、`README` などと同じディレクトリに `LICENSE.txt` または `COPYING` などのファイルを用意して、そこにライセンスの本文や要約をコピーします。 ライセンスの本文の上部に `Copyright (c) [year] [fullname]` のような形式の穴埋めテンプレートがある場合は、そこ「だけ」を現在の年と自分の名前に変更します。いくつかのライセンスは、本文を「改変しない限り再配布して良い」と許諾してゐること、そして不用意に本文を変更したり条件を追加することで利用者に混乱をきたすことがその理由です。 GitHubではリポジトリ作成時にライセンスを選択して、自動で作成させることもできます([Open source licensing · GitHub Help](https://help.github.com/articles/open-source-licensing))。 ただし、Webで使用されるJavaScriptなどファイル単位で読み込まれる場合、当然ながら `README` や `LICENSE` などのテキストファイルは添付しにくくなります。参考までに、そのような場合のライセンス表示のサンプルを紹介します。 [prototype.js](https://ajax.googleapis.com/ajax/libs/prototype/1.7.1.0/prototype.js)の先頭には以下の記述があります。 ```prototype.js /* Prototype JavaScript framework, version 1.7.1 * (c) 2005-2010 Sam Stephenson * * Prototype is freely distributable under the terms of an MIT-style license. * For details, see the Prototype web site: http://www.prototypejs.org/ * *--------------------------------------------------------------------------*/ ``` また、jQuery の場合はもっと簡潔です。 ([jquery-2.0.3.min.js](http://code.jquery.com/jquery-2.0.3.min.js)) ```jquery-2.0.3.min.js /*! jQuery v2.0.3 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license */ ``` どちらの場合にも著作権表示があり、前者はライセンス名で表記、後者はライセンスを明記したページのURLへ誘導してゐることが特徴的です。 ### 補足2. マルチライセンス PerlはArtistic Licenseで公開されてゐると上記にありますが、実際には [Perl Licensing - dev.perl.org](http://dev.perl.org/licenses/) 及び配布パッケージ内の `README` 内には以下の記述があります。 > Perl5 is Copyright (C) 1993-2005, by Larry Wall and others. > > It is free software; you can redistribute it and/or modify it under the terms of either: > > * a) the [GNU General Public License](http://dev.perl.org/licenses/gpl1.html) as published by the [Free Software Foundation; either](http://www.fsf.org/) [1](http://dev.perl.org/licenses/gpl1.html), or (at your option) any [later version](http://www.fsf.org/licenses/licenses.html#GNUGPL), or > * b) the "Artistic License". *—— http://dev.perl.org/licenses/ より引用 (2013年9月7日 閲覧)* これは、ライセンスとして「GPLv1(または任意に指定された番号)以上のバージョン」または「Artistic License」のうちの **どれか** の条件に従へば良いといふことになります。このような状態を「マルチライセンス」と呼びます(この場合は2つなので「デュアルライセンス」、3つなら「トリプルライセンス」とも呼ばれます)。 このマルチライセンスに含めるライセンスは必ずしもフリーソフトウェアライセンスである必要はなく、金銭を対価にサービスを提供するような商業契約(コマーシャルライセンス)であることもあります。[MySQL](https://ja.wikipedia.org/wiki/MySQL#.E3.83.A9.E3.82.A4.E3.82.BB.E3.83.B3.E3.82.B9)がこの形態のソフトウェアの代表例です。 ### 補足3. ライセンスを明示しないこと サイボウズ・ラボの西尾泰和さんのことばに次のようなものがあります。 > 著作権を主張しないつもりなんだったらそれを明示的に宣言して欲しい。ライセンスが明示されていないのは、どんなライセンスよりも厳しいライセンスだ。 *([Ruby 1.9.2リリースとWEBrick脆弱性問題の顛末 - 西尾泰和のはてなダイアリー](http://d.hatena.ne.jp/nishiohirokazu/20100819/1282200581) より引用)* また、2chの [Navi2ch for Emacs (Part 20)](http://toro.2ch.net/test/read.cgi/unix/1265413075/) スレのテンプレには次のようなFAQがあります。 > Q. パッチを作ったんだけど、ここに up すれば良いの? > A. 2ch で晒すと取り込めなくなるので、sourceforge に投げてね。 > ttp://sourceforge.net/tracker/?atid=435774&group_id=39552&func=browse > > Q. 自作の関数や設定を晒したいんだけど、どうすれば良い? > A. 設定はともかく、関数を 2ch で晒すのは NG。 > navi2ch に取り込んで欲しいなら、上記にパッチを投げてね。 > ただ晒したいだけなら、メーリングリストやグループに投げてね。 *(http://toro.2ch.net/test/read.cgi/unix/1265413075/2 より引用)* 匿名でライセンスを明示しないソースコードを公開するリスクは本文中に訳註として書いた通りです。特に2chでは後から作者を特定することができず、よってライセンスを変更することも不可能になり、合法的に二次利用することができなくなります。 ### 補足4. より信頼のおける情報源 もっと基礎から詳しくライセンスについて学びたいのでしたら、有料(800円)の電子書籍ですが、「[知る、読む、使う! オープンソースライセンス](http://tatsu-zine.com/books/osslicense)」が良いかもしれません。(実は訳者はまだ読んでゐません!) もっと深くライセンスについて理解するための資料としては、IPA(独立行政法人 情報処理推進機構)のWebサイトに[OSSライセンス関連情報](http://www.ipa.go.jp/osc/osslegal.html)として、多数まとめられてゐます。特に[GNU GPL v3 逐条解説書](http://ossipedia.ipa.go.jp/doc/187/)は非常に詳細なレポートで、[SFLC](http://ja.wikipedia.org/wiki/Software_Freedom_Law_Center)の協力を受けてまとめられた、極めて信頼度の高いものです。さらにこのレポートは[Creative Commons 表示 - 非営利 - 改変禁止 2.1 日本(CC BY-NC-ND 2.1 JP)](http://creativecommons.org/licenses/by-nc-nd/2.1/jp/)として利用可能です。 ### 補足5. ライセンスの互換性 *(この話はわかりやすさ重視の例で、技術的な正確性はありません)* 突然ですが、あなたは画像ブラウザを作りました。これは、[BMP形式](https://ja.wikipedia.org/wiki/Windows%E3%83%93%E3%83%83%E3%83%88%E3%83%9E%E3%83%83%E3%83%97)の画像をとても快適に管理できる、べんりなアプリケーションです *(言語は何でも良いですが、[Delphi](https://ja.wikipedia.org/wiki/Delphi)でネイティブコードにコンパイルしたとします)*。 ある日、あなたは気がつきました。「画像をBMP形式に変換して読み込めば、どんな形式の画像でもサポートできるぞ!」と。 あなたが最初に見付けたのは、JPEG画像をBMPに変換するライブラリでした。これは一切著作権を主張しないことが宣言された、パブリックライセンスのライブラリでした。このソースコードを使って、あなたのアプリケーションはJPEG対応になりました。 <dl><dt>Q.</dt><dd>このアプリケーションのソースコードを公開する必要はありますか?</dd><dt>A.</dt><dd>公開義務はありません。なぜなら、著作権はわたしが持ってゐるからです。また、JPEGライブラリには著作権は<em>ありませんでした。</em></dd></dl> 次に見付けたのは、John Smithさんが作った、PNG画像をBMPに変換するライブラリでした。これは2条項BSDライセンスでした。このライブラリを静的リンクすることで、あなたのアプリケーションはPNG形式対応になりました。 <dl><dt>Q.</dt><dd>このアプリケーションのソースコードを公開する必要はありますか?</dd><dt>A.</dt><dd>公開義務はありません。なぜなら、わたしが著作権を持つコードと、2条項BSDライセンスのコードから構成されるからです。</dd><dt>Q.</dt><dd>あなたは何をしなければなりませんか?</dd><dt>A.</dt><dd>ドキュメントにPNGライブラリの作者名とライセンスを明記することです。わたしは<code>README</code>ファイルに<strong>License</strong>の章を設け、次のように追記しました。</dd></dl> > ## License > ### HogePngLibrary >> (C) Copyright 2001 John Smith >> >> All rights reserved. >> >> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: >> >> * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. >> * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. > * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. >> >> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <!-- 次にあなたは、JPEG2000形式の画像をBMP形式に変換するライブラリを見付けました。 --> |
|
| 10位 |
|
|||
|
11:54:21 |
(Souzoh, Inc. (affiliated by Mercari, Inc.) 所属) |
|
# 本家サイトと日本語訳サイト
## [本家サイト](http://golang.org/) 標準ライブラリの使い方はここを見ればよい。 APIドキュメントからソースが簡単に見れるので、勉強になる。 ## [Go Wiki](https://github.com/golang/go/wiki) [SliceTrics](https://github.com/golang/go/wiki/SliceTricks)など,初心者向けの情報があるのでチェックするとよい.少し情報が古いところもある。 ## [golang-jp.org](http://golang-jp.org/) 本家サイトを日本語に翻訳しているサイト.情報は新しいが,一部未翻訳の箇所あり.翻訳プロジェクトはgithubで管理されているので,翻訳に[協力](https://github.com/gophersjp/go/wiki/%E7%BF%BB%E8%A8%B3%E5%8D%94%E5%8A%9B%E3%83%95%E3%83%AD%E3%83%BC)もできる. # 入門 ## [A Tour of Go](http://go-tour-jp.appspot.com/#1) A Tour of Goの日本語版。Go言語の機能の説明を一通り網羅している。練習問題が若干初心者には厳しい(ニュートン法とか)。 追記:英語版は改定されてて,取っ付きにくい練習問題はなくなっているらしい。 ## [Whispering Gophers](http://whispering-gophers.appspot.com/talk.slide#1) チャットアプリを作る順々に作っていけるサンプル.channelやgoroutineについて学べる. リポジトリ:https://code.google.com/p/whispering-gophers/ ## [はじめてのGo](http://gihyo.jp/dev/feature/01/go_4beginners) Jxckさんの入門記事です。非常に分かりやすいので,入門としておすすめ。 ## [Go Web プログラミング](http://astaxie.gitbooks.io/build-web-application-with-golang/content/ja/index.html) astaxieさんのGoによるWebプログラミング入門の日本語訳です。 # よいコードを書くために見た方がよさそうなところ ## [Go Proverbs](https://go-proverbs.github.io/) Rob Pike氏のGoの格言をまとめたサイト。Gophers Slackのロード画面にも出てくる。 ## [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)([日本語訳](http://qiita.com/knsh14/items/8b73b31822c109d4c497)) Goのコードを書く上で、気をつけることがまとめられているサイト。公式ドキュメントの[Effective Go](https://golang.org/doc/effective_go.html)をまとめたものが多い。 # コミュニティ ## [golang-nuts](https://groups.google.com/forum/#!forum/golang-nuts) Google Groups上のグループ。Go言語の情報はここにほとんど集まっている。但し英語。 ## [Go+](https://plus.google.com/communities/114112804251407510571) 英語のGoogle+上のコミュニティ。golang-nutsよりは情報が少ないが、Google+上なので情報をキャッチしやすい。 ## [Golang JP(Google+)](https://plus.google.com/communities/107075098212007233819) Google+上の日本のGo言語コミュニティ。日本語なので、質問しやすい。日本のGo言語のイベントの情報等もここに流れる。 ##[Lingr-Go言語](http://lingr.com/room/golang) Go言語の話題を話すチャットルーム。 リアルタイムで聞けます! ## [Slack-Gophers](http://blog.gopheracademy.com/gophers-slack-community/) Slackのコミュニティです。`#japan`や`#tokyo`には日本の方がいるので,日本語で質問しやすい。この他にも分野や地域ごとに部屋があるので,いろいろ部屋を見るとよい。 # ドキュメントやまとめサイト ## [Go Search](http://go-search.org/) Go言語のライブラリを探せる.検索結果にgodoc.orgやGo Walkerへのリンクがある. どういうライブラリがよく利用されているかわかるので便利. ## [godoc.org](http://godoc.org/) 様々なGo言語のライブラリのAPIドキュメントを生成するサービス。 URLの後ろにライブラリのインポートパスを入れると自動でAPIドキュメントが生成、公開される。トップページのPopular Packagesを眺めるだけで、人気のパッケージが分かる。 ## [Go Walker](https://gowalker.org/) APIドキュメントを生成するサービス.個人的には,godoc.orgより使いやすい. ## [golang-samples](https://github.com/golang-samples) Go言語のサンプルを集めたGithubのOrganization。誰でも参加して、新しいレポジトリを追加できる。参加したければ、golang.samples@gmail.com にメールを送れば良い。pull requestを送るだけでもよい。 ## [goz](http://goz.hexacosa.net/) Go言語の熱い記事をチェックできるサイト. ## [Go by Example](https://gobyexample.com/)([日本語訳](https://oohira.github.io/gobyexample-jp/)) 豊富なサンプルを見つけることができるサイト. ## [Go Tutorial: Dot Net Perls](http://www.dotnetperls.com/go) 基本的な機能を具体的な例と実行結果を提示しているサイト. `regexp`パッケージや`strings`パッケージのサンプルがあって分かりやすい. # 便利サイト,便利ツール ## [Go Playground](http://play.golang.org/) Web上でGo言語を実行できるサイト。Go言語をインストールする前にここで試してみるといいかもしれない。 ## [goenv](https://bitbucket.org/ymotongpoo/goenv) プロジェクトごとにGOPATHを設定できるツール. ## [Dash](https://itunes.apple.com/us/app/dash-docs-snippets/id458034879?mt=12) 色々な言語のドキュメントをすばやく閲覧したり、スニペットを登録できるMacのアプリ。Go言語のドキュメントもある。 ## [お気楽 Go 言語プログラミング入門](http://www.geocities.jp/m_hiroi/golang/index.html) Go言語の文法や演習問題など網羅的に解説してあるサイト。 # 環境構築・エディタの使い方 * vim : [Vimを使ったGo言語開発手法](http://mattn.kaoriya.net/software/vim/20130531000559.htm) * Emacs : [Emacsを使ったGo言語開発手法(2013.07版)](http://ymotongpoo.hatenablog.com/entry/2013/07/06/154448) * LiteIDE: [go言語の始め方(1)](http://python.matrix.jp/2013/05/09/go_start1.html) #書籍 参考:https://github.com/golang/go/wiki/Books#japanese * [はじめての「Go言語」](http://www.amazon.co.jp/%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AE%E3%80%8CGo%E8%A8%80%E8%AA%9E%E3%80%8D-I%E3%83%BBO-BOOKS-%E8%8C%A8%E6%9C%A8-%E9%9A%86%E5%BD%B0/dp/4777515591) * [Go言語プログラミング入門on Google App Engine](http://www.amazon.co.jp/Go%E8%A8%80%E8%AA%9E%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E5%85%A5%E9%96%80on-Google-App-Engine-%E6%A8%AA%E5%B1%B1/dp/4798031801) * [はじめてのGoogle App Engine Go言語編](http://www.amazon.co.jp/%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AEGoogle-App-Engine-Go%E8%A8%80%E8%AA%9E%E7%B7%A8-BOOKS/dp/4777516601) * [プログラミング言語Goフレーズブック](http://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9EGo%E3%83%95%E3%83%AC%E3%83%BC%E3%82%BA%E3%83%96%E3%83%83%E3%82%AF-David-Chisnall/dp/486401096X) * [基礎からわかる Go言語](http://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E3%82%8F%E3%81%8B%E3%82%8B-Go%E8%A8%80%E8%AA%9E-%E5%8F%A4%E5%B7%9D-%E6%98%87/dp/4863541171)([改訂2版](http://www.c-r.com/book/detail/1018)) * [An Introduction to Programming in Go](http://www.golang-book.com/) * [プログラミング言語Go](https://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9EGo-ADDISON-WESLEY-PROFESSIONAL-COMPUTING-Donovan/dp/4621300253) |
|
| 11位 |
|
|||
|
13:31:42 |
|
|
#これからiOSアプリ開発をやりたい!という人へ
自分が実際にiOSアプリ開発をやって、便利だったと思う情報を残しておきます。 これからアプリ開発やりたいという人の参考になれば。 ##iOSアプリ開発の第一歩 まずは間違いなくMacを買うこと。 Macを書いましょう。Windowsじゃ開発できません。 MacじゃないとiOSアプリの開発はできないので、 これがないとお話になりません。。。 ##インストールしておくもの 基本的にXcodeさえインストールしておけばアプリの開発はできます。 `Mac App Store`からダウンロードできます。 実機インストールや`App Store`に公開したい場合は、 `iOS Developer Program`を購入する必要があります。 ##まずはこれを読むべき Appleのドキュメント集(日本語訳Version) https://developer.apple.com/jp/devcenter/ios/library/japanese.html 日本語タイトルのリンクを開くと日本語PDFを開けます。 初心者向けの内容から各フレームワークにフォーカスしたドキュメントが置いてあります。 iOSのアーキテクチャについて触れているドキュメントも多いので、これを読んでおけば無敵です。 ###まず読むべきモノ 意味がわからなくてもさらっと見ておくと良いと思います。 - iOS ヒューマンインターフェイス ガイドライン(←これ面白い) - iOS View Controller プログラミングガイド - iOS Table View プログラミングガイド - iOS View Controllerカタログ(←これ面白い) ###初心者向け - 初めての iOS アプリケーション - 2つ目のiOSアプリケーション:ストーリーボード - 3つ目のiOSアプリケーション:iCloud ##入門資料 - Mixiの研修資料 https://github.com/mixi-inc/iOSTraining - ドットインストール iOSプログラミング入門 (全24回) http://dotinstall.com/lessons/basic_ios ##よくお世話になったサイト iPhoneアプリ開発の虎の巻 http://iphone-tora.sakura.ne.jp/ Cocoaの日々 http://cocoadays.blogspot.jp/ 強火で進め http://d.hatena.ne.jp/nakamura001/ Over&Out その後 http://d.hatena.ne.jp/shu223/ iPhone | Developers.IO http://dev.classmethod.jp/category/iphone/ ##便利系ツール #####AppCode http://www.jetbrains.com/objc/ jetbrains社が提供しているObjective-C用IDEです。 Storyboardなどを編集する際はXcodeを使わないと編集できませんが、 コードを書くときは圧倒的にこちらのほうが速いです。 補完が優秀であったりリファクタリングが優秀だったりと、 色々機能も豊富なのでストレスも少ないです。 気になる人はぐぐってみてください。 紹介記事も多いようです。 追記:Xcodeとの比較をしている記事があったのでご紹介。 http://developer.smartnews.be/blog/2013/07/16/appcode-increases-objective-c-productivity/ ショートカット集などなど [AppCodeをInstallして最初にすること&ショートカット](http://qiita.com/kouchi67/items/defaaba875546210fece) [AppCodeで知っていると便利なショートカット12選](http://qiita.com/happy_ryo/items/8cf569990eb8f7aa582f) [AppCodeショートカットメモ(コード補完周り)](http://qiita.com/kouchi67/items/9bff6cd9be2d7070fbdf) #####Xcodeチートシート http://www.dotapon.sakura.ne.jp/blog/?p=305 http://devcheatsheet.com/tag/xcode/ 効率は正義。 ショートカットを駆使して爆速コーディング! #####github https://github.com/ iOSのオープンソースがたくさんあります。 開発者として登録もしておくと良いでしょう。 #####Cocoa Controls https://www.cocoacontrols.com/ MacやiOSのオープンソース紹介サイト。 スクリーンショット付きで紹介されているので、 目的のモノを探すときに便利です。 #####Cocoapods http://cocoapods.org/ Objective-Cで書かれたオープンソースのパッケージ管理ソフトです。 基本的にコマンドラインです。 yumとかgemとかに馴染みがある人はとっつきやすいと思います。 ---- ひとまず以上です。 個人的にですが、学習する上で大事だと思う事をいくつか紹介。 - ドキュメントを探す/読む 実装したい機能について、Appleからドキュメントが提供されているかどうかくらいは確認しましょう。 あれば読み込みましょう。 下手なブログ読むよりよっぽど確実で詳しいです。 - ショートカットを覚える 効率的に作業するというのは想像以上に大事です。 効率良く開発をすることは、快適でかつストレスが少ないです。 楽しく効率的に開発を続けていくために是非習得しましょう。 頑張って自分のアプリを作れるようになりましょう! ---- #おまけ - 書籍 独断と偏見で良書だと思ってる書籍を紹介。 個人的には初心者向けな書籍とか必要ない気もします。 最近はブログ等も多く、何よりAppleのドキュメントが豊富なので、 基本的にそちらを見て勉強するのが良い気がします。 ##[iPhone SDK 3 プログラミング大全 実践プログラミング](http://www.amazon.co.jp/exec/obidos/ASIN/4048679910/22301-22/ref=nosim/) 初めて手にとって読んだ本。 iOSの仕組みについて詳しく解説されていた印象がある。 バージョンは古いけど入門資料としてはまだまだ役立つ書籍かもしれない。 ##[iPhoneアプリ開発エキスパートガイド iOS 6対応](http://www.amazon.co.jp/iPhone%E3%82%A2%E3%83%97%E3%83%AA%E9%96%8B%E7%99%BA%E3%82%A8%E3%82%AD%E3%82%B9%E3%83%91%E3%83%BC%E3%83%88%E3%82%AC%E3%82%A4%E3%83%89-iOS-6%E5%AF%BE%E5%BF%9C-%E5%8A%A0%E8%97%A4-%E5%AF%9B%E4%BA%BA/dp/4844333852/ref=pd_sim_b_2) iOS6から追加された機能を丁寧に紹介している。良書。 ##[iOS5プログラミングブック](http://www.amazon.co.jp/iOS5%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%83%96%E3%83%83%E3%82%AF-%E5%8A%A0%E8%97%A4-%E5%AF%9B%E4%BA%BA/dp/4844332228) iOS5から追加された機能を丁寧に紹介している。良書。 ##[iOS4プログラミングブック](http://www.amazon.co.jp/iOS4%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%83%96%E3%83%83%E3%82%AF-%E7%95%91-%E5%9C%AD%E8%BC%94/dp/4844329766/ref=pd_bxgy_b_img_y) iOS4から(略 ##[iOSアプリ開発達人のレシピ100](http://www.amazon.co.jp/iOS%E3%82%A2%E3%83%97%E3%83%AA%E9%96%8B%E7%99%BA%E9%81%94%E4%BA%BA%E3%81%AE%E3%83%AC%E3%82%B7%E3%83%94100%E2%80%95%E9%96%8B%E7%99%BA%E7%8F%BE%E5%A0%B4%E3%81%A7%E5%AE%9F%E8%A8%BC%E3%81%95%E3%82%8C%E3%81%9F%E5%AE%9F%E7%94%A8%E3%82%B3%E3%83%BC%E3%83%89%E9%9B%86-%E5%A0%A4-%E4%BF%AE%E4%B8%80/dp/4798038180/ref=pd_sim_b_1) ちょっと手の込んだ役に立ちそうな処理がたくさん紹介されててオススメ。 上で紹介した「Over&Out その後」の中の人の書籍。 ##[詳解 Objective-C 2.0 第3版](http://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3-Objective-C-2-0-%E7%AC%AC3%E7%89%88-%E8%8D%BB%E5%8E%9F/dp/4797368276/ref=pd_sim_b_44) Objective-Cについて学びたいならこれ。 ちょっと難しいかも。 ##[エキスパートObjective-Cプログラミング -iOS/OS Xのメモリ管理とマルチスレッド](http://www.amazon.co.jp/%E3%82%A8%E3%82%AD%E3%82%B9%E3%83%91%E3%83%BC%E3%83%88Objective-C%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%EF%BC%8DiOS-OS-X%E3%81%AE%E3%83%A1%E3%83%A2%E3%83%AA%E7%AE%A1%E7%90%86%E3%81%A8%E3%83%9E%E3%83%AB%E3%83%81%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%EF%BC%8D-%E5%9D%82%E6%9C%AC/dp/4844331094/ref=pd_sim_b_12) Objective-Cでのマルチスレッドやメモリ管理といった少し複雑な処理について詳しく解説。 これが読めたら脱初心者。 一度は読んでおきたい。 ##[iOS開発におけるパターンによるオートマティズム](http://www.amazon.co.jp/iOS%E9%96%8B%E7%99%BA%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%AB%E3%82%88%E3%82%8B%E3%82%AA%E3%83%BC%E3%83%88%E3%83%9E%E3%83%86%E3%82%A3%E3%82%BA%E3%83%A0-%E6%9C%A8%E4%B8%8B-%E8%AA%A0/dp/4861007348/ref=pd_sim_b_3) RSSリーダーを作成する事を題材にクラス設計などについても紹介されている。 簡単なコードは書けるようになってきたけど、次どうすればいいかわからない・・・ そんな人にオススメ。 自分もこれを読んで勉強しました。 ##[iOS Core Frameworks テクニカルガイド](http://www.amazon.co.jp/iOS-Core-Frameworks%E3%83%86%E3%82%AF%E3%83%8B%E3%82%AB%E3%83%AB%E3%82%AC%E3%82%A4%E3%83%89-Shawn-Welch/dp/4844332724) CoreFrameworkに焦点を当てている。 CoreData、位置情報、グラフィック、音声周りと色々紹介されていてとてもありがたい書籍。 こういった機能に焦点を当てて紹介している書籍は割と少ないので貴重。 ---- 良書があれば随時追加していく予定。 |
|
| 12位 |
|
|||
|
23:04:54 |
(ShouldBee 所属) |
|
![golang] Go言語を始めて38日たったので、これからGo言語を始めるプログラマにちょっとでもヒントになるように、どのようにGo言語を始めて、どのような手順で学んできたかまとめておきます。 # 筆者のバックグランド * PHP歴、JavaScript歴10年~ * Python 3ヶ月くらい * Ruby 1日 * 静的言語経験ほぼ皆無(Java 3日くらい、Scala 3日くらい、Objective-C 1週間くらい、Cそもそも挫折、C# 1日) 静的言語というと、「コンパイルの一手間が面倒」だとか「環境を作るのが面倒」だとか先入観があり、ほぼ食わず嫌い状態でした。 # Go言語とは? Go言語は2009年にGoogleによって作られたオープンソースの静的言語で、Linux・Mac・WindowsやAndroidで動作します。最近ではGoogle App Engineで使うこともできます。 [golang.jpの解説](http://golang.jp/about_go)によると次のような特徴があります。 * シンプルな言語である。 * コンパイル・実行速度が早い。 * 安全性が高い。 * 同期処理が容易に行える。 * なにより楽しい。 * オープンソースである。 # なぜGo言語を学んだか? ひとことで言うと並行処理を簡単に書きたかったからです。Go言語にはGoルーチンというシンプルな並行処理の仕組みがあります。たまたま業務で、同時に複数台のHTTPサーバーにリクエストを出して大量の命令と大量のデータを並行処理するプログラムを実装する必要が出てきて、得意なPHPでは難しいのでGo言語を学ぶことにしました。言語にはそれぞれ特徴があって、適材適所だと思っています。 # 読んだ本  Go言語を始める前に、『[はじめての「Go言語」](http://www.amazon.co.jp/gp/product/4777515591?ie=UTF8&camp=1207&creative=8411&creativeASIN=4777515591&linkCode=shr&tag=suinyeze-22&f_rd_p=466449256&pf_rd_s=lpo-top-stripe&pf_rd_t=201&pf_rd_i=4863541171&pf_rd_m=AN1VRQENFRJN5&pf_rd_r=0WA4QMWKHBWB1PVGQH0X)』をパラパラと読みました。じっくり読むというよりは最低限必要なこと、例えば、基本的な`if`や`for`などの構文の書き方、私のやったことある言語にないGoルーチン(並行処理ができる)の概念などを読んで学びました。 # 参考にしたドキュメント ドキュメント英語版: http://golang.org/doc/ ドキュメント日本語版: http://golang.jp/go_spec # Go関連情報のググり方 「go」だけで検索するとGo言語の情報がヒットしにくい問題があります。英語の情報を探すときは「golang」、日本語の情報を探すときは「Go言語」といったキーワードで探すと情報を見つけやすくなります。 # どのようなことを学んだか? ここからは、38日間にどのようなことを学んできたか、Qiitaに投稿した記事を振り返りながら、概ね時系列でご紹介します。 ## Go言語の実行環境を作る入門以前の段階 * MacにGoをどうやってインストールするのか? * そもそもどうやって実行するのか? * コンパイルはどうするのか? * Macで作った実行形式がLinuxで使えるのか? このような疑問を解決するために調べた内容をまとめた最初の記事です。 2013-12-04 [PHPerだが今日初めてGo言語に手を出してみる試み](http://qiita.com/suin/items/a0b7eb585eeb6bda4174) ## ひとつのファイルにガリガリ書くだけの段階 さすがに1ファイルに収まらない量になってきたので、複数のファイルにコードを分けることについて学ぶ。 2013-12-05 [Go言語のpackageの作り方: 長くなったコードを別ファイルに切り出す方法](http://qiita.com/suin/items/f4ad02e4123c8a3c75eb) ## 試行錯誤の段階 学んでいく過程でstackoverflowやQiitaに出てくるスニペットを試してみたくなりました。そこで、スニペットをさくっと実行できる環境を構築することにしました。 2013-12-05 [ワンクリックでGoのスニペットを試せる環境を作る!: CodeRunnerの設定方法](http://qiita.com/suin/items/599057e5e30627d2ed10) ## 言語仕様の理解を深める段階 Go言語を使うモチベーションが並行処理にあったので、真っ先にGoルーチンについて学びました。また、オブジェクト指向プログラミングを実践するにあたって、オブジェクトの作り方といった基礎的な言語仕様について学びました。 2013-12-05 [Goルーチンで並列化する方法: 6秒かかる処理を3秒にしよう](http://qiita.com/suin/items/82ecb6f63ff4104d4f5d) 2013-12-10 [Go言語でオブジェクトを作ってみる: オブジェクト・メソッド・コンストラクタなど](http://qiita.com/suin/items/b2a81227c75c51579c04) ## 実際にアプリを開発する段階 アプリの開発に着手した段階です。ライブラリ・モジュールの具体的な使い方について調べていました。 2013-12-07 [こんなに簡単! Goで作るRESTサーバー](http://qiita.com/suin/items/f32fa82d6c35a34e8d16) 2013-12-10 [Go言語でファイル操作: ディレクトリの作り方](http://qiita.com/suin/items/af8f306dc6b38a293ef5) 2013-12-10 [Go言語でファイル操作: ファイルを作って書き込む](http://qiita.com/suin/items/7eb4fc405ac73846a9b1) 2013-12-10 [Go言語でコマンドが実行可能かチェックする](http://qiita.com/suin/items/2724f15fff9c1948c5b3) > この頃、PHPerでGo言語はじめたての僕が「型の宣言めんどくさい」とか「ポインタなんてなんで用意したし」とか不満を言っていたら、僕より先にGoを始めてた同僚に「Goに入ればGoに従えと言うじゃないですか」と言われる(笑) 2013-12-11 [Go言語のクロスコンパイル設定値 $GOOS, $GOARCH 一覧リスト](http://qiita.com/suin/items/7ddfcbc708c8863ea76a) 2013-12-11 [Go言語: ディレクトリ内の全ファイルをフォーマットする方法](http://qiita.com/suin/items/9f9bdaa0cb9cb80cf752) (コードスタイルがぐちゃぐちゃになって気持ち悪くなってきた頃) 2013-12-11 [Go言語でRESTful APIクライアント: Qiita APIを叩いてみよう!](http://qiita.com/suin/items/2a38f3e072523fa49a5b) 2013-12-11 [Go言語でPHPのvar_dumpやJavaScriptのconsole.log的なもの](http://qiita.com/suin/items/d952fb963956ac31b243) (そろそろデバッグを覚えたいと思った頃) 2013-12-12 [Go言語でランダムな文字列を生成する](http://qiita.com/suin/items/062dab1e6dc82c81c320) 2013-12-12 [Go言語でPHPのarray_chunkのようなことをする](http://qiita.com/suin/items/d0deb76ff03373b22a0b) 2013-12-12 [Go言語でCSVを書き出す!エクセル用のSJIS版も!](http://qiita.com/suin/items/bb9d817af5ccedb18be9) 2013-12-18 [Go言語:SJISのCSVファイルを読み込む!](http://qiita.com/suin/items/23b2c2e7cb7c80ca857e) 2013-12-18 [Go言語: SJISのCSVを読み込みながら1行毎に処理を実行する](http://qiita.com/suin/items/347ecda071a6999e5d22) 2013-12-18 [Go言語: PHPのarray_combine()のような処理をする関数](http://qiita.com/suin/items/3e978caa0c7f4ffcdd97) 2014-01-06 [[教えて]Go言語:なぜインターフェイスはポインタにできない?](http://qiita.com/suin/items/68ed7020d21dca047a73) (このあたりからやっとポインタを意識し始める) 2014-01-07 [Go言語:変数が関数かどうかを返す関数](http://qiita.com/suin/items/468108496cd0dc002f64) 2014-01-07 [Go言語:メソッドの引数の型を調べる方法](http://qiita.com/suin/items/ec2070fde6700ffc4a7f) 2014-01-10 [Go言語:Riakに書き込む](http://qiita.com/suin/items/942773fd059272825eb8) ##「そろそろテストを書かなきゃ」と思い始めた段階 モジュール化やオブジェクト指向プログラミングをGo言語でできるようになってきて、一ファイルの一枚岩コードから脱却していました。テストも書きやすい状態になっていたので、単体テストのやり方について学びました。また、切り出したコードをオープンソースとして公開したり、継続的インテグレーションのやり方について学びました。 2013-12-27 [Go言語: 単体テストの始め方!Gospelを使ってBDDをやってみよう](http://qiita.com/suin/items/71f28f06e7a49414cc93) 2014-01-06 [Go言語:単体テストを複数のパッケージに対して実行する](http://qiita.com/suin/items/850b39625c92437d2362) 2014-01-08 [Go言語:自分のライブラリをGitHubで公開する方法+drone.ioで継続的インテグレーション](http://qiita.com/suin/items/1237f210ef9e81b6475d) # マスコット Gopher このかわいいマスコットは、Go言語のマスコットです。Gopherと言います。日本語ではホリネズミというそうです。 <center>Gopherのフィギュア  </center> # 使ってみて気づいたGo言語の魅力 Goを始めて3日目たったころにGoの魅力を感じ始めました。下記はそのときに書いた感想です。 > * クロスコンパイルが超カンタン。Macで開発しててもLinux、Windows用のバイナリが作れる > * コンパイル時にポカミスが洗い出される。動的言語にはない安心感 > * まるでスクリプト言語を書いてる感覚。いつコンパイルしてるの? 型推論すごすぎ! > * 並行処理たったそれだけでできるの!? > * ビルドすると一つの実行形式バイナリファイルになる! このポータビリティは気持ちいい > * パッケージ管理システムがビルトインなので、"import github.com/user/project" って書くだけでパッケージがすぐ使える! 神! > * defer。finallyがない言語はやってらんないと思ってたけど、deferのほうがいいじゃん! > * どんな汚いコードを書いてもGoが整頓(format)してくれる。コーディング規約じゃなくて、自動フォーマットでコードの個人差を吸収するほうが健全な気がしてきた > * ドキュメントがしっかりしてる。 > * タブインデントがデフォ! 俺得 # Go言語はこれからどこで役立つ? DevOpsやImmutable Infrastructureなどインフラ構築では今後欠かせなくなりそうな [Docker]、[Packer]、[Serf]といったオープンソースはGoで書かれています。これらのプロダクトは既に注目されていますが、今後ますます標準的なものになっていくだろうと予想します。そうした中でDevOpsを中心にGoで書かれるプロダクトも増えていくのではないでしょうか。 <center>Go言語のトレンド [Google Trands](http://www.google.com/trends/explore#q=golang&date=1%2F2009%2061m&cmpt=q) より  </center> # Go言語を学んで変わったこと 僕は15歳から22歳までずっと趣味のアマチュアプログラマでした。当時は好きなコードを好きなように書いていて、ストレスフリーでコーディングそのものが楽しいと感じていました。 趣味のコーディングと業務のコーディングの決定的な違いは、「品質に責任を負うかどうか」という点だと思います。品質を担保するために、ドメイン駆動設計やDevOps、テスト駆動開発、リーンソフトウェア開発、オブジェクト指向、アジャイル、デザインパターン、継続的デリバリーといった言語を超えた上位レイヤにばかり注目していて、コーディングは単なる作業になっていました。 Goは「プログラミングは楽しくあるべき」という考えから生まれた言語らしいです。その思想はいろんなかたちで現れてて、書けるようになればなるほど楽しくなる言語だと思います。ひさしぶりにコーディングそのものの「楽しさ」を思い出すことができました。 --- もしGoをやっていて困ったときは、suinがチャットで相談に乗ります。もちろん、無償です(๑•̀ㅂ•́)و✧ [suinのプログラミング相談室](https://help.suin.io/) [Packer]: http://www.packer.io/ [Docker]: https://www.docker.io/ [Serf]: http://www.serfdom.io/ [golang]: https://qiita-image-store.s3.amazonaws.com/0/889/5bc1dd05-2d92-e4bc-afa0-7c6cfe1eb6d7.png |
|
| 13位 |
|
|||
|
18:08:50 |
(フリーランス 所属)
|
|
by @mixiappwchr
iOSで開発する上で見ておくべきサービスやツールをまとめてみました。 ほかにもたくさんあると思いますが、基本的なやつを集めました。 アプリ配布 ---- depolygate https://deploygate.com/?locale=ja  旧TestFlightがオワコンのため、今ではDeployGateを使うしかありません! [まだTestFlight使ってたの?急げ!終了目前のTestFlightから,今すぐにiOSもDeployGateに移行しよう!移行パターンも紹介するよ。](http://qiita.com/appwatcher/items/632460e15fbdb81b7a71) CI ---- Circle CI https://circleci.com/  iOS対応されアプリもクラウドでCI クラッシュログ ---- Crittercism https://www.crittercism.com/  ユーザービリティテスト --- Repro https://repro.io/  リモートでユーザーの行動をアプリの実際の画面や操作しているユーザーの表情、音声まで録画してアップロード、解析できるサービスです バグに負けない。 ライブラリ管理 ---- cocoapods http://cocoapods.org/  ないともう無理です。 cocoapodsの検索、人気順などのサイトも http://cocoapods.wantedly.com/  UIライブラリ集 ---- cocoacontrols https://www.cocoacontrols.com/  ちょっとしたときに使えるものがあったりします。 実装の参考にも アプリストア解析 ---- appannie http://www.appannie.com/  自分のアプリの動向や海外ではやっているアプリの情報なども確認できますね。 アプリ解析 ---- Google Analytics http://www.google.co.jp/intl/ja/analytics/  Flurry http://www.flurry.com/  Google analyticsにない情報もあり、こっちを使うのもあり マネタイズ ---- 数が多すぎるので2つだけ。 Admob http://www.google.co.jp/ads/admob/  nend http://nend.net/  日本だとnendが良さそうですね。 Baas ---- parse https://parse.com  pushの部分だけでも便利ですね。 mobile backend starter https://developers.google.com/cloud/samples/mbs/  google さんのやつや aws sns http://aws.amazon.com/jp/sns/  amazonさんのやつ UI/UXのネタ ---- UI大事です。 dribbble http://dribbble.com/  http://jp.pinterest.com/  pinterstもUI/UXのボードなどみると、インスピレーションが色々わきます。 sixux http://sixux.com  UIのアニメーション、インタラクションの参考に非常になりますね。iOSは細かい部分のインタラクションがキモです capptivate.co http://capptivate.co/  こちらはUIコンポーネントやトランジッションパターンごとにきれいに分類されているため見やすいです プログラミング情報源 ---- stackoverflow http://stackoverflow.com/  だいたいのことはここに詰まってます。 qiita http://qiita.com/  ここ! UIモックアップ ---- ツール ---- 有料ですが、便利 reveal http://revealapp.com/  webのようにUIの解析ができるツール SparkInspector http://sparkinspector.com/  Revealに類似しているツールでSparkInspectorという製品を使っています。ビューとNotificationを検査することが出来てます。後者が意外と便利。[keyさんより](http://qiita.com/key) Reflector http://www.airsquirrels.com/reflector/  airplayで端末を表示。アプリのレビューに便利。 Sketch http://www.bohemiancoding.com/sketch/  AppStoreで購入できるデザインツールで、iOSデバイスに作成中のデザインをライブプレビューできます。デザイナ寄りのツールですが、UIパーツのサイズ感がわかるので重宝しています。[keyさんより](http://qiita.com/key) MAKEAPPICON http://www.makeappicon.com/  1024x1024 の画像から iOS7の120xのアイコンを作ってくれるサイトです.[u16suzu@githubさんより](http://qiita.com/u16suzu@github) ほかに役に立つものがあればぜひ教えてください! ##### appwchr post ---- [あんなすごいエンジニアともであえるかも??Qiitaユーザーが集まるSlack Teamを作ってみたよ!](http://qiita.com/appwatcher/items/00faadbf02f691186a54) [goからiOSまで一人でアプリ開発をしてたらいつの間にかマインクラフトエンジニアになった話](http://qiita.com/appwatcher/items/6c0280cda9c4c8b3c65f) [API開発の効率化の架け橋!APIのStubサーバーを導入して,API開発に効率化、スピード化、柔軟性を手に入れよう!](http://qiita.com/appwatcher/items/49807e72d9600db7196d) [アプリエンジニアから見てAPI設計において気をつけてもらえるとうれしいこと](http://qiita.com/appwatcher/items/4a0dd09c393cc70d961a) [Goodbye... Jenkins... Jenkinsを卒業してお手軽CI! iOSもAndroidもCircle CIでアプリのCIを回そう](http://qiita.com/appwatcher/items/4cdf39804d6e46ab7af5) [まだTestFlight使ってたの?急げ!終了目前のTestFlightから,今すぐにiOSもDeployGateに移行しよう!移行パターンも紹介するよ。](http://qiita.com/appwatcher/items/632460e15fbdb81b7a71) [Swiftを使ってみて直面した闇。現時点で現場でSwiftを採用すべきかどうかの判断材料](http://qiita.com/appwatcher/items/50295e82ab0095902aaa) [iOSの開発をする上で絶対に使うべき!外せない!webサービス、開発ツール集【完全版】](http://qiita.com/appwatcher/items/07a3babcb9b6cefb307e) [注目のiBeacomなどの波に乗り遅れないために!iOSのBluetooth開発を容易にするライブラリを書きました。] (http://qiita.com/appwatcher/items/7491beffd7260b713542) [まだまだあった!iOSの開発を劇的に改善する最新のwebサービス、開発ツール集1] (http://qiita.com/appwatcher/items/f0024fe2ac34da345f04) [さらに快適なアプリ開発を!iOSの開発をもっと劇的に改善する最新のwebサービス、開発ツール集2] (http://qiita.com/appwatcher/items/c15d7311e71b4c2b77f1) [スパゲッティから脱出!iOS開発における遷移の問題をすっきり解決する便利ルーティングライブラリをご紹介] (http://qiita.com/appwatcher/items/259e8d13fff0547e90af) |
|
| 14位 |
|
|||
|
21:57:39 |
(サイバーエージェント 所属) |
|
#javascriptの「this」は「4種類」??
この記事ではベースとなる4種類の「this」を紹介します。 実際は4種類ではないのですが、 このベースの4種類を理解できれば他もすぐに理解できます。 #thisの4種類のパターン 1:メソッド呼び出しパターン 2:関数呼び出しパターン 3:コンストラクタ呼び出しパターン 4:apply,call呼び出しパターン ここで重要なのは「呼び出し元」をみることです。 なぜなら「呼び出し元」に「this」は左右されるからです。 #メソッド呼び出しパターン これはもう一番直感的にわかりやすいです。 説明はいらないんじゃないかという感じなのですが、 一応ソースを。。 ```javascript:メソッド呼び出しパターン //メソッド呼び出しパターン var myObject = { value: 10, show: function() { console.log(this.value); } } myObject.show(); // 10 ``` かなり直感的ですね。 thisにはmyObjectが入っています。 #関数呼び出しパターン 関数とメソッドと二つの呼び方をしてるのが、 ややこしく聞こえるかもしれません。 ただコードを見たらすぐに理解できます。 ```javascript myObject.show(); // メソッド呼び出し show(); // 関数呼び出し ``` 「.」で呼ばれているかどうかの違いのみですね。 では関数呼び出しパターンの「this」はいかに… ```javascript:関数呼び出しパターン function show() { console.log(this); this.value = 1; // 注1 } show(); // thisはグローバルオブジェクトをさす ``` この場合は「this」は「グローバルオブジェクト」を指してしまいます。 なので、注1の「value」は「グローバル変数」となります。 これを抑えておくのがポイントです。 次の例を考えてみましょう。 ```javascript:関数呼び出しパターン var myObject = { value: 1, show: function() { console.log(this.value); // 注1 function show() { console.log(this.value); // 注2 } show(); } }; myObject.show(); ``` ここで注1と注2の答えはわかりましたか? 注1の「this.value」は「1」 注2の「this.value」は「undefind」 注1は一つ目のメソッド呼び出しパターンですね。 注2が最初紛らわしいです。。 メソッド呼び出しの中で関数呼び出しされているので、 あくまで注2の「this」はグローバルを指してしまいます。 メソッド内で関数呼び出しになっているっていうのが落とし穴ですね。 でもこれも簡単に解決できます。 ```javascript:関数呼び出しパターン var myObject = { value: 1, show: function() { var self = this; console.log(self.value); // 1 function show() { console.log(self.value); // 1 } show(); } }; myObject.show(); ``` 「this」を別の変数で持っておきます。この手法はよく使われます。 慣用的に変数は「self」, 「that」, 「_this」のどれかが使われる事が多いです。 #コンストラクタ呼び出しパターン 次はコンストラクタ呼び出しパターンです。 ```javascript:コンストラクタ呼び出しパターン function MyObject(value) { this.value = value; this.increment = function() { this.value++; }; } var myObject = new MyObject(0); console.log(myObject.value); // 0 myObject.increment(); console.log(myObject.value); // 1 ``` これもこれだけみるとわりと直感的ですね。 「new」をつけてインスタンス生成ってことですね。 そしてその生成されるインスタンス自身が「this」にsetされます。 ただ気をつけたいのが、これはただの「関数」です。。 「new」をつけなかった場合, MyObject(0); これは関数呼び出しなので、 「this」はグローバルオブジェクトを指してしまいます。。 この場合はvalueとincrementは 二つともグローバル変数として定義されることになります。 コンストラクタ呼び出しを期待する場合は、 最初の文字を大きくするのが慣例ですので、この場合は「new」は必ずつけましょう! #apply, call呼び出しパターン これは「this」を好き勝手に設定できますよっていうことです。 ライブラリとかを自作するときは非常に重宝します。 ```javascript:apply,call呼び出しパターン var myObject = { value: 1, show: function() { console.log(this.value); } }; var yourObject = { value: 3 }; myObject.show(); // 1 myObject.show.apply(yourObject); // 3 myObject.show.call(yourObject); // 3 ``` 「apply」と「call」を使うと「強制的にthisを束縛」できます。 「apply」と「call」の第一引数は「this」にsetしたいオブジェクトです。 あれ?…じゃあ「apply」と「call」は何が違うのかって、 それは第二引数以降の取り方が変わってきます。 ```javascript:apply,call呼び出しパターン var myObject = { add: function(value1, value2) { console.log(this.value + value1 + value2); } }; var yourObject = { value: 3 }; myObject.add.apply(yourObject, [2, 10]); // 15 myObject.add.call(yourObject, 2, 10); // 15 ``` 「apply」は第二引数に配列をとり、配列の中身が引数として渡されます。 「call」は直感的で、第二引数以降がそのまま渡されます。 二つとも第一引数は「this」で、その後の引数の取り方が違うだけです。 #まとめ 「呼び出し元」に気をつけろ! 覚えるべきベースは4種類。 1:メソッド呼び出しパターン 2:関数呼び出しパターン 3:コンストラクタ呼び出しパターン 4:apply,call呼び出しパターン てな感じです。 あっさりまとめたので、わからないところがあれば聞いてください♪ それからコンストラクタについて詳しく知りたい方はこちら [JavaScriptのクラス?コンストラクタ?](http://qiita.com/items/010752b1427773558f7c) # 外部アカウント 技術情報のみつぶやくアカウント作成しました。JavaScriptは最新情報も追っていきます。 [Twitterはこちら] (https://twitter.com/takeharumikami) [Feedlyのフォローはこちら] (http://cloud.feedly.com/#subscription%2Ffeed%2Fhttp%3A%2F%2Fqiita.com%2Ftakeharu%2Ffeed) # おすすめの記事 もういい時期です。そろそろ始めましょう。ECMAScript6。 [もうはじめよう、ES6~ECMAScript6の基本構文まとめ(JavaScript)](http://qiita.com/takeharu/items/cbbe017bbdd120015ca0) JavaScriptはオブジェクト指向?プロトタイプベースのオブジェクト指向を学ぶなら。 [JavaScriptのプロトタイプからオブジェクト指向を学ぶ](http://qiita.com/takeharu/items/809114f943208aaf55b3) JavaScriptでは関数はすべてクロージャ。 [そもそもクロージャって?JavaScriptでクロージャ入門](http://qiita.com/takeharu/items/4975031faf6f7baf077a) |
|
| 15位 |
|
|||
|
11:17:52 |
|
|
# 背景
自前のサービスでhttps通信をサポートするには、SSL証明書が必要になります。 自分で使用するだけなら、SSL証明書も自前で作成するいわゆるオレオレ証明書を用いても良いのですが、外部に公開するサービスの場合そうとも行きません。 SSL証明書というと値段が高い印象がありましたが、[StartSSL](https://www.startssl.com/)というサービスで無料でSSL証明書の発行を受けられると言うことで試してみました。 # StartSSLにユーザー登録する 証明書の発行を行う前に、StartSSLにユーザー登録する必要があります。 [StartSSL](https://www.startssl.com/)から、"StartSSL Free (Class1)"を選択します。  Certificate Control Panelを選択。  Sign-upに進みます。  名前、住所、メールアドレスなど 個人情報の登録を行います。  登録したメールアドレスに本人確認のメールが届くので、受信したメールのauthentication codeを入力して登録を完了させます。 この時、「もっと詳細な住所を教えて欲しい」と言う旨のメールが送られてくることがあります。 その場合はStartSSLの担当者とメールをやりとりして登録を完了させましょう。  登録を完了すると、StartSSLへのログイン用SSL証明書発行のためのURLが送られてきます。 そのリンクからブラウザに証明書をインストールしましょう。 暗号化セキュリティーレベルはデフォルトの2048(高)で良いと思います。 この時にインストールした証明書が無いとStartSSLにログインできません。 他の端末や、ブラウザでアクセスする場合は証明書のエクスポートが必要になります。  # ドメインの認証 ユーザー登録が完了したら、次はドメインの認証です。 Validations Wizardタブを選択します。  証明書を発行したいドメインを登録します。  自分が管理しているドメインであることを示すために、登録したドメインのpostmaster, hostmaster, webmasterなど、いずれかのメールアドレスでメールを受け取る必要があります。 メールを受信できるように事前に設定しておきましょう。  先ほど指定した名前に確認用メールが送られてくるので、メールに記載されているコードを入力して進みます。  # 秘密鍵の発行 ドメインの認証が終わったら、秘密鍵の発行です。 Certificates Wizardタブを選択し、Certificate TargetはWeb Server SSL/TLS Certificateを指定します。  パスワードを設定して進みます。  private keyが作成されたので、テキストエリアの情報を保存します。  # 公開鍵の発行 登録したドメインを選択します。  サブドメインを登録します。  次に進むとメールを送信するという表示がされるので、メールが来るのを待ちます。 メールを受信したら、Tool Boxタブに移動し、Retrieve Certificateを選択します。  すると公開鍵が表示されるので、テキストエリアの内容を保存しておきます。  # 証明書の設置 サーバーにSSL証明書を設置し、HTTPSでの通信を出来るようにします。 今回はnginxでSSL証明書を利用できるようにしてみます。 SSL証明書を設置するディレクトリに移動します。 ``` cd /etc/pki/tls/certs/ ``` StartSSLの証明書をダウンロードします。 ``` wget https://www.startssl.com/certs/ca.pem wget https://www.startssl.com/certs/sub.class1.server.ca.pem ``` 先ほど保存しておいた秘密鍵と公開鍵を配置します。 今回はそれぞれ`startssl.key`と`startssl.crt`と言う名前で設置しました。 例えばvimでファイルを作成して鍵の中身を貼り付けます。 ``` vim startssl.key vim startssl.crt ``` 証明書と鍵のパーミッションを変更します。 ``` chmod 400 startssl.crt startssl.key ca.pem sub.class1.server.ca.pem ``` Apacheを使用する場合は中間証明書を指定するディレクティブがありますが、nginxには中間証明書を直接指定するディレクティブが用意されていません。 そこでサーバ証明書と中間証明書を結合します。 証明書は順に辿らないと駄目なので、結合の順番には注意しましょう。 ``` cat sub.class1.server.ca.pem >> startssl.crt ``` 秘密鍵にはパスワードが設定されているため、nginxのstart/restart時にそのパスワードを入力する必要が出てきます。 自動起動する場合には不便なので、パスワードを解除する場合は以下のようにバックアップを作成した後、パスワードがない鍵を作成します。 ``` cp startssl.key startssl.key.org openssl rsa -in startssl.key -out startssl.key ``` nginxの設定を一部抜粋します。 `ssl_certificate`で中間証明書と結合した証明書を、`ssl_certificate_key `で秘密鍵を指定します。 オプションとしてSSLダウングレード攻撃を考慮して、SSLバージョン指定`ssl_protocols`,暗号スイート指定`ssl_ciphers `,暗号選択方式指定`ssl_prefer_server_ciphers `を変更しました。 ```nginx server { listen 443 ssl; ssl_certificate /etc/pki/tls/certs/startssl.crt; ssl_certificate_key /etc/pki/tls/certs/startssl.key; ssl_session_timeout 5m; # SSLv2以下は利用しない ssl_protocols SSLv3 TLSv1; # 暗号スイートの指定 ssl_ciphers RC4-SHA:HIGH:!ADH; # 暗号の選択をサーバ側で決定 ssl_prefer_server_ciphers on; } ``` # まとめ 無料のSSL証明書ではありますが、PCブラウザおよびiPhoneからのアクセス等でも警告なくHTTPSでの接続が可能になるので便利に活用できるのではないでしょうか。 |
|
| 16位 |
|
|||
|
16:13:28 |
|
|
 システムには障害がつきものです。どんなにしっかりと作られたサービスであっても思わぬところで、バグやミスが発覚して、トラブルになるものです。大事なのはこういった障害を次への糧にしていくこと。失敗というのは大事な資産なので、管理できるようにしましょうという話。 # あわせて読みたい * [新人プログラマに知ってもらいたいメソッドを読みやすく維持するいくつかの原則](http://qiita.com/hirokidaichi/items/c9a76191216f3cc6c4b2) * [新人プログラマに知っておいてもらいたい人類がオブジェクト指向を手に入れるまでの軌跡](http://qiita.com/hirokidaichi/items/591ad96ab12938878fe1) * [ペアプログラミングして気がついた新人プログラマの成長を阻害する悪習](http://qiita.com/hirokidaichi/items/27c757d92b6915e8ecf7) * [あきらめるにはまだ早い!ソースコードの品質向上に効果的なアプローチ](http://qiita.com/hirokidaichi/items/5a5cb63ef5499143bc40) メンタリングの方法について基礎をまとめました。内心でなく行動を変えることが障害報告とも共通します。 * [新入社員が来てメンターになれって言われたけど、どうすればいいのかという対話テクニック](http://qiita.com/hirokidaichi/items/2e8e731acfd7b6c7e02f) #障害の種類と障害報告について 障害には、小さなもの、たとえば画面に表示されているテキストの乱れから、すべての画面で50xエラーが発生しているもの、決済とのつなぎ込みが失敗し、金銭的な影響が大きいものなど様々あります。どのような時に障害報告を書くのかと言われれば、「できればすべて書きましょう」というのが原則になると思います。 大前提として、「障害報告は始末書や反省文ではない」ということです。仕方なく、嫌嫌恥ずかしい思いをしながら書く必要はありません。うちはそういう文化じゃないんだと言う場合は、自分たちのチームから障害報告を始めてみるとよいのではないでしょうか。 #ハインリッヒの法則、あるいはヒヤリハット http://ja.wikipedia.org/wiki/ハインリッヒの法則 ハインリッヒの法則というものがあります。これは、1件の重大な事故の背後には29件の軽微な事故、300件のヒヤリハット事例(事故にはならなかったけど、ヒヤッとしたこと)が隠れているという統計的な経験則です。 これは、逆説的に言えば、小さな障害報告であってもしっかりとマネージすることで重大な事故を防ぐことができるだろうということを意味しています。 #障害報告のフォーマットについて 障害報告はできれば、ticket/issue管理システムなどで管理した方が良いでしょう。なければ、wikiのようなものでもかまいません。そのときのフォーマットとしては、最低限、次のようなものを用意するといいでしょう。 * 障害内容サマリ * カスタマーフィードバック * 障害タイムライン * 対応差分 * 再発防止策 ## 障害内容サマリ 障害内容のサマリを端的な文章で記述しましょう。このとき、やってしまいがちなのは、「起きた現象」の内容ではなくて、「原因やバグが発生しているモジュール名」などを書いてしまいがちだったりします。ここに書くべきは、技術者以外も含めたメンバーが理解できるような現象面について言及することです。 たとえば、 「HogeFuga_master DBの物理障害が発生」 ではなくて、 「Hoge表示画面でinternal server errorと表示される」 「Hogeの投稿ができない不具合が発生」 などと記述します。 これは、影響の大きさを営業部門やサポート部門と共有するためにも大事なことです。 ## カスタマーフィードバック 発生した障害に関して、問い合わせの件数やその内容などを記述します。あるいは関連部門の人に記述してもらいましょう。これは、影響の大きさを可視化するのに役立ちます。また、障害発生時にそういった部門との情報共有を必要とするフォーマットになるため、障害対応から問い合わせ応答や、障害発生のお知らせを出すための連携もしやすくなります。 ## 障害タイムライン 障害タイムラインとは、発生時刻、認知時刻、対応開始時刻、対応完了時刻、その他必要な時系列の対応状況に関する報告です。また、それぞれについて、「誰によって」「いつ」「どのように」がわかるとよりよいでしょう。 これを記述する理由は、再発防止策の検討の際にも役に立つからです。たとえば、発生時刻から認知時刻までの差が「2時間」であった場合、これを「10分」にすることができるだけでも、障害の影響度は変わってきます。また、「認知時刻から対応開始まで」「対応開始から対応完了まで」の時刻も同様です。 障害の影響度については、 ``` 障害の影響度=影響範囲 * 金銭的重要性 * 対応完了までにかかった時間 ``` このように考えるとよいでしょう。 ## 対応差分 対応差分へのリンクを必ず張りましょう。障害の理由は文章だけでは、伝わらないケースも多いです。 ## 再発防止策 再発防止策は、障害報告を書く最大の理由です。障害報告を書いた時点では、すべてを記述する必要はありませんが、再発防止策が完了するまでは、チケットをクローズしないことが重要です。また、再発防止策を記述するときは、「原因(だと思ったもの)」と「対策」をしっかりと記述しましょう。なぜなら、原因だと思ったものが共有されていない場合、対策が的外れになってしまう場合もあるからです。 また、その際に * 直接原因 : その不具合が発生した直接的原因 * 間接原因 : その不具合に至るまでの間接的な原因 * 本当の理由: なぜ、このようなパターンの障害が発生したか のように3段階くらいに再発防止策を検討していけるとよいでしょう。これが三段階なのは前述のハインリッヒの法則をイメージしてもらうとわかりやすいと思います。一つの障害には、300のヒヤリとしている事例、あるいはメンバーの慣れや、習慣、努力によってたまたま防ぐことができている障害が隠れています。直接的な原因を潰すだけでは、再発防止策としてはあまり適していないケースが多いです。そうではなくて、もう一段トラブルを引き起こした間接的な問題がはらんでいて、その問題を解決することで本当の再発防止となることが多いからです。 また、前述の通り、障害の影響度の式をイメージして、影響度を減らすための方策も重要です。 * 影響範囲 :同様の問題が起こっても影響範囲の極小化はできないか? * 対応完了までの時間 :同様の問題が起こっても、短い時間で、あるいは自動的に対応できないか? という問いも再発防止策を検討する上では重要です。 #再発防止検討サイクルについて ## チーム振り返り 再発防止策の検討は、定期的にチームで振り返ることが大事です。この際に障害を引き起こしたチームメンバーに対する糾弾のようにならない配慮が重要です。再発防止策の検討には、かならずファシリテータを用意し、画面やホワイトボードに注目させ、また、ブレストのような自由闊達とした雰囲気づくりが重要です。お菓子を用意してもいいですし、適度なアイスブレイクも重要でしょう。 この際に、次に述べる良い再発防止策と悪い再発防止策のペーパーをメンバーは見ることができるようにしておくのも大事です。 また、これはファシリテータの仕事ですが、原因->対策という順番を忘れずに議論することが大事です。 ## セカンドオピニオン 再発防止策は、チーム外のメンバーで振り返るシーンを作った方が良いでしょう。その理由の1つは、情報を他のチームのメンバーと共有することでより広範囲の問題を解決する案が出てくるかもしれないこと。もうひとつは、チームの事情によってかかるバイアスを排除した意見をもらうことができることです。チームで行動していると、忙しさから場当たり的な問題解決をしがちになりますし、それを互いに指摘し合える関係を他のチームと構築することで、障害という資産を活かすことができる文化が生まれてくるでしょう。 # 良い再発防止策と悪い再発防止策 ## 良い再発防止策 良い再発防止策について、順位付けするとしたら、 1. その種類の問題について二度と意識することがなくなる解決策 1. その種類の問題を開発時に自動的に検知することができる解決策 1. その種類の問題が発生しても自動的に復旧することができる解決策 1. その種類の問題が発生しても影響が局所化される、フールプルーフ、フェールセーフになる解決策 このような順序でしょうか。 ## 悪い再発防止策 悪い再発防止策は、シンプルで人間というコンポーネントを使うことです。 1. 責任回避のため稟議、決済経路を追加する策 1. 他者の努力/忍耐/根性の不足を指摘し改善を求める策 1. 個人/チームの注意力を原因とし、「より注意深く確認します/させます」といった策 1. それっぽい言い訳付きのダブルチェック/トリプルチェック体制 1. ドキュメントにその旨追記します!的な解決策 これは、そんなことやらないよと思っていると、ついついやってしまいがちなので注意が必要です。 「ちゃんと」「しっかり」というフレーズが出てきたら、危険信号です。 # まとめ + 障害報告は資産。どんなものでも作成し有効活用しよう。 + 人間というコンポーネントを再発防止に入れない + セカンドオピニオンをもうけよう 以上です。 |
|
| 17位 |
|
|||
|
00:16:48 |
|
|
#はじめに
ここでは、MacBookAirで私が使っている便利ツールを紹介していきます。長文過ぎると、途中で表示できなくなってしまうことを学習したため、不要な解説は省略します。また、個人的な価値観から形成された表現を含むかもしれませんが、その点の説明も省略します。ご了承ください。 便利なアプリを知っていたら、是非コメントをお願いします。  ##MacBookAirにインストールしたアプリ  01. [BetterTouchTool](http://blog.boastr.net/) //トラックパッド拡張、ショートカットキー拡張 02. [Google Chrome](http://www.google.co.jp/chrome/intl/ja/landing_ch.html) //インターネットブラウザ 03. [Growl](http://growl.info/downloads) //通知を拡張するアプリ 04. [Kopypasta](http://itunes.apple.com/jp/app/kopypasta/id463243494?mt=12) //クリップボードをバックアップ 05. [WindowFlow](http://itunes.apple.com/jp/app/windowflow/id414445104?mt=12) //ウィンドウ切り替え 06. [XtraFinder](http://www.trankynam.com/xtrafinder/) //Finderを拡張するアプリ 07. [Xcode](https://developer.apple.com/xcode/) //開発環境を提供するアプリ 08. [TinkerTool](http://www.bresink.com/osx/TinkerTool.html) //Macの隠し機能を簡単に有効にするアプリ 09. [VirtualBox](https://www.virtualbox.org/wiki/Downloads) //仮想環境を提供するアプリ 10. [Skitch](http://itunes.apple.com/jp/app/skitch/id425955336?mt=12) //スクーンショットの撮影と画像の編集を行うアプリ 11. [DashExpander](http://itunes.apple.com/jp/app/dashexpander/id458867049?mt=12) //無料の高機能エディタ 12. [Alfred](http://www.alfredapp.com/) //キーボード操作拡張 13. [Google IME](http://www.google.co.jp/ime/index-mac.html) //Google 日本語入力 14. [Dropbox](https://www.dropbox.com/) //共有ドライブ 15. [ShiftIt](http://code.google.com/p/shiftit/) //ウィンドウ操作を簡単にするアプリ 16. [Kobito](http://kobitoapp.com/) //Qiitaへの投稿を簡単にするアプリ 17. [GitHub for Mac](http://mac.github.com/) //GitHubへの投稿を簡単にするアプリ 18. [GIMP](http://www.gimp.org/downloads/) //言わずと知れた高機能の画像作成・編集アプリ 19. [Notational Velocity](http://notational.net/) //Simplenoteクライアント。メモ帳 20. [Evernote](http://www.evernote.com/about/intl/jp/) //高機能メモ帳Evernoteのクライアントアプリ。 21. [KeePassX](http://keepass.info/download.html) //パスワード管理アプリ。 22. [MacVim](http://code.google.com/p/macvim-kaoriya/) //Vimエディタの日本語拡張版 23. [iTerm2](http://www.iterm2.com/) //ターミナルアプリ。設定項目が豊富で安定しています。 24. [ClamXav](http://www.clamxav.com/download.php) //スキャン 25. [ClipMenu](http://www.clipmenu.com/ja/) //履歴機能を強化 26. [CheatSheet](http://www.cheatsheetapp.com/CheatSheet/) //ショートカットキーヘルプ 27. [NetBeans](http://ja.netbeans.org/) //日本語ドキュメントがしっかりしたIDE 28. [VLC](http://www.videolan.org/vlc/) //メディアプレイヤー 29. [Type2Phone](http://itunes.apple.com/jp/app/type2phone/id472717129?mt=12) //iPhoneのBluetoothキーボードにする 30. [dolipo](http://drikin.com/2010/11/dolipo.html) //ネット環境の高速化 31. [TeamViewer](http://www.teamviewer.com/ja/index.aspx) //WAN経由でのGUIリモート補助 32. [AccessMenuBarApps](http://www.ortisoft.de/accessmenubarapps/) //メニューバーへのアクセス改善 ##Macの基本操作について  私は、トラックパッドを全く使いませんので、あまり関係ないのですが、`BetterTouchTool`よりも`jitouch 2`のほうがよさそうだという話があります。このアプリの紹介とMacの操作全般について参考になりそうな記事を載せておきます。 参考記事 01. [ガチのガチでトラックパッド操作が捗る「jitouch 2」を紹介するよ!だから「BetterTouchTool」は今すぐ窓から捨てなよ。](http://boku-note.com/mac/jitouch2_apptivate_clcllite/2155/) ##ブラウザ環境 便利なGoogleChrome拡張機能を紹介します。動作は`:help`などで確認してください。特に、[連続HIT-A-HINT](http://k2nr.blogspot.jp/2011/09/vichrome-extension.html)が便利です。  01. [ViChrome](https://chrome.google.com/webstore/detail/gghkfhpblkcmlkmpcpgaajbbiikbhpdi) //キー操作補助 02. [gleeBox](http://thegleebox.com/) //コマンド操作 ##メイン環境  01. iTerm2 //Terminal 02. zsh //Shell 03. Vim //Editor 04. tmux //Virtual 05. NetBeans //IDE 06. w3m //Browser ##各種設定ファイル 長くなりすぎると、記事が途中で切れるので、設定ファイルはあくまで参考程度に一部のみ載せています。GitHubで公開されているものを参考にするのがよいと思います。 ```~/.zshrc # w3mでgoogle検索 function google() { local str opt if [ $ != 0 ]; then for i in $*; do str="$str+$i" done str=`echo $str | sed 's/^\+//'` opt='search?num=50&hl=ja&lr=lang_ja' opt="${opt}&q=${str}" fi w3m http://www.google.co.jp/$opt } ``` ```~/.vimrc " jj でノーマルモードに移動する inoremap jj <esc> ``` ```~/.w3m/keymap # m でカーソル位置のURLをクリップボードに保存する keymap m EXTERN_LINK "echo %s | pbcopy" ``` ```~/.tmux.conf # Control+s Control+vで画面分割 bind-key -n M-s split-window -v bind-key -n M-v split-window -h ``` 参考記事 01. [tmux Keymap](https://bytebucket.org/ns9tks/tmux-ja/wiki/tmux-ja.html) 02. [tmux.conf](http://d.hatena.ne.jp/mizchi/20100829/1283076112) 02. [zshrc](http://www.clear-code.com/blog/2011/9/5.html) 03. [zsh Prompt](http://www.understudy.net/custom.html) 04. [vim Keymap](http://vim.wikia.com/wiki/Mapping_keys_in_Vim_-_Tutorial) 05. [vimrc](https://github.com/Shougo/shougo-s-github/blob/master/vim/.vimrc) 06. [w3m/keymap](https://github.com/mottram/dotfiles/blob/master/w3m/keymap) `zsh`を簡易にカスタマイズしたい場合は、`Oh-My ZSH`を参考にするとよいでしょう。プロンプトの設定あたりが特に参考になると思います。 参考記事 01. [oh-my-zsh を使って zsh の便利な設定をまとめて取り入れる](http://d.hatena.ne.jp/mollifier/20101009/p1) ##Homebrewでインストールするアプリ  ``` brew update //アップデート brew doctor //問題解決 brew install 'Appname' //インストール brew search 'Appname' //検索 ``` 以下、アプリはコマンドでインストールできるため、URLには参考記事を載せています。 01. [w3m](http://www.office-iwata.com/archives/47) //テキストブラウザ 02. [mutt](http://www.emaillab.org/mutt/) //メール 03. [git](http://www8.atwiki.jp/git_jp/) //バージョン管理 04. [mplayer](http://pspfour.blog106.fc2.com/blog-entry-1111.html) //メディアプレイヤー 05. [ffmpeg](http://pspfour.blog106.fc2.com/blog-entry-1111.html) //エンコード 06. [swftools](http://pspfour.blog106.fc2.com/blog-entry-1111.html) //swfのエンコード 07. [mc](http://ja.wikipedia.org/wiki/Midnight_Commander) //ファイルマネージャ-Midnight Commander 08. [zsh](http://news.mynavi.jp/column/zsh/index.html) //補完が冴えたシェル 09. [vim](http://ja.wikipedia.org/wiki/Vim) //プラグインが優れたエディタ 10. [tmux](http://d.hatena.ne.jp/tmatsuu/20090709/1247150771) //ウィンドウ操作に優れた仮想環境 11. [devtodo](http://pnuts.cc/2009/12/devtodo/) //To-do管理 12. [googlecl](http://code.google.com/p/googlecl/) //Googleサービスの利用 13. [youtube-dl](http://pspfour.blog106.fc2.com/blog-entry-1111.html) //YouTube動画のダウンロード 14. [nmap](http://nmap.org/man/jp/) //ネットワークスキャナ 15. [wireshark](http://www.wireshark.org/) //パケット解析ツール 16. [munin](http://munin-monitoring.org/) //初心者に手軽なサーバー管理 17. [aircrack-ng](http://www.aircrack-ng.org/doku.php?id=ja:cracking_wpa) //無線キャプチャ 18. [metasploit](http://www.ibm.com/developerworks/jp/web/library/wa-metasploit/) //脆弱性評価ツール 19. [exiftool](http://pspfour.blog106.fc2.com/blog-entry-1027.html) //Exif情報操作 20. [mobile-shell](http://pspfour.blog106.fc2.com/blog-entry-1069.html) //SSHに変わるリモートツール 21. [rtorrent](http://libtorrent.rakshasa.no/) //Downloader ##MacPortでインストールするアプリ たまに、`Homebrew`にはないアプリがあります。その場合、`MacPort`を使いインストールできます。なお、`Homebrew`を利用する場合、`MacPort`の導入は推奨されていませんので注意が必要です。 01. [ophcrack](http://ophcrack.sourceforge.net/) //パスワードクラック。辞書ファイルもダウンロード可 02. [snownews](https://kiza.kcore.de/software/snownews/) //RSSクライアント ##その他 01. [sheet](http://oscardelben.com/sheet/) //スニペットツール 02. [nicovideo-dl](http://pspfour.blog106.fc2.com/blog-entry-1111.html) //ニコニコ動画のダウンロード 03. [metrical](http://ja.asciicasts.com/episodes/252-metrics-metrics-metrics) //メトリクス計測 04. [google-apps-manager](http://code.google.com/p/google-apps-manager/) //Googleアカウント管理 05. [orpheus](http://ihaveapc.com/2011/04/orpheus-a-cool-command-line-mp3-player-for-linux-mint-ubuntu/) //Media Player 06. [gcalcli](https://github.com/insanum/gcalcli) //Googleカレンダーやリマインダーなど ##初期設定 パソコンを初期化したあと、もしくは他人のパソコンを使用する場合、即座に自分環境を構築する手はずを整えておきましょう。具体的には、アプリをインストール、設定するシェルスクリプトを書き、それをDropboxで共有しておきます。また、設定ファイルはGitHubに公開してもいいでしょう。 私の場合、Dropboxをインストールし、1つのコマンドを実行すれば、アプリのインストール、設定からzshなどの設定ファイルへシンボリックリンクを貼る作業までを自動で実行されるようにしています。これは、ある程度、OS環境を考慮した内容で書きます。 Check ```~/setup.sh DOT_FILES=( .vim .vimrc .bazaar) for file in ${DOT_FILES[@]} do if [ -a $HOME/$file ]; then echo "既にファイルが存在します: $file" else ln -s $HOME/dotfiles/$file $HOME/$file echo "シンボリックリンクを貼りました: $file" fi done ``` 参考記事 01. [ボク式dotfiles](http://d.hatena.ne.jp/Yuzuemon/20120220/1329747301) 02. [DotfilesをDropboxで管理するときに若干役に立ちそうなスクリプト](https://gist.github.com/1221938) 03. [dotfile (~/.file) の類を Dropbox で一元管理](http://voqn.blogspot.jp/2011/04/dotfile-file-dropbox.html) 04. [Dropboxをサーバにインストールして簡単自動バックアップ](http://blog.monoweb.info/article/2011033122.html) その他、Editorに`Vim`を使う場合は、`GoogleIME`の設定を行なっておくと、コマンド入力が便利になります。Google IME > 環境設定 > 一般_キー設定_編集 にて以下を変更します。これで、`Esc`を押すと、日本語入力がOFFになります。  ##便利なVimプラグイン  01. [simplenote](https://github.com/mrtazz/simplenote.vim) //クラウドメモ帳 02. [commnad-t](https://github.com/wincent/Command-T) //ファイルアクセスの便宜 03. [w3m](https://github.com/yuratomo/w3m.vim) //yuratomo/w3m.vim 04. [tagbar](https://github.com/majutsushi/tagbar.git) //majutsushi/tagbar その他のプラグインは下記を参考にしてください。なお、プラグインの導入は、`Bundle`か`NeoBundle`を使うと簡単になります。 参考記事 01. [ワシのVimプラグインは百八式まであるぞ](http://daisuzu.hatenablog.com/entry/2012/05/06/204019) ##ブログ環境 ブログ環境は、執筆環境を視野に入れた使い慣れたサービスをメインに構築するのが一番です。ここでは、私が考えるクールなブログ環境を紹介します。 01. [GitHub Page](http://pages.github.com/) //無料で使える公開ページ 02. [Bitbucket](https://bitbucket.org/) //非公開に対応 03. [Octopress](http://octopress.org/) //Webアプリケーションの一種 04. [Dropbox](https://www.dropbox.com/) //ファイルサーバー 参考記事 01. [Octopressのインストールから運用管理まで](http://tokkonopapa.github.com/blog/2011/12/30/octopress-on-github-and-bitbucket/) 02. [Octopress + Github Pages + Dropbox でブログを構築](http://www.sankitch.me/blog/2012/05/05/build-octopress-on-github-dropbox/) ##使ってないけど有効なツール 01. [Eclipse](http://www.eclipse.org/downloads/) //IDE 02. [Coda2](http://panic.com/jp/coda/) //IDE or Editor ? IDE 03. [Emacs](http://ja.wikipedia.org/wiki/Emacs) //Editor ###なぜ、これらのツールを使わないのか? これはあくまで個人的な意見ですが、`Eclipse`は日本語ドキュメントが不足しているような気がします。よって、初心者には敷居が高いです。`Coda2`はMac限定で、他のOSとの設定の共有が難しいと考えます。`Emacs`は`Vim`と比べた場合、私が求めるプラグインが多少不足している印象があります。なぜ、これらのツールを使わないのか?その理由のほとんどは、私が初心者であることが一番の原因だと思います。 ##VirtualBoxなどで使っているOSと便利ツール  01. [Cygwin](http://www.cygwin.com/) //Windowsでunix環境を再現 02. [MinGW](http://sourceforge.net/projects/mingw/files/Installer/mingw-get-inst/) //cygwin同様 03. [mintty](http://code.google.com/p/mintty/) //cygwinのTerminal 04. [Msiexec.exe](http://support.microsoft.com/kb/314881/ja) //Windowsインストーラー 05. [Xubuntu](http://xubuntu.org/) //ubuntuを軽量に再構築 06. [Gentoo](http://www.gentoo.gr.jp/) //Gentoo Linuxは、開発に特化したOS 07. [BackTrack](http://www.backtrack-linux.org/) //脆弱性検査や無線LANツールが豊富 参考記事 01. [Windowsで最高のターミナルを構築する方法](http://tanakh.jp/posts/2011-11-15-windows-terminal.html) 02. [Gentoo Haskell Overlay 使うときの基本的な設定](http://d.hatena.ne.jp/notogawa/20120603/1338738104) 03. [BackTrackを使ってセキュリティをテストする](http://www.itmedia.co.jp/enterprise/articles/0806/19/news035.html) ## GIMPのプラグイン一覧  01. [Separate+](http://cue.yellowmagic.info/softwares/separate.html) //CMYKファイルを扱うプラグイン 02. [PSPI](http://tml.pp.fi/gimp/pspi.html) //Photoshop用のプラグインをGIMPでも使う 03. [Liquid Rescale](http://liquidrescale.wikidot.com/en:start) //画像のリサイズに使えるプラグイン 04. [Wavelet Denoise](http://registry.gimp.org/node/4235) //ノイズを除去するためのプラグイン 05. [Gimp-reflection](http://registry.gimp.org/node/1025) //特殊効果としての反射を行うプラグイン 06. [Z-design Tech brushes set v2](http://z-design.deviantart.com/art/Z-design-Tech-brushes-set-v2-20701749) //サイバー風ブラシセット ##人気のある有料アプリ  ここでは、一般的に人気があるMacの有料アプリを紹介してみたいと思います。 01. [PopClip](http://pilotmoon.com/popclip/) //マウスのCopy補助 02. [MarsEdit](http://www.red-sweater.com/marsedit/) //ブログエディタ `代替 : Vim` 03. [Transmit](http://panic.com/jp/transmit/) //FTPクライアント 04. [1Password](https://agilebits.com/onepassword) //パスワード管理 `代替 : keepass` 05. [iStat Menus](http://bjango.com/mac/istatmenus/) //CPUやメモリ監視 `代替 : topとvm_stat` 06. [Vmware fusion3](http://www.act2.com/software/vmware/fusion3) //仮想環境 `代替 : VirtualBox` 07. [Osfoora](http://www.osfoora.com/mac/) //Twitterクライアント `代替 : tweetvim` 08. [MenuPop](http://www.binarybakery.com/) //右クリックメニューの呼び出し 09. [TextExpander](http://itunes.apple.com/jp/app/textexpander-for-mac/id405274824?mt=12) //入力補完 `代替 : sheet` 10. [Photoshop](http://www.adobe.com/jp/products/photoshop.html) //画像編集 `代替 : GIMP` 11. [Illustrator](http://www.adobe.com/jp/products/illustrator.html) //グラフィックサポート 参考記事 01. [Macでtmuxのステータスラインに起動時間/ロードアベレージ/メモリ使用率を表示](http://d.hatena.ne.jp/yonchu/20120414/1334422075)  ##最後に 私は、パソコンのことは何もわからない初心者なので、色々と教えて下さい。また、WindowsやLinuxで使用されている便利なアプリについても教えて欲しいです。OSに限定はなく、どんどんコメントいただきたいと思っています。 ##追記 FireAlpaca [http://firealpaca.com/](http://firealpaca.com/) Alfred 2 [https://www.alfredapp.com](https://www.alfredapp.com) |
|
| 18位 |
|
|||
|
23:58:30 |
(Mercari, Inc. 所属) |
|
この記事は [Git Advent Calendar](http://qiita.com/advent-calendar/git) 6日目の記事です!
Git submodule って最初わかりにくいと思うので、基本的な説明をしようと思います。 ## git submodule とは `git submodule` は、外部の git リポジトリを、自分の git リポジトリのサブディレクトリとして登録し、特定の commit を参照する仕組みです。 Subversion でいうところの、external と似ています。 さて、解説のため、手元に、リポジトリA (/path/to/a) とAの submodule として、よく使う例として Bootstrap (元Twitter Bootstrap) を登録してみます。 git submodule を理解するうえで重要なのは、 * リポジトリAが指し示すsubmoduleとしてのBootstrapのcommit * 現在のBootstrapのcommit です。 ## git submodule add してみる では、リポジトリA内、submodule add してみます。 ```bash: $ git submodule add https://github.com/twbs/bootstrap.git bootstrap Cloning into 'bootstrap'... remote: Counting objects: 73135, done. remote: Compressing objects: 100% (15/15), done. remote: Total 73135 (delta 6), reused 0 (delta 0), pack-reused 73120 Receiving objects: 100% (73135/73135), 85.84 MiB | 3.91 MiB/s, done. Resolving deltas: 100% (44541/44541), done. Checking connectivity... done. ``` これで 手元の `bootstrap` ディレクトリに、GitHub にある Bootstrap が submodule として登録されました。 git status をみてみると .gitmodules というファイルと、submodule の入れものである `bootstrap` というディレクトリが新しく登録されています。 ```bash: $ git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # new file: .gitmodules # new file: bootstrap # ``` diff も確認します。 ```diff: $ git diff --cached diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2fdbccb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "bootstrap"] + path = bootstrap + url = https://github.com/twbs/bootstrap.git diff --git a/bootstrap b/bootstrap new file mode 160000 index 0000000..5c02844 --- /dev/null +++ b/bootstrap @@ -0,0 +1 @@ +Subproject commit 5c028448c1fc171cf4a0fd99d3a672b649bef2aa ``` 注目すべきは、 `+Subproject commit 5c028448c1fc171cf4a0fd99d3a672b649bef2aa` の diff で、これは、 * Bootstrap の、 `5c028448c1fc171cf4a0fd99d3a672b649bef2aa` コミットを、 * `bootstrap` ディレクトリに Submodule として登録した という意味になります。 これをコミットしておきます。 ```bash: $ git commit -m "Add Twitter Bootstrap as a submodule" ``` では、ためしに、bootstrapディレクトリに移動して、`git show` してみると、 ```bash: $ cd bootstrap $ git show commit 5c028448c1fc171cf4a0fd99d3a672b649bef2aa Merge: 5e7c5a6 0c4c1e4 Author: Chris Rebert <github@rebertia.com> Date: Wed Jul 29 15:00:54 2015 -0700 Merge pull request #16908 from twbs/crbug-309483 Remove http://crbug.com/309483 from the Wall of Browser Bugs ``` たしかに、現在、`5c028448c1fc171cf4a0fd99d3a672b649bef2aa` にいることがわかります。 ## Submodule を更新する いま追加した Bootstrap は、master ブランチの最新のコミットでした。 プロジェクトでは、別のブランチの Bootstarp が使いたくなったとします。たとえば、現在進行中の `14226-rebased` ブランチを使いたいとしましょう。 (このブランチ名等は、タイミングや、Submodule に追加するプロジェクトによって変わりますので、適宜読み替えてください) このときの手順は、 * submodule のディレクトリに入り、対象のブランチやコミットをチェックアウトする * submodule の外側に戻り、その submodule の現在のコミットを記録する (指し示す先を変更する) という手順になります。 ```bash: $ cd bootstrap $ git branch -a * master remotes/origin/14226-rebased remotes/origin/HEAD -> origin/master remotes/origin/bhamodi-update-dependencies remotes/origin/bundler remotes/origin/derp remotes/origin/fix-15534 remotes/origin/fix-popover-setContent remotes/origin/gh-pages remotes/origin/master remotes/origin/travis2 $ git checkout 14226-rebased Branch 14226-rebased set up to track remote branch 14226-rebased from origin. Switched to a new branch '14226-rebased' ``` この状態で外に戻ると、 ```bash: $ cd .. $ git status Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: bootstrap (new commits) no changes added to commit (use "git add" and/or "git commit -a") ``` bootstrap ディレクトリに diff が出た状態となっています。 ```diff: $ git diff diff --git a/bootstrap b/bootstrap index 5c02844..88697c0 160000 --- a/bootstrap +++ b/bootstrap @@ -1 +1 @@ -Subproject commit 5c028448c1fc171cf4a0fd99d3a672b649bef2aa +Subproject commit 88697c03a9315b5f1944fc82534a824aafebda0e ``` これが、submodule がコミットを記録する仕組みで、つまり、 * リポジトリAの参照する Bootstrap のコミットが、`5c028448c1fc171cf4a0fd99d3a672b649bef2aa` から `88697c03a9315b5f1944fc82534a824aafebda0e` に変更されたという意味になります。 これを add してコミットしておきます ```bash: $ git add bootstrap $ git commit -m "Update submodule: Bootstrap" ``` ## リポジトリAで別のコミットをチェックアウトする & git submodule update submodule のわかりづらい点としては、おそらく、**リポジトリA (submodule の外側) と submodule の中が連動しない** という点ではないでしょうか。 たとえば、今の状態で、リポジトリAでひとつ前のコミットをチェックアウトしてみるとします。(今回はミニマムなgitリポジトリなので直前のコミットをチェックアウトしますが、実際いろいろな作業では、他のブランチをチェックアウトしたり、そういう操作の例として、です) まずは、チェックアウトするため、ログを確認。 ```bash: $ git log --oneline 284ffed Update submodule: Bootstrap 45db3c1 Add Twitter Bootstrap as a submodule 30ef9ed initial commit ``` 一つ前なので、 `45db3c1` をチェックアウトします。 ```bash: $ git checkout 45db3c1 ... HEAD is now at 45db3c1... Add Twitter Bootstrap as a submodule ``` この状態で status を見てみると、 ```bash: $ git status HEAD detached at 45db3c1 Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: bootstrap (new commits) no changes added to commit (use "git add" and/or "git commit -a") ``` あれ!bootstrap ディレクトリに diff がでてしまいました。 これが、**連動しない、というわかりにくい点** と言った点です。 * 外側のリポジトリで前のコミットをチェックアウトしたため、そのコミット時点での submodule としての bootstrap は `5c028448c1fc171cf4a0fd99d3a672b649bef2aa` が記録されている * しかし、bootstrap の内側は、先程操作したため、`88697c03a9315b5f1944fc82534a824aafebda0e` を指し示したまま という状態になっています。リポジトリA上で色々なブランチやコミットを移動しても、submodule 内は自動的にチェックアウトされたりはしないんですよね。 そこで、 * boostrapディレクトリ内を、コミット `88697c0` が指し示している bootstrap のコミット `5c02844` にする という操作が、 ```bash: $ git submodule update ``` というコマンドになります。submodule update というコマンド名は、「submoduleを更新する」という意味なので、考えてみればそのままの意味なのですが、わかってないと混乱してしまいますね。 これで、 ```bash: $ git status ``` が clean な状態となり、 bootstrap ディレクトリの中に入って確認してみると、`5c02844` がチェックアウトされた状態になっています。 ## まとめとその他のこと 時間がなくなってきたので強引にまとめに入ると、 * submodule として追加したディレクトリは、ただの外部リポジトリへ参照を記録した箱である * submodule の外側は、その箱のどのコミットを参照しているのかを記録している * submodule の内側は、基本的には、自分が操作したときにしか更新されない よくある現象集: * git pull したら submodule に `(new commits)` とか出た * これは、自分以外の誰かが submodule を更新した (指し示すコミットを変更した) 場合です * さっきの、`0a30745` をチェックアウトしたときと同じ * `git submodule update` すれば、その指し示すコミットがチェックアウトされる * git status したら submodule に `(modified content)` とか出てる * これは、submodule 内に diff がある状態です。今回の例で言えば、bootstrapディレクトリ内のなにかのファイルを変更したりすると、リポジトリAではこういうふうにいわれます * もとに戻したいなら、bootstrapディレクトリ内をcleanな状態にします。`git checkout`とかで変更ファイルをなくしたり。 * 中でコミットなどをすると、 `(new commits)` になります。 * submodule の中身を変更して、そのコミットをリポジトリAから指し示したい場合、さらに `git add bootstrap` して `git commit` します。内側でコミット、外側でもそれをコミット、という形 あとなんだっけ、もっと書きたいことがあったけど忘れたのでまた思い出したら書きます。 |
|
| 19位 |
|
|||
|
21:19:41 |
|
|
皆さん、tigコマンドを活用していますか? tigは、コンソール上で使えるgitブラウザです。実はずっと、ただのきれいな`git log`だと思っていたのですが、本当はそんなことはありません。かなり使えるやつなのです。 ### インストール ソースコード: https://github.com/jonas/tig インストール方法: https://github.com/jonas/tig/blob/master/INSTALL.adoc この辺りを参考にしてみてください。詳細は割愛します。 ### 基本の使い方 この状態の差分を扱っていきます。いつものこれだとこんな感じ。 ``` $ git status ```  ``` $ tig ```  `git log`が素敵にビジュアライズされてます。この画面を`main view`といいます。 ここでエンターを押すと、下半分に差分の詳細(`diff view`)が表示されます。  下矢印で、Unstaged changesの差分を見てみるとこんな感じです。`git status`で `Changes not staged for commit`に表示されている部分の差分ですね。  ちなみに、この画面で`Ctrl+u`、`Ctrl+d`、`j`、`k`で上下移動できます。vimと同じですね。 また、`main view`にて`d`を押すと、コミット毎の差分の詳細(`diff view`)が全画面で表示されます。画面分割では見辛い場合にはこちらをどうぞ。 ### tigでaddする ##### ファイルごと `u` `main view`にて、`S`(`shift+s`)を押すと、次のように、`git status`の結果が表示されます。  この画面でも、エンターを押すとそのファイルの差分を閲覧できます。 好きなファイルを選択した状態で、`u`を押してみます。  `MusicLife/DetailViewController.m`が、`Changed but not updated:`から`Changes to be committed:`に移動しました。 これで、`git add MusicLife/DetailViewController.m`完了です! 超お手軽。 `Changes to be committed:`に表示されているファイル名の上で`u`を押すと、indexから外すこともできます。 ##### 一部だけ `u` さて、先ほどaddしたファイルの差分を見てみたら、まだcommitしたくない差分も混じっていました。  ここだけaddしなかったことにしたい…だけどそのためにファイルを修正するのは面倒です。 そんなときは、下の画像のように、「ここからここまで」の間の行( **どの行でもOKです。** 自分で範囲選択する必要はありません)を選択して、`u`を押すと、その範囲だけadd/unstageすることができます。  その後の様子。部分的にaddされているため、同じファイルが上段と中段の両方に表示されています。  ##### 1行だけ `1` さっきの状態に戻って、今度は1行だけadd/unstageしてみようと思います。下の画像のように操作したい行を選択して、`1`を押します。  すると……  1行だけunstageされました。画面上部の`Changed but not updated:`にもファイル名が表示されています。 このように、tigを使うことによって、細かなコミット管理が簡単に行えるようになります! ### tigでcommitする `C` `status view`の画面で、`C`(`shift+c`)を押します。  いつもの`git commit`の画面ですね! いつも通り、1行目にコミットメッセージを書いて`:wq`すればokです。 なお、そこから`main view`に戻ってきても今作ったコミットは表示されていないと思うのですが、`R`(`shift+r`)か`F5`して更新すれば表示されます。 ### tigでpushする addとcommitが出来るならpushも、と思って調べてみたのですが、今のところ出来ません。 https://github.com/jonas/tig/issues/199 いいキーバインドがないこと、誤ってリモートリポジトリに修正を加えてしまうことへの不安などが、現在実装されていない理由のようですね。上記Issueで作者の方が書いている方法で、tig上でpushを行うこともできるようですので、tig上で全て完結させたい方は試してみてください。 ### tigでbranch一覧を見る `main view`で`H`(`shift+h`)を押すと、branchの一覧が表示されます。いちばん上が現在のブランチ、中央の青字がローカルブランチ、下のオレンジ色がリモートブランチです。右はそれぞれのブランチの最後のコミットメッセージです。  ### 他にも色々できる! `main view`にて`h`を押すとヘルプ画面になります。上に挙げた以外にも色々なことができるので、ぜひ試してみてください。 ``` [-] status bindings External commands: 'C' `git commit` [-] branch bindings External commands: 'C' `git checkout %(branch)` [-] main bindings External commands: 'C' `git cherry-pick %(commit)` [-] generic bindings View switching 'm' view-main Show main view 'd' view-diff Show diff view 'l' view-log Show log view 't' view-tree Show tree view 'f' view-blob Show blob view 'B' view-blame Show blame view 'H' view-branch Show branch view 'h' view-help Show help view 'p' view-pager Show pager view 'S' view-status Show status view 'c' view-stage Show stage view (以下略) ``` |
|
| 20位 |
|
|||
|
17:54:09 |
(Drivemode, inc. / DroidKaigi 所属) |
|
今から Android やるならチェックしておきたい、厳選イカしたオープンソースライブラリ一覧。
support-v4 や support-v7-appcompat などは公式のものなので割愛。 # 開発環境 ## Android SDK - [Android SDK Installer](https://github.com/embarkmobile/android-sdk-installer) 公式からダウンロードしてポチポチとチェックを入れてはダウンロードして…が面倒くさいならコレ。 シェルからコマンド一発でダウンロード出来るので、CI で使うのにも便利。 - [ADB Idea](https://github.com/pbreault/adb-idea) AndroidStudio および IntelliJ 用のプラグインで、IDE から ADB コマンドを簡単に利用できるようにするためのもの。メニューから選択してコマンドを実行できるようにしてくれる。 # コード最適化 ## DI コンテナ - [Dagger](https://github.com/square/dagger) square 社の Android および Java 向け DI コンテナ。 `javax.inject.Inject` アノテーションを使う。 コンパイル時にコードを生成する。 - [Proton](https://github.com/hnakagawa/proton) Android 向け DI コンテナ。 `javax.inject.Inject` アノテーションを使う。 実行時にインスタンスを注入する。 - [ButterKnife](https://github.com/JakeWharton/butterknife) View のインジェクションに特化している。 - [RoboGuice](https://github.com/roboguice/roboguice) Google が提供する Guice の Android 向けラッパー。 `javax.inject.Inject` アノテーションを使う他、独自にインジェクション用のアノテーションも提供している。 ## View の生成 - [Michelangelo](https://github.com/RomainPiel/Michelangelo) アノテーションを用いてカスタムビューのコードを整理できる。 ビューがインフレートされた後に実行したい処理をアノテーションで示すことも出来る。 ## リソース - [Paraphrase](https://github.com/JakeWharton/paraphrase) 文字列リソースのフォーマットをよりリーダブルに、かつデバッガブルにするためのライブラリ。 文字列リソースのフォーマットに関するプログラミングのエラーが、ランタイムではなくコンパイル時に判明するようになる。 ## ユーティリティ - [AndroidAnnotations](https://github.com/excilys/androidannotations) アノテーションを使って、コンパイル時にコードを自動生成させることで、プログラマの書くコード量を減らすライブラリ。 DI コンテナとして、View やシステムサービス、リソース、Intent に含まれる Extra を注入することが出来る他、メソッドがメインスレッドで実行されるかバックグラウンドスレッドで実行されるかも制御できる。 - [Amalgam](https://github.com/nohana/Amalgam/) Apache Commons のような Commons ライブラリで、Android 用。 `Context#getSystemService(String)` のラッパーや、各種 Manager 類のラッパーがある。 - [Guava](https://github.com/google/guava) Apache Commons のような Commons ライブラリ。 Java 向けに各種のユーティリティが取り揃えられている。 ## 機種依存解決 - [AndroidDeviceCompatibility](https://github.com/mixi-inc/Android-Device-Compatibility) 機種ごとの依存を吸収する為のライブラリ。 ## モジュール化 - [android-promise](https://github.com/hnakagawa/android-promise) 非同期な処理をモジュール化する為のライブラリ。 # UI コンポーネント ## Pull to refresh - [ActionBar PullToRefresh](https://github.com/chrisbanes/ActionBar-PullToRefresh) 引っ張ると ActionBar に更新の表示が出る UI コンポーネント。 - [Android PullToRefresh](https://github.com/chrisbanes/Android-PullToRefresh/) Twitter スタイルの引っ張り更新の UI コンポーネント。 現在メンテは終了しており、使うのであれば、上記の ActionBar PullToRefresh を使うほうが良い。 ## ActionBar - [FadingActionBar](https://github.com/ManuelPeinado/FadingActionBar) ActionBar にフェードアウト効果を適用するためのライブラリ。 - [GlassActionBar](https://github.com/ManuelPeinado/GlassActionBar) ActionBar に透かし窓のような効果を適用するためのライブラリ。 ## WebView - [Chromium WebView](https://github.com/mogoweb/chromium_webview) KitKat で導入された、Chromium エンジンの WebView のバックポートライブラリ。 これを使えば、OS バージョンや機種ごとの WebKit の実装依存問題が解決できるかもしれない。 ## Text・Font - [emojicon](https://github.com/rockerhieu/emojicon) TextView 上に絵文字を表示するためのライブラリ。 - [IonIconView](https://github.com/MarsVard/IonIconView) ionicons.com で提供されているアイコンを View に表示可能にするライブラリ。 - [Android Iconify](https://github.com/JoanZapata/android-iconify) FontAwesome の各種フォントをアプリ内で使えるようにするライブラリ。 - [Calligraphy](https://github.com/chrisjenx/Calligraphy) カスタムフォントを容易に扱えるようにしてくれるライブラリ。 ## Calendar - [ExtendedCalendarView](https://github.com/tyczj/ExtendedCalendarView) カレンダーのコンテンツを保持する DB とセットで、イベントを表示できるカレンダー View を提供してくれるライブラリ。 - [GoogleCalendarView](https://github.com/tyczj/GoogleCalendarView) Google 公式のカレンダーアプリからのバックポート。 ## エフェクト - [Android StackBlur](https://github.com/kikoso/android-stackblur) Bitmap にブラーのエフェクトをかける。 - [GPUImage Android](https://github.com/CyberAgent/android-gpuimage) iOS 向けに作られていた GPU Image の Android 移植版。 ## 通知 - [SuperToasts](https://github.com/JohnPersano/SuperToasts) Toast を拡張して、ボタンを置いたり見た目を少し変えたりすることが出来るようになるライブラリ。 - [Crouton](https://github.com/keyboardsurfer/Crouton) 標準の Toast の代替として、Context に依存してカスタム可能な Toast を提供する。 ## レイアウト - [ImageLayout](https://github.com/ManuelPeinado/ImageLayout) 画像の上に View を配置するための特別なレイアウトコンポーネント。 独自の座標系を持って管理している。 ## ListView・GridView - [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) Instagram のような、ListView のスクロールに追従してくるヘッダを提供する。 - [StickyGridHeaders](https://github.com/TonicArtos/StickyGridHeaders) 上記ライブラリの GridView 版。 - [Android StaggeredGrid](https://github.com/etsy/AndroidStaggeredGrid) 高さの異なるグリッドを扱える。 - [AdapterView Animator](https://github.com/SimonVT/adapterviewanimator) AdapterView の行を追加したときのアニメーションを実現するヘルパライブラリ。 - [cwac-touchlist](https://github.com/commonsguy/cwac-touchlist) List のドラッグ・アンド・ドロップを実現するライブラリ。 ## ViewPager - [JazzyViewPager](https://github.com/jfeinstein10/JazzyViewPager) スワイプ時のアニメーションをカスタマイズできる。 - [Android ViewPagerIndicator](https://github.com/JakeWharton/Android-ViewPagerIndicator) ViewPager に追随して位置表示する View。 ## Dialog - [Android Styled Dialogs](https://github.com/inmite/android-styled-dialogs) ダイアログにテーマでスタイルを適用出来るようにするためのライブラリ。 ## ImageView - [CustomShape ImageView](https://github.com/MostafaGazar/CustomShapeImageView) 画像をくり抜いて、様々な形に出来る ImageView。 - [Rounded ImageView](https://github.com/vinc3m1/RoundedImageView) 角丸の ImageView を作れるライブラリ。 - [PhotoView](https://github.com/chrisbanes/PhotoView) - [ImageViewZoom](https://github.com/sephiroth74/ImageViewZoom) 画像の拡大縮小機能を持つ ImageView。 ## チャート - [Holo Graph Library](https://bitbucket.org/danielnadeau/holographlibrary) Holo スタイルのグラフ・チャートを作るライブラリ。 - [AChartEngine](https://code.google.com/p/achartengine/) こちらも、グラフ・チャートを作るライブラリ。 ## Interaction - [Android Swipe To Dismiss](https://github.com/romannurik/Android-SwipeToDismiss) View をスワイプして消す操作を実現するライブラリ。 - [EnhancedListView](https://github.com/timroes/EnhancedListView) Gmail のように、行をスワイプすることで削除を実行するインタラクションを実現でき、かつ、Undo の機能も有する ListView の拡張。 ## その他 - [Android Sliding Up Panel](https://github.com/umano/AndroidSlidingUpPanel) Google Play Music のように、下からせり出してくるパネルを表示するライブラリ。 - [CardsLib](https://github.com/gabrielemariotti/cardslib) カード UI を構築するためのライブラリセット。 - [ProgressWheel](https://github.com/Todd-Davies/ProgressWheel) ぐるぐる回るプログレスバーをカスタマイズするライブラリ。 - [Android Bootstrap](https://github.com/Bearded-Hen/Android-Bootstrap) FontAwesome と共に Bootstrap スタイルのボタン類を使えるようにするライブラリ。 - [Android Satellite Menu](https://github.com/siyamed/android-satellite-menu) Path みたいなメニューを構築するライブラリ。 - [NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids) API 11 から登場したアニメーションフレームワークをバックポートするライブラリ。 - [Holo Everywhere](https://github.com/Prototik/HoloEverywhere) Holo テーマのバックポートライブラリ。 - [Holo ColorPicker](https://github.com/LarsWerkman/HoloColorPicker) 色を選択する View のライブラリ。 - [Android Validator](https://github.com/throrin19/Android-Validator) 入力のバリデーションのためのライブラリ。 - [android-gifview](https://code.google.com/p/android-gifview/) Gif アニメーションを再生可能な View のライブラリ。 # シリアライズ ## Json - [Google Gson](https://code.google.com/p/google-gson/) リフレクションによってマッピングするライブラリ。 - [Jackson](http://jackson.codehaus.org/) 超高速にシリアライズ・デシリアライズできるライブラリ。 # データベース ## ORM - [GreenDAO](https://github.com/greenrobot/greenDAO) グリーンだよ!いいんだよ! スキーマ、エンティティをそれぞれ定義するクラスを使ってマッピングする。 - [Active Android](https://github.com/pardom/ActiveAndroid) エンティティクラスの宣言にアノテーションを付けてマッピングする。 # イベントバス 使い方は[こちら](http://qiita.com/KeithYokoma/items/793aaac6994c9242808f)。 - [otto](https://github.com/square/otto) Guava ベースの Android 向け軽量イベントバス。 - [EventBus](https://github.com/greenrobot/EventBus) otto より機能的に拡張のあるイベントバス。 # デバッグ支援 ## View - [scalpel](https://github.com/JakeWharton/scalpel) View の階層構造を 3D 表示するためのツール。 ## リソース - [Madge](https://github.com/JakeWharton/madge) Bitmap がオリジナルのサイズで表示されているかどうかを一目見て分かるようにする。 ## クラッシュログ - [Native Crash Handler](https://github.com/SalomonBrys/Native-Crash-Handler) Native コードのエラーハンドリングとして、SIGSEGV などを検出して Java の例外を生成してくれる。 これによって、Google Play などで拾いきれなかったクラッシュのログを収集できるようになる。 ## ログ出力 - [hugo](https://github.com/JakeWharton/hugo) アノテーションによるメソッドコールのロギングライブラリ。 - [deploygate](http://deploygate.com/) ログ収集だけでなく、apk の配布までも楽ちんにしてくれるサービス。 sdk を組み込めばかなりのことが Web コンソール上で出来る。 # 画像 ## 画像読み込み・キャッシュ - [Android Universal Image Loader](https://github.com/nostra13/Android-Universal-Image-Loader) 各種設定に基いて画像の読み込み・キャッシュ・表示をしてくれるライブラリ。 - [picasso](https://github.com/square/picasso) メソッドチェーンによる画像読み込み・キャッシュ・表示の為のライブラリ。 ## 加工 - [CropImage](https://github.com/biokys/cropimage) 画像の切り抜きをサポートするライブラリ。 # ハイブリッドアプリケーション開発 ネイティブ実装をベースに、JavaScriptInterface と連携してハイブリッドアプリケーションを開発するフレームワーク。 - [Hybridge](https://github.com/rejasupotaro/Hybridge) - [triaina](https://github.com/mixi-inc/triaina) # テスト ## テストユーティリティ - [fest-android](https://github.com/square/fest-android) Android のテストに FEST を導入できる。 - [robolectric](https://github.com/robolectric/robolectric) Android のテストの高速化 - [Robotium](https://code.google.com/p/robotium/) UI のテストを Selenium のようにかける、かつ Android フレームワークが持つ UI テストフレームワークを拡張している。 ## モック - [Mockito](https://code.google.com/p/mockito/) いろんなものをモックするためのライブラリ。 - [RobotGirl](https://github.com/rejasupotaro/RobotGirl) ActiveAndroid 向けのフィクスチャライブラリ。 # 通信 ## REST - [Retrofit](https://github.com/square/retrofit) - [RoboZombie](https://github.com/sahan/RoboZombie) REST クライアントを簡単に生成するためのライブラリ。 ## Http通信・SPDY - [okhttp](https://github.com/square/okhttp) http(s) 通信 と SPDY に対応したクライアント。 - [volley](https://android.googlesource.com/platform/frameworks/volley/) 通信処理をいろいろ便利にしてくれるユーティリティ。 # ビルドスクリプト ## Gradle - [Gradle Android Utils](https://github.com/gfx/gradle-android-utils) ビルドスクリプトのユーティリティ集。 - [Gradle MVN Push](https://github.com/chrisbanes/gradle-mvn-push) Maven Repository に aar な成果物を push するヘルパ。 - [Gradle Android Test Plugin](https://github.com/JakeWharton/gradle-android-test-plugin) Gradle で Android のテストを実行するためのヘルパプラグイン。 # その他便利系 ## アップデートチェック - [UpdateChecker](https://github.com/rampo/UpdateChecker) アップデートがあった時にダイアログを出す。 ## レビュー - [RateThisApp](https://github.com/kskkbys/Android-RateThisApp) レビューを促すダイアログを出す。 ## Job Queue - [Priority JobQueue](https://github.com/path/android-priority-jobqueue) 優先度付きのジョブキュー - [nirai](https://github.com/jfsso/nirai) IntentService によるジョブキューの仕組み。 詳しくは[こちら](http://qiita.com/jfsso/items/106f48de7ec0612038bb) ## SharedPreferences - [esperandro](https://github.com/dkunzler/esperandro) SharedPreferences のラッパー。 |
|
| 21位 |
|
|||
|
02:02:36 |
|
|
この記事は、[日経ソフトウェア 2013年 06月号 04/24発売](http://www.amazon.co.jp/gp/product/B00C7AMKTU?ie=UTF8&camp=1207&creative=8411&creativeASIN=B00C7AMKTU&linkCode=shr&tag=damenako-22) のステマ記事です。 6月号に、[@keiji_ariyama](https://twitter.com/keiji_ariyama)(前座部分担当) と [僕](https://twitter.com/vvakame)(Gradle概要担当) と [@sys1yagi](https://twitter.com/sys1yagi)(Android対応部分担当) という分担でGradle入門的な記事を書きました。 ですが、僕の目的としてはトップゲート社員への布教を主目的と考えて書いたため、本来要求されている分量を(わざと)大幅にオーバーして書いていました。雑誌に掲載されなかった溢れ分をネットで公開して良いか打診した所、「全文掲載でも良いですよ」という豪気なお許しを頂いたのでここに公開します。この場を借りてお礼申し上げます。 プロの編集さんの手を経て、だいぶわかりやすく噛み砕かれたものが雑誌のほうには掲載されていますので、ここに書いてある内容が難しいなぁ…と思ったら是非雑誌の方を手に取ってみてください。 また、僕が担当している箇所以外の、「何故Gradleが必要か」とか「AndroidとGradleを組み合わせるには?」について知りたい人は同じく雑誌を手にとってみてくださいw なお、執筆時は 3/25 とかで、1.4が最新でした。今は1.5が出ています。 # Gradleとは? ビルドシステム、ひいてはGradleがあると助かる場面とは、一体どういう場面でしょうか? 筆者の具体例でいうと、ある日上司がやってきて、「今お前たちが作っているアプリを俺の端末にインストールしてくれ。」と言ってくるわけですね。1回だけならば話しは簡単です。 PCと端末をmicroUSBで接続して、Eclipseを起動してADTでアプリをコンパイルして、端末に転送してインストールして返してあげるだけです。 ですが、それが2回も3回も…となってくると、思わず「自分でやってください!><」と言ってしまいたくなりますが、上司のPCには開発環境が整っていません。それに、新しいAndroidのSDKがリリースされたら上司のPCの環境を更新する作業をやってあげなければいけません。しかし、僕はそんな面倒くさいことは引き受けたくありません! そこで、登場してくるのがビルドシステムです。今のAndroidの標準のビルドシステムではantが使われています。antでも、「このバッチファイルを叩いたら勝手にアプリが端末に入りますから!><」ぐらいまでは持っていけます。ですが、Android SDKのインストールやantのセットアップはやらなければいけません。これだけでもだいぶ楽になりましたが、今一歩というところですね。 もし、Gradleであれば、Gradle自身のダウンロードもGradleにやらせることができますし、Android SDKのダウンロードも気合を入れればさせることができるでしょう。また、GUIで操作することもできるので、上司にソース一式を渡して「自分でやってください!><」で済ませることもできるようになるでしょう。 ここまでできれば、格段に楽になったといっても納得でしょう。 さて、ここからは一般的なお話です。ちまたには、色々なビルド方法があふれています。Makeだとか、Antだとか、Mavenだとか色々です。自分で書いたことがないまでも、利用したことさえない、という人は滅多にいないでしょう。 Makefileは利用できるプラットフォームを選びます。また、その自由度の高さ故にプロジェクト毎に共通の使い方というものが決められているわけではありません。 Antは柔軟で便利なのは良いのですが、Makefileと同じように自由度が高く、さらに記述もXMLで書かなければいけないため、慣れるまでが大変です。また、ライブラリの依存関係の管理や追加などは行う機能はないためチームで開発する時にみんなの環境を併せるのに苦労します。 Mavenは依存関係の管理や追加もできますが、理解するまでに多くの事を理解しなければならず、またXMLで全ての設定を行わなければいけないため習得の難易度が高く、いわゆる"ビルド職人"と呼ばれるような特定の個人に依存せざるをえない状態でした。 そこで、登場してきたのがGradleです。Gradleは設定をGroovyと呼ばれるJVM上で動くスクリプト言語で書きます。 Groovyで書かれた設定ファイルはXMLよりも可読性が高く、処理の自由度も大変高いです。依存関係の管理も行うことが出来ます。チームで仕事をするために便利な機能もよく考えられて作られていて、大変便利です。今後開発を行なっていく上で無くてはならない存在になっていくでしょう。そのために、まずはあなたがGradle職人になってみるのも良いと思います。 Gradleが行なっている工夫については後々紹介していきます。 その他のセールスポイントとして、ドキュメントが非常に充実していて優れていることがあげられます。公式の英語のドキュメントのみならず、和訳のドキュメントも大変充実していて、これを拾い読みするだけでGradleの事がかなりたくさんわかってきます。 * [公式ドキュメント](http://www.gradle.org/documentation) * [和訳ドキュメント](http://gradle.monochromeroad.com/docs/index.html) 残念な所を無理に上げると、Mavenのpluginとの互換性がないため、熱心なMavenユーザ(筆者の事です)からすると、今までの環境を一部捨てざるをえないことは少しつらいですね。 ## Gradleのインストール ### 方法1 古き良き方法を使う http://www.gradle.org/ Gradle公式サイトのDOWNLOADSリンクから最新のGradleを落としてきましょう。 gradle-1.4-bin.zip を解凍し、適当な所に置きます。解凍したフォルダを環境変数`GRADLE_HOME`にそのパスを設定します。また、`GRADLE_HOME/bin`にパスを通しておきます。 ### 方法2 gvmを使う Groovy enVironment Manager(GVM)を使うのが古き良き方法より、優れているでしょう。可能であればこちらを使うことをオススメします。 利用できる環境や詳細な情報については公式サイト http://gvmtool.net/ を参照してください。Mac OS XやLinuxの類、WindowsであればCygwin環境であれば利用できます。 インストール方法は以下の通りです。 `curl -s get.gvmtool.net | bash` を実行後、その後どうしたら良いかの指示が表示されるはずですので、それに従ってください。 gvmが利用できるようになったら、`gvm list gradle`とするとインストール可能なGradleのバージョンが表示されます。ここでは`gvm install gradle 1.4`とすることで、Gradleの1.4がインストールできます。1.4をデフォルトバージョンとするか聞かれると思いますので、`Y`を選択しておきます。 ### 動作確認 `gradle -version`を実行してみて、Gradle 1.4がインストールされた事が確認できれば成功です。 ```shell ------------------------------------------------------------ Gradle 1.4 ------------------------------------------------------------ Gradle build time: Monday, January 28, 2013 3:42:46 AM UTC Groovy: 1.8.6 Ant: Apache Ant(TM) version 1.8.4 compiled on May 22 2012 Ivy: 2.2.0 JVM: 1.7.0_17 (Oracle Corporation 23.7-b01) OS: Mac OS X 10.8.3 x86_64 ``` ## Gradleを使ってみよう ### Gradleを使うと何が出来るの? まずは、Gradleを使ってみましょう。Gradleではタスクという作業の固まりを作って、それを動かすことで仕事をこなして行きます。 最終的には「Androidのソースをコンパイルして、必要があればJUnitを動かして、端末にアプリをインストールする」とか、「無料版と有料版の2バージョンをリリースしているアプリのコンパイルを自動化し2つのapkを作成する」などの複雑な作業をこなすことが出来るようになるでしょう。(yagiさんパートにぶん投げ) 通常は、他の人が作った便利なタスクを借りて実行させます。そのため、ほんのすこしの記述をしておくだけで、手作業でするとしたらかなりの時間がかかる作業も何度でも繰り返し行わせることができるようになります。もし、「アプリをコンパイルする前にバージョン番号を書き換えたい…」という時は、自分で追加のタスクを書く必要があります。 ### Gradleを使ってみよう まずは簡単なタスクを定義する所から初めてみましょう。最終的には他のタスクに自分の好きな処理を追加できるところまで行きましょう。 以下のファイルを作成し、同じフォルダで`gradle helloWorld`を実行してみます。短縮して、`gradle hW`や`gradle hell`を実行しても同じ結果が得られます。gradleではタスクの名前が一意になる程度に短縮してもよしなに計らってくれます。便利ですね。(`gradle he`などを実行すると`gradle help`が実行されてしまいます。さもありなん。) ```groovy:helloworld1/build.gradle task helloWorld << { println "Hello, world!" } ``` *実行結果* ```shell :helloWorld Hello, world! BUILD SUCCESSFUL Total time: 4.648 secs ``` 新しく定義したhelloWorldタスクが実行され、"Hello, world!"が表示されました。 何が起こっているか簡単に説明しましょう。`task helloWorld`で新しいタスクを定義し、`{}`部分が処理を行うクロージャになっています。Gradleの記述言語であるGroovyでは演算子の定義を書き換えることができます。`<<`はleftShiftメソッドの呼び出しになります。 このコードを少し書き換えるとこのようになります。 ```groovy:helloworld2/build.gradle task helloWorld { doLast { println "Hello, world!" } } ``` 実行してみると、同じ結果になりますね。`<<`は、doLastと同じ意味合いになります。もし、`<<`も`doLast`もなしにするとどうなるでしょうか?試してみましょう。 ```groovy:helloworld3/build.gradle task helloWorld { println "Hello, world!" } ``` これに対して、`gradle tasks`を実行してみます。tasksは、利用可能なタスクの一覧を表示してくれるプリインのタスクです。 ```shell Hello, world! :tasks ------------------------------------------------------------ All tasks runnable from root project ------------------------------------------------------------ Help tasks ---------- dependencies - Displays all dependencies declared in root project 'helloworld3'. dependencyInsight - Displays the insight into a specific dependency in root project 'helloworld3'. help - Displays a help message projects - Displays the sub-projects of root project 'helloworld3'. properties - Displays the properties of root project 'helloworld3'. tasks - Displays the tasks runnable from root project 'helloworld3' (some of the displayed tasks may belong to subprojects). Other tasks ----------- helloWorld To see all tasks and more detail, run with --all. BUILD SUCCESSFUL Total time: 0.884 secs ``` おや?一番先頭にhelloWorldタスクが実行されてしまっている様が見て取れます。 doLastを使わない部分は、タスクの設定などを行う箇所になるのですね。doLastを使う書き方に戻してみましょう。 ```groovy:helloworld4/build.gradle task helloWorld { description = "こんにちは!します。" doLast { println "Hello, world!" } } ``` 結果を見てみましょう。 ```shell :tasks ------------------------------------------------------------ All tasks runnable from root project ------------------------------------------------------------ 中略 Other tasks ----------- helloWorld - こんにちは!します。 後略 ``` helloWorldタスクは実行されなくなっていますね。代わりにタスクの概要が表示されるようになりました。descriptionとして設定した内容ですね。このように、タスクの設定を行う箇所とすると良いでしょう。 ### Gradleの自由さよ さて、そろそろGradleの本気を発揮してまいりましょう。GradleはGroovyという言語で記述しなければならないのですが、ぶっちゃけJavaのコードはGroovyのコードとしても正しいのです。そのため、まずは必要最低限のGradleが使える程度の勉強だけして、大部分をJavaコードで補うというのも手です。 ```groovy:weatherReport/build.gradle import org.json.JSONObject; buildscript { repositories { mavenCentral() } dependencies { classpath 'org.json:json:20090211' } } task weatherReport { description = "今日の東京のお天気をお伝えします" doLast { // Livedoorのお天気情報APIを利用してJSON形式のデータを取得する URL url = new URL("http://weather.livedoor.com/forecast/webservice/json/v1?city=130010"); URLConnection connection = url.openConnection(); InputStream is = connection.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); JSONObject json = new JSONObject(reader.readLine()); String cityName = json.getJSONObject("location").getString("city"); String weather = json.getJSONArray("forecasts").getJSONObject(0).getString("telop"); println "${cityName}の今日のお天気は${weather}です!" } } ``` 実行してみましょう。 ```shell $ gradle wR :weatherReport 東京の今日のお天気は晴のち曇です! BUILD SUCCESSFUL Total time: 2.009 secs ``` うーむ!素晴らしい自由度!タスクの中でWebサービスにアクセスして今日の天気を調べて表示することもできてしまいました。 GroovyではJavaよりも多くのpackageがデフォルトでimportされているため、`java.io.InputStream`や`java.net.URL`をimportせずとも済んでいます。( 参考 http://groovy.codehaus.org/Differences+from+Java ) その他の目新しい点として、`buildscript`が増えた事でしょうか。ここでは、build.gradle中で使うライブラリの設定を行なっています。MavenのCentralリポジトリを依存性解決のために利用し、json.orgのライブラリを利用するように設定しています。 MavenのCentralリポジトリには色々な人が作った多大な資産が眠っているため、是非活用してみましょう。 http://search.maven.org/ で検索すると、色々と出てきます。筆者が出しているライブラリも`net.vvakame`で検索するといくつか出てきます。(宣伝) さて、これでJavaで出来る範囲であれば、なんであれ自分でオリジナルタスクを作ってどうとでもできそうな気がしてきました。例えば、ファイルのコピーだとかクリーンアップなど、色々と出来そうです。 GradleとGroovyには様々な便利メソッドやタスクがあるので、慣れてきたら自分で調べて裾野を広げていけると良いでしょう。 ### Javaのプロジェクトをビルドできるようにしよう さて、Gradleで天気予報を表示するのもなかなか便利かもしれませんが、ビルドツールとしての本領を発揮してもらうことにしましょう。ここでは、多くの読者の環境とマッチするであろう、Java+Eclipseでの開発環境を整えてみます。 ```groovy:javaProject1/build.gradle apply plugin: "java" apply plugin: "eclipse" repositories { mavenCentral() } dependencies { testCompile "junit:junit:4.11" } ``` これだけ!簡単ですね。Gradleのはプラグインという仕組みがあり、複数のタスクを追加してくれたりします。ここでは`java`と`eclipse`を追加しています。これだけで、非常に多くの物事をこなしてくれるようになります。 あとは`repositories`と`dependencies`ですね。お天気予報タスク作成の段では、build.gradleで利用するためにjson.orgのライブラリを追加したのでした。一方、こっちではUnitTestを書くために、開発プロジェクトで使うためにJUnitへの依存することを追加しました。 さて、次はJavaコードを書いてみましょう。下準備として、`src/main/java` フォルダと `src/test/java` フォルダを手作業で作っておきます。その後で`gradle eclipse`を実行すると、Eclipseにプロジェクトをimportできるようにするためのメタファイルが生成されます。もちろん、build.gradleで設定したJUnitへの依存関係もちゃんと考慮され、Maven CentralリポジトリからJUnitのjarファイルを自動的にダウンロードしてEclipse上でもビルドパスに追加してくれます。 さくさくとJavaコードを追加してみます。 ```java:src/main/java/Sample.java public class Sample { public static void main(String[] args) { Sample sample = new Sample(); System.out.println(sample.getMessage()); } public String getMessage() { return "Sample!"; } } ``` ```java:src/test/java/SampleTest.java import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import org.junit.Test; public class SampleTest { @Test public void test() { Sample sample = new Sample(); assertThat(sample.getMessage(), is("Sample!")); } } ``` 試しに、Eclipse上でプロジェクトを右クリック→Run as→JUnit Testを選択してテストを走らせてみます。おそらく、緑になったことでしょう。素晴らしい! 次に、gradle上でもテストを動かしてみます。`gradle test`でテストが実行できます。 ```shell $ gradle test :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava :processTestResources UP-TO-DATE :testClasses :test Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8 BUILD SUCCESSFUL Total time: 2.284 secs ``` テストが走って、完走したようです。念の為にわざとテストを失敗させてみます。 ```shell $ gradle test :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava :processTestResources UP-TO-DATE :testClasses :test SampleTest > test FAILED java.lang.AssertionError at SampleTest.java:11 1 test completed, 1 failed :test FAILED FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':test'. > There were failing tests. See the report at: file:///Users/vvakame/Dropbox/work/docs/nikkei-software-2013-06-gradle/sample/javaProject1/build/reports/tests/index.html * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. BUILD FAILED Total time: 2.156 secs ``` よしよし、良い感じですね。これでJavaプロジェクトをGradleで管理できるようになりました。 ### AndroidのプロジェクトをEclipseで開発できるようにしてみよう(序章) (Android公式のやり方とは違います。公式のやり方はyagiさんパートを参照。) さて、この記事の後半ではyagiさんがGradle+Android開発の方法を紹介してくださいますが、ここではGradleのAndroid Pluginを使わずに無理矢理EclipseでAndroid開発が出来るようにしてみます。 まず、ざっくりとEclipse自体の説明を行います。Eclipseはプロジェクトに対して、いくつか設定ファイルを持ちます。代表的なのは、プロジェクトの設定が書いてある`.project`、プロジェクトのビルドパスが書いてある`.classpath`、Java開発用プラグインの設定ファイルである`.settings/org.eclipse.jdt.core.prefs`などです。 要は、それらファイルを`gradle eclipse`した時に生成してやれば良いわけです。 まずは、Gradle関係なくADTのWizardで普通にAndroidプロジェクトを生成し、どういう設定ファイルか確認してみます。そして、その結果を再現してみます。 ```xml:androidProject/.project <?xml version="1.0" encoding="UTF-8"?> <projectDescription> <name>AndroidProject</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name> <arguments> </arguments> </buildCommand> <buildCommand> <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name> <arguments> </arguments> </buildCommand> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> <buildCommand> <name>com.android.ide.eclipse.adt.ApkBuilder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>com.android.ide.eclipse.adt.AndroidNature</nature> <nature>org.eclipse.jdt.core.javanature</nature> </natures> </projectDescription> ``` ```xml:androidProject/.classpath <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> <classpathentry kind="src" path="gen"/> <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/> <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/> <classpathentry kind="output" path="bin/classes"/> </classpath> ``` なるほど。こんな感じですね。通常のJavaプロジェクトとくらべてみると、ビルドコマンドが3つ(javabuilder以外)と、ネイチャーが1つ(AndroidNature)が追加されているようです。また、クラスパスにソースフォルダとしてgenフォルダが追加され、JREコンテナが削除され、代わりにAndroidのフレームワークとライブラリが追加されています。また、コンパイル後のアウトプットフォルダがbin/classesとなっています。 また、AndroidのサポートしているJavaのバージョンが5と6だけなので、そのための設定も追加する必要がありそうです。AndroidもはやくJava7対応してほしいですね。7はまだいいとして、8が来なかったら発狂してオワコン扱いし始めますよ僕は。invokeDynamicのサポートとかdalvikの改修もめんどくさいし色々な機種にそれを反映させていくのにも時間がかかるのだから段階的に進めていってほしいものです。古い環境でコードを書かされる事を喜ぶエンジニアはいないのですよ! それでは、build.gradleを書いてみましょう。Androidの場合はライブラリの追加は、普通のJavaプロジェクトと同じようにやるのではなく、libsフォルダにjarを入れることで行いますね。そのため、dependenciesで`compile "com.google.android:support-v4:r7"`などとやってしまうわけには行きません。そのため、`androidLibs`を作成して独自に依存関係の管理をしてやるようにします。dependenciesでの記述ではそちらを使います。また、libsフォルダに依存ライブラリをコピーするために`copyDependenciesToAndroidLibs`タスクを作成し、`gradle eclipse`が実行された時に依存関係が勝手にlibsフォルダに入るようにしてやります。ついでに、`clean`タスクでlibsフォルダを綺麗にするようにしてやります。 また、.classpathと.projectファイルの生成も`eclipse`に追加の設定を行い、望む物が生成されるようにしてやります。 ```groovy:androidProject/build.gradle apply plugin: "java" apply plugin: "eclipse" sourceCompatibility = 1.6 targetCompatibility = 1.6 repositories { mavenCentral() } configurations { androidLibs } dependencies { androidLibs "com.google.android:support-v4:r7" androidLibs "com.googlecode.android-query:android-query:0.24.3" } sourceSets { main { java.srcDirs "src", "gen" output.classesDir = "bin/classes" } } task clean(overwrite: true) { delete "libs" } task copyDependenciesToAndroidLibs(type: Copy) { into "libs" from configurations.androidLibs eclipseJdt.dependsOn copyDependenciesToAndroidLibs } eclipse { project { name = 'AndroidProject' natures 'com.android.ide.eclipse.adt.AndroidNature' buildCommand 'com.android.ide.eclipse.adt.ResourceManagerBuilder' buildCommand 'com.android.ide.eclipse.adt.PreCompilerBuilder' buildCommand 'com.android.ide.eclipse.adt.ApkBuilder' } classpath { containers.clear(); containers 'com.android.ide.eclipse.adt.ANDROID_FRAMEWORK', 'com.android.ide.eclipse.adt.LIBRARIES' downloadSources = true downloadJavadoc = true } } ``` 今回のbuild.gradleではbuildは行わないので、これで十分でしょう。`gradle eclipse`でAndroid開発用の設定ファイルが生成されるようになりました。 ### antの代わりに 現在、antを使っているプロジェクトがGradleに移行してくるのもなかなか簡単です。やってみましょう。 ```xml:ant/build.xml <?xml version="1.0" ?> <project name="AntSample"> <target name="roll"> <script language="javascript"> <![CDATA[ // 6面ダイスを2個ふり、合計値を表示する var dice = Math.ceil(Math.random() * 6) + Math.ceil(Math.random() * 6); var echo = AntSample.createTask("echo"); echo.setMessage("2D6: " + dice); echo.perform(); ]]> </script> </target> </project> ``` ```groovy:ant/build.gradle ant.importBuild 'build.xml' task rollBefore { doLast { println "ダメージ計算を行います。" } roll.dependsOn rollBefore } task rollAfter(dependsOn: roll) << { ant.echo "ダメージロールの結果をHPから引いてください。" } ``` 実行してみます。 ```shell $ gradle rollAfter :rollBefore ダメージ計算を行います。 :roll [ant:echo] 2D6: 11 :rollAfter [ant:echo] ダメージロールの結果をHPから引いてください。 BUILD SUCCESSFUL Total time: 1.49 secs ``` うまくいったようです。既存のantの設定ファイルを`ant.importBuild`でインポートするとantのタスクがそのままgradleのタスクとして利用できるようになります。本来のgradleタスクと同様に扱うことができます。ここでは、rollBeforeタスクをGradle側で定義し、Ant側タスクであるrollに依存するように設定しています(`roll.dependsOn rollBefore`部分)。 また、rollAfterタスクも定義し、rollタスクとの依存関係を定義しています。このため、rollAfterはrollに依存し、rollはrollBeforeに依存しているため、rollAfterを実行するとrollBefore→roll→rollAfterの順で実行されました。 `gradle roll`で上記と同じ結果を得たい場合はrollAfterの代わりに以下のよう書いてrollタスクのdoLastの処理をにすることもできます。 ```groovy roll << { ant.echo "ダメージロールの結果をHPから引いてください。" } ``` また、rollAfterタスクの中で使っているように、antのechoタスクを利用しています。既にantに便利なものがあるのであればさくさく使ってしまい、徐々に移行していくのも良いでしょう。筆者はantのjavacタスクを利用し、AnnotationProcessorによるJavaコードの前処理を行ったりしています。 ### Mavenの代わりに antがとっても簡単にとりあえず移行できたのと対照的に、MavenユーザがGradleに移行するにはあんまり便利なパスが用意されていません。 既存のMaven PluginはGradleと互換性がないため、既存の資産は使うことができません。Maven Centralリポジトリを利用して依存性の解決はできるので楽なんですけどね。 筆者は普段の開発にGoogleAppEngine/Javaを多用しております。そのためのフレームワークとしてSlim3を利用し、サーバ側からクライアント側へのデータのやり取りのために筆者作のJSON処理ライブラリであるJsonPullParserを利用しています。 [gist](https://gist.github.com/vvakame/5176993) GradleのEclipse pluginさんはかなり柔軟で、ご覧のとおりEclipse用設定ファイルをかなり柔軟に弄る事ができます。このbuild.gradleでは`.settings/org.eclipse.jdt.core.prefs`を弄るパートが`properties.setProperty`を連発することになりあまり美しくないですね。処理後に手書き処理で無理矢理差し替えてしまったほうが綺麗に行きそうです。 ## よりGradleが便利になる設定・使い方 ここからは、更にGradleを幸せに使える方法を紹介していきます。 ### gradle --daemon について さて、Gradleを使っていると不満に思えてくるのが、実行時間の遅さです。なにせ、筆者の環境では先のJavaプロジェクトで`gradle test`すると実行に4.732秒ほどかかります。少し、長いなと感じますね。 その不満を解消するために、gradleには裏で常に待機していてもらうモードがあります。それが--daemonオプションです。 試しに`gradle --daemon test`としてみましょう。1回目は裏で待機していないため、やはり5秒くらいかかりますがもう一回実行してみると、1.041秒というごく短時間でタスクを完了することができます。うーん、素晴らしい! 毎回毎回 --daemon とつけるのもメンドクサイので、`GRADLE_OPTS`環境変数に`-Dorg.gradle.daemon=true`をセットすると、デフォルトでdaemonを使ってくれるようになります。 動いているdaemonを止めたい時や、一時的に使いたくない時のためにそれぞれ`--stop`や`--no-daemon`オプションが用意されています。 ### gradle wrapper について さて、Gradleを導入したい!と思っても、チーム全員の開発環境をGradle用に整えて全員で歩調を併せて…といったことを考えるととたんに腰が重くなるものです。しかし、Gradleならその点簡単に解決できます。 gradle wrapperという仕組みが用意されていて、チームの中でGradleをマジメにインストールするのは貴方1人、それ以外の人はsvnやgitのリポジトリにコミットされているファイルだけ使えばOK!という塩梅にすることができます。 見てみましょう。 ```groovy:gradleWrapper/build.gradle apply plugin: "java" apply plugin: "eclipse" repositories { mavenCentral() } dependencies { testCompile "junit:junit:4.11" } task wrapper(type: Wrapper) { gradleVersion = '1.4' } ``` task wrapper の記述が増えていますね。この記述を増やした後に `gradle wrapper`を実行します。 そうすると`gradlew`(Mac, Linux用)と`gradlew.bat`(Windows用)と`gradleフォルダ`が生成されます。これらをリポジトリにコミットするようにします。すると、他の人達はgradleコマンドの代わりに`gradlew`を使えるようになります。`gradlew`を実行すると、もし必要であればgradleコマンド実行用のバイナリを勝手にダウンロードしてくれます。 まとめると、「みんな!gradleを使うためにはgradlewを使ってくれればOKだから!」と宣言すれば、すぐにチーム全員で使いはじめることが出来るようになるということです。 ### gradle --gui について いやいや、gradlewは確かに嬉しいけど、`gradlew eclipse`とか、`gradlew test`とか、みんなに覚えされるのは大変だよ…!という意見もあるかもしれません。 ご安心ください!gradleにはなんとGUIモードも用意されているのです。試しに、`gradle --gui`または`gradlew --gui`を実行してみてください。  おぉ!素晴らしい!実行可能なタスクの一覧のウィンドウが表示され、マウスでもって好きなタスクを実行することができます。 あとは、`gradlew --gui`を実行してくれるバッチファイルでも置いておいて、それをダブルクリックするように教えればよいですね。お気に入りタスクを登録することもできるので、よく使うタスクを教えてあげると良いでしょう。 これで、みんなに簡単にgradleを使ってもらうことができますね :) ### Gradleの記述環境 codehausによる[Groovy Eclipseプラグイン](http://groovy.codehaus.org/Eclipse+Plugin)を利用するか、[IntelliJ IDEA](http://www.jetbrains.com/idea/)を利用すると良いでしょう。 筆者は本稿執筆のために、Groovy Eclipseプラグインをインストールして、build.gradleをGroovy Editorで編集しています。  ### 参考書籍・資料について 英語の本ですが、[毛虫本](http://www.packtpub.com/gradle-effective-implementation-guide/book)が大変良い資料になるでしょう。僕もこの記事を読むためにコードの部分を拾い読みするとかしましたが、大変参考になりました。 * [わかめのはてブ](http://b.hatena.ne.jp/vvakame/gradle/) * [G*ワークショップZのGradleハンズオンのスライド](http://www.slideshare.net/nobusue/gws-20130315-gradlehandson) |
|
| 22位 |
|
|||
|
17:50:41 |
(Increments Inc 所属) |
|
[Git Advent Calendar / Jun.](http://qiita.com/advent-calendar/git) 最終日(30日目)の記事です.29日目は「[いざという時のためのgit reflog](http://qiita.com/items/e37c707938847aee671b)」でした.
Git Advent Calendar最後なので,git操作でやりがちなミスからどう回復するかをまとめます.他にもあればコメントもらえるとマージしていきます. ## ブランチを切り忘れてmasterでコミットしてしまった その時点でブランチを切る&reset --hardで間違ったコミットたちをmasterから消す ```sh $ git checkout -b new-branch # masterの最新コミットを消す $ git checkout master && git reset --hard HEAD~ ``` ## ブランチを移動し忘れてコミットしてしまった cherry-pickでコミットを引っ張ってくる. ```sh # 現在間違ったブランチ(bad-branch)にいる $ git checkout good-branch $ git cherry-pick bad-branch # 間違ったブランチに行なったコミットを持ってくる # 間違ったブランチに行なったコミットを消す $ git checkout bad-branch && git reset --hard HEAD~ ``` ## 間違ったコミットをpushしてしまった 個人プロジェクトならコミットを消して[反則技のgit push -f](http://qiita.com/items/e37c707938847aee671b)する.そうでないなら,可能な限り速やかにgit revertを使って「間違ったコミットを打ち消すコミット」を行ない,pushする ## マージ済みのブランチが大量にある `git branch --merged`で一覧が見られるので,masterとカレントブランチ以外を以下のようにして消す. ```sh $ git branch -d $(git branch --merged | grep -v master | grep -v '*') # masterと現在いるブランチは消さない ``` ## 間違ってreset --hardで戻しすぎた git reflogを使う.詳しくは昨日のエントリ([いざという時のためのgit reflog](http://qiita.com/items/e37c707938847aee671b))を参照. ## 重要なstashを消しちゃった `git stash drop`したり,`git stash pop` -> `git reset --hard`したとき. `git fsck`するとdangling commitが見られるので,`git cherry-pick <SHA1>`か`git stash apply <SHA1>`してあげるとよい. git gcが走ってたら消えてるかも. ## コミットメッセージでtypoした 直前のコミットの場合は`git commit --amend`を使う.amendと打つのが面倒なので,`git commit --amend`は`git fix`にエイリアスしてる ```sh $ git commit # コミットメッセージでtypoした $ git commit --amend # 入力しなおす ``` ## ブランチを移動しようとしたら怒られた 以下のようなメッセージが出たとき ```sh $ git checkout fix/piyo error: Your local changes to the following files would be overwritten by checkout: config/deploy.rb Please, commit your changes or stash them before you can switch branches. Aborting ``` 消してもいい変更なら`git reset --hard HEAD`とか`git co .`する.後者をよく使う. 消さずに保存したい場合はきちんとコミットするか,stash saveする. git stashについては[fukajuneさんの「変更を一時的に退避!キメろgit stash」](http://qiita.com/items/41288806e4733cb9c342)が詳しいです. ## 昔のコミット内容にちょっとしたバグを見つけた **そのコミットをまだpushしていない場合限定** 新しく行なった変更を以前のコミットに含めたい. ### 直前のコミットに含めたい 上記の`commit --amend`を使うとよい. ### それ以上古いコミットに含めたい 変更をcommitしたあと,`git rebase`を使う. ```sh $ git commit -m '適当なメッセージ' $ git rebase -i HEAD~3 # 修正したいコミットを指定 # エディタが立ち上がるので,'適当なメッセージ'のコミットを修正したいコミットの下に移し,先頭をsquashに変更する ``` ## その他困った [QiitaのGitタグ投稿](http://qiita.com/tags/Git)を見るか,[git-tasukete](https://github.com/rosylilly/git-tasukete)に助けを求めてみる |
|
| 23位 |
|
|||
|
20:47:54 |
(株式会社Bizcast 所属) |
|
[国内注目のWebサービスを支える言語・フレームワーク・アーキテクチャ一覧【2013年版】](http://www.find-job.net/startup/architecture-2013) をもとにナウい感じのサービスなどをまとめた。分類がおかしいかもしれないのん
## コード管理 ソースコードは Git で管理しているところが増えている ### Git - [GitHub](https://github.com/) - [GitHub Enterprise](https://enterprise.github.com/): Enterprise 向け - Gitlab - [Gitlab Cloud](https://www.gitlab.com/cloud/) - [Gitlab CE](http://gitlab.org/gitlab-ce/): オープンソース - [Gitlab EE](https://www.gitlab.com/features/): Enterprise 向け - [BitBucket](https://bitbucket.org/) - [Stash](https://www.atlassian.com/ja/software/stash/overview): Enterprise 向け - [codebreak;](http://codebreak.com/) - [RhodeCode](https://rhodecode.com/pricing) - [Gitorious](https://gitorious.org/) - [GitPrep](https://github.com/yuki-kimoto/gitprep) - [GitBucket](https://github.com/takezoe/gitbucket) [GitHubクローンまとめ 無料でGitHubのような機能を実現するための候補 | Act as Professional - hiroki.jp](http://hiroki.jp/github-clone) #### 料金 private repository を作りたい場合は Gitlab Cloud (無料で無制限 repository, 無制限 private collaborators)。次に BitBucket (無料で無制限 private repository, 5 人までの private collaborators) ### コードレビュア - [Review Board](http://www.reviewboard.org/) - [Phabricator](http://phabricator.org/) - [rietveld](https://code.google.com/p/rietveld/) - [gerrit](http://code.google.com/p/gerrit/) - [Crucible](https://www.atlassian.com/ja/software/crucible/overview) - [Barkeep](http://getbarkeep.org/) - [rietveld](https://code.google.com/p/rietveld/) [コードレビューツール 6選 どれが最適? | Act as Professional - hiroki.jp](http://hiroki.jp/2012/09/13/5626/) ### バグトラッカー - [Bugzilla](http://www.bugzilla.org/) - [Trac](http://trac.edgewall.org/) - [mantis](http://www.mantisbt.org/) [フリーのバグ管理システム比較 - SOA導入するまで](http://d.hatena.ne.jp/ryoben/20100628/1277740446) ## DB ### RDBMS MySQL 使っているところやっぱり多い - [MySQL](http://www-jp.mysql.com/) - [Jet Profiler](http://www.jetprofiler.com/) - [PostgreSQL](http://www.postgresql.org/) - [Oracle Database](http://www.oracle.com/jp/products/database/overview/index.html) ### NoSQL (KVS) - [Redis](http://redis.io/) - [Memcached](http://memcached.org/) - [KyotoTycoon](http://fallabs.com/kyototycoon/) - [TokyoTyrant](http://fallabs.com/tokyotyrant/) - [KyotoTycoonとTokyoTyrantの違い | mutter](http://nplll.com/archives/2012/04/kyototycoontokyotyrant.php) [パフォーマンス比較 Cassandra、Mongodb、SQLite、H2、MySQL、Postgres - C/pHeR Memo - Java とか。Eclipse とか。](http://d.hatena.ne.jp/cypher256/20121013/p1) ### NoSQL (その他) - [MongoDB](http://www.mongodb.org/) - [特集:MongoDBで理解する「ドキュメント・データベース」の世界(前編):開発者が知っておくべき、ドキュメント・データベースの基礎 (1/3) - @IT](http://www.atmarkit.co.jp/ait/articles/1211/09/news056.html) ## 開発環境管理 ### セットアップツール Boxen は GitHub が開発した Mac OSX 向けセットアップツール。Qiita も使ってるらしい。 - [Boxen](http://boxen.github.com/) ## サーバ管理 ### 仮想化ソフト VirtualBox で Linux 環境構築 (Ubuntu か CentOS) して、その上に Docker でコンテナ作るの流行りっぽい - ハイパーバイザ - [KVM](http://www.linux-kvm.org/page/Main_Page) - [Xen](http://www.xenproject.org/) - ホスト OS - [VirtualBox](https://www.virtualbox.org/) - [Vagrant](http://www.vagrantup.com/) - [VMWare](http://www.vmware.com/jp/) - コンテナ - [LXC](http://linuxcontainers.org/) - [Docker](https://www.docker.io/) ### サーバプロビジョニング 環境構築を自動化する。Chef や Puppet が人気。Ansible も分かりやすいという評判を聞く - [Chef](http://www.getchef.com/chef/) - [chef-solo はじめてのLinux環境構築の自動化入門 | Act as Professional - hiroki.jp](http://hiroki.jp/2012/08/13/4989/) - [Puppet](http://puppetlabs.com/) - [オープンソースなシステム自動管理ツール Puppet:連載|gihyo.jp … 技術評論社](http://gihyo.jp/admin/serial/01/puppet) - [Ansible](http://www.ansibleworks.com/) - [Ansible Tutorial](http://yteraoka.github.io/ansible-tutorial/) #### サーバテスト - [serverspec](http://serverspec.org/) ### サーバオーケストレーション - [Capistrano](https://github.com/capistrano/capistrano/wiki) - [CapistranoとChefでサーバー管理を自動化~インスタンス作成から15分で実戦投入~ : ニフティクラウド ユーザーブログ](http://blog.cloud.nifty.com/157/) - [Fabric](http://docs.fabfile.org/en/1.8/) ### ロードバランサ - LVS + Keepalived - [Linux Virtual ServerとKeepalivedで作る冗長化ロードバランサ | SourceForge.JP Magazine](http://sourceforge.jp/magazine/13/05/14/083000) ### モニタリングツール - [nagios](http://www.nagios.org/): 統合監視ソフト - [特選フリーソフト - サーバーを多彩な手法で監視 Nagios:ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20070410/267919/) - [Zabbix](http://www.zabbix.com/jp/) - [Opsview](http://www.opsview.com/): Nagios 高級版 - [Munin](http://munin-monitoring.org/): リソース監視 - [Monit](http://mmonit.com/monit/): プロセス監視 - [New Relic](http://newrelic.com/): パフォーマンス監視 [意外と普通?大規模サービスにおけるサーバの監視方法 | nanapi TechBlog](http://nanapi.co.jp/blog/2013/09/11/monitor_nanapi_servers/) ### ログ収集ツール - [fluentd](http://fluentd.org/) - [TreasureData](http://www.treasuredata.com/) ### CI ツール - [Jenkins](http://jenkins-ci.org/) - [Travis CI](https://travis-ci.org/) - [Circle CI](https://circleci.com/) - [drone.io](https://drone.io/) ### その他 #### Job Queue - [TheSchwartz](http://search.cpan.org/~sixapart/TheSchwartz-1.10/lib/TheSchwartz.pm) - [Gearman](http://gearman.org/) [第10回 ジョブキューで後回し大作戦―TheSchwartz,Qudo,Q4M(1):Perl Hackers Hub|gihyo.jp … 技術評論社](http://gihyo.jp/dev/serial/01/perl-hackers-hub/001001) #### ログ可視化 - [GrowthForecast](http://kazeburo.github.io/GrowthForecast/) - [タムタムの日記 - GrowthForecastが便利な件について](http://mt.orz.at/archives/2012/06/growthforecast.html) - [HRForecast](http://blog.nomadscafe.jp/2013/02/hrforecast--.html) - [RRDTool](http://oss.oetiker.ch/rrdtool/) #### スクリプトのデーモン化 - [Supervisor](http://supervisord.org/) - [適当なスクリプトをデーモン化するのにSupervisorが便利 - id:anatooのブログ](http://d.hatena.ne.jp/anatoo/20120310/1331321778) ## Web ### アクセス管理 - GoogleAnalytics - [Mixpanel](https://mixpanel.com/) - [Chartbeat](https://chartbeat.com/) - リアルタイムアクセス解析 ### Web サイト監視 - [AlertSite](http://alertsite.com/) ### 負荷試験 - siege ## チーム - [Google Apps](https://www.google.co.jp/intx/ja/enterprise/apps/business/) ### グループウェア - [Office 365](http://www.microsoft.com/ja-jp/office/365/default.aspx) ### チケットトラッカー - [Pivotal Tracker](http://www.pivotaltracker.com/) - [Trello](https://trello.com/) - [JIRA](https://www.atlassian.com/ja/software/jira) - [Redmine](http://www.redmine.org/) [経験者によるRedmine、Trac、JIRA比較 - Ricksoftブログ - Confluence](http://www.ricksoft.jp/document/blog/view-blog-post.action?pageId=58982417) ### ドキュメント共有 - [Confluence](https://www.atlassian.com/ja/software/confluence) - [Qiita:Team](https://teams.qiita.com) - Wiki - [MoinMoin](http://moinmo.in/): アドレスが可愛い - [MediaWiki](http://www.mediawiki.org/wiki/MediaWiki/ja) - [PukiWiki](http://pukiwiki.sourceforge.jp/) ### チャットツール IRC が根強い人気を誇る。最近は Hipchat も人気出てきた - [Hipchat](https://www.atlassian.com/ja/software/hipchat/overview) - [How we use HipChat to keep the (distributed) UserVoice team in sync | UserCentered](https://community.uservoice.com/blog/how-we-use-hipchat/) - [Slack](https://slack.com/) - [ChatWork](http://www.chatwork.com/ja/) - [Idobata](https://idobata.io/) - [typetalk](http://www.typetalk.in/ja/) - [flowdock](https://www.flowdock.com/) - [Gitter](https://gitter.im) - [Hall](https://hall.com/) - IRC - [Skype](http://skype.com/) - [Hangouts](http://www.google.com/+/learnmore/hangouts/) ### SNS - [Yammer](https://www.yammer.com/) ## その他 ### カスタマーサービス - [Zendesk](http://jp.zendesk.com/) ### テスト - [Optimizely](https://www.optimizely.jp/): A/B テストツール |
|
| 24位 |
|
|||
|
19:21:52 |
(Increments Inc. 所属) |
|
 :warning: 2014年02月27日に書いた記事なため、内容が古くなっています。注意してください :sweat_smile: ## Atom とは GitHub 製テキストエディタ。Sublime のようにデフォルト状態でも十分使える、かつカスタマイズ可能。2014/02/27 現在では招待制。 [公式サイト - atom.io](https://atom.io) ### 他のエディタと何が違うのか Chromium をベースにできており、エディタのどのページもローカルで Web ページがレンダリングされてる。 Node.js を使って各ウィンドウの JavaScript 環境で自由にコードを実行できる。使用されてる技術的に Web 開発者にとっては馴染みが深いエディタになると思われる。 (Chromium ベースなので Developer Tools が開ける (`option-cmd-I`))  #### Sublime かなり似てる。git gutter とか package control がデフォルトで入ってるイメージ。プラグインの充実度はまだ比較にならないが、プラグインやテーマが GitHub と密に連携していたりとエディタのエコシステムが強みに感じる。 速さは体感ではそこまで変わらない。(少なくとも Sublime より速い、とかではない)Timecop(下記にスクショつけてる)でボトルネック見つけて改善もできそう。 #### Vim Vim と Sublime の違いに近い。vim-mode plugin は存在するが、vim ライクに使えるレベルといった感じ。`ctrl-V` が存在する!が、先頭インサートとか諸々使えなかったりで、今後に期待+キーバインドをカスタマイズする必要あり。 ~~今のところ Mac でしか動かないのも使うか迷うポイントである。(NeoVim vs Atom となっていくのかどうか)~~ 2015年03月20日現在は OS X 10.8 以降、 Windows 7 & 8、 RedHat Linux、 及び Ubuntu Linux で利用できます。 > #### What platforms does Atom run on? Prebuilt versions of Atom are available for OS X 10.8 or later, Windows 7 & 8, RedHat Linux, and Ubuntu Linux. https://atom.io/faq #### Emacs わかりません。(Emacs 使いの方、編集リクエストください) ## 操作 ### コマンドパレット `cmd+shift-P`  #### Open On GitHub: File 現在見てるファイルの GitHub ページを開く #### Open On GitHub: Blame コードを範囲選択した上で、`Open on GitHub: Blame` を実行すると、該当箇所についての GitHub blame ページが開く #### Open On GitHub: History 現在見ているファイルの History の GitHub History ページを開く #### Open On GitHub: Copy URL `http://github.com/wantedly/wantedly.github.io/blob/develop/README.md#L14-L19` のようなリンクをコピーする。Blame と同じく範囲選択した上でコマンドパレットから `Open On GitHub: Copy URL` を実行 ### ツリー #### ツリー開閉 `cmd-\` #### ツリーにフォーカスを移動 `ctl-0` ### ファイル #### ファイルの追加、移動、削除 `ツリー上で右クリック` キーボードショートカットもある * 追加 `a` * 移動 `m` * 削除 `delete` ### 検索 #### ファイル検索 `cmd-t` #### 現在開いてるファイルから検索 `cmd-b` #### 変更のあるファイルと Git でまだトラッキングしてないファイルを検索 `cmd-shit-b`(Git を使ってるプロジェクトであれば) #### ファイル内検索と置換 `cmd-f` #### プロジェクト全体からファイル内検索 `cmd-shift-f` ウィンドウ下部に置換文字列を入力することで置換 #### シンボルへのジャンプ `cmd-r` #### プロジェクト横断でシンボルへのジャンプ まず、 ```bash $ brew install ctags ``` プロジェクトルートにて ```bash $ ctags -R ./ ``` atom にて `cmd-shift-r` ### パネルの分割 #### 分割 `cmd-k` の後に `上下キー` 例えば、右にパネルを出したかったら `cmd-k` + `右` #### 移動 `cmd-k` の後に `cmd-上下キー` 例えば、左に移動したかったら `cmd-k` + `cmd-左` #### 閉じる `meta-w` (`cmd-w`) ### ブロックをたたむ 行の上にカーソルを合わせると表示される矢印をクリック キーボードから行うには * たたむ - `alt-cmd-[` または `alt-cmd-]` * 開く - `alt-cmd-shift-}` または `alt-cmd-shift-}` ## カスタマイズ (ちょっとずつ書いてく)  ### 設定画面を開く コマンドパレットから `settings` と検索 ### テーマの変更 設定画面の Theme タブから、全体のテーマとコード部分のテーマ変更が可能。 デフォルトが一番良いと思う ### パッケージのインストール 設定画面の Packages からインストール可能なパッケージインストールするだけ もしくは ```bash $ apm install <package> ``` ## 便利機能 ### Markdown プレビュー  Packages -> Markdown -> Preview Buffer 保存するとレンダリングってのがあと一歩? ### 変更ファイル・新規ファイルが一目でわかるツリー  * 変更したファイルはオレンジ * 新規ファイルはグリーン ### どこが遅いか調べられる Timecop  パッケージのローディングやテーマのローディング等、どこでどれくらい時間かかってるかわかる ### 自前テーマを作るのに便利そうな StyleGuide  エディタの Web ページなので、CSS (less)でテーマ変えられる。 ## 感想 * GitHub 製 * ロードイメージが Octocat だったり * まだ git でトラッキングしてないファイル用に + マークのアイコンがついたり * ブランチ名デフォルトで出たり * デフォルトのままで十分使えると書いてるあるとおりだった * テーマが少ないのもあるけどデフォルトが一番良かった * 設定画面が GUI で簡単 * 設定ファイルの実体は `~/.atom` ディレクトリにある。dotfiles での管理簡単 * プラグインの管理が楽 * 何を入れてるか、どれが有効か簡単に切り替え可能 * GitHub と密に連携してるので Issue 上げたり README に飛べたり * キーマップ、閲覧は GUI、編集はファイルベース、良い * Yet Another Sublime 感 * package とか thema の generator * markdown preview やばい... * markdown の syntax highlight が GitHub Markdown * theme とか less で書ける * Timecop っていうエディタのどこが遅いか表示できる * vim-mode プラグインもある(vim 代替レベルではない...) * [open-source と closed でゆれてる?](http://discuss.atom.io/t/why-is-atom-closed-source/82/6) |
|
| 25位 |
|
|||
|
19:28:05 |
(株式会社nanapi 所属) |
|
iPhoneアプリの申請作業を行う上で知っておいた方が良いことをまとめておきます。 * 一部個人の見解も混ざってます * 申請ルールなどは予告なしに変更になることがよくあるので、本記事の内容は古い場合があります # アプリ申請前 ## アプリはどこから申請するの? [iTunes Connect](https://itunesconnect.apple.com)というサイトから申請できます。ブラウザはSafariでアクセスすることをおすすめします。 ## 誰でも申請できるの? Apple Developer Programに登録する必要があります。(年間参加費 ¥11,800) また、アプリのアップロード時にはXcodeが必要なので、Macは必須になります。 ## アプリの申請時には何が必要なの? 次の情報が必要になるので事前に準備しておきましょう。 * アプリ名(30文字以内、すでに使われているものはダメ) * サブタイトル(30文字以内、iOS 11移行を搭載したデバイスにのみ表示される) * カテゴリ(主カテゴリと副カテゴリを指定可。詳細は後述) * アプリの説明文(4000文字以内) * プロモーション用テキスト(170文字以内、iOS 11移行を搭載したデバイスにのみ表示される) * アプリの価格 * アプリのレーティング(性的なコンテンツ等が表示される可能性がある場合は高めに設定しておかないとリジェクトされる) * 検索のためのキーワード(100文字以内) * サポート用サイト * App Store用のアイコン(1024x1024, 高画質JPEG/TIFF/PNG) * スクリーンショット(詳細は後述) * 連絡用のメールアドレス、電話番号(公開はされない) ## 主カテゴリと副カテゴリはどう違うの? 第一希望と第二希望だと思っていたのですが、違うようです。 >アプリケーションに指定する主カテゴリ(PrimaryCategory)も、見つけやすさの面で重要なポイントです。閲覧するユーザに便利なよう、アプリケーションはこのカテゴリ別にリストアップされます。 そのアプリケーションの主機能を最も適切に示しているカテゴリを注意深く選択してください。提出されたすべてのカテゴリはAppleがレビューを行います。 また、オプションの副カテゴリ(Secondary Category)も指定できます。アプリケーションは副カテゴリではリストアップされませんが、その副カテゴリに対する検索結果としては返されます。たとえば、そのアプリケーションの主カテゴリが「財務会計(Finance)」で副カテゴリが「ビジネス(Business)」だった場合、ユーザは財務会計のカテゴリをチェックしたときにそのアプリケーションを見つけることができ、また「ビジネス」で検索したときにもその検索結果としてそのアプリケーションを見つけることができます。 [iTunes Connectデベロッパガイド](https://developer.apple.com/jp/devcenter/ios/library/documentation/iTunesConnect_Guide.pdf)より抜粋 詳しくはこちらの記事をご覧ください。 * [iOSアプリ公開時に設定できる「Secondary Category」はどこで使われるのか?](http://blog.koogawa.com/entry/2014/01/28/174240) ## 検索キーワードには何を入れればいいの? アプリケーション名、会社名は検索ワードとして自動的に設定されるので、表記のゆらぎ(例:Twitterアプリなら「ついったー」「とぅいったー」)等を登録しておくと良いでしょう。 ちなみに、アプリと関係ないキーワードや他社アプリ名などを入れるとリジェクトされるので注意です。 ## アプリ申請時にスクリーンショットは何種類用意すればよいの? 2016/08/09より、スクリーンショットの簡易提出が可能になりました。これにより、iPhone/iPadそれぞれの最大サイズのスクリーンショット(例えばiPhoneなら5.5inch)をアップロードするだけで、自動で小さいサイズ用にリサイズしてくれるようになりました。詳しくは @mono0926 さんの記事にとても詳しくまとめられています。 * [iTunesConnectでのアプリスクリーンショットの簡易提出が可能になりました🎉](http://qiita.com/mono0926/items/0b1122e5a254f945f6b0) ## アップロードできるスクリーンショットのサイズ・ファイル形式は決まってるの? はい。決められたサイズの画像しかアップロードできません。画像ファイル形式はJPEG、TIFF、またはPNGが使用可能です。 ### iPhoneアプリの場合 #### 3.5インチディスプレイ用(iPhone 3GS、iPhone 4など、iPhone 5より前の機種) * 640×960ピクセル(ステータスバーを除く場合は640×920ピクセル) * 最低でも1枚。最大5枚まで。 #### 4インチディスプレイ用(iPhone 5、iPod touch 第5世代など、縦長の機種) * 640×1136ピクセル(ステータスバーを除く場合は640×1096ピクセル) * 最低でも1枚。最大5枚まで。 #### 4.7インチディスプレイ用(iPhone 6) * 750×1334ピクセル * iPhone 6をサポートする場合は最低でも1枚必要(最大5枚まで) #### 5.5インチディスプレイ用(iPhone 6 Plus) * 1242×2208ピクセル * iPhone 6 Plusをサポートする場合は最低でも1枚必要(最大5枚まで) スクリーンショットに機能説明用のテキストなど、装飾を入れても問題ないようです(過度な使用は避けたほうがよいでしょう) ### iPadアプリの場合 iPadアプリの場合は更に多くの種類の画像が必要になります。詳しくは[iTunes Connectデベロッパガイド](https://developer.apple.com/jp/devcenter/ios/library/documentation/iTunesConnect_Guide.pdf)を参照してください。 ## アプリ紹介用のビデオをアップするには? iOS 8のApp Storeから、動画によるアプリ紹介(App Video Previews)が可能になりました。 動画を撮影・アップするにはYosemite以上(OS X 10.10以上)のMacとSafariが必要になります。 また、動画にもいろいろな制約(30fps以下であること等)があるので、[Appleの公式ドキュメント](https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/wo/1.0.0.13.7.2.7.9.3.1.2.3.3.1.1.12.1)を読むことをオススメします。 ## アプリの公開日はコントロールできるの? できます。 新規アプリの場合は「Availability Date」で公開日を指定できます。 アップデートの場合は、アプリ申請時にリリースタイミングを * 自動リリース - 審査が通ったら即時リリース * 手動リリース - 審査が通ったら一旦保留状態になり、好きなタイミングでリリースできる * 時間指定リリース - 審査が通ったあと、指定した時間に自動リリース の中から選択できます。 # アプリ申請後 ## アプリの審査にはどのくらい期間がかかるの? アプリの内容や審査の時期にもよりますが、スムーズに行けば新規アプリの場合は大体2週間、アップデート審査の場合は1週間ぐらいで完了します。もちろん、これより早く審査が完了したり、逆にもっと長くかかったりする場合もあります。 (私が実際に申請したアプリの平均データであり、公式な情報ではありません) **2017/6/14 追記:アップルさんが本気を出し始めたので、最近は1〜2日で完了するようになりました。** アプリ審査の混み具合を把握できるサイトもあります * [App Review Status](https://developer.apple.com/support/app-store/) - Apple公式。5営業日以内にレビューされる割合がわかる(5営業日以内にレビューが完了する割合ではないので注意) * [Average App Store Review Times](http://reviewtimes.shinydevelopment.com/) - 世界中のアプリ開発者から自己申告されたレビュー日数を元に平均日数を算出してくれるサービス。 ## アップデート審査完了後の公開日はコントロールできるの? できます。アップデート申請時に自動リリースか手動更新か選択できますので、後者を選択しましょう。手動更新を選択すると、審査完了後はリリース保留状態になり、リリース指示を出すまでApp Storeには公開されません。 ## Waiting For Review(レビュー待ち)中に変更できる情報は? * バージョン番号 * コピーライト * カテゴリ * レーティング * アプリアイコン * アプリ名 * Appサブタイトル * アプリ説明文 * アップデート内容 * キーワード * 各種URL * スクリーンショット * 開発者連絡先 * EULA(End User License Agreement) ※これらは変更になる可能性があるので、最新の情報は[公式ドキュメント](http://help.apple.com/itunes-connect/developer/#/dev18557d60e)を参照して下さい。 ## In Review(レビュー中)に変更できる情報は? * コピーライト * 各種URL * 開発者連絡先 * EULA(End User License Agreement) ※これらは変更になる可能性があるので、最新の情報は[公式ドキュメント](http://help.apple.com/itunes-connect/developer/#/dev18557d60e)を参照して下さい。 ## アプリ審査中にバグが見つかったんだけど、審査を中止してもらえるの? 審査状態が以下のいずれかである場合のみキャンセルできます。 * MissingScreenshot(スクリーンショットの欠落) * WaitingforExport Compliance(輸出コンプライアンス待ち) * Waiting For Review(レビュー待ち) * In Review(レビュー中) * Pending Apple Release(Appleリリース待ち) iTunes Connectにログインし、「マイ App」ページの「このバージョンをレビュー審査から削除」をクリックすると、バイナリが審査待機リストから削除されます。  ## どんなアプリがリジェクトされるの? Appleが公開している「アプリケーション審査ガイドライン」に従っていないアプリはリジェクトされます。 * [アプリケーション審査ガイドライン](https://developer.apple.com/jp/appstore/guidelines.html) * [iPhoneアプリ審査での111の禁止項目(意訳)](http://fladdict.net/blog/2010/09/reject-list.html) - [fladdict](http://twitter.com/fladdict)さんによる意訳。 ## 段階的にリリースはできるの? できます(2017年6月5日〜)。段階的リリースにより、**自動更新がオンになっているユーザ**に対して、7日間かけて徐々にバージョンのアップデートがリリースされていきます。(段階的リリースに当たらなかったユーザーも、自分からApp Storeを見に行った場合はダウンロードできてしまう点にご注意ください) また、バージョンのアップデートで問題が発生した場合、いつでも段階的リリースを一時停止できます。 詳しい設定方法については[iTunes Connect のリソースとヘルプ](https://itunespartner.apple.com/jp/apps/news/31070842?sc_cid=ITC-AP-PSRL)を参照してください。 ## 審査中にアップルから電話がかかってくることはあるの? 何度もリジェクトが続くと電話でやりとりすることになる場合もあるようです。というか、ありました。(私の場合は日本語が通じる方でした) # アプリ公開後 ## 公開後にスクリーンショットは自由に変更できるの? 2013年1月9日よりスクリーンショットの常時変更ができなくなりました。公開後はアップデート時のみ変更可能です。 [Updating Screenshots in iTunes Connect](https://developer.apple.com/news/index.php?id=1092013a) ## 公開後はどんな情報が変更できるの? 次の情報は常時変更できます。 * ~~アプリの説明文~~([2017年6月5日](https://itunespartner.apple.com/jp/apps/news/61604499?sc_cid=ITC-AP-UPDIN)から変更できなくなりました) * アップデート内容文 * プロモーション用テキスト * Support URL * Marketing URL * Privacy Policy URL * Copyright 次の情報はアップデート時にのみ変更可能です。 * アプリ名 * アプリの説明文 * カテゴリ * レーティング * キーワード * App Store用アイコン * スクリーンショット ## アプリのアップデートには何の情報が必要なの? アップデート内容を説明する文が必要になります(4000文字以内) ※空欄でも審査には出せるようです ## アプリのアップデート審査にはどのくらいかかるの? 先述した通り、アップデート審査の場合は1週間ぐらいで完了します。もちろん、これより早く審査が完了したり、逆にもっと長くかかったりする場合もあります。 (私が実際に申請してきたアプリの平均データであり、公式な情報ではありません) **2017/6/14 追記:アップルさんが本気を出し始めたため、最近は1〜2日で完了するようになりました。** ## 表示されているカテゴリがおかしいんだけど? カテゴリを変更した際などは、App Storeに反映されるまで3〜4日かかる場合もあるようです。(実際にありました) ## アプリをAppStoreから削除できるの? iTunes Connectから削除可能です。削除されたアプリケーションは24時間以内にApp Storeに表示されなくなります。 ## アプリの公開を一時的に停止したいんだけど 公開する国をゼロにすることで、非公開状態になります。 1. iTunes Connectにログインして非公開にしたいアプリを選択します 2. 「Rights and Pricing」をクリックします 3. 「Select the App Store territories…」からすべての国のチェックボックスをオフにして「Save」ボタンをクリックします すると、アプリのステータスが「Developer Removed From Sale」に変わります。数時間でApp Storeからアプリが消えます。 公開を再開したい場合は、公開先の国にチェックを入れて保存します。ステータスが「Ready for Sale」に変わり、アプリがApp Storeに公開されます。 ## アプリを公開したあとに致命的なバグが見つかったんだけど、どうしたらいい? 1. **まずは落ち着きましょう** 2. iTunes Connectから「Developer Rejected(デベロッパ取り下げ)」を行い、App Storeでの公開を中止しましょう。 3. 急いで修正版を申請しましょう 何日も審査を待ってられないよ!!という場合は「Expedited reviews(通称:特急審査)」 を使う手段もあります。必ず受理されるわけではありませんが、受理されれば1〜2営業日で審査してくれます。(早急な審査が必要な理由を英語で書く必要があります) [Request an Expedited App Review](https://developer.apple.com/appstore/contact/appreviewteam/index.html) ## アプリのダウンロード数は見れるの? はい。iTunes Connectから、App Storeでのアプリケーションダウンロード数、売上データ(日次、週次、月次、年次、累計)を閲覧・ダウンロードできます。 ## アプリを他の人に譲渡できるの? できるようになったみたいです。 > 開発者どうしでアプリケーションを売買することが多くなり、譲渡手続きを自動化する仕組みが構築されました。この手続きにより、アプリケーションの所有権を、App Storeに置いたままで他の開発者に譲渡できます。それまでに寄せられた意見や要望、人気度の順位などもそのままです。譲渡するアプリケーションの数に制限はありませんが、個別に行う必要があります。 [iTunes Connectデベロッパガイド](https://developer.apple.com/jp/devcenter/ios/library/documentation/iTunesConnect_Guide.pdf)より引用。 アプリを譲渡するには色々と条件があるようなので、具体的な手順はデベロッパガイドを参照してください。 # リンク * [iTunes Connect](https://itunesconnect.apple.com) * [iTunes Connectデベロッパガイド](https://developer.apple.com/jp/devcenter/ios/library/documentation/iTunesConnect_Guide.pdf) |
|
| 26位 |
|
|||
|
22:31:45 |
|
|
ちょっと書きたくなったので書くんじゃーい! この文章を読み終わった時、読者がそれなりわかめ品質な文章を出力できるようになり、どこかに寄稿した時に全面リテイクを食らったりしないようになることを目指します。 mhidaka が [0歩目](http://qiita.com/mhidaka/items/c5fe729716c640b50ff7)を書いてくれました! ## 背景 筆者は普通のエンジニアです。その辺の開発とかしてる会社に勤めています。技術系の原稿も書きます。 原稿書きでご飯食べてるわけではありません(晩ご飯が豪華になることは稀にあります)。 今まで有能なレビューワー(muなんとかさんとか)編集さんとか(某社の安藤さんとか)とかとかに鍛えていただきました。 この場を借りてお礼を述べておきたいと思います。ありがとうございます。 なお、この文章は2013年10月時点での筆者(わかめ)のやり方です。 将来的にはより良いやり方を見つけるでしょうし、これとは全く違う書き方で上手にやっている人もいると思います。 これから偉そうな事述べるであろう[筆者の書いた記事](http://b.hatena.ne.jp/vvakame/%E4%BF%BA%E3%81%AE%E8%A8%98%E4%BA%8B)とか[スライド](http://www.slideshare.net/vvakame/presentations)とか。 あ、僕の今までのQiitaの記事の[ストック数](http://qiita.com/vvakame)見ます?(ドヤッ # 準備体操 なぜ文章を書くのか いきなりですが、質問です。貴方は何故原稿を書こうとしているのでしょうか?考えてみましょう。 * 自分の知識・経験に広める価値があると判断している * 言い換えると、何かを布教したい * 最新の情報について勉強したい * 言い換えると、ついでに原稿料貰いたい だいたいこんな所だと思います。 ## 知らない事も書いてみよう 多くの人が勘違いしがちなのですが、自分が知らない事について書くというのは、実は罪ではありません。貴方は許されています。知らない事について調べて書いてもよいのです。 文章を書くということは、その内容について責任を持つということと不可分です。つまり、貴方は日本語を話す人としては、一時的にであれその分野について最も詳しく知っている人になる必要があります。 その分野について最も詳しく、というと死ぬほど難易度が高そうに思いますが、実のところそんなに難しいことではないのです。 例えば、モンスターハンター4を極めし者 になるのは死ぬほど難しいと思います。ですが、モンスターハンター4の下位地底火山で最短ルートで大地の結晶を集めて帰還することを極めし者 くらいまで限定すると、不可能ではないように思われます。 まずはそこを筆者自身の技術的到達点として狙って行きましょう。 もし万が一、責任を持ちたくない場合は、見るからにちゃらんぽらんで、信頼がおけないような文章に仕上がるように努力しましょう。 # 第1歩 設計 まず、最初にこれから作成する文章の設計を行います。 設計するのは以下の要素についてです。 * 対象読者を考える * 言い換えると、原稿を読む前の読者のレベルを設定する * 読み終わった時に読者が得られるものを考える * 言い換えると、原稿を読み終わった後の読者の到達点を設定する * 文章の構造について考える * 章・節・項 を考えます。 * Markdownでいうと、# と ## と ### を先に書く * 文章の長さに応じて考える深さを変えます。この文章は深さ1で設計しました。 * サンプルを作成する場合、どういうサンプルを作成するか検討する * 実はこれがかなり難しいです * 説明したい要素が十分に含まれている * 説明しない要素かつ難しいものがなるべく含まれないようにする * 短いソースコードにできる * 説明する技術のメリット(とデメリット)が明確にわかる 筆者(わかめ)の場合、この工程に意識的に取り組む時間はおおよそ10分〜2時間くらい消費されます。 かける時間は短いですが、ここを真面目にやらないと後から大炎上するのは皆様も知る通りです。 ## 文章構造を考えるうえで 文章構造について考える。について少し詳しく書いておきます。 筆者が留意しているのは、読者は[有限オートマトン(有限状態機械)](http://ja.wikipedia.org/wiki/%E6%9C%89%E9%99%90%E3%82%AA%E3%83%BC%E3%83%88%E3%83%9E%E3%83%88%E3%83%B3)だということです。 初期状態(対象読者を考える)と受理状態(読み終わった後に読者が得られるものを考える)は既に考えてあるはずです。 ここでいう章・節・項を設計することは、読者の状態を初期状態から受理状態に変化させるための途中の状態を設計することに他なりません。 読者を無理なく初期状態から受理状態に遷移させるために、情報を与える順序は前後してはいけませんし、途中に歯抜けが発生してもいけません。 そこを正しく行うにはどういう状態で情報を渡していけばよいのかを考え、設計を行います。 筆者は、筆者内部での課題の設定(と読者への共有)、筆者の考えた解決方法の開陳(と読者への共有)、筆者の考えた解決方法の提示と結論 を示すと良いと考えています。 また、その他の重要な点として、サンプルコードを用意する場合について。 ソースコードを解説する順番に説明の順番がかなり依存するので、サンプルを用意する場合は文章構造がサンプルコードの説明をしやすい順番にできるかも考慮に入れます。 ## それからそれから? さて、上記4要素を書ききるためには、まずは書く対象について概要を知らなければなりません。 ドキュメントやサンプルコードがあれば、それに目を通して概要を掴んでおくと良いでしょう。 ここまで書き終わったら、完成までにどういう事を調べなければいけないか、どういう障害がありうるか、ある程度あたりがつくようになっているでしょう。 この時点で、何か憂いがある場合は解決しておきます。他の人とのネタ被り・自社の機密に抵触するか・機材や経費は許容範囲内か、などなどが考えられます。 懸念事項は全てクリアしておき、もし編集さんなどの第三者がいる場合はこの時点で一旦相談しておくと良いでしょう。 この辺りの手を抜いてしまうと、後々例外やエラーの処理方法を最初に設計しておかなかったシステムの如く、後から仕組みを追加してテストを追加して工数は倍々ドン!みたいな身に覚えのある辛い展開になってしまいます。 ## ネタが被るのは悲しいことですね ネタ被りについても少し言及しておきます。 他の人が書いた原稿について、ネタが大なり小なり被ることはある程度仕方がない部分があります。 パクリは論外ですが、ネタが被るのは仕方がないと割りきってしまいましょう。 もし、徹底的にネタ被りを避けようとするとどうなるでしょうか? 英語の公式ドキュメントがあるのにそれを日本語に翻訳することは言語は違いますが内容的には完全にネタ被りなので、行わないほうがいいでしょう(実際は母国語で公式ドキュメントが読めることは多くの人にとって価値があります)。 C言語についての本はたったの1冊(K&Rですかね)あれば十分で、2冊目以降はネタ被りになるので不要になります(実際は2013年にもなって未だに新しいC言語の本が出版されています)。 ですが、実際には括弧内に書いた通り、ネタ被りと判定しようと思えば出来る文章でもどんどん作成されていますし、広く受け入れられています。 つまり、あなたが付け加えられるオリジナルな価値・説明があればそれで良いのです。 もし、ネタの被り先が知合いであれば、一言断ってみると、精神衛生上大変良くなるので、聞いてみると良いでしょう。 # 第2歩 調査 設計が終わったら、技術的な調査に入ります。 実際のシステム開発だと、技術的な調査を行ってから設計に入ると思います。ですが、原稿の場合は本格的な調査は設計の後になります。 調査フェーズで行わなければいけないのは以下のとおりです。 * サンプルコードの完成 * サンプルコードの出来によって、原稿の設計を変える必要が生じるかもしれません。完成させていきましょう。 * 技術的不明点の解消 * サンプルコードを作るにつれ、不明点が発生してくるはずなので可能な限り調査する * Androidとかだとオープンソースなのでどこまでも追っていけるので楽ですね * 調査終了の基準点として こうすれば何かが達成できる、筆者が知らない機能や仕様は多分ない、これはどうあがいても実現できない事がわかる あたりでしょう。 筆者(わかめ)の場合、この工程に意識的に取り組む時間はおおよそ2日〜3日(1日6〜8時間換算)くらい消費されます。 # 第3歩 実装 さて、実際に原稿をもりもり書いていきましょう。 設計段階で枝(章・節・項)は定義しているはずなので、そこにもさもさと葉を生やしていくだけの簡単な作業です。 既にサンプルコードも出来ており、判明している技術的に不明な点は消化されているはずです。 そのため、ここからは日本語と戦う作業になります。 文章を書く時に筆者が意識的に行っている事がありますので述べていきます。 * 日本語が一意に解釈できるか。誤解を生まないか。 * 対象読者に未知の用語を使って説明していないか * 説明の順番を変更する必要があれば思い切って変更する。そうならないように設計時によく検討していくことも重要。 * 筆者の中で常識だと思っている事が本当に常識かどうかを検討する。社内で常識になっているものは誤判断しやすいので留意する。 * 常識じゃないものを常識だと判断すると、説明をすっ飛ばすというミスに派生する * 要求・結論を先に述べているか。犯人はヤスは正義。オリエント急行殺人事件の犯人は☓☓☓。 * これから説明する内容をざっくり最初に説明するのは大事 * そこをサボると、読者の中に無数の解釈の枝(主旨の推測)が発生してしまい、書いてある内容がするりと入っていかなくなってしまいます。読者の計算量を減らすために積極的に枝刈りを行いましょう。 * 言い換えを考えてみて、元の言い方と言い換えの方のどちらが意図が伝わりやすいかを検討し、必要があれば書き換える。 * 断定口調で書けているか。だと思う、とかであろう、とかは だ、である、に変更する。不安がある場合は追加の技術的調査を行う。 * 用語が統一され、正しく用いられているか。 * 例えば、1つの文章中に if文 と if式 という書き方が混ざっていてはいけません。それぞれ、指すものが違います。用語が正確に用いられているのかというのは、文章全体の信頼性に関わります。 * 例えば、完全に正しくて有用な技術的文章でも、Javaの事をJavaScriptと書いて説明していたら、信頼度は地に落ち、他の部分も間違っている可能性のある、取るに足らない文章と判断されるでしょう。 * 文章を分割できないか検討する。 * 長い1文よりも、短い2文です。句点で切っても正しく意味が伝わる場合は積極的に切ります。 * 文の先頭に何かをつけるのを我慢する。 * または、しかし、さらに、なので、 などは文の先頭に付けたくなってしまう魔力を持っています。そうしたほうが読みやすい文に見えてしまう気がするのです。それは気のせいなのでなるべく省けるように努力します。 * 句読点は適切に打たれているか。 * わかめさんめっちゃ読点多いマンです。 また、気がついたら随時リファクタリングを行います。 節の位置の交換とかも随時行いますが、なるべく最初の設計から変更しないで済むように留意します。 筆者(わかめ)の場合、この工程に意識的に取り組む時間はおおよそ1日(1日6〜8時間換算)くらい消費されます。 レビュワーに頼んだ後の指摘事項修正や校正などに費やす時間はここに含みません。 # まとめ さて、いかがでしたでしょうか? みなさんもぶりばり技術文章を書いて、読んでくれた人をどんどん洗脳して自分のシンパにしましょう。 さぁ!みんなもTypeScriptをやろう!(オー 僕も、こういう事を言語化して考え、なるべく体系的に把握するよう務めるようになったのも、ここ2年くらいの事です。 この文章を書いて、あだむろっかとかに『[2.3本](http://www.amazon.co.jp/gp/product/4822284719?ie=UTF8&camp=1207&creative=8411&creativeASIN=4822284719&linkCode=shr&tag=damenako-22&qid=1381757964&sr=8-7)書いてた奴があんな事言ってるwwwwm9(^Д^)プギャー』と煽られました。 2.3本はたぶん僕が多くの人に読ませるために書いた初めての技術的文章で、あだむろっかとかますいさんにボッコボコにされた事を懐かしく思い出します。今はあの時よりはマトモになってると思うんですけどね。 簡潔に言うと、最初はクソみたいな文章を出力してしまうものだし、使い古されている言葉ではありますが継続は力なり、ということです。まずは恐れず、クソを生産して周囲の識者にボコボコにしてもらえたら最初の洗礼を受けられた、と捉え一安心すると良いと思います。 なお、この文章を書くにあたり、[編集履歴](http://qiita.com/vvakame/items/d657baf26cf83ac98bd0/revisions)に作業風景がなるべく残るようにしてみました。 興味があればご覧ください。 これだけの文章を書くのに約3時間かかっているようですね。短いのか長いのかもわかりませんが。 |
|
| 27位 |
|
|||
|
20:46:14 |
|
|
最新の類似投稿として[シェルスクリプトのコーディングルール2014](http://qiita.com/b4b4r07/items/62d56b7de2b9d6844bb5)も併せてどうぞ。
**2014/10/09追記** ぼくがシェルスクリプトを書くときに気にしていること、過去の失敗で書き留めたことを忘れないために。 # 1. グローバル変数は大文字 `PATH` や `HOME` など、環境変数が大文字なので、エクスポートする変数を大文字で書くという習慣は一般的であるような気がしますが、エクスポートする変数を抱えるシェルスクリプトを作成する機会が稀なので。 * グローバル変数は大文字 * ローカル変数は小文字 * エクスポートする変数も大文字 関数内からグローバル変数にアクセスする場合がありますが、やはり区別していると、可読性が増すような気がするのでお勧めです。 # 2. awk を知る Unix 上にて文書処理をするときに、数多くのフィルタコマンド(grep、cut、tr、head、sort、uniq、sed、awk、wc、paste)を駆使して加工してゆくと思いますが、awk オンリーで解決できる場合が多かったりします。 例として家計簿を記した csv ファイルをあげます。 ```bash:sh b4b4r07:~$ cat test.csv 日付,カテゴリ,金額,備考 09/25,仕送り,+25000, 10/01,食費,-1253, 10/02,医療,-1200,皮膚科 10/02,医療,-760,薬局の処方薬 10/02,PC用品,-3180,外付けCDドライブ 10/03,食費,-379, 10/03,食費,-398, 10/03,美容室,3150, 10/04,食費,-580, 10/05,食費,-667, 10/06,食費,-420, b4b4r07:~$ declare -i sum=0; for i in $(seq 1 `grep "食費" test.csv | wc -l`); do sum+=`grep "食費" test.csv | cut -d, -f3 | head -$i | tail -1`; done b4b4r07:~$ echo $sum -3697 b4b4r07:~$ ``` 家計簿から食費だけを算出しようとしたとき、フィルタコマンドの組み合わせでやろうとすると、結構複雑に書かなければなりません。しかし awk を知ると b4b4r07:~$ awk -F, '$2=="食費"{ sum += $3 }; END{ print sum }' test.csv -3697 と書けてしまいます。収支の算出も簡単です。 b4b4r07:~$ awk -F, '$3 ~ /\+/{p += $3}; $3 ~ /^\-/{m += $3}; END{print "[income]",p, "[used]",m, "[rest]",p+m}' test.csv [income] 25000 [used] -8837 [rest] 16163 スマートに書くことが出来ます。awk や sed はフィルタコマンドの域を超えた小さなプログラミング言語と称されることが多いです。そのくらいに多くのことができるので、知っておくまたは使いこなせるようになるとできることの幅がグッと広がります。 # 3. bash に依存しているのに #!/bin/sh と書かない 普段シェバンとしてシェルスクリプトの冒頭に書くアレですが、`#!/bin/sh` の実態は環境によってまちまちです。さまざまなシェルのシンボリックリンクになっていることが多いです(Ubuntu では ash 亜種の dash となっている模様)。なので、予期せぬエラーや動作しないといったことが起きる場合があります。bash やその他シェルに依存したスクリプトを書くのなら、それ上での動作を予定しているわけだから、シェバンには `#!/bin/bash` と書いたほうが良い。 # 4. 改行コードに気をつける 改行コードについては[Wikipedia: 改行コード](http://ja.wikipedia.org/wiki/%E6%94%B9%E8%A1%8C%E3%82%B3%E3%83%BC%E3%83%89)を参照してください。歴史的背景やその実践について書かれています。 簡単に書き表すと以下のようになります。Mac OS とはバージョン9までを指します。今はなかなかお目にかからないので、その心配は大丈夫かと思います。 | | Windows | Mac OS | Unix (OS X含む) | |:--------|:-------:|:------:|:--------------:| |改行コード| CR+LF | CR(復帰)| LF(改行)| Windows で作成したスクリプトを Unix 環境で実行すると `command not found` や `No such file or directory` など予期せぬエラーが出る場合があります。これらの大抵の原因は改行コードによるものです。Windows 側で指定しなければデフォルトでは `CR+LF` で保存されます。狭義での"改行"とは Unix でも標準である `LF` のみです。 ゆえに Windows での `#!/bin/bash<CR><LF>` は `#!/bin/bash` ですが、Unix などでは `#!/bin/bash<CR>` となるのです。つまり、改行判断の際に余った `CR` をコマンドの一部として解釈しようとするため、エラーが出るのです。 $ cat test.sh #!/bin/bash cat myfile $ ./test.sh cat: file\r: No such file or directory 一見、問題のないようなスクリプトですが実行してみると、エラーが吐かれてしまいます。 $ cat -A test.sh #!/bin/bash^M$ cat file^M$ `cat -A` で確認してみると `CR` を表す `^M` が可視化されています。ちなみに"改行"は `$` です。 $ tr -d '\r' test.sh で、削除できるので、ようやく解決です。 ちなみに、BSD 版の `cat` には `-A` オプションはありません。GNU 版オンリーです。BSD 版では `cat -e` で同様のことが出来ます。 > \r = CR 復帰 キャリッジリターン ^M > \n = LF 改行 ラインフィード ^J # 5. サブシェルを意識する 意識するという表現は、嵌ってしまいやすいが、逆に利用してやるとスマートな記述が可能になるという意味です。 ## 嵌りやすい場合 **(1) while ループで変数が変更されない** ファイルからデータを読みこませる方法として `while` と `read` を組み合わせることはよくあります。 while read LINE do …… …… done < file 注意するのはこの `while` ループはサブシェルで動作しているということです。このとき、`while` の入力をファイルからリダイレクトしているためです。 ループで利用した変数はループの外では無価値です。いくらループの中で何かに値を入れたりデータを編集しても、ループの外ではその結果を利用できません。 これを解決するには、 exec < file while read LINE do …… …… done として、`exec` コマンドにリダイレクトし、カレントシェルとしてやらなくてはいけません。 **(2) while ループで変数が変更されない・2** 上の例で紹介した `while` は少し前の時代の `while` です。今使用されている多くのシェルではこの仕様(バグ?)は修正されて「変数が変更されない」ということはなくなったようです。しかし、後述する汎用性を高めるという点で、(1) の方法が一番無難でしょう。 **(3) パイプ先はサブシェル** `find` したファイルを `files` という配列に代入したいとします。 files=() find . -type f | while read f; do files+=("$f") done echo "${files[@]}" しかし、この結果で `echo` は何も出力しません。パイプの先はサブシェルで動作します。そもそもパイプとは標準出力をそのまま別のプロセスの標準入力に渡す仕込みのことです。「|」で多段に接続された各々のコマンドは別プロセスとして動きますので、そのいずれのプロセスも親である shell に変数を渡すことは出来ません。 パイプの場合、前段の実行が完了しなくても後段の実行が始まります。 これは個々のプロセスが親である shell とは独立して動いていることを意味しますので、その `while` は sub shell と呼ばれる子プロセスによって実行されている訳です。 shell 変数は一つのプロセス内でしか共有出来ませんから、その内容を他のプロセスから見ることは不可能です。 これを解決するには以下のようにするのが良いでしょう。 files=() while read f; do files+=("$f") done < <(find . -type f) echo "${files[@]}" `diff` のときによくやるあれです。プロセス置換と呼ばれています。この機能を使用しないで期待通りの結果を導きたい場合は、いったん外部ファイル(tmpfile のような)に出力して、それを入力としてやるくらいしかありません。しかしそれだと、外部ファイルの後始末の処理を組み込まないといけないので、すこし面倒だし、美しくないでしょう。 ## 利用してやる場合 サブシェルでの変数の変更などは、カレントシェルに影響しません。これを巧みに使用すると `IFS` をサブシェルに閉じ込めることで、変更による影響をなくすことができます。 (IFS=$'\n'; echo "${array[*]}") `cd` なども同じです。サブシェルでの移動は影響しないです。カレントディレクトリを一時的に変更して、すぐに戻りたい場合なんかに利用するといいでしょう。 ( cd ~/src && tar cf - myproject ) | gzip -c > myproject.tar.gz ファイルの上書きでもスマートな記述が可能になります。通常、ファイルを上書きしようとすると、空ファイルになってしまいます。 ```bash:sh b4b4r07:~$ cat file apple orange grape banana strawberry cherry pear pineapple mandarin tangerine melon b4b4r07:~$ grep -v "e$" file >file #フィルタリングして上書き b4b4r07:~$ cat file #何も出力されない(空) b4b4r07:~$ ``` コマンドオプションなどの標準で上書きできるのは、`sort` や `sed` くらいしかありません。自前で上書きするには、いっかい一時ファイルに書き出して、リネームしなければなりません。 $ grep -v "e$" file >file.tmp $ mv -f file.tmp file しかし、サブシェルを利用することで簡単に記述できます。 ```bash:sh b4b4r07:~$ (rm file; grep "e$" >file) <file b4b4r07:~$ cat file apple orange grape pineapple tangerine b4b4r07:~$ ``` しかし、`rm` を最初に行う関係上危険性が生じます。`rm` ゴミ箱スクリプトのエイリアスや関数を使用されることをおすすめします。 # 6. 汎用性を高める シェルスクリプトは、Unix 系 OS のユーザにとって最も身近なプログラミング言語でもあり、その習得は必須の技能であると言えます。ほとんど改変を加えずに、様々なシステムでそのまま利用できるという汎用性の高さは非常に優れたものです。 シェルスクリプトの使用は以下の様な利益をもたらします。 * システムの標準の機能 組み込み機器の問題などの様々な制約で、`perl` や `ruby` が利用できない場合においても Bourne Shell は使用可能なので頼もしいです。 * 互換性が非常に高い OS の種類やバージョンに依らず利用できるということは、システム間の互換性が非常に高いということです。シェルごとの若干の方言や、インストールされている外部コマンドの違いにさえ注意すれば、同じスクリプトを使いまわすことができます。移植の際の再コンパイル作業等も必要ありません。 などなど、数多くのメリットが有ります。しかし、Unix 系に関して些細な方言が存在するのを頭の隅にでもいれておかなければならない状況が来ると思います。「汎用性を高める」とは要するに、どのシステムにも含まれており、処理が異ならないコマンドだけを使っていれば、一番汎用性が高いことになります。つまり `/bin/sh` でシェルスクリプトを書くということです。しかしながら、そういうコマンドばかり使っているわけにもいかない(たとえば、`echo` コマンド。System V 系と BSD 系では動作が異なる)ので、その対処法についてなのですが、これに関しては記述量が多いため、別記事にしたいと思います。 # 7. 例外に関する対応 `$?` によって条件分岐したり、`&&` や `||` を利用しましょう。また、直前のコマンドにパイプが含まれる場合は、一番最後のコマンドの終了結果しか見てくれないので、`$?` を使う際は最初のコマンドのみをまず捕捉するか、bash などの限定表現にはなりますが、`${PIPESTATUS[@]}`を使いましょう。 # 8. _@(#)_ を書き示す シェルスクリプトの冒頭に、 # @(#) This script is ~. といった記述がありますが、これは `what` コマンドで使用するためです。 `what` コマンドとはプログラムやそれを構成するモジュールのバージョンを調べるときに使用します。シェルスクリプトの場合もこれを書くことで対応させることができます。 $ what mycmd mycmd This script is ~. # 9. 環境変数 PATH を管理する `PATH` 変数に .(ドット)を含ませることの危険性についてです。セキュリティ意識を高めるためにも重要な項目です。 シェルスクリプトやプログラムを新規に作ったとき、当然ためしに実行させてみるものです。`abc` というコマンドを作ったとして、それをそのディレクトリで `abc` とタイプしても `not found` と言われてしまうことがあります。それは `PATH` 変数に .(ドット)が含まれていないからです。ドットはカレントディレクトリを指すので、`PATH` にこれがセットされていない場合は、現在のディレクトリにある `abc` というコマンドを探してはくれません。よってテスト実行するときには、`./abc` というように「このディレクトリの」の部分を明示しなくてはなりません。 これを避けるために、`PATH` にドットを含めている人も少なからずいます。確かにこれがあるとないとでは使い勝手がずいぶん違うため、ある方がよっぽど快適に作業を行えます。 しかし、ドットを `PATH` に入れておくことは非常に危険なことであるということも認識しておいてください。`ls` というコマンドは非常に頻繁に利用されるかと思います。誰か悪意を持った人が、誰かのディレクトリの下に `ls` というシェルスクリプトを置いたとします。そのシェルスクリプトの内容が「`rm -rf *`」というものであったらどうなるでしょうか。その人は気づかないままリストを表示しようとして `ls` とタイプし、全部ファイルを消してしまうことにもなりかねません。 `PATH` の先頭にドットを置くからこのようなことが起こり得るのであって、`PATH` の一番最後に置けば問題ないと考えられるかもしれませんが、結局は同じです。`ls` については、これで問題回避できるかもしれませんが、`l` というファイルを作って、あなたがタイプミスするのを待っているのかもしれないのです。 自分のデータは自分で守る、ということをきちんとやらなくてはならない場合、「`PATH` 変数にドットを入れない」ことをまず最初に考慮します。 また、シェルスクリプトの中で `PATH` に相対パスを追加してはいけません。ディレクトリが変わったら相対パスも変わってくるので、`PATH` の中に不要なディレクトリのリストが含まれてしまうだけです。 ##まとめ * 可読性(保守性) * 汎用性(移植性) この2点を意識するというのに尽きるかもしれません。可読性が増せば、コード自体が美しくなるし、保守もしやすくなります(シェルスクリプト自体の可読性が良くないというツッコミはなしで)。汎用性が高いコードを書けば、もちろん移植のしやすさに直結するでしょう。Unix の精神にも徹することができるわけです。9項目にはランク・インしていませんが、「``」の使用なんかも可読性の問題です。ダブルクオートや括弧が入り混じる中にバッククォートなんか使っていては目が大変です。「$()」を使用すると、スッキリ見やすいでしょう(でもこの書き方は一部では利用できないようなので、汎用性という点で少し劣りますね)。 |
|
| 28位 |
|
|||
|
09:48:15 |
|
|
この記事は[Vim Advent Calendar 2013 : ATND](http://atnd.org/events/45072)の99日目の記事です。
# 画面分割・タブページのススメ みなさん、分割してますか? みなさん、タブページ使ってますか? 僕はどちらも大好きです。 - サンプルを見ながら写経する - TODOやメモを見ながら編集 - 関数の定義元などを見ながら編集 - 編集目的に合わせてタブを分ける などなど。 ものぐさな僕は、コードを書くときはガシガシ分割したりしながらコードを書いています。 これらの機能は当然設定やプラグインの導入などをしなくても使えますが自分はsキーをprefixとしたキーマッピングをしています。 元は`<C-w>`の代わりに`s`を使っていただけですが、タブウィンドウ関連やバッファ関連にも使うようにしたことで、格段に便利になりました。自分の設定例を合わせて掲載するので、良ければ参考にしてください。 # 各種機能とマッピング ## ウィンドウを分割する  | 項目名 | デフォルト | オリジナル設定 | |:---------|:-----------|:---------------| | 水平分割 | `:split` | `ss` | | 垂直分割 | `:vsplit` | `sv` | ## 分割したウィンドウ間を移動する  | 項目名 | デフォルト | オリジナル設定 | |:---------|:-----------|:---------------| | 左に移動 | `<C-w>h` | `sh` | | 下に移動 | `<C-w>j` | `sj` | | 上に移動 | `<C-w>k` | `sk` | | 右に移動 | `<C-w>l` | `sl` | | 次に移動 | `<C-w>w` | `sw` | ## 分割したウィンドウそのものを移動する  | 項目名 | デフォルト | オリジナル設定 | |:---------|:-----------|:---------------| | 左に移動 | `<C-w>H` | `sH` | | 下に移動 | `<C-w>J` | `sJ` | | 上に移動 | `<C-w>K` | `sK` | | 右に移動 | `<C-w>L` | `sL` | | 回転 | `<C-w>r` | `sr` | ## カレントウィンドウの大きさを変える  | 項目名 | デフォルト | オリジナル設定 | |:---------------|:-------------|:-----------------| | 縦に最大化 | `<C-w>_` | `なし` | | 横に最大化 | `<C-w>パイプ` | `なし` | | 縦横最大化 | `なし` | `so` | | 大きさを揃える | `<C-w>=` | `sO` もしくは `s=` | | 幅を増やす | `<C-w>>` | `s>` | | 幅を減らす | `<C-w><` | `s<` | | 高さを増やす | `<C-w>+` | `s+` | | 高さを減らす | `<C-w>-` | `s-` | テーブル内で`|`を表示すると崩れるみたいなので、パイプと表記してます。 また`s>`系のマッピングは、実際には「[vim-submode](https://github.com/kana/vim-submode)」というプラグインを使って`s>>>`などでサイズ変更出来るようにしてます。詳しくは最後のVimrcを参照してください。 ## タブページ関連  | 項目名 | デフォルト | オリジナル設定 | |:---------------|:-------------|:-----------------| | 新規タブ | `:tabnew` | `st` | | 次のタブに切替 | `gt` | `sn` | | 前のタブに切替 | `gT` | `sp` | ## 閉じる | 項目名 | デフォルト | オリジナル設定 | |:---------------|:-------------|:-----------------| | ウィンドウを閉じる | `:q` | `sq` | | バッファを閉じる | `:bd` | `sQ` | ## Unite.vim  | 項目名 | デフォルト | オリジナル設定 | |:---------------|:-------------|:-----------------| | タブ一覧 | `:Unite tab` | `sT` | | 現在のタブで開いたバッファ一覧 | `:Unite buffer_tab` | `sb` | | バッファ一覧 | `:Unite buffer` | `sB` | 当然ですが「[unite.vim](https://github.com/Shougo/unite.vim)」が必要です。 # 最後に ## ウィンドウとバッファとタブページについて バッファはファイル等の内容そのもの。 ウィンドウはバッファを表示するもので、別ウィンドウで同じファイルを開いて編集したり出来る。 タブページはウィンドウを束ねるもの。 詳しくは「[Vim のバッファとウィンドウを理解する - 反省はしても後悔はしない](http://cohama.hateblo.jp/entry/2013/09/28/220808)」で解説されています。 この記事も去年のVACの記事ですね。歴史長い! ## sキーの元々の機能について 今回潰した`s`キーには元々割り当たっている機能がありますが、ヘルプに書かれている通り、他で代用が効くので気にせず潰しています。 まったく別のマッピングをする場合でも`s`キーはかなり押しやすい位置にあるので、マッピング先に便利でおすすめです。 > ["x]s [count] 文字を [レジスタ x に入れ] 削除し、挿入を始め > る (s は Substitute --置換 を意味する)。"cl" と同義で > ある (行単位でない|linewise|)。 ## vimrc 最後に、当該箇所の自分のvimrcを貼っておきます。 ```vim nnoremap s <Nop> nnoremap sj <C-w>j nnoremap sk <C-w>k nnoremap sl <C-w>l nnoremap sh <C-w>h nnoremap sJ <C-w>J nnoremap sK <C-w>K nnoremap sL <C-w>L nnoremap sH <C-w>H nnoremap sn gt nnoremap sp gT nnoremap sr <C-w>r nnoremap s= <C-w>= nnoremap sw <C-w>w nnoremap so <C-w>_<C-w>| nnoremap sO <C-w>= nnoremap sN :<C-u>bn<CR> nnoremap sP :<C-u>bp<CR> nnoremap st :<C-u>tabnew<CR> nnoremap sT :<C-u>Unite tab<CR> nnoremap ss :<C-u>sp<CR> nnoremap sv :<C-u>vs<CR> nnoremap sq :<C-u>q<CR> nnoremap sQ :<C-u>bd<CR> nnoremap sb :<C-u>Unite buffer_tab -buffer-name=file<CR> nnoremap sB :<C-u>Unite buffer -buffer-name=file<CR> call submode#enter_with('bufmove', 'n', '', 's>', '<C-w>>') call submode#enter_with('bufmove', 'n', '', 's<', '<C-w><') call submode#enter_with('bufmove', 'n', '', 's+', '<C-w>+') call submode#enter_with('bufmove', 'n', '', 's-', '<C-w>-') call submode#map('bufmove', 'n', '', '>', '<C-w>>') call submode#map('bufmove', 'n', '', '<', '<C-w><') call submode#map('bufmove', 'n', '', '+', '<C-w>+') call submode#map('bufmove', 'n', '', '-', '<C-w>-') ``` # 追記(2014/03/10 8:36) > @hisatake_i > 最初の 画像が コロンとセミコロンが間違ってるな。 `:`と`;`をノーマルモードでのみ入れ替えるマッピングをしているため、画像上では`;`を打ったと表示されています。紛らわしくてすみません。実際には`:`を打つ必要があります。 英字キーボードではオススメの設定なので、あわせてどうぞ。 ```vim " コロンとセミコロンを入れ替え noremap : ; ``` |
|
| 29位 |
|
|||
|
10:44:33 |
(Akatsuki Inc. 所属) |
|
(2015/10/13追記)
今なら、他言語には無名関数やcallback関数というものがありますねとか、イベント駆動の世界を覗いてから戻ってくるとより腑に落ちるかもしれませんとか、もう少し全体観の中で説明する気がしますが、当時は本記事の様な理解が役に立ったことは事実なので、引き続き公開を続けます。 (2013/11/29追記) block_given? について Twitter上で「Kernel.#block_given?についての解説があってもよさそう」と 指摘を頂きましたので、[本文下部](http://qiita.com/kidachi_/items/15cfee9ec66804c3afd2#ps1)に追記しました。 ##概要 Ruby on Rails Tutorialのエッセンスを自分なりに整理してみる4 Railsを触る際知っていると便利なRubyの基礎 [ブロックとかシンボルとか] http://qiita.com/kidachi_/items/46a6e49b6306655ccd64 の続き。 Ruby on Rails Tutorial(chapter4) http://railstutorial.jp/chapters/rails-flavored-ruby?version=4.0#fnref-4_11 ##流れ - ブロックとは何か - yield、Procとは何か - Proc補足 - で、ブロックやProcって何が嬉しいの? ##ブロックとは何か ひとことで言うと ###do~end(もしくは{~})で囲われた、引数となるためのカタマリ。 ブロックについて調べようとするとだいたい「yield(いーるど)」とか「Proc」とかが 混ざってきてよく分からなくなるけど、実際はこれだけです。 yieldは「ブロックを呼び出すもの」、Procは「ブロックをオブジェクト化したもの」であり、 ブロック自体とは別物(詳しくは後述)。 #### ブロックの性質 - それ単体では存在できず、メソッドの引数にしかなれない - 「do~endのカタマリ」がその辺に単体で転がっているのは見た事ないはず。 - 引数として渡されたブロックは、yieldによって実行される ```rb #ブロックを受け取るメソッドの定義 def give_me_block yield end #メソッドの引数としてブロック(do~end)を渡して、実行 give_me_block do p "Hello, block!" end => "Hello, block!" #give_me_block内で、yieldによって呼び出された。 ``` ただ、yieldというのはあるべき手順をすっ飛ばしているため分かりづらい。 以下の流れを追っていくと理解しやすいです。 ###「そもそもブロックとは引数であること」を明示した書き方も存在する ```rb #メソッド定義 def give_me_block( &block ) block.call end #実行 give_me_block do p "Hello, block!" end => "Hello, block!" ``` `def give_me_block( &block )`で、ブロック引数を受け取ることを明示している。 ###&blockの「&」って? &を付けることで、実際に引数にブロックが渡ってきた際、 Procオブジェクトに変換している。 ###Procオブジェクトって? - ブロックをオブジェクト化したものがProc - ブロックがそれ単体では存在できないことを思い出す (→オブジェクト化してしまえばok) - ブロックをオブジェクトに変換することで、引き渡されたメソッド(give_me_block)内で扱えるようにする - Procオブジェクトは、callで呼び出すことが出来る - 2行目の`block.call`の正体はこれ。 ###ちなみに、ブロック引数は、仮引数の中で一番最後に定義されていなければならない 以下みたいなのはNG - def give_me_block( &block1, param1 ) - def give_me_block( &block1, &block2 ) つまるところ<strong>引数として渡せるブロックは一つだけ</strong>。 ###以上を踏まえると こんな考え方も出来る。 「どうせブロック引数は一つしか取れないんだから ####呼び出し箇所を`block.call`なんて明示せずに、`yield`で統一しちゃえば良いじゃん」 ```rb #メソッド定義 def give_me_block( &block ) yield # block.callをやめてyieldに変更 end #実行 give_me_block do p "Hello, block!" end => "Hello, block!" ``` ###さらに ####「ブロックは全部yieldが示すんだから、仮引数(&block)もいらなくね?」 ```rb #メソッド定義 def give_me_block # (&block)を除去 yield end #実行 give_me_block do p "Hello, block!" end => "Hello, block!" ``` これが<strong>色々省略されまくったyield文の正体</strong>です。 一見give_me_blockは何も引数をとらないメソッドのように見えるのに、 その内部にyieldを持っている以上「ブロックを引数として受けとるメソッドである」 ということを認識しないといけない。 Rubyコードを読み解く際によく注意しておかなければならない点の一つのみたいです。 以下記事でまつもとさんが話しているのもそんな話。 http://itpro.nikkeibp.co.jp/article/COLUMN/20070621/275509/?P=3 「もし最初から暗黙の引数みたいなデザインをしていたら、 今許しているアンパサンド「&」の文法は必須にして、yieldはなくして、 「&」が付いていないのにブロックを渡したらエラーになるとか…」 ##Proc補足 上でも説明していますが補足含めて。 - ブロックをオブジェクト化したものがProc - Procオブジェクトはcallで呼び出すことが出来る - Procに引数を期待する書き方も出来る - 「メソッドの引数となるProcにさらに引数が期待できる」って混乱しそうですが・・ - Proc.newとlambdaはほぼ同義 ####Procオブジェクトの定義と呼び出しサンプル ```rb #インスタンス生成 proc1 = Proc.new do p "hoge" end #実行 proc1.call => "hoge" ``` ####呼び出される(callされる)際に引数が渡されることを期待する書き方サンプル ```rb #インスタンス生成 proc2 = Proc.new do |s| #sが仮引数 p "Hello, #{s}!" end #実行 proc2.call("Proc") #"Proc"が実引数 => "Hello, Proc!" ``` ####Proc.newとlambdaはほぼ同義 以下の記述で上の2例と同じ結果が得られる ```rb #インスタンス生成 lambda1 = lambda { p "hoge" } #実行 lambda1.call => "hoge" lambda2 = lambda { |s| p "Hello, #{s}!" } lambda2.call("Proc") => "Hello, Proc!" ``` ※細かくはProcとlambdaには違いが存在しますが、ここでは触れません。 補足は以下等を参照ください。 RubyでlambdaとProcの違いは? http://qa.atmarkit.co.jp/q/68 ##で、ブロックやProcって何が嬉しいの? 一つ目は ###メソッドを後々の利用時に柔軟に拡張出来る 例えば、開発者Aが「5という数で好きな事をしてもらうメソッドを作ろう」と 考えた場合、以下のようになります。 ```rb def magic_five_box(after_input, someProc) someProc.call(5, after_input) end ``` 開発者B「私は5を使って足し算します」 ```rb sum_proc = Proc.new do |x, y| p x + y end magic_five_box(3, sum_proc) => 8 ``` 開発者C「私なら文字列を5回表示させます」 ```rb string_proc = Proc.new do |x, string| p string * x end magic_five_box('happy_proc! ', string_proc) => "happy_proc! happy_proc! happy_proc! happy_proc! happy_proc! " ``` このメソッド例は大した意味は持ちませんが、 使い方によって有効に利用できそうな雰囲気が伝わったでしょうか。 下の記事はブロックのことを「メソッドに対するメソッドのMix-in」と表現しています。 Rubyのブロックはメソッドに対するメソッドのMix-inだ! http://melborne.github.io/2008/08/09/Ruby-Mix-in/ よく考えてみると、Ruby標準の<strong>eachやmapなどもブロックを受け取るメソッド</strong>です。 「配列を扱うならこれ、という『土台』は用意しておくから、 細かい部分はブロックを加えて自由に料理して」という思想ですね。 ###状態を持った関数(クロージャ)としての機能が得られる ブロック/Proc二つ目のメリットはこちら。 ```rb n = 1 proc = Proc.new do n = n + 1 end proc.call =>2 proc.call =>3 proc.call =>4 ``` Procオブジェクト(proc)は外側のスコープに存在するnへの参照を保持し、 呼び出す度にnをインクリメントできるメソッドができました。 が、クロージャの細かい特徴/用途となるとまた長い話になるので、 詳しくは以下等を参照してみてください。 私が今までクロージャを理解できなかった理由Add Starfoussin http://d.hatena.ne.jp/artgear/20120115/1326635158 <a name="ps1"></a> ## (2013/11/29追記) Kernel.#block_given? について ### block_given?とは 名前の通り、引数としてブロックが与えられたかどうかを判別するメソッド。 ### 利用例 開発者A 「7という数で好きな事をしてもらうメソッドを作ろう。 でも、<strong>もしブロックが渡されなくても使えるようにしよう。</strong>」 ```rb def wonder_seven_box if block_given? yield(7) else p "Don't mind. Feel free to call me." # ブロックが与えられなければこちら end end ``` 開発者B「私は7を使って足し算します」 ```rb wonder_seven_box do |x| p 3 + x end => 10 ``` 開発者C「私なら文字列を7回表示させます」 ```rb wonder_seven_box do |x| p "happy_block! " * x end => "happy_block! happy_block! happy_block! happy_block! happy_block! happy_block! happy_block! " ``` 開発者D「ブロックよく分からない」 ```rb wonder_seven_box => "Don't mind. Feel free to call me." ``` ブロックを与えなくても呼び出すことが出来ました。 ## block_given?を整理していて気になった事 ※本項はRubyの言語仕様に関する疑問点の備忘録です。 「ブロックとProcをちゃんと理解する」からは少しずれますので、 不要であれば読み飛ばして頂ければと思います。 ### 細かいけど お気づきかと思いますが、magic_five_boxの例ではProcベースでしたが、 今回のwonder_seven_boxはブロックベースとなっています。 というのも、<strong>proc_given?のようなメソッドが見当たらなかったので。</strong> つまり、procの場合は、 ```rb def magic_five_box(after_input, someProc) someProc.call(5, after_input) end ``` の例が示す通り、<strong>Procオブジェクトを受け取る前提</strong>の記述となるため、 block_given?に類する判定がそもそも必要ない。 一方でブロックの場合は ```rb def wonder_seven_box if block_given? yield(7) else p "Don't mind. Feel free to call me." # ブロックが与えられなければこちら end end ``` 上記例で見た通り、 <strong>ブロックを引数に取っても取らなくても良い、より柔軟なメソッド</strong> を作ることが出来ます。 ただ、まつもとさんが仰っている内容(以下再掲)を鑑みた時、ひとつ疑問が生まれました。 http://itpro.nikkeibp.co.jp/article/COLUMN/20070621/275509/?P=3 「もし最初から暗黙の引数みたいなデザインをしていたら、 今許しているアンパサンド「&」の文法は必須にして、yieldはなくして、 「&」が付いていないのにブロックを渡したらエラーになるとか…」 つまり以下の記述をスタンダードにした方が良いかも、ということですね。 ```rb def give_me_block( &block ) block.call end ``` そしてこの記述の場合、ブロックは<strong>渡されて来ることが前提</strong>となりそうです。 (もちろん、 - 引数にブロックを期待する場合のみ、引数が渡されないケースも許容する (メソッド内でblock_given?で判定) というデザインもあり得るとは思いますが、 <strong>通常のメソッド定義と見た目はほぼ一緒なのにルールだけ違う</strong>という状況になってしまいます。) 何が気になっているのかというと、 #### 本来 Kernel.#block_given? は必須の実装なのか? ということ。実は #### (yieldという特殊な存在に引き摺られて)後付けで実装された ものであり #### 想定外の(もっと言うと本来不要な)柔軟性を生み出している ものだったりしないのでしょうか。 →仮に通常の引数に対してblock_given?に類するものがあったとすると、 こんなことをしてる訳で。 ```rb def test_method ( args ) if args_given? hoge else fuga end end ``` 果たしてこれをポジティブに「柔軟」と捉えるべきか。 ##### ご相談 もしこのあたりの成り立ちのストーリーに詳しい方、 または言語デザインに対するご指摘をお持ちの方がいらっしゃいましたら ぜひご教示頂けると嬉しいです! ※同時に、Ruby初心者の考察であるため、的を外している点ありましたら 突っ込みを頂けますと幸いですm(_ _)m ##ブロック/Proc参考&御礼 数あるブロックの記事を読んでもいまいちすっきりできなかった自分でしたが、 以下の記事で目から鱗が落ちました。 Rubyの動かないコード(初級編)ブロックとクロージャの性質 http://d.hatena.ne.jp/language_and_engineering/20101118/p1 分かりやすい記事をありがとうございました! ##以下に続く [Rails] TwitterBootstrapとSassとAssetPipeline http://qiita.com/kidachi_/items/4c47b28b2a74c723d835 |
|
| 30位 |
|
|||
|
11:16:55 |
(Increments inc. 所属) |
|
結局jQuery.Deferredの何が嬉しいのか分からない、という人向けの小話
一年ほど前に [JavaScript - jQuery.Deferredを使って楽しい非同期生活を送る方法 - Qiita [キータ]](http://qiita.com/yuku_t/items/3d1cf51d7ae91305eaaa) という記事を書きました。 で、一年経って、ふと、「もっと分かりやすくjQuery.Deferredの便利さを説明できるんじゃないか」と思い立ってざざざっと書いてみました。 小話と言うにはちょっと長いけど。 -- jQuery.Deferredを使うと嬉しいのは、jQuery.Deferredの仕様を満たす部品同士を簡単に組み合わせることが可能だからです。中には処理を書き下すことができるとかコールバックのネストを防げるのがいいとか言う人もいますが、個人的にこっちのほうがよっぽど重要だと感じます。 例えるならレゴブロックです。レゴブロックはあの凸と凹を持ってるブロックを自由に組み合わせて、実に様々な物を表現することができますよね。まさにあんな感じです。 と言ってもなかなかピンとこないと思うので、具体例を使って説明しましょう。 # jQuery.Deferredを使わない場合 *注:これから書くのはダメなJavaScriptコードなので真似しないでください。* ここに一つのリンクがあります。 ```html <a href="/path" id="link">リンク</a> ``` このボタンがどれくらいクリックされているのか計測したくなりました。そこであなたはサーバにリンククリックを計測するためのAPIを作成し、こんな風にJavaScriptのコードを書きました。 ```javascript $('#link').on('click', function (e) { e.preventDefault(); // リンク遷移を一旦中止 $.ajax({ …, // 自前の計測APIを叩く success: function () { // XHRが終わったら改めて遷移させる document.location = e.target.href; } }); }); ``` ある日、会社の上司に突然「Mixpanelってのが便利らしいからそっちも使ってみて」と言われました。曰く「Mixpanelと自前の両方で計測して、あとで比較したい」らしいです。 とりあえずこんな感じに実装しました。( `$('#link')` とかは冗長なので省略します) ```javascript $.ajax({ …, // 自前の計測APIを叩く success: function () { // mixpanelにデータを送る mixpanel.track('click', {}, function () { // 自前APIとmixpanelへの送信が終わったら遷移させる document.location = e.target.href; }); } }); ``` しばらくすると今度は「Google Analyticsも(ry」と言われました。 やれやれ、コードをこう修正しました。 ```javascript $.ajax({ …, // 自前の計測APIを叩く success: function () { // Mixpanelにデータを送る mixpanel.track('click', {}, function () { // Google Analyticsへデータを送る ga('send', 'event', 'link', 'click', { hitCallback: function () { // 自前APIとmixpanelとgaへの送信が終わったら遷移させる document.location = e.target.href; } }); }); } }); ``` するとユーザから「リンクをクリックしたときの反応が悪い」と苦情が出ました。それもそのはず、リンククリックしたら「自前API」→「Mixpanel」→「Google Analytics」と順番に通信を行なって、初めてリンク遷移を実行するからです。 次なる課題は、この3つの通信を並列実行させることですが、さてさてどうしようかなーと考えているあなたのもとに驚くべき情報がもたらされました。「特定のAdblock系プラグインを入れている環境ではmixpanelなどへの通信が行われずコールバックも実行されない」。つまり3つのうちどれかが通信に失敗しても大丈夫なようにしなければなりません。 うわーーーもうやってられん!!! # jQuery.Deferredを使う場合 先ほどの事例をjQuery.Deferredを使うとどのようにうまい具合に実装できるか見て行きましょう。 最初に「レゴブロックを組み合わせるみたいに書ける」と書きました。とりあえず「jQuery.Deferredを使った自前APIにデータを送る関数」、「jQuery.Deferredを使ったMixpanelにデータを送る関数」、「jQuery.Deferredを使ったGoogle Analyticsにデータを送る関数」を用意することから始めましょう。 ところで、Javaとかを使っている人には馴染み深いと思いますが、このようなある振る舞いを実装しているオブジェクトを、インタフェースを備えているとか、実装していると言います。なので、以後「jQuery.Deferredを使っ」ていることを「Deferredインタフェースを備えている」と表現することにします。 ## 復習:Deferredインタフェースの実装方法 Deferredインタフェースを備えた関数を実装するのは極めて簡単です。守らなければならないのはたった1つだけ。それは **jQuery.Deferred#promise を返すこと** それだけです。 ```js:Deferredインタフェースを備えた最も単純な関数 var dfdFunc = function () { var dfd = jQuery.Deferred(); return dfd.promise(); }; ``` `promise` は状態を持っていてこのままでは初期状態である「実行中」状態になります。普通は `resolve` か `reject` で状態を確定させます。resolveは正常に終了した状態、rejectは失敗した状態を表します。 ```js:引数に応じて状態が変化するDeferredインタフェースを持った関数 var dfdFunc = function (cond) { var dfd = jQuery.Deferred(); cond ? dfd.resolve() : dfd.reject(); return dfd.promise(); }; ``` jQuery.Deferredというと非同期処理に使うものだという認識が一般的だと思いますが、これだって立派なDeferredインタフェースを備えた関数なのです。 ```js dfdFunc(true) .done(function () { console.log('Resolved!'); }) .fail(function () { console.log('Rejected!'); }); // => Resolved! ``` `promise#done` と `promise#fail` はそれぞれ `resolved` か `rejected` な `promise` に対して実行されるコールバックを設定するメソッドです。 ### 非同期関数を含む場合 非同期関数の場合は `promise` の状態を確定するのが非同期関数のコールバックの中になります。 例えばMixpanelへのデータ送信をDeferred化するとこうなります。 `mixpanel.track` の第1引数と第2引数は気にしないでください。ここで重要なのは、データを送信が完了したら第3引数の関数を実行する、ということだけです。 ```js:Deferredインタフェースを備えたmixpanel関数 var dfdMixpanel = function (event, props) { var dfd = jQuery.Deferred(); mixpanel.track(event, props, function () { dfd.resolve(); }); return dfd.promise(); }; ``` 簡単ですね。 ## さきほどの事例をjQuery.Deferredを使って実装してみる 「jQuery.Deferredを使わない場合」で取り上げた事例をjQuery.Deferredを使うとどう解決できるか見て行きましょう。 まずは「自前APIだけにデータを送っている」頃のコードです。実は `jQuery.ajax` はDeferredインタフェースを備えています。なので、以下のように書けます。 ```js $.ajax({ … }) // 自前APIにデータを送る .done(function () { // XHRが終わったら改めて遷移させる document.location = e.target.href; }); ``` さて次にMixpanelにもデータを送ります。先ほど作った `dfdMixpanel` を使うとこのように書けますね。 ```js $.ajax({ … }) // 自前APIにデータを送る .done(function () { dfdMixpanel('click', {}) // mixpanelにデータを送る .done(function () { // 自前APIとmixpanelへの送信が終わったら遷移させる document.location = e.target.href; }); }); ``` しかしこれだと結局どんどん階層が深くなっていってしまっています。このように `done` の中で `done` を実行するような場合は、代わりに `then` を使うといいでしょう。 `then` を使うと先ほどのコードは次のように書き換えられます。 ```js $.ajax({ … }) // 自前APIにデータを送る .then(function () { return dfdMixpanel('click', {}) // return を忘れないように }) .done(function () { // 自前APIとmixpanelへの送信が終わったら遷移させる document.location = e.target.href; }); ``` 次はGoogle Analyticsです。 `dfdGa` という関数を作ってあるとします。 ```js $.ajax({ … }) // 自前APIにデータを送る .then(function () { return dfdMixpanel('click', {}); // mixpanelにデータを送る }) .then(function () { return dfdGa('link', 'click'); // Google Analyticsにデータを送る }) .done(function () { // 自前APIとmixpanelとGoogle Analyticsへの送信が終わったら遷移させる document.location = e.target.href; }); ``` さて、ここまではjQuery.Deferredを使わない場合でもなんとか書くことができました。この時点で既に元々のコードより読みやすいという利点はあると思うのですが、Deferredの真価が発揮されるのはここからです。 ### 並列化 まずは自前APIとMixpanelとGoogle Analyticsへのデータ送信を並列化しましょう。 Deferredインタフェースを備えた関数を並列実行するのは実に簡単です。 `jQuery.when` を使うだけです。 ```js $.when( $.ajax({ … }), // 自前APIにデータを送る dfdMixpanel('click', {}), // mixpanelにデータを送る dfdGa('link', 'click') // Google Analyticsにデータを送る ).done(function () { // 自前APIとmixpanelとGoogle Analyticsへの送信が終わったら遷移させる document.location = e.target.href; }); ``` そしてよくよくみてみると `jQuery.when` の返り値に `done` を実行していますね?つまり `jQuery.when` もDeferredインタフェースを備えていることが分かります。 ### エラー処理 続いて特定条件下でMixpanelにデータが送れない問題を解決しましょう。やるべきことはデータ送信に失敗した場合は `dfdMixpanel` や `dfdGa` が返した `promise` を `reject` する、それだけです。 実装方法は色々考えられると思いますが、ここでは1秒間待って、データ送信が完了しなかった場合は `reject` することにします。 `dfdMixpanel` を次のように書き換えます。 ```js var dfdMixpanel = function (event, props) { var dfd = jQuery.Deferred(); mixpanel.track(event, props, function () { dfd.resolve(); }); setTimeout(function () { dfd.reject(); }, 1000); return dfd.promise(); }; ``` 「これだと `resolve` した後に `reject` も実行されるんじゃない?」と思った方もいると思いますが、 `promise` は一旦 `resolve` か `reject` されたらそれ以降状態が変化しなくなるので、これで問題ありません。 こんな感じのタイムアウト処理を自前APIとGoogle Analytics送信にも実装したら、`done` の代わりに `always` を使うようにして完了です。 `always` は resolve でも reject どちらの場合も実行される関数を登録するためのものです。 ```js $.when( dfdAjax(), // 自前APIにデータを送る dfdMixpanel('click', {}), // mixpanelにデータを送る dfdGa('link', 'click') // Google Analyticsにデータを送る ).always(function () { // doneの代わりにalwaysを使う // 自前APIとmixpanelとGoogle Analyticsへの送信が終わったら遷移させる // ただし、どれか1つでも送信に失敗したら即座に遷移する document.location = e.target.href; }); ``` ## jQuery.Deferredの便利さを言葉にする さてjQuery.Deferredを使うと、一見複雑な処理を、簡単に書けることがなんとなく分かったと思います。この「なんとなく分かった」jQuery.Deferredの便利さを、きちんと言葉にしてみます。 ### 処理が関数の中に隠蔽される jQuery.Deferredだから、という訳ではないですが、意識しなくても関数の中に処理がひとまとめにされるので、メンテナンス性が高まると言えます。 ### インタフェースが統一される ここまで読んだ人なら分かると思いますが、jQuery.Deferredは「成功」と「失敗」になりうる関数のための統一されたインタフェースを提供してくれます。もちろん非同期処理に使うのが特に便利なわけですが、非同期関数・同期関数問わず同じように扱えることが便利です。 また、いちいちインタフェースを考える手間が省けるので開発速度も向上します。 ### 非同期処理のエラー処理が容易 JavaScriptには `try〜catch` 文がありますが、非同期処理では非常に扱いにくいです。 ```js var asyncError = function () { setTimeout(function () { throw "Error"; }, 0); }; try { asyncError(); } catch (e) { // キャッチされない } //=> Uncount Error ``` 非同期関数の中でエラー処理をしようと思ってエラーを `throw` しても、その非同期関数を `try〜catch` してもエラーを補足できないのです。仮に正常系と異常系の2つのコールバックを受け付けるようにしたとしても、例えば `finally` のような機能をもたせるのはなかなか難しいです。 このような場合もDeferredインタフェースなら単に `reject` すればよく、呼び出し側で `finally` 的なことをやりたければ `always` で実現できます。 ### 再利用性が高まる 「処理が関数の中に隠蔽」され、「インタフェースが統一」されると再利用性が高まります。 ## jQuery.Deferredを使わない方がいい場面 ここまでベタ褒めしてきたjQuery.Deferredですが、なんでもかんでもDeferred化すればいいというものではないです。非同期関数はとりあえずDeferred化してしまえばいいですが、同期関数は場合によりけり。 じゃあどういう同期関数をDeferred化すべきかといえば、それはDeferred化された非同期関数と同じように扱いたい関数です。具体的には、キャッシュが無ければ非同期にサーバからデータを取ってきて、キャッシュがあったらそれを使う、というような場合、キャッシュからデータを取ってくる処理をDeferred化するとよいでしょう。実装については [JavaScript - jQuery.Deferredを使って楽しい非同期生活を送る方法 - Qiita [キータ]](http://qiita.com/yuku_t/items/3d1cf51d7ae91305eaaa) にちょろっと書いてあるので、そちらを参照してください。 # おまけ また上司がやってきました。 「MixpanelとGoogle Analyticsは並列でいいが、自前APIは直列にしたい」 Deferred化された今、恐れることは何もありません。 ```js dfdAjax() .then(function () { return $.when(dfdMixpanel(…), dfdGa(…)); }) .always(function () { document.location = e.target.href; }); ``` 「あー順番が逆だ、MixpanelとGoogle Analyticsが先で、自前APIが後。」 ```js $.when(dfdMixpanel(…), dfdGa(…)) .then(dfdAjax) .always(function () { document.location = e.target.href; }); ``` 「ぐぬぬ…!!」 歯ぎしりする上司を尻目に、今日も華麗に定時退社を決めたのでした。 おわり |
|
| 31位 |
|
|||
|
23:11:04 |
|
|
index.docker.ioから指定したイメージを取り込む
```bash docker pull REPOSITORY[:TAG] docker image pull REPOSITORY[:TAG] ``` イメージ一覧を得る ```bash docker images docker image list ``` イメージからコンテナを起動する ```bash docker run -itd IMAGE docker container run -itd IMAGE ``` イメージからコンテナを起動して、接続する。コンテナに名前をつける ```bash docker run -it IMAGE bash docker container run -it IMAGE bash docker run -it --name NAME IMAGE bash docker container run -it --name NAME IMAGE bash ``` ホストの/var/wwwを、コンテナ内の/var/htmlからアクセスできるように共有する ```bash docker run -it -v /var/www:/var/html IMAGE bash docker container run -it -v /var/www:/var/html IMAGE bash ``` ホスト8080番portへの通信をコンテナ80番portへ転送する ```bash docker run -it -p 8080:80 IMAGE bash docker container run -it -p 8080:80 IMAGE bash ``` ゲストのPRIVATE_PORTに指定したPortがホストのどのPortにポートフォワードしてるかを調べる ```bash docker port CONTAINER docker container port CONTAINER docker port CONTAINER PRIVATE_PORT docker container port CONTAINER PRIVATE_PORT ``` イメージからコンテナを作る ```bash docker create IMAGE docker container create IMAGE docker create IMAGE docker container create --name NAME IMAGE ``` コンテナを起動する ```bash docker start CONTAINER docker container start CONTAINER ``` コンテナを停止する ```bash docker stop CONTAINER docker container stop CONTAINER ``` コンテナを再起動する ```bash docker restart CONTAINER docker container restart CONTAINER ``` コンテナを削除する ```bash docker rm CONTAINER [CONTAINER...] docker CONTAINER rm CONTAINER [CONTAINER...] ``` コンテナをすべて削除する ```bash docker rm $(docker ps -aq) ``` イメージを削除する ```bash docker rmi IMAGE [IMAGE...] ``` タグなしのイメージをすべて削除する ```bash docker rmi $(docker images | grep '<none>' | awk '{print$3}') ``` 起動しているコンテナに接続する ```bash # exitするとコンテナが終了してしまう # コンテナを終了せずに抜ける「Ctrl + p, Ctrl + q」 docker attach CONTAINER # exitしてもコンテナは終了しない docker exec -it CONTAINER /bin/bash ``` リポジトリにタグを貼る(:TAGを省略すると、latestになる) ```bash docker tag IMAGE REPOSITORY[:TAG] ``` ./にあるDockerfileをビルドして、イメージを作成する ```bash docker build ./ docker build -t REPOSITORY[:TAG] ./ ``` ビルドを最初からやりなおす ```bash docker build --no-cache . ``` 起動中のコンテナ一覧を得る ```bash docker ps docker container list ``` 停止中のコンテナも含めすべての一覧を得る ```bash docker ps -a docker container list -a ``` コンテナのハッシュリストを得る ```bash docker ps -aq docker container list -aq ``` index.docker.ioからイメージを検索する ```bash docker search TERM ``` イメージをビルドした際のコマンドリストを得る。Dockerfileに記述したもののみ ```bash docker history IMAGE ``` イメージをファイル出力する ```bash docker save IMAGE > filename.tar ``` ファイルをイメージとして取り込む ```bash docker load < filename.tar ``` コンテナをファイル出力する ```bash docker export CONTAINER > filename.tar ``` コンテナからイメージを作成する ```bash docker commit CONTAINER REPOSITORY[:TAG] ``` URLを指定してイメージを取り込む ```bash docker import url REPOSITORY[:TAG] ``` ファイルからイメージを取り込む ```bash cat filename.tar | docker import - REPOSITORY[:TAG] ``` コンテナの標準出力を見る ```bash docker logs CONTAINER ``` コンテナ内のファイルをホストにコピーする ```bash docker cp CONTAINER:filename ./ ``` イメージがコンテナ化されてから変更されたファイル差分を得る ```bash docker diff CONTAINER ``` URLのファイルをイメージ内のPATHに生成する ```bash docker insert IMAGE URL PATH ``` コンテナの実行中のプロセス一覧を見る ```bash docker top CONTAINER ``` dockerの現在インストールしているバージョンと最新のバージョンを得る ```bash docker version ``` コンテナ内のイベントを監視する(コンテナが作られた、起動した、停止したなど) ```bash docker events ``` コンテナの詳細な情報を得る。formatオプションで情報の絞り込みができる ```bash docker inspect CONTAINER docker inspect --format="{{.NetworkSettings.IPAddress}}" CONTAINER ``` コンテナ内でコマンドを実行する ```bash docker exec CONTAINER コマンド ``` コンテナ内でコマンドを対話式に実行する ```bash docker exec -it CONTAINER コマンド ``` |
|
| 32位 |
|
|||
|
00:51:51 |
(Freelancer 所属) |
|
1年半ほどコツコツと書きためてきた iOS アプリ開発 / Objective-C 関連の Tips をカテゴリ別にまとめてみました。
数が多いので、オススメのものを太字にしてあります。 (更新情報 2012.8.11)ちゃんと数えてみたら88個しかなかったので、20個ほど追加しました。 (追記 2013.10.1) **本記事をきっかけに秀和システム社よりお声がけいただき、書籍を執筆** させていただきました。iOS開発に役立つTips集です。ぜひ下記ページより **目次を見ていただき、興味のある項目がありそうであれば** ご購入いただけるととても嬉しいです。 [『iOSアプリ開発 達人のレシピ100』という本を書きました](http://d.hatena.ne.jp/shu223/20130528/1369714635) <a href="http://d.hatena.ne.jp/shu223/20130528/1369714635"><img src="http://sphotos-f.ak.fbcdn.net/hphotos-ak-ash3/6263_640439495984820_1107973169_n.jpg" width="400"></a> ##ライブラリ/フレームワーク - **[ゲームアプリ向け UI コンポーネントセット "UIKitForGame"](http://d.hatena.ne.jp/shu223/20120806/1344151527)** ゲームアプリをつくる際に便利な UIKit のカテゴリ/サブクラスの詰め合わせライブラリ - **[vImageのススメ](http://d.hatena.ne.jp/shu223/20120721/1342858736)** iOS 5 から追加された高速画像処理ライブラリ、vImage の紹介と、サンプルコードの解説 - **[アプリのメモリ使用量をリアルタイムに表示するクラスを公開しました](http://d.hatena.ne.jp/shu223/20110428/1303930059)** メモリ消費量やUIViewの数など、「負荷を示すパラメータ」をリアルタイム表示するライブラリ - **[NSUserDefaults に保存する際に自動的に iCloud にも保存してくれるライブラリ "MKiCloudSync"](http://d.hatena.ne.jp/shu223/20120327/1332862169)** - **[CIFilter の効果を一通り試せるサンプルコード(フィルタ名一覧つき)](http://d.hatena.ne.jp/shu223/20111117/1321496682)** - **[iOS SDK用音声認識機能ライブラリVocalKitの使い方](http://d.hatena.ne.jp/shu223/20110227/1299368179)** iPhone SDKで利用できる音声認識ライブラリ - **[iPhone用グラフ描画ライブラリ s7graphview の使い方](http://d.hatena.ne.jp/shu223/20110201/1296564673)** - **[様々なアニメーションを実現するiOSアプリ用トゥイーンライブラリ3種を比較!](http://gihyo.jp/dev/serial/01/ios_oss/0001)** - **[アプリ内でログを閲覧するためのライブラリ iConsole](http://gihyo.jp/dev/serial/01/ios_oss/0002)** - **[実装の面倒な部分をうまくラップしてくれている便利カテゴリ3選](http://gihyo.jp/dev/serial/01/ios_oss/0003)** UIImageに各種画像処理機能を持たせるNYXImagesKit 面倒なデリゲート処理を Blocks で記述できるカテゴリ 英単語の単数形/複数形変換をメソッド1つで行えるようにするNSStringのカテゴリ - **[UILabelでのテキスト表示をリッチにする機能拡張サブクラス3種](http://gihyo.jp/dev/serial/01/ios_oss/0004)** - **[グラデーション描画や色のアニメーションの実装を簡単にする色処理関連の便利OSS3選](http://gihyo.jp/dev/serial/01/ios_oss/0005)** グラデーション描画のヘルパークラスLBGradient 2つのUIColorの間でクロスフェードさせるUIColorのカテゴリ 色の重ね合わせやCSSカラー名での色の指定ができるようになるUIColorのカテゴリ - [Path や Facebook ライクなスライドメニューを実現するライブラリ "IIViewDeckController"](http://d.hatena.ne.jp/shu223/20120629/1340731114) - [スクロール付きタブバー "InfiniTabBar"](http://d.hatena.ne.jp/shu223/20120627/1340718674) ##SNS連携 - [Facebook SDK 3.0 for iOS のサンプルを一通り試してみました](http://d.hatena.ne.jp/shu223/20120712/1342102850) - **[Twitter や Facebook への投稿ライブラリ ShareKit の Tips](http://d.hatena.ne.jp/shu223/20120305/1330966788)** - **[カメラアプリにFacebookへの写真投稿機能をつける](http://d.hatena.ne.jp/shu223/20110223/1298415231)** - [Facebook iOS SDK を使用して近況の投稿やプロフィール取得を実装する](http://d.hatena.ne.jp/shu223/20110314/1300018597) ##デバッグ関連 - **[ログ出力用マクロ](http://d.hatena.ne.jp/shu223/20120317/1331914823)** クラス名、メソッド名、コード内の何行目か、を出力するマクロ - **[クラッシュイベントを受け取る](http://d.hatena.ne.jp/shu223/20120316/1331819831)** TestFlight SDK のように、クラッシュイベントを受け取って Stack Trace をレポートするしくみの実装方法 - [バッテリー消費量の計測方法など、iOSのデバッグに関するドキュメント](http://d.hatena.ne.jp/shu223/20110504/1304500466) iOS Debugging Magic を読んで勉強になったことのメモ。 - [Instruments の Leaks の見方(Live Bytes や Living の意味)](http://d.hatena.ne.jp/shu223/20110502/1304333582) - [iPhone の空きメモリ量や使用中のメモリ量を取得する](http://d.hatena.ne.jp/shu223/20110427/1303921157) - [KVC で setValue:forUndefinedKey: エラーが発生する場合の対処法](http://d.hatena.ne.jp/shu223/20111102/1321492522) - [アップロード時に An error occurred uploading to the iTunes Store エラーが出た場合の対処方法](http://d.hatena.ne.jp/shu223/20110217/1298826921) ##XCode - **[$(SRCROOT) や $(BUILD_DIR) 等の Xcode で使用しているマクロの置換内容の一覧を調べる方法](http://d.hatena.ne.jp/shu223/20120804/1344155002)** - **[Xcode 4 に自作ファイルテンプレートを追加する](http://d.hatena.ne.jp/shu223/20110318/1300390974)** - **[ARC の有効/無効をファイルごとに設定する方法](http://d.hatena.ne.jp/shu223/20111229/1325141509)** プロジェクト全体でARCを有効にした状態で、一部ファイルを「まとめて」無効にする方法など。 - [Beta 版の Xcode で開いたプロジェクトが現行 Xcode で起動しなくなった場合の対処法](http://d.hatena.ne.jp/shu223/20120725/1344152423) - [ひとつのプロジェクトで複数の Bundle identifier を使い分ける方法](http://d.hatena.ne.jp/shu223/20120724/1344145314) - [ビルド設定の "Other Linker Flags" に "-ObjC" を設定する意味](http://d.hatena.ne.jp/shu223/20110426/1304694650) - [Xcode 4 の Code Snippet Library にスニペットを追加する](http://d.hatena.ne.jp/shu223/20110315/1300403884) - [Xcode 4 でのプロジェクトテンプレート作成手順](http://d.hatena.ne.jp/shu223/20110328/1301713208) - [2つの Xcode 4 プロジェクトのビルド設定を比較する](http://d.hatena.ne.jp/shu223/20110414/1302751793) - [LLVM GCC と LLVM Compiler 2.0 とどっちがいいのか?](http://d.hatena.ne.jp/shu223/20110530/1307258057) ##UI関連 - **[リファクタリング講座メモ](http://d.hatena.ne.jp/shu223/20110531/1307300906)** 「リファクタリング講座」で勉強になったことのメモ。breakpointの右クリックエディットの話、xibファイルの話、クラスエクステンション、LLVMなどなど - **[スクリーンショットを撮る](http://d.hatena.ne.jp/shu223/20110417/1303138458)** スクリーンショットをとって UIImage で返してくれるメソッド - [UIWebView からアプリ内に入れたカスタムフォントを使う](http://d.hatena.ne.jp/shu223/20120722/1343061397) - **[放物線状にアニメーションさせる](http://d.hatena.ne.jp/shu223/20120209/1328793577)** ジャンプに見立て、始点(startPos)と終点(targetPos)と高さ(height)を指定するように CAKeyframeAnimation をラップしたスニペット - [クロスディゾルブで画面遷移](http://d.hatena.ne.jp/shu223/20120401/1334019533) - [UIFont の fontName 一覧](http://d.hatena.ne.jp/shu223/20110604/1307890173) UIFont の fontWithName:size: メソッドに渡す fontName の一覧。 - [BMPファイルのカラーパレットを書き換える](http://d.hatena.ne.jp/shu223/20110516/1305550835) - [UITableViewController のreloadDataをコールするタイミング](http://d.hatena.ne.jp/shu223/20110403/1304235112) - [UINavigationBar に UISegmentControlを置く](http://d.hatena.ne.jp/shu223/20110321/1301709440) - [UIPopoverControllerをiPhoneで使う](http://d.hatena.ne.jp/shu223/20110316/1300375000) - [UITextFieldへの入力時にキーボードではなくUIPickerViewを出す方法](http://d.hatena.ne.jp/shu223/20110222/1299431262) - [nibファイルの読み込みパフォーマンスを改善するUINibクラス](http://d.hatena.ne.jp/shu223/20110221/1299439884) - [UITextViewのheight調整](http://d.hatena.ne.jp/shu223/20110218/1298226268) - [iPhoneアプリの起動アニメーションの実装方法](http://d.hatena.ne.jp/shu223/20110208/1299336956) - [アプリアップデート後の初回起動時にUIAlertViewでお知らせを表示する方法](http://d.hatena.ne.jp/shu223/20110207/1299335775) - [入門本から得たTipsなど](http://d.hatena.ne.jp/shu223/20110206/1296942353) - [UIAlertViewを左寄せにする](http://d.hatena.ne.jp/shu223/20110204/1299333570) ##Foundation - [構造体を NSDictionary や NSArray に格納する方法](http://d.hatena.ne.jp/shu223/20110509/1304952981) - [NSUserDefaults の内容を一括消去する方法](http://d.hatena.ne.jp/shu223/20111101/1321307235) 開発時の動作確認用リセット(初期化)ボタン等に - [NSUserDefaults に保存されている全てのキーと値の一覧を取得する](http://d.hatena.ne.jp/shu223/20111002/1330232118) - [NSFileManagerによるファイル・ディレクトリ操作](http://d.hatena.ne.jp/shu223/20110304/1300983192) - [NSStringの全角/半角バリデーションチェック](http://d.hatena.ne.jp/shu223/20110219/1299330910) - [NSLogのフォーマット指定子一覧](http://d.hatena.ne.jp/shu223/20110311/1300983799) - [NSAssertの使いどころ](http://d.hatena.ne.jp/shu223/20110308/1300397404) ##アプリ内課金 / 広告 - **[In-App Purchase の脆弱性への対応方法](http://d.hatena.ne.jp/shu223/20120723/1343060671)** - [In-App Purchase の Status が "Developer Action Needed" な場合の対処方法](http://d.hatena.ne.jp/shu223/20120204/1328941106) - [In-App Purchase で、プロダクトID が invalid になる場合の対処方法](http://d.hatena.ne.jp/shu223/20120114/1327794728) - [In-App Purchase のステータスが "Ready to Submit" な場合の対処法](http://d.hatena.ne.jp/shu223/20120107/1327782618) - [新しいAdMob SDK(Google AdMob Ads SDK for iOS)での広告設置方法](http://d.hatena.ne.jp/shu223/20110324/1300985815) - [AdMobの入金タイミング](http://d.hatena.ne.jp/shu223/20110503/1304488825) ##位置情報関連 - **[すれ違い通信アプリ開発で得たBluetoothの知見まとめ](http://d.hatena.ne.jp/shu223/20110220/1298223410)** すれ違い通信アプリ『EncountMe』の開発を通して得たiPhoneのBluetoothまわりの諸々について - [iOS 5.0 より追加された CLGeocoder を使用して逆ジオコーディング](http://d.hatena.ne.jp/shu223/20120506/1336283901) - **[Objective-C版GeoHexを試してみました](http://d.hatena.ne.jp/shu223/20110528/1306564167)** - [位置情報のOn/Off、許可/不許可を確認する](http://d.hatena.ne.jp/shu223/20111001/1321305649) - [位置情報サービス利用の際のバッテリー電力節約のためのヒント](http://d.hatena.ne.jp/shu223/20110710/1311627118) - [MKAnnotationViewのコールアウトをカスタマイズする](http://d.hatena.ne.jp/shu223/20110506/1307312369) - [CLLocationCoordinate2D から指定方向に指定距離離れた場所の緯度経度を計算する](http://d.hatena.ne.jp/shu223/20110406/1302028258) - [MKMapViewのピンをタッチせずにコールアウトを出す](http://d.hatena.ne.jp/shu223/20110325/1301709495) ##Unity / cocos2d / Titanium - [NGUIの使い方に関する記事まとめ](http://d.hatena.ne.jp/shu223/20120614/1339777051) iOS SDK の UIKit に用意されているような UI を Unity で実装できるプラグイン - [Unity での Bundle Identifier の設定方法](http://d.hatena.ne.jp/shu223/20110702/1310314681) - [cocos2d を使用したゲームアプリのソースコード集](http://d.hatena.ne.jp/shu223/20110421/1303343800) - [Titanium開発情報(日本語)を集めてみました](http://d.hatena.ne.jp/shu223/20110430/1304147261) - [MonoDevelopの文字化け対策最終手段](http://d.hatena.ne.jp/shu223/20110705/1310403782) - [cocos2dでの3Dアプリケーションの開発を可能とするエクステンション「cocos3d」](http://d.hatena.ne.jp/shu223/20110415/1302792358) - **[cocos2d のトランジションエフェクトのラッパークラスを公開しました](http://d.hatena.ne.jp/shu223/20110329/1301396786)** ##その他 - **[デバイスにインストールされているアプリ一覧を取得する](http://d.hatena.ne.jp/shu223/20110224/1299430528)** - [ARC 有効/無効に両対応させたい場合に便利なマクロ](http://d.hatena.ne.jp/shu223/20120312/1331565988) __has_feature(objc_arc)で処理を書き分けるのが面倒な場合に便利なマクロ - **[オブジェクトが持つプロパティの型と名前のリストを取得する](http://d.hatena.ne.jp/shu223/20120226/1330231240)** - **[iOS Advent Calender 2011 で勉強になったことメモ](http://d.hatena.ne.jp/shu223/20120117/1326822089)** CocoaPods の話、Cocoa Controls の話、DTrace でのデバッグの話、appCode という IDE の話、、などなど - **[NSNotification, NSNotificationCenter によるオブジェクト間通知のパフォーマンス](http://d.hatena.ne.jp/shu223/20120210/1328878654)** NSNotification, NSNotificationCenter を用いたオブジェクト間連携は使いやすいのでついつい多用してしまうのですが、具体的に内部でどのような処理が行われていて、パフォーマンス面での影響はどのくらいなんだろう、ということをイメージできずにいたので、Appleのドキュメントで調べてみました - **[パフォーマンスチューニングに関するアップルのドキュメント](http://d.hatena.ne.jp/shu223/20110827/1314520691)** アップルの『iOSアプリケーションプログラミングガイド(英語版)』にある、『パフォーマンスと応答性のチューニング』という章のためになった部分の紹介 - **[Assets Library からの読み込みを高速化するための試行錯誤](http://d.hatena.ne.jp/shu223/20110418/1303137062)** - **[csvファイルからplistを生成するPHPスクリプト](http://d.hatena.ne.jp/shu223/20110511/1305129443)** エクセルとかGoogle docsで編集されたデータをplistファイルとしてアプリに持たせたい、といった場合用の、csvファイルからplistファイルへの変換スクリプト - **[Push Notification おさらい](http://d.hatena.ne.jp/shu223/20110828/1314516533)** プッシュ通知を実装するときに、「バックグラウンドで通知受けたときはapplication:didFinishLaunchingWithOptions:とapplication:didReceiveRemoteNotification:、どっちが呼ばれるんだっけ?」とかなんだかいつも自信なくなってきてドキュメントを見返したりしてしまうので、ドキュメントと、実際の動作を確認した内容をまとめました。 - [otoolでバイナリの内容をいろいろと調べる方法](http://d.hatena.ne.jp/shu223/20110518/1305738227) - [mecab での形態素解析を試してみました](http://d.hatena.ne.jp/shu223/20111119/1321586312) - [音声録音の実装方法](http://d.hatena.ne.jp/shu223/20110301/1299904208) - [プロセス情報を取得する](http://d.hatena.ne.jp/shu223/20110401/1303917228) NSProcessInfo というクラスを使ってプロセス情報をいろいろと取得する方法 - [AppStoreに並ぶ最終的なバイナリサイズを計測する方法](http://d.hatena.ne.jp/shu223/20120121/1327117228) - [大量の画像ファイルを圧縮したzipファイルをiPhoneで解凍するのに要する時間](http://d.hatena.ne.jp/shu223/20110425/1304953175) - [バックグラウンドのアプリが殺される優先度](http://d.hatena.ne.jp/shu223/20110423/1307314926) - [Objective-Cの.gitignoreテンプレート](http://d.hatena.ne.jp/shu223/20110826/1314519637) - [iOSアップデート中に"This device isn't eligible for the requested build"エラーが出る場合の対処](http://d.hatena.ne.jp/shu223/20110601/1307559462) - [コードスニペットまとめサイト](http://d.hatena.ne.jp/shu223/20110527/1306770936) - [ボタンやアイコンなどの素材管理ツール](http://d.hatena.ne.jp/shu223/20110330/1301713140) - [初回起動の判定方法](http://d.hatena.ne.jp/shu223/20110404/1304692649) - [寄稿を受け付けているアプリ紹介ブログ/サイトリスト](http://d.hatena.ne.jp/shu223/20110216/1299441142) - [ユーザー登録なしでユーザーを識別するベストプラクティス](http://d.hatena.ne.jp/shu223/20110407/1304693658) - [iPhoneバイブの制御](http://d.hatena.ne.jp/shu223/20110215/1299431099) - [iPhoneアプリ開発(iPhone SDK)の解説本、一挙14冊レビュー](http://d.hatena.ne.jp/nns/20100123/1264202050) - [GKLeaderboard でどのようなスコアデータの絞り込みができるか](http://d.hatena.ne.jp/shu223/20120315/1331819111) - [GameCenterでどこまでサーバーサイド機能を代替できるか](http://d.hatena.ne.jp/shu223/20110317/1300380674) - **[Game Center のリーダーボード(スコアランキング)対応アプリの作り方](http://d.hatena.ne.jp/shu223/20110131/1296408051)** |
|
| 33位 |
|
|||
|
23:41:33 |
|
|
 理解しやすいように適当に遮ったり、言い切ってしまったところもあるがご容赦いただきたい。 # MVCの登場 MVCは、SmalltalkのGUIライブラリのモデルとして登場した。 これはGUIアプリケーションを記述する際に、適切なモデル化を進めるのにとてもいい考え方だと思われていたし、実際にそうだった。 これはアーキテクチャパターンとして、それぞれがどのように依存するべきか、どこにコードを書くべきかということを端的に表している。 安定依存の原則というものがある。これは、要件が安定しているモジュールに依存し、要件が変動しやすいモジュールには依存しないようにするという原則だ。MVCアーキテクチャでは、GUIアプリケーションの安定関係をModel > View > Controllerの順でとらえている。データ処理や業務要件というのは安定しており、UIパーツもまた比較的安定している。それらを統合してアプリケーションとしてくみ上げる部分が一番安定していないというようにとらえた。 なので、ModelはViewやControllerから依存されるが、Controllerは依存されない。その他の関係はイベントのように抽象度が高いものを使っている。 実際にはもう少し粒度の細かいデザインパターンの集合として記述されることが多い。その代表例がイベントを抽象化するObserverパターンや、インタフェースを集約するFacadeパターンだ。 このモデルは現在に至るまで長いこと支持されている。もしかしたら金科玉条のように宗教的な信仰を受けているといっても言い過ぎじゃないかもしれない。 今日は、MVCが最近どのように変化しているかという話をしたい。 #Document-Viewモデル MVCをもとに実際にアプリケーションを書いていくと、UIとコントローラには実際的には依存関係が強いことが多い。結局のところ1つのUIのために1つのControllerというように1:1の関係があった。 そこで、1つの割り切りとしてMFC(WindowsのC++ GUIフレームワーク)ではDocument-ViewモデルというM+VCというModel層とイベントを内包したUI部品という形を採用した。 これはイベント駆動のUIと切り離されたスレッドを持つ状態を共存させるのにいい考えだった。 #ドメインモデル貧血症 一方で、MVCでの開発にはやっかいな現象が起き始めた。ドメインモデル貧血症である。 http://en.wikipedia.org/wiki/Anemic_domain_model これは、要件追加が主に安定しないViewに近い部分から発生するために起こりやすい現象で、あるフィーチャーを開発する際にデータ処理をモデリングできず、Controller層で直接データベースを触って、必要なトランザクションまで記述してしまうというために起きた。そのためデータ処理の完全性を保証することが難しくなったり、テストの難しいコードとなってしまいバグの増加を招いた。 ドメインモデルは、業務上のロジックをすべてモデルとして隠蔽することが望まれていたが、追加要件に対してもろいことが多かった。 これは同時にO/Rマッパーの普及によるところも大きい。O/Rマッパーはデータベース処理をObject指向のライブラリに隠蔽することができるが、ドメインモデルの設計を助ける訳ではない。複数のデータベースにまたがるドメインモデルも存在するし、それがDBである必要性も本来的には無いのだから当然のことだ。 O/Rマッパーも複雑な要件に耐えるため、ActiveRecordというデータベース上のRowとDomainModel上のEntityを一致させる簡易的なものからDataMapperというデータベース処理をEntityにマッピングさせるというタイプのフレームワークなどを提供するようになってきた。 ライブラリの普及だけではなく、デザインパターンに関してもDDD(Domain Driven Design)と呼ばれるドメインモデルの分析から入る設計の必要性が説かれるようになった。 これは、ドメイン駆動による設計を実現するための共通言語を作ることが目的だった。そのなかでサービス層/サービスクラスという概念が登場した。これは問題領域そのものの概念化ではなく、インタフェース提供や粗結合化を実現するためのモジュールである。 #MMVC これに関連して、MMVC( Model Model View Controller )という考え方が出てきた。 http://c2.com/cgi/wiki?ModelModelViewController モデル層を2つに分割し、DomainModelとApplicationModelとよぶ。このため MAVCと呼ぶ人もいる。(DAVCと呼ぶ人はいない。) DomainModelは状態を持つオブジェクトとして作り、複数のDomainModelを利用した処理をApplicationModelというサービス層に隠蔽する考え方だ。 このようにControllerが太る理由は、Model層についてモジュール化するためのノウハウやルールが確立されていないからだという考えのもとMVCの進歩は進んできた。 #DCIの登場 最近ではDCIという考え方が出てきた。DCIの問題意識は、Modelに次々に追加される要件に対応することだ。これはAgileな開発というスタイルにも関連している。DCIの考え方はDataよばれるデータソース層に対して、Roleと呼ばれる役割をContext上でアドホックに組み込んで提供するものだ。(なぜ DRC:Data Role Contextじゃないのかはよくわからない。) RoleはTraitで実装される。とても現代的なものではあるが、有効性や問題点はまだ未知数といってもいいだろう。 このようにControllerにModelで書くべきことが記述される問題に関してはアプローチされてきた。ではControllerとViewの関係はどうであろうか。 #MVVMあるいは双方向バインディング 実際のところ、ViewとControllerの間で記述されている処理は非常に明快なことが多い。Viewへのアクションに伴うイベントのハンドリングや、データのアサインをModelを介して行うだけだ。これはModelが十分な機能を提供している限り、これ以上のことをする必要は無い。であれば、ViewとModelの関係を明快にすることでControllerの肥大化は防ぐことができる。 そこで出てきた考え方がMVVMだ。(Model View ViewModel ) UIには入力フォームや表示データのように見た目とは別のデータ構造を隠し持っていることがわかる。そのUIパーツのためにアサインしているデータを考えればわかりやすいだろう。 このデータ構造をViewModelといい、その変更を監視しモデルに伝え、モデルがViewModelを変更するといったインタフェースを持つことで煩雑なイベント処理を隠蔽できる。 これはmicrosoftのsilverlightのためのRIAであるWPFから出てきた考え方だ。しかし、現在ではknockout.jsやandroid-bindingでも同様のことができる。 (はてぶより追記:WPF上でのMVVMについて、より詳細にはこちらのスライド http://www.slideboom.com/presentations/381148 をご確認ください。) この考え方をより明快にWebアプリケーションと結びつけようとするむきがある。これがModel driven Viewというgoogleがリードして提案しているDOM拡張である。これは、viewModelにあたるオブジェクトをviewであるdomオブジェクトとmodelオブジェクトがどちらも参照をもっており、それぞれの変更をそれぞれが通知され処理するというものだ。これは、WebComponentsの流れに関連しているものだ。まだ開発中であるがpolymer.jsを利用することである程度先取りすることができる。また、実用を考えるのであれば、angular.jsなどでも近しいの考え方で実装されている。 DCIとMVVMあるいはmdvは今後、アプリケーション開発においては主流になっていくだろう。これによって、Controllerの責務はほぼなくなっていき、Fat Controllerの問題は別の問題にすり替わっていくだろうということが想像される。 |
|
| 34位 |
|
|||
|
22:34:50 |
(サイバーエージェント 所属) |
|
# JavaScriptの「プロトタイプ入門」
JavaScriptはオブジェクト指向です。 クラスという概念はないため、 擬似的なクラスの表現を使ったパターンがあります。 ただ今回はそちらではなくプロトタイプベースの説明をします。 その前にコンストラクタについて知らない人はこちらへ。 [JavaScriptのクラス?コンストラクタ?](http://qiita.com/items/010752b1427773558f7c) #プロトタイプって? そもそもプロトタイプってなに? 実は、、プロトタイプとは「オブジェクト」のことなんです。 そして感覚としては親だと思って大丈夫です。 親の能力は子にも受け継がれている。。的なニュアンスですね。 (まぁ意味合いはちがいますがw) #prototypeプロパティ ここで新規に関数を生成してみましょう。 そうするとprototypeプロパティが自動で作成されます。 ```javascript: function proto() {} //proto.prototypeは自動で生成 ``` このように関数を生成した時点で、 proto.prototypeプロパティが自動で生成されます。 そしてそれは新たな空オブジェクトへの参照を持っているんですね。 そしてprototypeプロパティは自分の親への参照だと思って下さい。 ここは非常に重要です。。。 ということで見るより慣れろ! 先に実用例をチェックします。 #実用例 ```javascript: function Dog() {} Dog.prototype.bark = function() { console.log('わんわん'); }; var dog = new Dog(); dog.bark(); //'わんわん'; ``` といった感じです。。 dog自体は何も持っていませんが、 親がbark関数を持っているので、あたかも自分のようにつかえちゃいます。 あれ、でもこれって…下と何が違うの?? ```javascript: function Dog() { this.bark = function() { console.log('わんわん'); }; } var dog = new Dog(); dog.bark(); //'わんわん'; ``` ふーむ、挙動は一緒ですね。 ただ前者のほうがbetterです。 実は後者だと毎回毎回関数が生成されちゃうんです! ・・・?? 前記事でも説明しましたが、 newはこんな暗黙のルールをもっています! ```javascript: function Dog() { // var this = {}; this.bark = function() { console.log('わんわん'); }; // return this; } ``` なのでこれでは毎回空オブジェクトを作成し、 毎回新しくbark関数を定義しちゃっていますね。。 そこでプロトタイプの出番です! そしてプロトタイプは親だと思ってください。 そして子は親のプロパティを共通で使えちゃいます! ここでも実践例! ```javascript: function Dog(cry) { this.cry = cry; } Dog.prototype.bark = function() { console.log(this.cry); }; var chiwawa = new Dog('きゃんきゃん'); chiwawa.bark(); //'きゃんきゃん'; var shiba = new Dog('わんわん'); shiba.bark(); //'わんわん'; ``` といった感じです。 これだと関数は無駄に生成されていませんね♪ なぜならnewを使った挙動は下記だからですね。 ```javascript: function Dog(cry) { // var this = {}; this.cry = cry; // return this; } ``` そしてポイントです! newを使った場合のthisのプロトタイプは、 Dogのプロトタイプと同じ参照を持っています! すなわちこれによって生成されたインスタンスはbark関数が使えちゃいます。 #実践のポイントは一点のみ 単純にプロトタイプを使用したい場合は、 ポイントは一点のみ! ・汎用的な関数はprototypeに定義 これだけです! プロトタイプ(親)に汎用的な関数を定義し、 あとはインスタンス(子)はみんなで仲良くその一つを使うわけですね。 そして関数以外は上のcryのように子自身に定義しちゃいます。 とまぁ、入門としてはこの使い方ができれば十分だと思います。 ではでは、おさらばです♪ # 外部アカウント 技術情報のみつぶやくアカウント作成しました。JavaScriptは最新情報も追っていきます。 [Twitterはこちら] (https://twitter.com/takeharumikami) [Feedlyのフォローはこちら] (http://cloud.feedly.com/#subscription%2Ffeed%2Fhttp%3A%2F%2Fqiita.com%2Ftakeharu%2Ffeed) # おすすめの記事 入門者がつまづく、thisの挙動を4種類に分けて簡単に学ぶならこれ。Apply, callの挙動までわかる。 [JavaScriptの「this」は「4種類」?」](http://qiita.com/takeharu/items/9935ce476a17d6258e27) JavaScriptでは関数はすべてクロージャ。 [そもそもクロージャって?JavaScriptでクロージャ入門](http://qiita.com/takeharu/items/4975031faf6f7baf077a) |
|
| 35位 |
|
|||
|
02:04:33 |
|
|

[Homebrew](http://brew.sh/)(ホームブルー)とは、Mac OS X オペレーティングシステム上でソフトウェアの導入を単純化するパッケージ管理システムのひとつです。`yum` や `apt-get` の類の一つです。 # MacPorts との違い Mac OS X には Homebrew と双璧をなすパッケージ管理システムがあります。 MacPorts です。以下に簡単な比較を載せておきます。 | | **Homebrew** | **MacPorts** | |:------:|:-----------:|:--------------:| | 既存のソフトウェアへの影響 | なるべく既存を利用 | 新しくインストールする | | インストール可能なユーザ | 一般ユーザ | スーパーユーザ(管理者権限 sudo が必要) | | パッケージのインストール先 | /usr/local | /opt/local | 扱えるパッケージ数 | 少ない| 多い | | インストールにかかる時間 | 少ない | 多い | | システムへの依存度 | 大きい | 小さい | MacPorts と比べて Homebrew は依存関係でインストールされるソフトが少ないためか、パッケージ管理システムとしての人気が高まってきています。 MacPorts は、Mac に最初から入っているソフトウェアを無視してパッケージが依存するソフトを新規でインストールするという性質を持っていますが、Homebrew は極力 Mac に入っているものを使うように作られています。ゆえに、パッケージ導入時のシステムへの負担や、インストールにかかる時間が比較的少なくて済むようです。 また、Homebrew はスーパーユーザでコマンドを実行する必要が無く、一般ユーザー権限で使うことが出来ます。 ※【2015/07/07 追記】最近では Homebrew が大きく台頭してきて、MacPorts の名前を見ることは減ってきました # Homebrew について Homebrew は「ユーザが自らパッケージをビルドして使用する」ことのメタファーで「ビールを自家醸造して保存する・飲む」ことを意味しています。そのため、独特なキーワードを用いるので下記で表にしておきます。 | キーワード | 本来の意味 | たとえ | |:-------:|:-------:|:-------: | **Brew** | ビールを醸造する | make する | | **Homebrew** | 自家醸造 | ユーザ自らがビルドする | | **Cellar** | セラー(ワインセラーのなどのセラー。ビール貯蔵庫) | インストール(保存)先 | | **Keg** | 樽、醸成用 | make 材料 | | **Formula** | 調理法、手順 | ビルド方法・手順が書かれたスクリプト | Homebrew 用のサブコマンドを叩いたり、エラー処理しているときなど、様々な局面で上記のようなキーワードを目にすることになると思うので覚えておくといいかもしれません。 # Homebrew をインストールする 何個かステップを経るため、先に手順だけ書いておきます。 1. Java をインストールする 2. Command Line Tools のインストール 3. Homebrew 本体のインストール ## 1. Java をインストールする OSX は Java を必要とするような動作をした時、OS 側から 「Java の未インストールの情報」と「インストールするかどうかの選択肢」を与えてくれます。 下記のコマンドを **ターミナル(Terminal.app)** から実行することで、Java がインストールされていればバージョンを表示、されていなければインストールするか聞いてきてくれます。 **インストール済み** ```console $ java -version java version "1.6.0_51" Java(TM) SE Runtime Environment (build 1.6.0_51-b11-457-11M4509) Java HotSpot(TM) 64-Bit Server VM (build 20.51-b01-457, mixed mode) ``` **未インストール** ```console $ java -version No Java runtime present, requesting install. ```  ## 2. Command Line Tools のインストール このインストール方法は2通りあります。 ちなみに、Command Line Tools をインストールすることで、`gcc` といったコマンドも使用することができるようになります。 * **2.1.** Xcode を利用する App Store にて [Xcode](https://itunes.apple.com/jp/app/xcode/id497799835?mt=12)をダウンロードすることが出来ます。インストールが完了したら、ターミナルから ```console $ xcode-select --install ``` * **2.2.** Developer を利用する Xcode をインストールしたくない人、出来ない人などはこの方法がおすすめです。しかし、Apple ID などが必要になるので準備しておいてください。無料です。 <https://developer.apple.com/downloads/index.action>  ## 3. Homebrew 本体をインストールする [Homebrew 公式サイト](http://brew.sh/)に書かれている方法を用いてインストールします。 下記のコマンドを **ターミナル(Terminal.app)** から実行するだけです。 ※この下記コマンドは公式サイト上でまれに変更される場合があるので、公式サイトへ飛んでコピペしたほうが最新です。 ```console $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" ``` インストールが完了すると、その旨と簡単なセットアップを促すメッセージが出力されます。 ==> Installation successful! You should run `brew doctor' *before* you install anything. # Homebrew を使用する インストールが完了したら、まず下記コマンドを実行します。 ```bash $ brew doctor ``` 問題がないかチェックし、あった場合はその解決策を示してくれます。 いざ、打ってみるとさっそく。。 Warning: You have MacPorts or Fink installed: /opt/local/bin/port This can cause trouble. You don't have to uninstall them, but you may want to temporarily move them out of the way, e.g. sudo mv /opt/local ~/macports Warning: You have a non-Homebrew 'pkg-config' in your PATH: /opt/local/bin/pkg-config `./configure` may have problems finding brew-installed packages using this other pkg-config. 今回は、MacPorts の存在がエラーの原因でした。解決策が示されているので、乗っかってみましよう。 ```bash $ sudo mv /opt/local ~/macports ``` `mv` の第二引数は何でもよくて、とりあえず除けたかったみたいです。もう一度 `brew doctor` してみて、 ```console Your system is ready to brew. ``` と表示されれば、インストールの完了です。 =========== 余談として、お決まりの本体アップデートをしておきます。 最新版にするには、 ```console $ brew update ``` バージョンチェックは、 ```console $ brew -v ``` です。 # Homebrew の基本操作をする `wget` コマンドをパッケージの例に説明していきます。 以下に示すコマンド郡を使用できたら、実使用では無問題でしょう。 **パッケージを探す** ```bash $ brew search wget ``` **パッケージをインストール** ```bash $ brew install wget ``` **パッケージの有効化と無効化** ```bash $ brew unlink wget # 一時的に無効化 $ brew link wget # 有効化 ``` **パッケージ一覧の更新** ```bash $ brew update # formula を更新 $ brew upgrade # 更新があるパッケージを再ビルドする ``` **インストールされたリストを表示する** ```bash $ brew list ``` **パッケージをアンインストール** ```bash $ brew remove wget ``` **Homebrew の設定一覧** ```bash $ brew --config HOMEBREW_VERSION: 0.9.5 ORIGIN: https://github.com/mxcl/homebrew HEAD: 57272977eaa0ec0509a29be6c8e90b5f4b338f65 HOMEBREW_PREFIX: /usr/local HOMEBREW_CELLAR: /usr/local/Cellar CPU: quad-core 64-bit ivybridge OS X: 10.8.5-x86_64 Xcode: 4.6.3 CLT: 4.6.0.0.1.1365549073 LLVM-GCC: build 2336 Clang: 4.2 build 425 X11: N/A System Ruby: 1.8.7-358 Perl: /usr/bin/perl Python: /usr/bin/python Ruby: /usr/bin/ruby => /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby ``` # Homebrew をアンインストール 世の中には気に食わないソフトウェアてのもあるものです。 そんな時は以下をあせらずに実行し、一掃しましょう。 ```bash $ cd `brew --prefix` $ rm -rf Cellar $ brew prune $ rm `git ls-files` $ rmdir Library/Homebrew Library/Aliases Library/Formula Library/Contributions $ rm -rf .git $ rm -rf ~/Library/Caches/Homebrew ``` |
|
| 36位 |
|
|||
|
00:01:43 |
(Toreta 所属) |
|
さいきんREST APIのドキュメントを書いていて、wiki使うのだるいし他に良い方法ないかな〜と調べてた時に見つけたツール群をまとめてみます。 追記: こちらも便利そうなので参考にどうぞ。 [REST APIドキュメント作成ツールはapiary.ioが決定版かもしれない - Qiita](http://qiita.com/horimislime/items/38327a3f4166b7b39eb5?utm_source=chrome-extention&utm_medium=browser-extention&utm_content=notifications) ## swagger [Swagger: A simple, open standard for describing REST APIs with JSON | Reverb for Developers](https://developers.helloreverb.com/swagger/) デモ: [Swagger UI](http://swagger.wordnik.com/) ソースコード中にAPIの概要を書いておくと、それを元にドキュメントを自動生成してくれる。wikiやmarkdownで書くのと決定的な違いは、↓のようにドキュメント上のformからAPIコールを試すことができる点。  様々な言語のWAFに対応したライブラリも開発されてる。 [Home · wordnik/swagger-core Wiki](https://github.com/wordnik/swagger-core/wiki) - [Scalatra2.2+scalatra-swaggerでREST APIのリファレンスを生成する #1 | Developers.IO](http://dev.classmethod.jp/server-side/scalatra-swagger01/) - [Scalatra2.2+scalatra-swaggerでREST APIのリファレンスを生成する #2 | Developers.IO](http://dev.classmethod.jp/server-side/scalatra-swagger02/) ## iodocs [mashery/iodocs](https://github.com/mashery/iodocs) node.jsで動作するドキュメントツール。API概要の定義はjsonで記述するようになっていて、こちらもswaggerと同様にページ上でAPIコールを試せるようになっている。iodocsに関しては最近postしたのでこちらもどうぞ。 [iodocsで便利なREST APIドキュメントを作成する - Qiita [キータ]](http://qiita.com/horimislime/items/ed13637c070b99dae959)  ## autodoc [r7kamura/autodoc](https://github.com/r7kamura/autodoc) APIのテストケースからドキュメントを自動生成してくれる。Markdownで出力してくれるので、別途Webサーバを起動する必要とかも無し。 - [Autodoc - r7kamura blog](http://r7kamura.github.io/2013/12/01/autodoc.html) - [Rails でつくる API のドキュメントを自動生成してくれる autodoc がすごい - 彼女からは、おいちゃんと呼ばれています](http://blog.inouetakuya.info/entry/2013/10/20/132928) ## RESTdoclet [RESTdoclet by IG-Group](http://ig-group.github.io/RESTdoclet/) JavaでSpringMVCな環境で使うツール。Mavenプラグインとして提供されている。ドキュメント生成にはJavadocやSpringのRequestMapping等のアノテーションを利用するので、生成のために余計なアノテーション等をコードに書かなくていいのが特徴的。 - [IG GroupがRESTdocletをオープンソース化](http://www.infoq.com/jp/news/2012/08/RESTDocletOpenSource) ## carte [devo-ps/carte](https://github.com/devo-ps/carte) デモ: [Carte](http://devo.ps/carte/) Markdownでドキュメント記述ができる。Jekyllベース。swaggerやiodocsと比較してシンプルな作りになっており、APIコールを試す機能は無い。  ## まとめ この手の自動生成や効率化はやり方が沢山ある分、各ツールそれぞれのアプローチが見えて面白いなーと思いました。まだまだ開発途中のものもあり、ちょっと試してて不便に感じる所もあったのですが今後に期待。 wikiやGithub READMEを使い続けるのもいいですが、こうした支援ツールも一度検討してみてはどうでしょうか。 |
|
| 37位 |
|
|||
|
14:02:44 |
(フリーランス 所属) |
|
ちょっと煽り気味のタイトルにしてみましたが、Railsで開発する時は意識的にOOPに寄せないとオブジェクトの力が活かせなくなるよってことと、Railsが提供しているクラスの責務を分割することを支援してくれる機能について話をします。
## ActiveRecordの性質 Rails開発においては、モデル層にロジックを書いてコントローラーは薄くしろ、というのはしつこく言われているので、概ね浸透してきていると思います。 それに加えて、最近私が結構しつこく主張しておきたいのが、モデル = ActiveRecordでは無いよ、ということです。 ActiveRecordは成り立ちから言うと、ロジックとDBへの永続化をまとめてカプセル化するアーキテクチャパターンから来ています。(詳しくはエンタープライズアプリケーションアーキテクチャパターンという書籍を読むと良いです) この方法はロジックが複雑でない場合、つまりCRUD+αぐらいの時には非常に上手くいきます。 RailsのActiveRecordは非常に便利なのでそこそこの規模ぐらいのものを作ってる時には余り問題になりません。 しかし、規模が大きくなってくるとActiveRecordだけでは色々と辛い側面が出てきます。 テーブルと1:1という形でクラス構造が縛られる事により構造化に限界が発生し、一つのクラスに何でもかんでもロジックを記述していくことになります。 これによって生じる問題がファットモデルです。 モデル層をActiveRecordだけに頼っていると、いずれこの問題にぶち当たることになります。 これを解決するには地道にオブジェクト指向の基本に立ち返ること、上手くアクティブレコードから責任を分離する事が必要です。 ActiveRecordは開発の初期には非常に便利で問題も余り顕在化しないため、そこに囚われていると気付いた時にはえらくファットになってるという事が十分にあり得ます。 ## Rails謹製 ダイエット食品 Railsは良く考えられたフレームワークなので、もちろんそれ自体の中にもちゃんと責務を細かく分けるための仕組みを準備してくれています。 Railsが提供してくれる機能を理解し、モデルの責任を分割することでより良いモデルを作っていくことができます。その中でも代表的な機能が以下の4つです。 - Callbackクラス - Validatorクラス - ActiveModelモジュール - 値オブジェクト (composed_of) 今回は、その中で忘れられがちな印象を受ける上の2つについて解説します。 ## Callbackクラス [ActiveRecord::Callbacks](http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html "ActiveRecord::Callbacks")からActiveRecordオブジェクトのコールバック機能の使い方を参照してみます。 ActiveRecordオブジェクトのコールバックはこんな感じで記述するのが簡単な方法です。 ```ruby class Subscription < ActiveRecord::Base before_create :record_signup private def record_signup self.signed_up_on = Date.today end end ``` この様に自分のオブジェクトのパラメーター調整等であればこのクラスの責任と言えるし、簡単なのでシンプルにprivateメソッドにしておけば他から変な形で利用されることもなくなります。 しかし、もしある程度複雑な処理が関係してくる場合は、もう少し分割したくなってきます。 例えば、RailsのAPIリファレンスにあるような特定カラムを透過的に暗号化したい場合や、コールバックの動作に条件がいくつかある場合等です。 こうなってくると、コールバック処理自体にもテストコードを書きたくもなりますね。 その場合は、機能名を表現するクラスを用意してそちらに処理を記述します。 Railsではクラスに特定のメソッドを定義しておくだけで、任意のクラスをコールバック処理に組み込むことができます。 APIリファレンスのサンプルを見てみましょう。 ```ruby class BankAccount < ActiveRecord::Base before_save EncryptionWrapper.new("credit_card_number") after_save EncryptionWrapper.new("credit_card_number") after_initialize EncryptionWrapper.new("credit_card_number") end class EncryptionWrapper def initialize(attribute) @attribute = attribute end def before_save(record) record.send("#{@attribute}=", encrypt(record.send("#{@attribute}"))) end def after_save(record) record.send("#{@attribute}=", decrypt(record.send("#{@attribute}"))) end alias_method :after_initialize, :after_save private def encrypt(value) # Secrecy is committed end def decrypt(value) # Secrecy is unveiled end end ``` このように`before_save`や`after_save`等のコールバックに対応するメソッドをパブリックメソッドとして定義しておけばOKです。引数にはコールバックを呼び出す切っ掛けになったオブジェクトが渡されます。 一つのモデルから分離することで、汎用的な処理を複数のクラスで再利用できるようになります。 こういった記述をする利点は他にもいくつかあります。 まず、機能に明確な名前と境界が付くことです。 一つのモデルにまとめて記述されていると、色々なものの中に埋もれてしまい、どういう時に利用されるメソッドなのかはっきりせず後から変更する時に悩む要因になります。 境界が明確になっている事で、後から見ても用途を把握しやすくなります。 また、テストが容易になるのも利点の一つです。 コールバックの起動を単なるpublicメソッドの呼び出しとして行えるため、テストコードで検証する際に無駄なトリックを使う必要が無くなります。 また、このクラスはActiveRecordのオブジェクトに縛られずにテストする事ができます。 このクラスをテストするのに必要なものは、適切なインターフェースを供えたオブジェクトであれば何でも良いのです。RSpecの`double`や`mock_model`等で、ダミーのオブジェクトは簡単に高速に準備することができます。 もし、最初のサンプルコードのようにモデル内のprivateメソッドとして定義していたり、直接ブロックを渡すような記述をしていた場合、ActiveRecordのオブジェクトを実際に保存して、状態の変化を確かめる、という面倒なテストが必要になってしまいます。そうなってくると他のバリデーションやコールバックが影響する可能性もあって非常に面倒です。(もしくはprivateメソッドを無理やり呼び出すとか) このように、コールバックに別のクラスを利用するのは単なるクラス定義だけで良いので非常にお手軽で、上手く分類できるとモデルがかなりすっきりします。 ## Validatorクラス コールバックとバリデーションは非常に似た記述方法を取ります。 そしてバリデーションにもコールバック同様、クラスを分割する仕組みがあります。 Validatorクラスを利用するには`validates_with`メソッドと`ActiveModel::Validator`クラスを利用します。 例えば、会議室の利用予約機能を作るとして、予定が他の予定と重なったらバリデーションエラーになる、という仕組みを考えてみます。 ```ruby # started_at: timestamp # finished_at: timestamp class Schedule < ActiveRecord::Base belongs_to :room validates_with MustNotOverlapValidator end class MustNotOverlapValidator < ActiveModel::Validator def validate(record) overlapped_schedules = Schedule .where(room_id: record.room_id) .where("finished_at > ?", record.started_at) .where("started_at < ?", record.finished_at) .where.not(id: record.id) if overlapped_schedules.exists? record.errors.add :base, "Schedule must not overlap on other schedules" end end end ``` こんな感じで、`ActiveModel::Validator`を継承したクラスを作り`validate`メソッドをオーバーライドします。 利用するモデル側からは、`validate_with`の引数としてValidatorクラスのクラス名を指定します。 Validatorクラスには一つ注意点があります。Validatorクラスは通常一度しかインスタンス化されず、そのオブジェクトをずっと使い回すことになります。変にインスタンス変数とかを利用するとずっと残ってしまうので利用する場合は、意図せず変更されないように注意しましょう。 ValidatorクラスもCallbackクラス同様、テストが容易になり責務がはっきりする利点があります。 Validatorクラスは`ActiveModel`の仕組みに依存しているので、バリデーション対象のオブジェクトがテスト時に必要になりますが、特定の検証ロジックを単体で呼び出してテストすることが簡単になりますし、境界がはっきりするのでモックを利用しやすくなります。 ちなみにテスト時にValidatorクラスのインスタンスを作る時には、コンストラクタに引数としてハッシュを渡しておく必要があります。これは`ActiveModel::Validator`クラスのコンストラクタが引数としてオプションの設定値を取るためです。 ## まとめ こんな感じで、Railsが提供している機能を活用すれば、モデルの責務をより細かく分割することができます。 利用するカラム値や関連等を上手く抽象化すれば再利用性も高まっていい感じです。 ただ、何でもかんでも分割すれば良いというものじゃないのが難しい所です。 ロジックがに無軌道にあちこちに散らばってしまうと、それはそれで全体像を把握することが難しくなります。 大事なのは名前がちゃんと付けられるか考えるということです。(ネームスペースどうする、suffixどうする、とか大分悩ましいですが) 一つのクラスとして独立してテストコードを書きたいかどうか、というのも私の中では指標の一つとして活用しています。 サンプルコードだと良い使い所を上手く表現できないのですが、継ぎ足し継ぎ足し開発していると結構色々あるものなんですよ。 なので、覚えておいて損は無いんじゃないでしょうか。 他にもモデル層を整理する考え方やテクニックは色々あります。 この記事で紹介したようなコールバックという体裁を取るよりも、トランザクションを一つにまとめるサービスクラスという形を取って明示的に呼び出した方が柔軟で扱いやすくなる場合もあります。 Advent Calendarの予告コメントを見ると、そういった記事も出てきそうなので引き続きのAdvent Calendarに期待してます。 ## おまけ 今までRailsにpull reqがマージされたこと無かったんですが、この記事書いてたらCallbackのサンプルコードのバグに気付いたので修正してpull reqを送った所、速攻でマージされて初コントリビュートが出来ました。 Advent Calendarに感謝です。 |
|
| 38位 |
|
|||
|
14:30:48 |
(COOKPAD Inc. 所属) |
|
Vagrant と Chef Solo ってとてもベンリそうに見えてたのですが、ネット上にあるのは断片的な情報が多かったり、そもそもいろんなやり方があって混乱してたので、サックリ始めるためのチュートリアルを書きました。これをきっかけにベンリな Vagrant ライフを堪能して頂ければ幸いです。
[追記10/10/2013] Window 上の Vagrant でも問題なく動きました。ただ1点注意があって、UAC のポップアップに反応しないと、Vagrant か VirtualBox 側でタイムアウトになってしまうので、ポップアップを見張るか、放置したいなら一時的に無効にしておくとよいです。 [/追記終わり] [追記 10/23/2013] VirtualBox 4.3 だとまだうまく動かないようです(私も host-only adapter の作成で VirtualBox 側のエラーになりました)。https://github.com/mitchellh/vagrant/issues/2392 なので https://www.virtualbox.org/wiki/Download_Old_Builds から 4.2 系をインストールすると良いです。 [/追記終わり] [追記 12/15/2013] フォロワーさんに教えてもらったのですが、VirtualBox 4.3 のサポートきました! http://docs.vagrantup.com/v2/virtualbox/index.html まだぼくは試してないのですが、ひとまず情報をアップデートしておきます。Thanks to @cosmo__ !! [/追記終わり] ## 3行のまとめ * Vagrant + Chef Solo + Berkshelf でいこう。プラグインは vagrant-berkshelf と vagrant-omnibus と sahara * http://berkshelf.com/ と http://docs.opscode.com/#id3 と http://docs.vagrantup.com/v2/ で全て解決 * Cookbook の README ベンリ。そこに情報なかったら `attributes` と `recipes` 以下を読むとその Cookbook 大体把握できる ## おすすめの進め方 もっとも良い進め方は **"入門Chef Solo - Infrastructure as Code"** を読んでからこのチュートリアルを始めるのが良いと思います。 ですが、いろいろと説明不足感ありますが簡単に試せるので、ひとまずこのチュートリアルを先に体験して、その後に Chef ってなんだろ?っていうのを解決する方法も悪くないようにも思えます。 わからないまま進めるのが嫌な人は Chef Solo のチュートリアルをしてから、とりあえず試してみたい人はこのチュートリアルから、という分け方になるやもしれないです。 あと今回のチュートリアルで使ったファイルを github に置いておきます。「これどこにかくんや!」ってなった時にどうぞ。 https://github.com/taiki45/vagrant-tutorial では、チュートリアル本編です☆ # チュートリアル〜 ## Vagrant Virtual Box のような仮想化ソフトウェアを便利に扱うツールです。 どんなご利益があるのか興味あると思うのですけど、公式的には「自由に使えて、ポータブルで、持続的で安定した環境を開発者に提供する」という風になってます。サイコーですね!「僕の環境なら動くんだけど…」っていうバグとはオサラバです! 個人的にはさらに、設定ファイルが柔軟に書けたり、仮想化ソフトウェアを抽象化するインターフェイスを提供していて、プラグインを書くことでそれらを使えるので、便利なんだと思います。Vagrant が他のツールをうまくまとめてくれる感じです。 Vagrant は特に難しいことはないので1つ VM を起動してみて感じをつかんでみましょう。 ### インストール * まずは Vertual Box をインストールします。 https://www.virtualbox.org/ * Vagrant をインストールします。 http://www.vagrantup.com/ `vagrant -v` が成功しました?さっそく VM を作りましょう! ### 初めての up まずは設定ファイルを書かなくてはなりません。そのための便利なコマンドがほら! ```sh vagrant init "Debian7" "https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-7.1.0_provisionerless.box" ``` カレントディレクトリに `Vagrantfile` が出力されたと思います。さっそく見てみましょう! ```ruby:Vagrantfile Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| #… #… end ``` こんな感じに Ruby のブロックを使ってその中で設定を書くようになってます。Ruby で書くので独自に `config.yml` とかを作ってそれを読む込むとかもできますが、まあ最初はいらないと思います。 さっきの `vagrant init` コマンドに渡した引数はここに反映されています。 ```ruby:Vagrantfile Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "Debian7" config.vm.box_url = "https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-7.1.0_provisionerless.box" end ``` フー、box ってなんのことだ?この box というのは(程度の違いがあるけれど)最低限の構成の VM のスケルトンのことです。ここでは Opscode という Chef を開発したりしている企業の box リポジトリからひとつを指定しています。この名前の box がローカルにある場合は新たにダウンロードせずにローカルの box を使いますし、なければ URL から引っ張ってくる、ってワケですね。ちなみにすでにダウンロードした box ファイルは `vagrant box add` コマンドで追加したりできます。 なが〜い説明もおわったことだし、いよいよ立ち上げてみましょう! ```sh vagrant up ``` なが〜いなが〜いダウンロードは終わりましたか?おつかれさまです☆ では VM の中に入っていきましょう! ```sh vagrant ssh ``` ヒュ~、クール。簡単でしたね。 ### Shared folders Vagrant は標準で Shared folders が設定されています。デフォルトだと Vagrantfile があるディレクトリと VM 上の `/vagrant` が同期されています。試してみましょう。 ```sh # ホストマシン上で touch created_on_host vagrant ssh # VM 上で cd /vagrant ls touch created_on_vm exit # ホストマシン上で ls ``` ### gitignore Vagrant を使ったプロジェクトを VCS で管理する場合 `.vagrant` ディレクトリを ignore するようにするのが(たぶん)おすすめです。 ### Vagrant まとめ Vagrant は基本的に仮想化ソフトウェアを便利に扱うツールです。そこから先の Provisioning といってサーバーに必要なものをいれたり設定するのは Chef とかシェルスクリプトとか別のツールの役割です。そのあたりをうまく統合してくれるから強力なツールになるのですね。 ということで Vagrant に関して最初に知ることはこれだけです。とってもシンプルです。これからこのまっさらな VM を調理していく方法を紹介しますよ! ## Chef Solo 実際に使う Chef Solo についてお話する前に Chef についてお伝えしなければなりませんね。 ### Chef って? Chef は複数台のサーバーの構成を管理するためのツールです。1台の Chef サーバーと呼ばれるマスターノードみたいなのがいて、他のサーバーはその Chef サーバーを定期的に見に行くことで最新のサーバー構成を知り自身に適用していきます。入門 Chef-solo によると Facebook は数万から数十万台規模のサーバーを Chef で管理しているようです。となると Chef サーバー(マスターノード)は1台以上でもいけるのかもしれませんね。 Chef を使う上でお世話になるのが `knife` コマンドです。この knife は実際にレシピのひな形をつくったりとか、レシピをあるサーバーに適用したりとか、Chef が実際に調理する時の道具です。Chef という仮想の調理師が knife を振り回すことで料理(サーバー)ができる、そんなイメージですね。 さて次にたくさんある Chef の要素を必要なものだけ見ていきます。まずは構築手順のパッケージとして **Cookbook** というのがあります。概念としては RubyGems での `gem` とかと似てます。この Cookbook の中に実際に実行される **Recipe** が1個以上入っています。覚えるものはまずはこの2つで大丈夫です。実際には実行手順の汎用性を高めるために様々な要素が Chef にはあるのですが、全てハードコーディングして書けば最初はこれ以上いりません。 * **Chef はサーバー/クライアントモデルで複数台のサーバーを管理する** * **実際に使うコマンドは `knife`** * **同じ領域の実行手順をまとめたものを Cookbook と呼ぶパッケージにまとめる。実際に実行されるのはその中の Recipe** ### Chef Solo このように本来は複数台のマシンでサーバー・クライアントモデルで使う Chef ですが、1台でも使えるように Chef Solo というコマンドがついてきてます。この Chef Solo は自分自身に対して Cookbook と呼ばれる構築手順を実行することができます。 * **Chef Solo は自分自身に対してのみサーバー構築手順を実行する Chef のサブセット** ## Vagrant + Chef Solo Vagrant はプロビジョニングツールとしていくつか選べるのですが、Vagrant + Chef Solo の場合、 1. Vagrant が VM を立ち上げる 2. Cookbook を VM に転送する 3. VM 上で Chef Solo を実行する という手順になっています。 ## もっと便利に、Berkshelf + Chef Solo + Vagrant Chef にはいくつかの抽象化するための要素があり、構築手順の汎用性を高めることができます。つまり1つの Cookbook をいろいろな OS、いろいろな設定に使いまわすことができるのです。 そこで、いろんな人が作った Cookbook を使いまわすために RubyGems に対する Bundler のような Cookbook 管理ツールがあります。それが Berkshelf です。 http://berkshelf.com/ この Berkshelf は Vagrant Plugin も開発されていて Vagrant 連携もバッチリです☆ Vagrant にプロビジョニングツールとして Chef Solo を使う場合いくつかやり方があるのですが、おすすめするのが Berkshelf を足したやり方です。 ### vagrant-omnibus 他の人が作った Box に chef が入っていなくて Chef Solo が走らない!っていうのはよくあることです。これを回避するために `vagrant up` 時に Chef が入っているか確認してインストールしてくれる Vagrant プラグインが vagrant-omnibus です。 https://github.com/schisamo/vagrant-omnibus README 通りにインストールして ```ruby:Vagrantfile Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # ... config.omnibus.chef_version = :latest # ... end ``` と書き加えればすぐに動きます。ベンリ! ## Berkshelf + Chef Solo とチュートリアル再開 さてこれで道具の紹介は終わったので実際にレシピを書いていきましょう。まずはインストールですね☆ ```sh vagrant plugin install vagrant-omnibus vagrant plugin install vagrant-berkshelf gem i berkshelf ``` とっても簡単ですね。 では Helloworld をしましょう! Berkshelf を使うには `Berksfile` というファイルが必要です。`Gemfile` のようなものです。さきほどの `Vagrantfile` と同じディレクトリに配置しましょう。 ```ruby:Berksfile site :opscode ``` これは `Gemfile` でいうところの `source "https://rubygems.org"` と同じようなもので特にソースを指定しない場合は opscode からダウンロードします〜という意味です。 さて次に Cookbook を置くためのディレクトリの説明です。 詳しい作法をぼくはまだ良くわかってないのですが、汎用的な Cookbook を置く場所が `cookbooks` で、その環境構築固有の Cookbook を `site-cookbooks` に置くことが多いようです。`berks init` をしてみても `cookbooks` は git ignore の対象になったりしています。なのでその作法に従います。 ということで `site-cookbooks` というディレクトリをつくりましょう。おつぎに hello という Cookbook をつくります。 ```sh cd site-cookbooks berks cookbook hello cd - ``` なにやら `hello/` 以下にいろいろ出力されたと思いますが、重要なのは `hello/recipes/default.rb` だけです。編集しましょう。 ```ruby:hello/recipes/default.rb log "Hello World!!!!!!!!!!!!!" ``` 次に `Vagrantfile` と `Berksfile` に Berkshelf と vagrant-berkshelf プラグインのための設定を書いていきます。 ```ruby:Vagrantfile Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # ... config.omnibus.chef_version = :latest config.berkshelf.enabled = true # ... end ``` ```ruby:Berksfile site :opscode cookbook "hello", path: "site-cookbooks/hello" ``` 最後に Chef Solo の設定を `Vagrantfile` に書き足します。 ```ruby:Vagrantfile Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # ... config.vm.provision :chef_solo do |chef| chef.run_list = ["hello"] end end ``` 最終的に `Vagrantfile` は次のようになります。 ```ruby:Vagrantfile # -*- mode: ruby -*- # vi: set ft=ruby : # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "Debian7" config.vm.box_url = "https://opscode-vm-bento.s3.amazonaws.com/vagrant/opscode_debian-7.1.0_provisionerless.box" config.vm.network :private_network, ip: "192.168.33.10" config.vm.provider :virtualbox do |vb| vb.gui = false vb.customize ["modifyvm", :id, "--memory", "1024"] end config.berkshelf.enabled = true config.omnibus.chef_version = :latest config.vm.provision :chef_solo do |chef| chef.run_list = ["hello"] end end ``` それではお待ちかねの `vagrant up` をしましょう! まずは最初に立ち上げた VM を捨てて新しく up します。 ```sh vagrant destroy ``` `y` と答えて無事デストロイされればお次は up です。 ```sh vagrant up ``` おそらく Chef のインストールにそれなりに時間がかかることと思います。VM の起動 -> Chef のインストール -> Chef Solo の実行、とうまく動けば成功です。ログに `Hello World!!!!` って表示されましたか?うんうん、表示された?やりましたね! ## MySQL をインストール☆ 最後にコミュニティの Cookbook を使ってみましょう。 https://github.com/opscode-cookbooks/mysql 他の人が作った Cookbook を使う時のコツは README を読んで、そのあと `attributes/` と `recipes/` 以下を読むことです。この2つが Chef Solo では大きなキーです。 まずは README の `Attributes` の欄を読んでみます。大体ここは Chef 実行時に自分用に書き換えれる設定の一覧になってます。もし README に情報がなければ `attributes` 以下のファイルを直接読みましょう。OS 毎に設定を振り分けてるくらいであんまり難しくないです。 なんとなく設定項目がわかったら `Usage` を読みます。Opscode の MySQL Cookbook は親切にも Chef Solo の時のメモを書いてくれています! さて大体のことはわかったと思いますので実践してみましょう。 ### 設定ファイルカキカキ まずは Berkshelf に MySQL Cookbook を使うってことを教えましょう。`Berksfile` にこう書き加えます。 ```ruby:Berksfile cookbook "mysql" ``` これだけで Opscode のサービスから Cookbook をインストールしてくれます。インストール先はデフォルトだと `~/.berkshelf` になるようです。 ではおつぎに Attributes の指定をします。この Attributes は Chef 本来のサーバー/クライアント運用であれば JSON 形式でサーバーからクライアントに渡されるものなので Vagrant-Chef でも `chef.json` という名前になっているようです。Hash で Attrubutes を設定していきます。 ```ruby:Vagrantfile config.vm.provision :chef_solo do |chef| chef.run_list = [ "hello", "mysql::client", "mysql::server" ] chef.json = { mysql: { server_root_password: "iloverandompasswordsbutthiswilldo", server_repl_password: "iloverandompasswordsbutthiswilldo", server_debian_password: "iloverandompasswordsbutthiswilldo", bind_address: "127.0.0.1" } } end ``` ついでに Chef の `run_list` にも実行するレシピとして mysql レシピを追加しています。これは Opscode の MySQL Cookbooks の `recipes/` 以下を見てもらえるとわかるのですが、このように `run_list` を設定すると `recipes` 以下の対応する名前のレシピが実行されます。Cookbook の名前のみを指定した場合(ここでは `hello` がそれ)、その Cookbook の `recipes/default.rb` が実行されます。 では VM を調理しましょう。えいや! ```sh vagrant provision ``` オオー、上手に焼けました!さてさてうまくインストールされたか確認しましょう。 ```sh vagrant ssh # VM 上で mysql -uroot -piloverandompasswordsbutthiswilldo ``` MySQL サーバーにつなげたでしょうか?このようにして、コミュニティの Cookbook を有効活用できれば VM 構築がもっと楽になりますよ! ## ボクタチのこれから くぅ~疲れましたw これにて完結です! たぶん、この分量だといろいろ足りなかったとおもいます。なので学習のポインタをカキカキさせて頂いておわりにします。 Vagrant について * 公式サイトが良いです。 http://docs.vagrantup.com/v2/ * ベンリ Vagrant プラグイン https://github.com/jedi4ever/sahara Chef について * Docs http://docs.opscode.com/ の Cookbooks あたりを読むと必要な情報が手に入ります。 * まずは Resources を覚えるところからがいいと思います。 http://docs.opscode.com/resource.html Berkshelf について * 公式サイトが良いです。 http://berkshelf.com/ * `:path` 指定してローカルの Cookbook を参照できたように `:git` とか `:github` とかのオプションを渡すとそこの Cookbook を参照できます。ベンリ! Ruby のリテラルについて * たぶん Ruby を知らない人は%記法にびっくりすると思うので http://doc.ruby-lang.org/ja/2.0.0/doc/spec=2fliteral.html#percent Cookbook について * https://github.com/opscode-cookbooks にベンリ Cookbook がある。 * なにかしたいことがあれば "したいこと cookbook" とかでググればわりと出やすい。その場合 github に置かれていることが多いので Berkshelf の `:github` オプションが役に立ちます。 **それでは楽しい Vagrant ライフを!** |
|
| 39位 |
|
|||
|
19:12:28 |
(私立リリアン女学園高等部 所属)
|
|
このテキストは JavaScript のコールバック地獄に疲れたひとのためのコールバック駆逐術指南書です。対象読者は JavaScript道初段くらいの人です。このテキストを読むと、以下のそれぞれの手段における非同期処理制御の仕組み、利点および欠点がわかるようになるかもしれません。
* コールバック地獄 * jQuery.Deferred * async.js * Concurrent.Thread * generators * co * fibers * Web Workers (※なぜか『進撃の巨人』の一部ネタバレが含まれるので注意してください) ## それは『何故人はコールバックするのか』という話でしょうか? 非同期処理って面倒ですよね。JavaScriptではいわゆる **コールバック地獄** というやつにしばしば陥りがちです。たとえば、Ajax でふたつのファイル hoge.txt と piyo.txt を持ってきて、それらを結合してコンソールに出力したいとします。もし同期的にリモートのファイルを読み込む関数 `get` があるとしたら、次のように書けるはずです。 ```js: var hoge = get("hoge.txt"); var piyo = get("piyo.txt"); console.log(hoge + piyo); ``` でもそんな同期的な関数は JavaScript にはありません(XMLHttpRequest#open には同期的に読み込むオプションもありますが、パフォーマンス上の問題があることから使用は勧められません。Nodeにも同期的なファイル読み込み関数 `fs.readFileSync` がありますが、XHR と同様の理由であまり使用は勧められません)。たとえば、jQuery で非同期でファイルを取得する関数 `$.get` を使ったとしても、素直に書くと次のような悲惨なことになるわけです。 ```js: $.get("hoge.txt", function(hoge){ $.get("piyo.txt", function(piyo){ console.log(hoge + piyo); }); }); ``` もし非同期だけど直列に処理したいモノが3つ、4つと増えていったとすると、それにしたがってさらにネストは深くなっていきます。あまりネストが深くなるとソースコードが読みづらいので、自分などはたまにネストを押しつぶして次のように書いたりします。 ```js: // こういうパターンを見たら、心のなかで var hoge = $.get("hoge.txt") と読み替える $.get("hoge.txt", function(hoge){ // なんだか var piyo = $.get("piyo.txt") に見えてきたぞ……? $.get("piyo.txt", function(piyo){ // どうみても var nyan = $.get("nyan.txt") ですね $.get("nyan.txt", function(nyan){ // var myon = $.get("myon.txt") のどこにコールバックが? $.get("myon.txt", function(myon){ console.log(hoge + piyo + nyan + nyan); });});});}); // ←見なかったことにしよう!(^o^)b ``` JavaScripter は5重、6重のネストが書けるようになって一人前。熟練した JavaScript 職人は、息をするように20重や30重のネストを書きます。いやそんなことはないです。そして、本当の恐怖はエラー処理をちゃんと書くときに始まります。同期的な処理なら try/catch で囲めばまるごと例外を捕獲できますが、こういう非同期処理だといちいち個別に例外処理しなければなりません。なんとかならないものでしょうか。 ## 有害なコールバックを駆除した!!たまたまjQuery.deferredと恰好が似ていただけだ!! こうした非同期処理の煩雑さを解決するために実に数多くの手段が考えだされてきましたが、その手段は大きくわけて2つあると思います。 * ネストが深くならないように、コールバックの構造を変形する(jQuery.Deferred, async.js など) * スレッド/ファイバー/コルーチンで処理の流れを複数つくり、同期処理する(generators, fibers, Web Workers など) 前者の方法はあくまで非同期処理であり、現在の JavaScript でも実現可能な方法です。現在ごく一般的に行われているコールバックを駆使する方法の延長線上にあるので比較的理解しやすいでしょう。ただし解決は部分的であることがほとんどで、同期処理ほどの読みやすいコードが実現できることは稀です。 それに対し、後者の方法は現在の JavaScript 環境にまったく新しい機能を追加し同期処理できるようにするというものなので、非同期処理にまつわる煩雑さはほとんど解決しますが、使える環境がまだまだ限られることがほとんどです。また、これまで同期処理ができなかった部分が同期処理できるようになったとしても、その同期処理どうしが並列に処理されるというような複雑なケースも発生しうることがあり、同期処理と非同期処理が混在していてもわかりやすくコントロールする手段はやはり必要になります。 本テキストでは主に前者のカテゴリの手段についてどのようなものであるかを検討していきます。ただし、後者の手段を以ってしも非同期処理の煩雑さは残されることがあり、そういった残された問題をどう解決するかも含めて包括的に検討していきます。 まずは前者のカテゴリの手段を幾つか見ていきましょう。JavaScript の有名なライブラリといえば jQuery はまっさきに挙げられるもののひとつですが、jQuery にもこうした非同期処理の煩雑さを解消しようとする機能、 [jQuery.deferred](http://api.jquery.com/category/deferred-object/) があります。ちまたで話題の jQuery.deferred を使えば、さっきみたいなネスト地獄のコードもきっとおしゃれに書けるはず……! ```js: var hoge; $.get("hoge.txt") .then(function(_hoge){ hoge = _hoge; return $.get("piyo.txt"); }) .then(function(piyo){ console.log(hoge + piyo); }); ``` えっ……確かにネストは増えていかないけど、なんかむしろキモさが増してる気が……!だいたい、いったんコールバックの外の変数に待避しないと次のコールバックで参照できないとか、なんかもう根本から破綻してるとしか思えません。これならさっきのネスト地獄のほうがまだわかりやすいレベル。 は◯ブのほうで『 jQuery.deferred の例のコードがキモいのは、お前の使い方が悪いだけじゃね?』(意訳)という声を見かけたので、もう少し詳しく検討してみます。jQuery.deferred の圧倒的実力を見せつけてやろうではないですか。 上記の例では、`$.get("hoge.txt")` の結果が `then` のコールバックの引数 `hoge` に渡されてくるわけなのですが、このコールバックを抜けるから `hoge` にアクセスできなくなって、いったん外の変数に退避しなければならなくなったわけでした。したがって、これを抜ける前に続けて `$.get("piyo.txt")` の結果のほうまで受け取ってしまえばいいのではないでしょうか。コードは次のようになります。 ```js: $.get("hoge.txt") .then(function(_hoge){ return $.get("piyo.txt").then(function(piyo){ console.log(hoge + piyo); });; }); ``` 無駄な退避用の変数がなくなりました!…… **って、これさっきのコールバック地獄じゃないですか** 。使うファイルが増えるとどんどんネストが深くなっていきます。コールバック地獄を逃れようとして jQuery.deferred にすがったのに、これではあんまり意味がありません。 まだだ……まだ終わらんよ……!あの jQuery がこの程度の実力なわけがない!そうだ!最後にまとめてそれぞれの結果にアクセスしようとするのが良くないのでしょう。このように順番に処理していく場合、直前の処理から受け取れるのは基本的に1個まで。今回の目的だととにかく最終的に全部つながった文字列が得られればいいのだから、それぞれの `$.get` が終わったその時点で、それまでの文字列を積算してしまえばいいのです。つまり、あらたに次のような Deferred を作る関数 `accum` を定義しましょう。他人に与えられた deferred を使うだけなんて素人のやること、オリジナル Deferred を作ってこそ真の jQuery 使いといえます。 ```js: function accum(path){ return function(previous){ var def = new $.Deferred(); $.get(path).then(function(data){ def.resolve(previous + data); }); return def.promise(); } } $.get("hoge.txt") .then(accum("piyo.txt")) .then(accum("pyon.txt")) .then(accum("nyan.txt")) .then(function(data){ console.log(data); }); ``` よし、これで OK! ネストも深くなっていかない!素晴らしい!……え?今度は piyo.txt のテキストだけ、全部大文字にしてくれだって?`accum` ではそんな処理はできません。じゃあまた新たにファイルを読み込んでそれを大文字にする deferred を定義して……。 め ん ど く さ い。同期処理なら `hoge + piyo.toUpperCase() + pyon + nyan` で済むのに、なんでちょっと処理が変わるたびに新たな deferred を定義しなければならないのでしょうか。じゃ、じゃあ、全部配列にして送ってしまえばいい! ```js: function accum2(path){ return function(array){ var def = new $.Deferred(); $.get(path).then(function(data){ array.push(data); def.resolve(array); }); return def.promise(); } } var def = new $.Deferred(); def.then(accum2("hoge.txt")) .then(accum2("piyo.txt")) .then(accum2("pyon.txt")) .then(accum2("nyan.txt")) .then(function(array){ console.log(array[0] + array[1].toUpperCase() + array[2] + array[3]); }); def.resolve([]); ``` **って配列かよ!** hoge とか piyo とかの名前はどこにいったんだよ!`array[3]` とか言われてもどのファイルの中身なのかさっぱりわからんよ!ちゃんと意味のあるデータを適当に配列に突っ込んじゃいけないって、プログラミング初めて習った時に口酸っぱくいわれたよ! そうか、直列に処理するからいけないんだ。`$.when` を使って並列に処理すれば……。 ```js: $.when($.get("hoge.txt"), $.get("piyo.txt")).then(function(hoge, piyo){ console.log(hoge[0] + piyo[0]); }); ``` うーん。さっきよりはずっとマシになったかな。なんで結果の `hoge` と `piyo` が配列になってるの?`[0]`とかなんなん?というのはまあ目をつぶるとして、`$.get("hoge")` と `hoge` がやけに離れた位置になってしまいました。同期的に書いていた時には `var hoge = get("hoge");` みたいに一行ごとにまとまっていたのに……。もし必要なファイルが増えていくと、さらに離れ離れになっていって、どれとどれが対応しているのかもはやわからなくなってしまいます。同じような処理を追加しているのに、やけに離れた位置を2箇所づつ編集することになります。 ```js: $.when( $.get("hoge.txt"), $.get("piyo.txt"), $.get("nyan.txt"), // 増えた $.get("myon.txt"), // 増えた $.get("pong.txt"), // 増えた $.get("chun.txt") // 増えた ).then(function( hoge, piyo, nyan, // 増えた pong, // 増えた myon, // 増えた chun // 増えた ){ console.log(hoge[0] + piyo[0] + nyan[0] + pong[0] + myon[0] + chun[0]); }); ``` これだとうっかり非同期処理とその結果を代入する変数の順番を間違えるようなこともありそうです。上記のコード、実は `myon` と `pong` の変数の順序が入れ替わっています。お気付きになったでしょうか。気付くわけないですよね。 これが同期的だったら、次にように一行づつ編集して増やせるのに……。非同期処理とその結果を代入する変数が一行ごとにまとまっているので、jQuery.Deferred のものより遥かに読みやすく書きやすいと思います。 ```js: var hoge = get("hoge.txt"); var piyo = get("piyo.txt"); var nyan = get("nyan.txt"); // 増えた var pong = get("pong.txt"); // 増えた var myon = get("myon.txt"); // 増えた var chun = get("chun.txt"); // 増えた console.log(hoge + piyo + nyan + pong + myon + chun); ``` また、今回はたまたま hoge.txt の piyo.txt の読み込みは独立しているので `$.when` で並列に処理できますが、もし一方の処理がもう一方に依存している場合、たとえば hoge.txt には別のファイルのパスが書かれていて、次にそのパスのファイルを読まなければならない場合には並列には処理できません。その場合はさっきの書きづらいバージョンに戻るしかありません。 いろいろ検討してみましたが、ネストを増やさずに、しかもなるべく自然にそれぞれの非同期処理の結果にアクセスしようとすると、結局のところ外の変数に退避するのが一番手っ取り早いように思いました。もし何かもっといい方法があったら教えて下さい。自分には jQuery.deferred は使いこなせませんでした。でも俺が敗れ去っても、第二、第三の jQuery.deferred 使いが現れ、その真の実力を引き出してくれるでしょう。きっと。 ## async.jsは調子に乗りすぎた……いつか私が然るべき報いを 非同期処理のフローを制御するライブラリとしては、[async.js](https://github.com/caolan/async#parallel) というものもあります。async.jsでもjQuery.deferredと似たような形式では書けるのですが、async.jsではさらに次のようも書けたりします。 ```js: function get(path){ return function(callback){ $.get(path).then(function(data){ callback(null, data); }); }; } async.parallel({ hoge: get("hoge.txt"), piyo: get("piyo.txt") }, function(err, results) { console.log(results.hoge + results.piyo); }); ``` `parallel` は `jQuery.when` と同様に並列に非同期処理する関数なのですが、名前と処理のテーブルとしてオブジェクトを渡すことができ、並列処理後に呼ばれるコールバックの引数のオブジェクトには、同名のプロパティで結果が格納されています。おお!わりとおしゃれだ!非同期で得たデータがそれぞれ直接変数に代入されるんじゃなくて `results` というオブジェクトにまとめられてるのはまあ許すとして、なんといっても `hoge: get("hoge.txt")` っていうかんじで `hoge` と `get("hoge.txt")` がすぐとなりにあるのが(・∀・)イイ!! これなら処理がもっと増えていっても自然に追加できる! ```js: function get(path){ return function(callback){ $.get(path).then(function(data){ callback(null, data); }); }; } async.parallel({ hoge: get("hoge.txt"), piyo: get("piyo.txt"), nyan: get("nyan.txt"), // 増えた pong: get("pong.txt"), // 増えた myon: get("myon.txt"), // 増えた chun: get("chun.txt") // 増えた }, function(err, results) { console.log(results.hoge + results.piyo + results.nyan + results.pong + results.myon + results.chun); }); ``` だがちょっとまってほしい。`async.parallel` の型ってどうなっているんだろう?『 **はあ!?なにいってんのおまえ!?** 』とおっしゃるかもしれませんが、我々のような静的型付け過激派は動的型の JavaScript を書いている時でさえ型を意識するのです。そんな我々から見れば、こんな動的な API はまさしく粛清の対象。JavaScriptのようなヒトならざる魔性なら、他者の辛苦を蜜の味とするのも頷けます。でも、それは罪人の魂です。罰せられるべき悪徳です。 じゃあネスト地獄やjQuery.deferredはどうなんだよといいますと、実はネスト地獄スタイルやjQuery.deferredスタイルは一応ちゃんと静的型付けできるのです。試しにTypeScriptで型注釈をつけてみましょう(TypeScriptのステマ)。 ```ts: declare var $: { get(path: string, callback: (data: string)=>void): void; }; $.get("hoge.txt", function(hoge: string){ $.get("piyo.txt", function(piyo: string){ console.log(hoge + piyo); }); }); ``` ネスト地獄は型付けという意味に関しては完璧です。一部の隙もありません。で……いや、やっぱりjQuery.deferredは静的型付けできませんでした。`$.when` に複数の引数を渡した場合、何をトチ狂ったのかコールバック関数の引数に `[data, textStatus, jqXHR]` という感じで全然違う型のオブジェクトをひとつの配列に押し込んで返してくるのが原因です。これが配列じゃなくてオブジェクトに入れて返してくるならちゃんとした静的型付けできるのですが……。自分に言わせれば、これはスタイルの問題というより単に jQuery の設計ミスだと思います。従って、jQuery.deferredスタイルも本質的には静的型付け可能、ということにしておきます。 それに対して、先ほどの `async.parallel` の場合では事前にどんなオブジェクトが渡されるかわかりませんから、どうにも静的型付けはできません。やっぱりネスト地獄やjQuery.deferredのほうがマシだったんだ!……いや、やっぱりどっちもどっちです。 ## 俺にはわかる、Concurrent.Threadは本物の化け物だ……『コールバック』とは無関係にな。どんな力で押さえようとも、どんな檻に閉じこめようとも、コイツの意識を服従させることは誰にもできない……! [Concurrent.Thread](http://sourceforge.net/apps/mediawiki/jsthread/index.php?title=Main_Page) はJavaScriptで擬似的なマルチスレッドを実現するライブラリです。このライブラリでも `yield` ができるようですから、コールバック地獄の回避にも応用できそうです。どのようにして擬似的なマルチスレッドを実現しているかというと、JavaScript のソースコードをパースしてぶつ切りにし、setTimeout で適宜制御を譲りながら交互に実行することで擬似的なスレッドを構成するというもの。その発想がないわけではなかったが、まさか本当にやるとは思わなかった、という感想しかでてこない豪快な方法です。Web 上にはあまり情報がありませんが、作者自身による解説記事 [JavaScriptによるマルチスレッドの実現‐Concurrent.Threadの裏側](http://www.infoq.com/jp/articles/js_multithread_2) が参考になります。 Web 上で閲覧できるサンプルとしては[Concurrent.Thread example](http://jsdo.it/__gfx__/thread-example/)があります。一応Concurrent.Threadが吐き出したコードも眺めてみましたが、とてもヒトに理解できるコードではなかったので内部の理解は諦めました。なんかもうそこまでするなら、俺ならベースの言語として JavaScript じゃなくて静的型付けできる他の言語をベースに選ぶけど……。 環境を選ばずどこでも使えるのはメリットです。独自の形式に変換するので、デバッガがまともに使えないことが最大の欠点でしょうか。トラブルが起きた時の原因究明は絶望的です。JavaScript のパースも行うので、JavaScript の新しい機能は使えないでしょう。残念ながら、更新も止まっているようです。すごいライブラリには違いないのですが、あくまで研究用だと思っておくのがよさそうです。 ## 私の特技はコールバックを削ぎ落とすことです…必要に迫られればいつでも披露します…私のgeneratorsを体験したい方がいれば…どうぞ一番先に近づいて来てください さて、ここからは JavaScript に根本的に機能を追加して解決をはかる手段です。これらは同期的な処理を複数同時に行う、つまりマルチスレッディングを可能にしようというものですが、マルチスレッドもいくつかの種類にわけられます。 1. メモリ空間を共有し、プリエンプティブに実行される古典的マルチスレッド 2. プリエンプティブに実行されるがメモリ空間を完全に分離する『プロセス』 3. メモリ空間を共有し、各スレッドが独自の判断で制御を譲る協調的マルチスレッド 1 は効率よく実行できるので昔からよく使われてきましたが、デッドロックなどマルチスレッド特有の問題が多く発生しやすく非常に扱いが難しいので近年では直接使われることが少なくなっているように思います。Java や C# などにあるスレッドはこれで、排他制御をしそこねると非常にわかりにくいバグになります。 2 は安全に複数のスレッドを実行できるのでもうすぐ JavaScript にも Web Workers という形で導入されることが決まっています。Erlang などにも『プロセス』として言語に組み込まれていることで知られていますし、OS レベルで提供されるプロセス間通信もこれに近いものです。ただし、プロセス間でデータを直接共有できないので、プロセス同士のメッセージングが煩雑になったり効率が悪化しやすいです。 3 は generators として JavaScript にも導入される予定で、各スレッド(?)でデータを共有でき比較的安全で軽量である反面、各スレッドが独自の判断で制御を交代するのでスレッドの流れについてよく把握しておかないとうまくコードを書くことができません。 また、いずれの方法を取るにしろそれぞれのスレッドがまったく独立して動くわけではないので、常に同期処理と非同期処理をうまく混在させていかなくてはなりません。マルチスレッドを導入して同期処理が増えていったしても、非同期処理がなくなるわけではないのです。以下のいくつかの項ではかつて非同期に処理してきたものを同期で処理する方法を示しますが、その同期処理どうしをどのように並列して非同期に協調させるかということについてはほとんど述べられていません。そのような残された非同期処理については、先ほど触れた jQuery.deferred や async.js のようなフレームワークを利用して解決していくことになるでしょう。 まずは次期の JavaScript で導入される **[generators](http://wiki.ecmascript.org/doku.php?id=harmony:generators)** という機能をみていきます。generator があれば、 **yield** というキーワードを使って次のように書けるようになるみたいです(`get` は generator で使えるように適当に定義されているものとします)。 ```js: var generator = (function*(){ var hoge = yield get("hoge.txt", generator); var piyo = yield get("piyo.txt", generator); console.log(hoge + piyo); })(); generator.next(); ``` このコードではあくまで直列にファイルを操作しています。ここでいう『直列』というのは、このコードでは hoge.txt を読み込んで、 **それが完了したら** piyo.txt の読み込みを開始しているということです(ちなみにこういうのを『直列』と呼んでいるのはたぶん俺だけです)。でも、この場合別に hoge.txt と piyo.txt の読み込みは並列に行なって構いません。piyo.txt の読み込みをするときにいちいち hoge.txt の読み込みを待っていたら遅くなってしまいます。並列に処理したい場合は次のような感じになるでしょう。くわしくはもっと下の方の完全なコードを参照してください。 ```ts: var generator = (function*(){ var x = get("hoge.txt", generator); var y = get("piyo.txt", generator); yield; yield; console.log(x() + y()); })(); generator.next(); ``` 並列で処理した結果を受け取るためにちょっと工夫しています。`get` は関数を返すようになっていて、この関数は `yield` から復帰したあとで呼び出すと、並列処理の結果を返すようになっています。 直列、並列いずれについても `generator` という変数が不思議な使われかたをしていて直感的にわかりづらいところがあったり、`get` にいちいち `generator` を渡さなければならないという難点はありますが、非同期処理としてのわかりやすさとしては比較的いい線をいっているように見えます。 他に気をつけるべき点としては、 `yield` の回数をうっかり間違えないようにすることあたりでしょうか。うっかりひとつ多く `yield` を書くと処理がそこでストップしてしまいますし、ひとつでも足りないとすべての非同期処理が完了しないまま先に進んでしまい、上のコードで言うと `y()` が `undefined` になってしまいます。非同期処理ひとつにたいして `yield` ひとつ、という対応関係が保証できないので、常に非同期処理が何個走っているか意識しておく必要があります。 かなり読みやすく直列な処理と並列な処理の書き分けもしやすいものの、おまじないともいえるような注意点がいろいろあるのがネックですし、そもそもこのコードが動く環境がほとんどないのが困ります。 ## 我々はgeneratorsによってコールバックの侵攻を阻止するのみならず、generators の正体にたどり着くすべを獲得した! さて、さきほどの generators の使い方についての表面的なコードを見て、なんとなく `yield`というキーワードを使えばいいことぐらいはわかったと思います。他の人が書いてくれたライブラリをありがたくダウンロードしてサンプルコードをコピペ、ちょっといじって実行してみてなんかエラーになったら詳しそうな先輩や同僚に見てもらって……っていう感じならそのくらいの理解でもいいのですが、自分でgeneratorsを使って同期的な仕組みを実装したくなる時もあるでしょう。それに、先ほどのコード、`yield get(...)` というような部分が何度かあるのが気になりませんでしたか?共通する部分があるなら、次のように関数としてまとめたくなりますよね。 ```ts: function hyperGet(path, generator){ return yield get(path, generator); // 常にyieldもくっついているすごいgetだ! } var generator = (function*(){ var hoge = hyperGet("hoge.txt", generator); var piyo = hyperGet("piyo.txt", generator); console.log(hoge + piyo); })(); generator.next(); ``` でもこのコードはうまく動きません。なぜなのでしょうか。これを理解するには、`yield` というキーワードの意味をちゃんと理解する必要があるのです。せっかくですから、generators の動作についても詳しく説明しておきます。generators は今後の非同期処理の主流になるはず。この知識はきっと無駄にならないと思いますが、面倒くさいひとは別に読まずに飛ばしてしまっていいと思います。他の手段について知りたい者は解散したまえ。……では今、ここを読んでいるものを新たな調査兵団として迎え入れます。よく恐怖に耐えてくれた。君たちは勇敢な戦士です。 Firefox Nightly で動く generators の完全なテストコードは次のようなものです。本当はXHRで実際にファイルを読むサンプルにしたかったんですが、Firefox のセキュリティ上の制限でローカルファイルが読めないので、代わりに `setTimeout` で非同期な処理をしているつもりにしています。` <any>` に無理矢理感が漂っていますが、これは TypeScript が generator に対応すれば必要なくなるはずです。`yield` は本当はキーワードですが、現時点の TypeScript でコンパイルできるように関数に見せかけてごまかしています。あと、さっきのサンプルコードとは違って `function*(){ ... }` っていう感じに functionキーワードの後ろにアスタリスクが付いていませんが、Firefox における generators の実装は現在提案されているものとは異なるということです。危なかった……もしこのアスタリスクが必要だったら、それが邪魔して TypeScript で書けなくなるところでした。このアスタリスクは正直要らない子だと俺も思います。 ポイントは `yield`、`next`、`send` です。 ```ts: declare function yield<T>(f: ()=>T): T; interface Generator { send(value: any): void; next(): void; } declare var StopIteration: new()=>void; function get(path: string, generator: Generator): ()=>string { var value: string; setTimeout(()=>{ try{ value = "[content: " + path + "]"; generator.send(value); }catch(e){ if (! (e instanceof StopIteration)) throw e; } }, 500); return ()=>value; } var generator: Generator = <any> (()=>{ // 直列バージョン var x: string = yield(get("hoge.txt", generator)); var y: string = yield(get("piyo.txt", generator)); console.log(x + y); // 並列バージョン var w: ()=>string = get("hoge.txt", generator); var z: ()=>string = get("piyo.txt", generator); yield; yield; console.log(w() + z()); })(); generator.next(); ``` (1) まずは `var generator: Generator = <any> (()=>{ ... })();` という無名関数の部分です。無名関数 `()=>{ ... }` を直後に `()` で呼び出す[即時関数パターン](http://d.hatena.ne.jp/shunsuk/20110824/1314190913)になっていますが、そこは別にどうでもいいです。最初のポイントは、 **この関数呼び出しで、この関数自身が呼び出されるわけじゃなくて、なぜか `Generator` オブジェクトが作成されて返ってくる** ことです。これはこの無名関数の本体に `yield` が含まれているからです。なんだかよくわかりませんがそういうものです。 (2) その `Generator` オブジェクトが変数 `generator` に代入されます。 **ここで `generator.next` を呼び出していますが、このときさっきの無名関数が呼び出されます** 。意味がわかりませんが、そういうものです。 (3) この無名関数の一行目は `var x: string = yield(get("hoge.txt", generator));` となっています。ここで generatos で導入された新しいキーワード `yield`が出てきますが、ひとまずそのまま素直に `yield` の隣の式を実行します。`get("hoge.txt", generator)` という式なので、`get` 関数の定義に飛びます。`generator` を引数で渡していることにも注目です。 (4) `get` 関数の中身は `setTimeout` の呼び出しだけです。これが非同期処理の開始にあたります。非同期処理を開始しただけで今はとくに何もしません。500 ミリ秒後にタイマーをセットして終了です。この関数の返り値は並列処理するときに非同期処理の結果を受け取るためのトリックなので気にしないでください。`get` 関数を脱出します。 (5) さて、無名関数に戻ってくると、`get` 関数の返り値がまるで `yield` という関数に渡されているように見えます。でもこの `yield` が曲者です。 **ここを実行しようとすると、なぜかさっきの (2) での `generator.next` の呼び出しが終了し、そこに戻ります。** 実はまたココにあとで戻ってきます。`return` で抜けだしたらそこで終了なのに対して、 `yield` で逃げ出したらいつかまたそこに戻ってくるのです。 (6) なぜか (2) の `generator.next` の呼び出しが終了しました。さて、ここでスクリプトの最後に到達したので、一旦実行は終了です。 (7) ブラウザが一休みしているところで、さっき仕掛けた `setTimeout` 時限爆弾がついに起動します!これはつまり非同期処理の終了です。`setTimeout` で渡した方の無名関数に飛びます。 (8) try/catch とその次の代入式はひとまず無視して、 **次にあるのは `generator.send(value)` です。これが実行されると、先ほど戻ってくると宣言した `yield` の位置に戻ります。`yield` で抜けだしたら、次の `next` や `send` で戻ってくるというのがポイントです。しかも、戻ってくるときに `send` に渡した引数を持ち帰って `yield` に戻ってきます。** このサンプルコードでは、ファイルの中身を模して `"[content: hoge.txt]"` という文字列を返すようにしています。 (9) **`yield` に戻ってきたら、`yield(...)` という式の値が先ほど `send` に渡した値になります。** そのため、この値が `hoge` に代入されるのです。`get` がファイルを取ってくる非同期処理だったら、`x` に hoge.txt の中身が代入されることになります。 (10) ここまでの (3) ~ (9) が `var x: string = yield(get("hoge.txt", generator));` という行が引き起こす結果です。この次もほぼ同じような `var y: string = yield(get("piyo.txt", generator));` という式ですから、(3) ~ (9) のような処理がもう一回行われます。その結果、`y` にも piyo.txt の中身が読み込まれます。 (11) 最後に `console.log(x + y);` でふたつのファイルの中身が結合されて出力されます。 (12) この無名関数の終端まで到達すると、さきほどの `send` の呼び出しに戻ります。戻ると言っても、`send` で `StopIteration` という例外が起こったという形で戻ってきます。そのため、try/catch でこの例外を補足します。これは異常な状態というよりは、generators では generator の実行の通常の終了を示す例外なので、そのままもみ消してしまって構いません。それ以外の種類の例外はもみ消しては困るので、`instanceof` で判別して投げ直します。 generators の動作がつかめてきたでしょうか。ポイントをまとめると、次のようになります。 * yield が含まれた関数を呼び出すと、`Generator` オブジェクトが作成される * その `Generator` オブジェクトの `next` を呼び出すと、さっきの関数が実行が開始される * `yield` に到達すると、直前に呼んだ `next` や `send` が終了する * `next` や `send` を呼び出すと、最後に到達した `yield` に戻ってくる * 関数の終端まで到達すると StopIteration が発生する こうやって通常の実行のコンテキストと generator のコンテキストを行き来して、マルチスレッドのような動作を実現しているのです。こんな奇妙な動作は従来の JavaScript ではどうやっても不可能です。これが generators の魔術なのです。 generators の用途は非同期処理だけではありません。このテキストでは触れませんが、調べてみるともっと面白い使い方がほかにもたくさん見つかると思います。 ## 彼の持つcoとgeneratorsが組み合わされば、この街の奪還も不可能ではありません!! 人類の栄光を願い、これから死に行くせめてもの間に、coの戦術的価値を説きます!! generator を利用したもっと格好いいライブラリには [co](https://github.com/visionmedia/co) というものもあります。Node の開発版でしか動かないようで自分はまだ試していないのですが、どうやら次のように書けるようになるみたいです。 ```js: co(function *(){ var a = yield get('http://google.com'); var b = yield get('http://yahoo.com'); var c = yield get('http://cloudup.com'); console.log(a.status); console.log(b.status); console.log(c.status); }) co(function *(){ var a = get('http://google.com'); var b = get('http://yahoo.com'); var c = get('http://cloudup.com'); var res = yield [a, b, c]; console.log(res); }) ``` なんと!これまであった問題点がだいたい解決しています。co を知ってしまった今では、jQuery.deferredは<del>まるでゴミクズ</del>少々力不足のようにも思えます。自分は `get` にも Generator を渡す必要があると思っていたのですが、うまいことやると `get("hoge.txt")` みたいなコードだけで実現できるようです。`yield [a, b, c]` というのも謎で、ここだけちょっと静的型付けとの折り合いがつかなそうですが……?どうなっているのかはまだよくわかりませんが、とにかく凄そうです。将来の非同期処理の大本命と言っていいでしょう。 ## fibersです!調理場に丁度頃合のものがあったので!つい! Node 環境に限っては、[fibers](https://github.com/laverdet/node-fibers) を使うという選択肢もないことはありません。node-fibers では V8 にネイティブな fibers を追加します。Node限定、しかもどこのNode環境でも使えるとは限りませんが、根本的な解決策ではあります。fiber ならコンソールに出力し、1秒停止し、その後またコンソールに出力するという一連の処理を次のようにとても自然に書くことができるようになります。 ```js: Fiber(function() { console.log('wait... ' + new Date); sleep(1000); console.log('ok... ' + new Date); }).run(); ``` 構文としては申し分なく、generators と並んでこれまで見てきた中ではもっとも扱いやすいもののひとつといえるでしょう。でも現実は非情で、もちろんブラウザではまったく使えません……。それに generatos が正式に導入されたら fibers はお払い箱になってしまう可能性が高いと思います。 ## あのぅ、みなさん……。上官の食糧庫から、Web Workers盗ってきました……。後でみなさんで分けましょう。スライスしてパンに挟んで…むふふ…。 Web Workers はブラウザのような環境でマルチスレッドを実現する API です。これは fiber のような協調的マルチスレッドではなくて本当のマルチスレッドなので、 yield で時々処理を譲るということも必要ありません。まだ Working Draft の段階の仕様ですが最近の PC のブラウザなら大抵実装されていますし、Node 環境でも Web Worker の API を実装したものもあるようです。 [File API](http://www.w3.org/TR/FileAPI/) には非同期 API のほかに同期API があって、Worker スレッド内で`FileReaderSync` を使えばブラウザのスレッドをブロックすることなく同期的にファイル操作をすることができます。Web Workers はマルチスレッドといっても、Web Workers の空間とブラウザの空間は完全に分離されていて、Worker とブラウザのスレッドの間はやはり非同期の API でやりとりすることになります。Worker スレッド側では `window` や `console` オブジェクトすらないので、Worker スレッドで処理した結果を出力するには `postMessage` でブラウザ側に結果を送り返し、ブラウザ側で `console.log` を呼び出します。Worker スレッド内で完結する処理であればとてもきれいに書けますが、頻繁にブラウザと Worker の間でやりとりがある場合は結局のところ非同期処理が多くなり、コールバックも増えてきます。ブラウザ側の処理と Worker 側の処理をどれだけうまく分離できるかが鍵になるでしょう。 Worker 内では XMLHttpRequest も使えます。ファイルをふたつ読み取り、結合して出力するサンプルは次のようになります。 ```js:app.js var worker = new Worker("worker.js"); worker.onmessage = function(event) { console.log(event.data); }; ``` ```js:worker.js function get(path){ var xhr = new XMLHttpRequest(); xhr.open("GET", path, false); xhr.send(null); return xhr.responseText; } var hoge = get("hoge.txt"); var piyo = get("piyo.txt"); postMessage(hoge + piyo); ``` worker.js 側の処理は同期で書けてとてもきれいです。Web Workers は並列処理のためのフレームワークを提供するものではありませんから、例えばふたつのファイルを並行して読み込んで両方の完了を待つ場合はなにか別のライブラリを使うことになるでしょう。また、Web Workers が登場したのは比較的最近で、時間のかかる処理はそれまで非同期処理が基本でしたから、同期と非同期両方が用意してある API はそれほど多くありません。同期 API が用意されていない場合は、Web Workers では解決できませんから、また別の手段を考慮する必要があります。 また、どうしても気になるのはやはり静的型付け(いやまあ、そんなこと気にしてるのは俺だけですけど)。Worker 内ではもちろんきれいに型付けできますが、メッセージングに関しては `postMessage` というたった一本のパイプであらゆるデータをやりとりするわけで、もちろんここには静的型付けなんて望むべくもありません。`onmessage ` で飛んでくるデータはいったい何なのかはさっぱりわからないので、やはりメッセージングの回数をどこまで減らせるかがポイントでしょう。 あと現時点の実際上の問題として、ブラウザでは Worker スレッドのデバッグができないというものがあります。代わりに [fakeworker.js](https://code.google.com/p/fakeworker-js/) で WebWorkers をシミュレートしてデバッグする方法はあります。 ## 調査兵団に入って……とにかくコールバックをぶっ殺したいです……! いろいろな非同期処理の制御の方法を見てきましたが、どうもしっくりくるものが見つからないので、静的に型付け可能でしかも同期的に処理してるっぽく書けるライブラリを作ってみました。このライブラリを使うと、非同期処理を次のように書けます。 ```ts: var hoge = masync.get("hoge.txt"); // get でファイルを ajax で持ってくる var piyo = masync.get("piyo.txt"); var main = masync.log(masync.concat(hoge, piyo)); // concat で結合、log で出力 masync.run(main); // run で実際に実行される ``` こんな感じで、非同期に取得しているはずのデータに対して自然に関数に適用できたりします。このとき、hoge.txt とpiyo.txtの読み込みは並列に走っています。もし直列に読み込みたい場合は、次のようにします。 ```ts: var hoge = masync.get("hoge.txt"); var piyo = masync.get("piyo.txt"); masync.run( hoge, piyo, masync.log(masync.concat(hoge, piyo)) ); ``` `masync.run` は引数に与えた処理を直列に順番に実行していく関数です。`hoge` は式のなかに2回出てきますが、この時本当に2回非同期処理を行うのか、それとも先に出現した方だけ実行し2回目の評価では結果だけ再利用するかをオプションで選択できます。<del>式の中では変数の宣言を行えないので、変数の宣言は少し離れた別の行になってしまっているのがちょっと不満です。</del>変数の宣言と初期化の位置が離れてしまう問題は多分解決しました。 でも『AをBより先に実行したい』という場合は『Bを実行するためにAの結果が必要』という場合が多いので、直列な操作を直接記述することはさほど多くはないでしょう。このライブラリで自然に書いていけば、直列に処理しなければならない部分は直列に、そうでない部分は並列に自動的に処理してくれます。 少々不思議なのは、`masync.get` を呼び出しても、その非同期な処理の結果が実際に使われることがないのなら、その非同期処理は行われないということです。たとえば、 ```ts: var hoge = masync.get("hoge.txt"); var piyo = masync.get("piyo.txt"); var main = masync.log(hoge); masync.run(main); ``` とすると `masync.log` で実際に出力しているのは hoge.txt の中身だけなので、piyo.txt への get は実際には行われません。『不要な操作は省略』というのも自動的にやってくれるのです。 同期的なデータと非同期的なデータを同じように扱えるので、たとえばhoge.txtにほかのファイルのパスが書いてあってhoge.txtのつぎに piyo.txt じゃなくてそれを読みたいという場合は、次のようにします。 ```ts: var hoge = masync.get("hoge.txt"); var nyan = masync.get(hoge); var main = masync.log(masync.concat(hoge, nyan)); masync.run(main); ``` これだとさっき並列に読み込んだ時のパターンに似ていますが、もしこれが並列だとすると hoge を読む前に nyan を読んでしまって失敗するということがときどき起こるはずです。でも内部でその辺りをうまく処理してくれて、nyan を読むのに hoge が必要だということから、ちゃんとhogeを先に読んでからnyanを読んでくれます。 先ほどの並列処理では並列に行っているすべての処理が完了してから次の処理に進んでいきますが、複数の処理を並列に行い、そのうちひとつでも完了すればすぐに次の処理に進む、という処理を実現する関数 `sooner` もあります。以下のコードで、piyo.txt より hoge.txt のほうがずっと大きいファイルなら、 piyo.txt の読み込みのほうが早く終わるので piyo.txt の内容が出力されます。それに対して、piyo.txt のほうがずっと大きければ hoge.txt の内容が出力されます。簡単に実装できたから実装してみただけで、正直この機能は何の役に立つのかよくわかりません。もし使い道が思いついたら教えてください。 ```ts: var hoge = masync.get("hoge.txt"); var piyo = masync.get("piyo.txt"); var main = masync.log(masync.sooner(hoge, piyo)); masync.run(main); ``` とはいえ、直列、並列(全部完了まで待つ)、並列(ひとつ終わるまで待つ)といった柔軟なコントロールフローが自在に記述できるのが便利だと思います。たとえば、あるファイルを読み込んでそれをすぐに出力する、という処理を行う関数 `getAndLog` を次のように簡単に定義できます。 ```ts: var getAndLog = path => masync.bind(masync.get(path), masync.log); ``` `a`、`b`、`c`、`d` をそれぞれ a.txt、b.txt、c.txt、d.txt を非同期に読み込んで出力する処理だとしましょう。`a`、`b`、`c`、`d` は `getAndLog` を使って次のように定義できます。 ```ts: var a = getAndLog("a.txt"); var b = getAndLog("b.txt"); var c = getAndLog("c.txt"); var d = getAndLog("d.txt"); ``` そして、`a` と並行して、`b` と `c` を直列にしたものを行い、そのあとで `d` を行う、というかなり複雑なコントロールをしたいという場合でも、次のようにとても自然に書けます。`masync.seq` および `masync.parallel` はそれぞれ直列処理、並列処理を明示的に行うときに使う関数です。 ```ts: masync.run( masync.parallel(a, masync.seq(b, c)), d ); ``` シーケンス図で描けば、例えばこんな処理になります。 ``` =====================================> 時間 a ---------> b ----------------> c ---> d ------------> ``` ファイルの get だけでなく、一時停止のような処理もまるで同期処理のように自然に書けます。次のコードでは、実行するとまず `"hoge"` と出力し、1秒待機してから、そのあと `"piyo"` と出力します。 ```ts: masync.run( masync.log("hoge"), // "hoge" と出力 masync.wait(1000), // 1秒待機 masync.log("piyo") // "piyo" と出力 ); ``` jQuery.deferred と相互にデータを交換する機能もつけときました。 ```ts: masync.run( masync.log( masync.strcat( // fromPromise で Promise を masync のデータ型に変換 masync.fromPromise(jQuery.get("a.txt")), masync.get("d.txt") ) ) ); // masync.log のような一部の関数は Promise を直接受け取って処理できる masync.run(masync.log(jQuery.get("a.txt"))); // toPromise で masync のデータ型を Promise に変換 masync.toPromise(masync.get("b.txt")).then((data)=>console.log(data)); ``` ついでに Node の `fs.readFile` にもアクセスできるようにしときました。同期的に処理してるっぽくみえますが、同期 API のほうの `fs.readFileSync` ではなく、あくまで非同期版の `fs.readFile` をラップしています。ちなみに、引数の最後にコールバックを渡すという例の Node の非同期処理の規約に従った関数を masync の非同期処理に変換する関数も定義してありますので、それを使えば比較的簡単にほかの Node の API もラップできるはずです。 ```ts: var a = masync.fs.readFile("a.txt"); var b = masync.fs.readFile("b.txt"); masync.run(masync.log(masync.strcat(a, b))); ``` エラー処理も簡単です。一番簡単には、非同期処理の失敗時に別の値を返すようにする `recover` 関数を使うことができます。以下のコードで、`findFile` は指定したパスのファイルを `get` しますが、ファイルが取得できない場合は "Error: No such file: notexistsfile.txt" のようなメッセージを代わりに返します。ついでにいうと、こんなふうに非同期処理を関数としてまとめることもまったく問題ありません。 ```ts: function findFile(path: string){ return masync.recover("Error: No such file: " + path, masync.get(path) ); } masync.run( masync.log(findFile("a.txt")), // a.txt のファイルの中身が出力される masync.log(findFile("noexistfile.txt")) // "Error: No such file: notexistsfile.txt" と出力される ); ``` ある程度の簡単な処理ならコールバックを使わずに書けるのですが、もっと複雑な処理を行いたければ非同期で取得したデータに直接アクセスする必要も出てくるでしょう。そのための方法は幾つかあるのですが、比較的に直感的に理解しやすい方法として `eject` という関数を用意してあります。この関数の最初の引数に `get` などの返り値、第2引数に直接のデータを受け取る関数を渡すと、その関数に直接のデータが渡されます。このライブラリでも、生のデータにアクセスしようとするとついにコールバックが露呈します。 ```ts: var hoge = masync.get("hoge.txt"); // hoge は string ではない masync.run( masync.eject(hoge, function(data){ console.log(data); }) // data は hoge.txt の中身。typeof data == "string" ); ``` このライブラリの特徴としては、以下のようなことが挙げられます。 * 簡単な処理であればコールバックを完全に排除できる * 同期処理っぽい見た目で書ける * TypeScript でガッチガチに静的型付けされていてる * どんな環境でも動く * 非同期なデータの操作を簡単に定義できる 特に、静的型付けされていることは大きなメリットで、ちょっとでも変な使い方をするとすぐコンパイルエラーになるので安心です(JavaScript からでも使えます)。こんなやたら長い記事をここまで読んできた方ならうっすら気付いていると思いますが、自分の静的型付けに関するこだわりは伊達や酔狂ではありません。このライブラリでも静的型付けについては徹底されていて、いわゆるダウンキャストのようなものが必要になる場面はいっさいなくなるように設計しています。あの子達の羨望の眼差しも、俺の潔癖すぎる性格を知れば幻滅するだろうね! また、先程から何度も使っている `masync.log` は `console.log` の非同期操作バージョンなのですが、`masync.log` は `masync.lift` という関数を使って `masync.lift(console.log.bind(console))` と表すことができます。つまり、 `lift` は同期操作バージョンの関数を非同期操作バージョンの関数に変えてくれる関数なのです。`this` の扱いの関係で `bind` の呼び出しが必要になっていますが、`this` に依存しない関数、例えば `Math.abs` なら `masync.lift(Math.abs)` だけで非同期バージョンの `abs` になるのです。これは jQuery.deferred でいえば独自の Deferred を定義することに相当しますが、Deferred を定義するよりはるかに簡単です。 それに対し、欠点としては次のようなものが挙げられます。 * `masync.run` で書ける直列な非同期処理の制限が一度に 26 個まで * 同期的に処理しているように見えても、そこにデバッガのブレークポイントが仕掛けられるわけではない * <del>変数の宣言と、実際に代入するソースコード上の位置が離れてしまう場合がある</del>多分解決しました `masync.run`の引数が26個までという謎制限はもうすこしライブラリに手を入れればいくらでも緩和できるのですが、とりあえず26個あれば十分かなと思うのでこうなっています。`masync.seq` という関数を入れ子にすることでも回避できます。 デバッガのブレークポイントを仕掛けにくいのもつらいですが、どうしてもブレークポイントを仕掛けたければ、さきほど示したような `eject` のコールバック関数内にならブレークポイントを仕掛けることができます。 ## まるで化け物を見るような...俺がそうだというのか…!? これらのコードにはコールバックは無いように見えますが、このライブラリが面倒なコールバックをほぼすべて覆い隠してくれるのです。実はライブラリ内部ではコールバックの嵐で大変なことになっています。見たらたぶん吐きます。というか、上記のコードはコールバックに見えないだけで、このライブラリは『コールバックがない』どころかむしろ **これらのコードはすべてコールバックでできているといっても過言ではない** レベルだったりします。 **『すべてのコールバックを駆逐してやる!』と決意して兵士になったら、実は自分がコールバックだった** とか、なんかどこかで聞いたことがあるような話ですね。なにそれこわい。ここでいわゆるタイトル回収です。 なんじゃそりゃ、いったいどーなってんだ?と思う人もいるとは思いますが、ソースコードは Haskell の Functor, Applicative, Monad あたりの概念がふんだんに使われていて、このあたりの知識がないひとにはちょっとばかり理解するのがたいへんです。例えば、先ほど挙げた `lift` は Haskell に存在する [lift](http://www.haskell.org/haskellwiki/Lifting) に対応する関数なのです。ほかにも、このライブラリで最初に定義されている関数は `fmap` という関数なのですが、よく訓練された Haskeller ならこの名前を見ただけで何の関数なのか一発で理解しますし、実装は見なくてもだいたい想像がつきます。ちなみに定義は ```ts: function fmap<T,S>(f: (t: T)=>S, x: Async<T>): Async<S> { return ap(pure(f), x); } ``` となっていますが、これを見た Haskeller は『ああ、やっぱりね』みたいに言い出します。このライブラリの実装の詳細について理解したければ、Haskell を学ぶのが最も早道だと思います。このライブラリの関数の実装はそれぞれ長くて 10数行なので一見簡単そうですが、とても抽象的な操作ばかりなので、Haskell を知らない人は処理の流れを追えば追うほどわけがわからなくなるので注意してください。 ( Haskellを知っている人向けに説明すると、このライブラリは **非同期処理モナドライブラリ** なのです。また、いわゆる[継続渡し](http://ja.wikipedia.org/wiki/%E7%B6%99%E7%B6%9A%E6%B8%A1%E3%81%97%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB) の応用で、成功と失敗の2系統の継続を管理するためのフレームワークを提供するものでもあります。 ) このへんの小難しい話にはみんな興味なさそうだし、もう疲れたのでこれ以上このライブラリの内部までの解説はしませんが、JavaScriptって<del>本当にクソ</del>本当に奥が深いですね。 この謎のライブラリのソースコードはこちら: [kontan/masync](https://github.com/kontan/masync) 直角三角形とかも見れます。readme に API リファレンスやほんのちょっとだけライブラリ内部の解説も書いておきましたので、知りたいヒトはどうぞ。あと、readme はネタ無しでとても真面目に書いていますので、ネタ混じりの文章にうんざりしたひとにもお勧めです。 ## まとめ * **generators がどのブラウザでも動くようになったら、co を使え。それまでは気合でなんとか持ちこたえろ** * Haskell ってすごい * 俺のmasyncは並列と直列が両方そなわり最強に見える ## あの…おなか痛いんで…負傷者に…してもらって…いいですか!? このテキストはあくまで『非同期処理を使わざるをえないときに、どのようにコールバックのネストの多重化を防ぐか』を検討するもので、『ふたつのファイルを読み込んでつなげて出力』というのは問題を把握しやすくするための例題に過ぎませんでした。しかし途中から『手段に関わらずこの例題をどう処理するか』の話とごっちゃになってます。 同期処理が使えるなら同期で処理すればいいです。本来の論旨は『同期処理が使えないとき』であって、fibers や同期版 XHR、FileReaderSync の話はよく考えたら微妙に別件でした。つまり、Web Workers を例に挙げると、ファイルの読み込みについてはWeb Wokers で同期的に可能なのでその部分は本テキストの話題の対象ではなく、Web Workers での Worker スレッドとブラウザのスレッド間のメッセージングについては非同期処理を使わざるをえないがこれをどうするか、というのが本来このテキストで検討したかった対象です。でも関連性はある話題ですし有用ではありそうなので、これらの微妙に主旨からズレた部分の話題に関してもそのまま残しておきます。このテキストの主旨に関して混乱したかたがいたらごめんなさい。 ## さいごに > InfoQ: そのライブラリを改善できるような、例えば、もっと簡潔にできるなど、JavaScript言語に対する新たな機能や変更というのはありますか? > Isaac氏 (slide-flow-control): いいえ。私のフローコントロールがベストです。だれも改善なんてできません。これがベストなのは私自身に直接かかわっているからです。だから、外部の影響というのは私にとってはマイナスで、ベストではなくなるのです。もし私と同じ経験をしたいなら、フローコントロールライブラリを書くことをおすすめします。自分で書けばすぐにそれがベストだとわかるでしょう。もし他のライブラリの方がよさそうに見えたなら、大急ぎでエディターに戻って、恥を隠して、すぐに彼らのアイデアを自分自身の少し違ったやり方で再発明しましょう。そうすれば、今やそれがベストなのだと実感できるでしょう。 ――[仮想パネル: JavaScriptで非同期プログラミングを乗り切る方法](http://www.infoq.com/jp/articles/surviving-asynchronous-programming-in-javascript) ## 参考文献 * [JavaScriptとコールバック地獄](http://techblog.yahoo.co.jp/programming/js_callback/) * [ jQuery.Deferredを使って楽しい非同期生活を送る方法](http://qiita.com/yuku_t/items/3d1cf51d7ae91305eaaa) * [結局jQuery.Deferredの何が嬉しいのか分からない、という人向けの小話](http://qiita.com/yuku_t/items/1b8ce6bba133a7eaeb23) * [爆速でわかるjQuery.Deferred超入門](http://techblog.yahoo.co.jp/programming/jquery-deferred/) * [仮想パネル: JavaScriptで非同期プログラミングを乗り切る方法](http://www.infoq.com/jp/articles/surviving-asynchronous-programming-in-javascript) * [進撃の巨人の名言・迷言をまとめようぜww](http://shingekikyojin.net/archives/28229454.html) * [進撃の巨人 名言 セリフ集](http://shingekiword.blogspot.jp/) * [ブロントさん名言集](http://www.geocities.jp/burontosan/) * [ボブ・ロス](http://dic.nicovideo.jp/a/%E3%83%9C%E3%83%96%E3%83%BB%E3%83%AD%E3%82%B9) * [はぁ!?なにいってんのおまえ!?](http://nobuhusa.blog118.fc2.com/blog-entry-630.html) * [言峰綺礼](http://wiki.cre.jp/typemoon/%E8%A8%80%E5%B3%B0%E7%B6%BA%E7%A4%BC) * [ゆゆ式](http://www.yuyushiki.net/) |
|
| 40位 |
|
|||
|
19:44:40 |
(ShouldBee 所属) |
|
iptablesを改めて勉強しなおしました。ツッコミ歓迎です^-^ GitHub: https://github.com/suin/iptables/blob/master/iptables.sh ```sh:iptables.sh #!/bin/bash ########################################################### # このスクリプトの特徴 # # 受信・通過については基本的に破棄し、ホワイトリストで許可するものを指定する。 # 送信については基本的に許可する。ただし、サーバが踏み台になり外部のサーバに迷惑をかける可能性があるので、 # 心配な場合は、送信も受信同様に基本破棄・ホワイトリストで許可するように書き換えると良い。 ########################################################### ########################################################### # 用語の統一 # わかりやすさのためルールとコメントの用語を以下に統一する # ACCEPT : 許可 # DROP : 破棄 # REJECT : 拒否 ########################################################### ########################################################### # チートシート # # -A, --append 指定チェインに1つ以上の新しいルールを追加 # -D, --delete 指定チェインから1つ以上のルールを削除 # -P, --policy 指定チェインのポリシーを指定したターゲットに設定 # -N, --new-chain 新しいユーザー定義チェインを作成 # -X, --delete-chain 指定ユーザー定義チェインを削除 # -F テーブル初期化 # # -p, --protocol プロコトル プロトコル(tcp、udp、icmp、all)を指定 # -s, --source IPアドレス[/mask] 送信元のアドレス。IPアドレスorホスト名を記述 # -d, --destination IPアドレス[/mask] 送信先のアドレス。IPアドレスorホスト名を記述 # -i, --in-interface デバイス パケットが入ってくるインターフェイスを指定 # -o, --out-interface デバイス パケットが出ていくインターフェイスを指定 # -j, --jump ターゲット 条件に合ったときのアクションを指定 # -t, --table テーブル テーブルを指定 # -m state --state 状態 パケットの状態を条件として指定 # stateは、 NEW、ESTABLISHED、RELATED、INVALIDが指定できる # ! 条件を反転(~以外となる) ########################################################### # パス PATH=/sbin:/usr/sbin:/bin:/usr/bin ########################################################### # IPの定義 # 必要に応じて定義する。定義しなくても動作する。 ########################################################### # 内部ネットワークとして許可する範囲 # LOCAL_NET="xxx.xxx.xxx.xxx/xx" # 内部ネットワークとして一部制限付きで許可する範囲 # LIMITED_LOCAL_NET="xxx.xxx.xxx.xxx/xx" # ZabbixサーバーIP # ZABBIX_IP="xxx.xxx.xxx.xxx" # 全てのIPを表す設定を定義 # ANY="0.0.0.0/0" # 信頼可能ホスト(配列) # ALLOW_HOSTS=( # "xxx.xxx.xxx.xxx" # "xxx.xxx.xxx.xxx" # "xxx.xxx.xxx.xxx" # ) # 無条件破棄するリスト(配列) # DENY_HOSTS=( # "xxx.xxx.xxx.xxx" # "xxx.xxx.xxx.xxx" # "xxx.xxx.xxx.xxx" # ) ########################################################### # ポート定義 ########################################################### SSH=22 FTP=20,21 DNS=53 SMTP=25,465,587 POP3=110,995 IMAP=143,993 HTTP=80,443 IDENT=113 NTP=123 MYSQL=3306 NET_BIOS=135,137,138,139,445 DHCP=67,68 ########################################################### # 関数 ########################################################### # iptablesの初期化, すべてのルールを削除 initialize() { iptables -F # テーブル初期化 iptables -X # チェーンを削除 iptables -Z # パケットカウンタ・バイトカウンタをクリア iptables -P INPUT ACCEPT iptables -P OUTPUT ACCEPT iptables -P FORWARD ACCEPT } # ルール適用後の処理 finailize() { /etc/init.d/iptables save && # 設定の保存 /etc/init.d/iptables restart && # 保存したもので再起動してみる return 0 return 1 } # 開発用 if [ "$1" == "dev" ] then iptables() { echo "iptables $@"; } finailize() { echo "finailize"; } fi ########################################################### # iptablesの初期化 ########################################################### initialize ########################################################### # ポリシーの決定 ########################################################### iptables -P INPUT DROP # すべてDROP。すべての穴をふさいでから必要なポートを空けていくのが良い。 iptables -P OUTPUT ACCEPT iptables -P FORWARD DROP ########################################################### # 信頼可能なホストは許可 ########################################################### # ローカルホスト # lo はローカルループバックのことで自分自身のホストを指す iptables -A INPUT -i lo -j ACCEPT # SELF -> SELF # ローカルネットワーク # $LOCAL_NET が設定されていれば LAN上の他のサーバとのやり取りを許可する if [ "$LOCAL_NET" ] then iptables -A INPUT -p tcp -s $LOCAL_NET -j ACCEPT # LOCAL_NET -> SELF fi # 信頼可能ホスト # $ALLOW_HOSTS が設定されていれば そのホストとのやり取りを許可する if [ "${ALLOW_HOSTS[@]}" ] then for allow_host in ${ALLOW_HOSTS[@]} do iptables -A INPUT -p tcp -s $allow_host -j ACCEPT # allow_host -> SELF done fi ########################################################### # $DENY_HOSTSからのアクセスは破棄 ########################################################### if [ "${DENY_HOSTS[@]}" ] then for host in ${DENY_HOSTS[@]} do iptables -A INPUT -s $ip -m limit --limit 1/s -j LOG --log-prefix "deny_host: " iptables -A INPUT -s $ip -j DROP done fi ########################################################### # セッション確立後のパケット疎通は許可 ########################################################### iptables -A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT ########################################################### # 攻撃対策: Stealth Scan ########################################################### iptables -N STEALTH_SCAN # "STEALTH_SCAN" という名前でチェーンを作る iptables -A STEALTH_SCAN -j LOG --log-prefix "stealth_scan_attack: " iptables -A STEALTH_SCAN -j DROP # ステルススキャンらしきパケットは "STEALTH_SCAN" チェーンへジャンプする iptables -A INPUT -p tcp --tcp-flags SYN,ACK SYN,ACK -m state --state NEW -j STEALTH_SCAN iptables -A INPUT -p tcp --tcp-flags ALL NONE -j STEALTH_SCAN iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j STEALTH_SCAN iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j STEALTH_SCAN iptables -A INPUT -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j STEALTH_SCAN iptables -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j STEALTH_SCAN iptables -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j STEALTH_SCAN iptables -A INPUT -p tcp --tcp-flags ACK,PSH PSH -j STEALTH_SCAN iptables -A INPUT -p tcp --tcp-flags ACK,URG URG -j STEALTH_SCAN ########################################################### # 攻撃対策: フラグメントパケットによるポートスキャン,DOS攻撃 # namap -v -sF などの対策 ########################################################### iptables -A INPUT -f -j LOG --log-prefix 'fragment_packet:' iptables -A INPUT -f -j DROP ########################################################### # 攻撃対策: Ping of Death ########################################################### # 毎秒1回を超えるpingが10回続いたら破棄 iptables -N PING_OF_DEATH # "PING_OF_DEATH" という名前でチェーンを作る iptables -A PING_OF_DEATH -p icmp --icmp-type echo-request \ -m hashlimit \ --hashlimit 1/s \ --hashlimit-burst 10 \ --hashlimit-htable-expire 300000 \ --hashlimit-mode srcip \ --hashlimit-name t_PING_OF_DEATH \ -j RETURN # 制限を超えたICMPを破棄 iptables -A PING_OF_DEATH -j LOG --log-prefix "ping_of_death_attack: " iptables -A PING_OF_DEATH -j DROP # ICMP は "PING_OF_DEATH" チェーンへジャンプ iptables -A INPUT -p icmp --icmp-type echo-request -j PING_OF_DEATH ########################################################### # 攻撃対策: SYN Flood Attack # この対策に加えて Syn Cookie を有効にすべし。 ########################################################### iptables -N SYN_FLOOD # "SYN_FLOOD" という名前でチェーンを作る iptables -A SYN_FLOOD -p tcp --syn \ -m hashlimit \ --hashlimit 200/s \ --hashlimit-burst 3 \ --hashlimit-htable-expire 300000 \ --hashlimit-mode srcip \ --hashlimit-name t_SYN_FLOOD \ -j RETURN # 解説 # -m hashlimit ホストごとに制限するため limit ではなく hashlimit を利用する # --hashlimit 200/s 秒間に200接続を上限にする # --hashlimit-burst 3 上記の上限を超えた接続が3回連続であれば制限がかかる # --hashlimit-htable-expire 300000 管理テーブル中のレコードの有効期間(単位:ms # --hashlimit-mode srcip 送信元アドレスでリクエスト数を管理する # --hashlimit-name t_SYN_FLOOD /proc/net/ipt_hashlimit に保存されるハッシュテーブル名 # -j RETURN 制限以内であれば、親チェーンに戻る # 制限を超えたSYNパケットを破棄 iptables -A SYN_FLOOD -j LOG --log-prefix "syn_flood_attack: " iptables -A SYN_FLOOD -j DROP # SYNパケットは "SYN_FLOOD" チェーンへジャンプ iptables -A INPUT -p tcp --syn -j SYN_FLOOD ########################################################### # 攻撃対策: HTTP DoS/DDoS Attack ########################################################### iptables -N HTTP_DOS # "HTTP_DOS" という名前でチェーンを作る iptables -A HTTP_DOS -p tcp -m multiport --dports $HTTP \ -m hashlimit \ --hashlimit 1/s \ --hashlimit-burst 100 \ --hashlimit-htable-expire 300000 \ --hashlimit-mode srcip \ --hashlimit-name t_HTTP_DOS \ -j RETURN # 解説 # -m hashlimit ホストごとに制限するため limit ではなく hashlimit を利用する # --hashlimit 1/s 秒間1接続を上限とする # --hashlimit-burst 100 上記の上限を100回連続で超えると制限がかかる # --hashlimit-htable-expire 300000 管理テーブル中のレコードの有効期間(単位:ms # --hashlimit-mode srcip 送信元アドレスでリクエスト数を管理する # --hashlimit-name t_HTTP_DOS /proc/net/ipt_hashlimit に保存されるハッシュテーブル名 # -j RETURN 制限以内であれば、親チェーンに戻る # 制限を超えた接続を破棄 iptables -A HTTP_DOS -j LOG --log-prefix "http_dos_attack: " iptables -A HTTP_DOS -j DROP # HTTPへのパケットは "HTTP_DOS" チェーンへジャンプ iptables -A INPUT -p tcp -m multiport --dports $HTTP -j HTTP_DOS ########################################################### # 攻撃対策: IDENT port probe # identを利用し攻撃者が将来の攻撃に備えるため、あるいはユーザーの # システムが攻撃しやすいかどうかを確認するために、ポート調査を実行 # する可能性があります。 # DROP ではメールサーバ等のレスポンス低下になるため REJECTする ########################################################### iptables -A INPUT -p tcp -m multiport --dports $IDENT -j REJECT --reject-with tcp-reset ########################################################### # 攻撃対策: SSH Brute Force # SSHはパスワード認証を利用しているサーバの場合、パスワード総当り攻撃に備える。 # 1分間に5回しか接続トライをできないようにする。 # SSHクライアント側が再接続を繰り返すのを防ぐためDROPではなくREJECTにする。 # SSHサーバがパスワード認証ONの場合、以下をアンコメントアウトする ########################################################### # iptables -A INPUT -p tcp --syn -m multiport --dports $SSH -m recent --name ssh_attack --set # iptables -A INPUT -p tcp --syn -m multiport --dports $SSH -m recent --name ssh_attack --rcheck --seconds 60 --hitcount 5 -j LOG --log-prefix "ssh_brute_force: " # iptables -A INPUT -p tcp --syn -m multiport --dports $SSH -m recent --name ssh_attack --rcheck --seconds 60 --hitcount 5 -j REJECT --reject-with tcp-reset ########################################################### # 攻撃対策: FTP Brute Force # FTPはパスワード認証のため、パスワード総当り攻撃に備える。 # 1分間に5回しか接続トライをできないようにする。 # FTPクライアント側が再接続を繰り返すのを防ぐためDROPではなくREJECTにする。 # FTPサーバを立ち上げている場合、以下をアンコメントアウトする ########################################################### # iptables -A INPUT -p tcp --syn -m multiport --dports $FTP -m recent --name ftp_attack --set # iptables -A INPUT -p tcp --syn -m multiport --dports $FTP -m recent --name ftp_attack --rcheck --seconds 60 --hitcount 5 -j LOG --log-prefix "ftp_brute_force: " # iptables -A INPUT -p tcp --syn -m multiport --dports $FTP -m recent --name ftp_attack --rcheck --seconds 60 --hitcount 5 -j REJECT --reject-with tcp-reset ########################################################### # 全ホスト(ブロードキャストアドレス、マルチキャストアドレス)宛パケットは破棄 ########################################################### iptables -A INPUT -d 192.168.1.255 -j LOG --log-prefix "drop_broadcast: " iptables -A INPUT -d 192.168.1.255 -j DROP iptables -A INPUT -d 255.255.255.255 -j LOG --log-prefix "drop_broadcast: " iptables -A INPUT -d 255.255.255.255 -j DROP iptables -A INPUT -d 224.0.0.1 -j LOG --log-prefix "drop_broadcast: " iptables -A INPUT -d 224.0.0.1 -j DROP ########################################################### # 全ホスト(ANY)からの入力許可 ########################################################### # ICMP: ping に応答する設定 iptables -A INPUT -p icmp -j ACCEPT # ANY -> SELF # HTTP, HTTPS iptables -A INPUT -p tcp -m multiport --dports $HTTP -j ACCEPT # ANY -> SELF # SSH: ホストを制限する場合は TRUST_HOSTS に信頼ホストを書き下記をコメントアウトする iptables -A INPUT -p tcp -m multiport --dports $SSH -j ACCEPT # ANY -> SEL # FTP # iptables -A INPUT -p tcp -m multiport --dports $FTP -j ACCEPT # ANY -> SELF # DNS # iptables -A INPUT -p tcp -m multiport --sports $DNS -j ACCEPT # ANY -> SELF # iptables -A INPUT -p udp -m multiport --sports $DNS -j ACCEPT # ANY -> SELF # SMTP # iptables -A INPUT -p tcp -m multiport --sports $SMTP -j ACCEPT # ANY -> SELF # POP3 # iptables -A INPUT -p tcp -m multiport --sports $POP3 -j ACCEPT # ANY -> SELF # IMAP # iptables -A INPUT -p tcp -m multiport --sports $IMAP -j ACCEPT # ANY -> SELF ########################################################### # ローカルネットワーク(制限付き)からの入力許可 ########################################################### if [ "$LIMITED_LOCAL_NET" ] then # SSH iptables -A INPUT -p tcp -s $LIMITED_LOCAL_NET -m multiport --dports $SSH -j ACCEPT # LIMITED_LOCAL_NET -> SELF # FTP iptables -A INPUT -p tcp -s $LIMITED_LOCAL_NET -m multiport --dports $FTP -j ACCEPT # LIMITED_LOCAL_NET -> SELF # MySQL iptables -A INPUT -p tcp -s $LIMITED_LOCAL_NET -m multiport --dports $MYSQL -j ACCEPT # LIMITED_LOCAL_NET -> SELF fi ########################################################### # 特定ホストからの入力許可 ########################################################### if [ "$ZABBIX_IP" ] then # Zabbix関連を許可 iptables -A INPUT -p tcp -s $ZABBIX_IP --dport 10050 -j ACCEPT # Zabbix -> SELF fi ########################################################### # それ以外 # 上記のルールにも当てはまらなかったものはロギングして破棄 ########################################################### iptables -A INPUT -j LOG --log-prefix "drop: " iptables -A INPUT -j DROP ########################################################### # SSH 締め出し回避策 # 30秒間スリープしてその後 iptables をリセットする。 # SSH が締め出されていなければ、 Ctrl-C を押せるはず。 ########################################################### trap 'finailize && exit 0' 2 # Ctrl-C をトラップする echo "In 30 seconds iptables will be automatically reset." echo "Don't forget to test new SSH connection!" echo "If there is no problem then press Ctrl-C to finish." sleep 30 echo "rollback..." initialize ``` ツッコミをうけての変更: ``` $ git log-graph * 65e8249 2012-08-23 Hidehito Nozawa aka Suin fix: -m multiport --dport"s" ``` |
|
| 41位 |
|
|||
|
03:17:55 |
|
|
**Gemfile** の 「group :test, :development do」ブロックに書いている **個人的に最強な設定** を晒したいと思います。(2013/10/24 現在)
## 作業環境 * Ruby 2.0.0p247 * Rails 4.0.0 ## Gemfileに設定している内容 ***※ コメントに随時変更したものを追記しますー*** ```ruby:Gemfile group :test, :development do gem 'pry-rails' gem 'pry-doc' gem 'pry-stack_explorer' if RUBY_VERSION >= '2.0.0' gem 'pry-byebug' else # 以下はRuby1.9の時のみ使う(pry-byebugの代わりに) # debuggerは1.9以下でしか動作しない, remote は byebug で使えないようになった gem 'pry-debugger' gem 'pry-remote' end gem 'better_errors' gem 'binding_of_caller' gem 'hirb' gem 'hirb-unicode' gem 'tapp' gem 'awesome_print' gem 'quiet_assets' gem 'annotate', git: 'git://github.com/ctran/annotate_models.git' gem 'timecop' gem 'rspec-rails' gem 'guard-rspec' gem 'factory_girl_rails' gem 'database_rewinder' end ``` ### 1.pry-rails について `rails console`をirbではなくpryにしてくれる https://github.com/rweng/pry-rails ### 2.pry-doc について pry上でクラスやメソッドなどのドキュメントや定義元のソースコードを確認できる ```rb:pry-doc ■ドキュメントを見る show-doc String.nil? OR ? String.nil? ■ソースを見る show-source String.nil? OR $ String.nil? ``` https://github.com/pry/pry-doc ### 3 デバッガーの各種Gem #### 3-Ⅰ. pry-byebug(ruby2.0以上専用) について `ブレークポイント`を仕込みたいソースコード上に`binding.pry` という文字列を仕込むことで、`rails server`で起動したコンソール上でブレークポイントを通るときにpryによるデバッガが立ち上がる。 *注) pry-byebug は `Ruby2.0 以上` でしか使えない* [Ruby 2.0 向けのデバッガー byebugについて](http://qiita.com/joker1007/items/f132db1b4a5e3f9278e9) [pryでのデバッグ中にnextがstepの様な挙動をしてしまう問題の解決法](http://qiita.com/awakia/items/b79188f1cc710bae4b8d) [About-byebug-for-debug-with-pry](http://philippos2.github.io/blog/2013/07/28/about-byebug-for-debug-with-pry/) #### 3-Ⅱ. pry-stack_explorer について pry-stack_explorerはデバッガ起動時に`show-stack`と入力することでその時のスタックの情報が見られる。 ■pry-stack_explorerで使えるコマンド |コマンド|機能| |:----:|:----:| |show-stack| スタック表示| |up|親のフレームに移動| |down|参照されているフレームに移動| |frame|詳細フレームに移動| #### 3-Ⅲ. pry-debugger(ruby1.9以下専用) について pry-byebugと使う用途は同じ。 *注) pry-debugger は `Ruby1.9 以下` でしか使えない。* https://github.com/nixme/pry-debugger https://github.com/pry/pry/wiki/Available-plugins#pry-debugger #### 3-Ⅳ. pry-remote について アプリサーバを`バックグランド`で動かしている時 ([Pow!!](http://pow.cx/) または [Unicorn](http://unicorn.bogomips.org/)をデーモンモードで動かしている時など) に`pry-remote`というコマンドでデバッグコンソールを立ち上げることができる。 https://github.com/pry/pry/wiki/Available-plugins#wiki-pry-remote アプリサーバを`フォアグランド`で動かしている時 (`rails server` で立ち上げている時など) は`pry-byebug` や `pry-debugger` のみ入れておけば起動したコンソールから確認できるので`pry-remote`は必要ない。 *注)pry-byebugではpry-remoteをサポートしていないので使えない* [Dropped pry-remote support というリリースログ](https://github.com/deivid-rodriguez/pry-byebug/blob/master/CHANGELOG.md#100-101-2013-05-07) ### 4.better_errors, binding_of_caller について エラー時にブラウザに表示するエラー画面をいい感じにしてくれる。`binding_of_caller`を入れることで、エラー画面でREPLを操作できるようになる。 [Railsならbetter_errorsを入れるとデバッグがはかどるなぁ ](http://blog.livedoor.jp/sasata299/archives/51937655.html) ### 5.hirb, hirb-unicode について ActiveRecordのデータをテーブル形式で表示してくれる。 http://rochefort.hatenablog.com/entry/20120104/p1 https://github.com/pry/pry/wiki/FAQ#wiki-hirb [Pryの秘めた力を最大限引き出す4つの拡張機能[Rails] 「モデルの中身を綺麗に表示」を参照](http://morizyun.github.io/blog/pry-tips-rails-ruby/) ```rb:pryを開いている時に一時的にhirbを無効/有効にする > Hirb.disable > Hirb.enable ``` ### 6.tapp について 任意のオブジェクトやメソッドに`.tapp`としてメソッドチェインを形成することで、その時のオブジェクトの状態を出力させる。 メソッドチェインの途中のオブジェクトの状態を確認したいときに、処理を止めることなくデバッグプリントさせる。 http://namakesugi.blog42.fc2.com/blog-entry-108.html ### 7.awesome_print について `p` や `puts` などのようにプリントデバッグにとても有用な `ap` メソッドを定義してくれる。pryコンソールの出力のフォーマットもawesom_printを利用している。(.pryrcで定義) http://qiita.com/Linda_pp/items/d75d7c3953faa34a1f0e https://github.com/pry/pry/wiki/FAQ#wiki-awesome_print ### 8.quiet_assets について assets関連のファイルへのアクセスログを出力させないようにする https://github.com/evrone/quiet_assets ### 9.annotate について DBの定義情報を読み取り、ModelのクラスファイルにコメントでDBの情報を書き込む http://yoshifumisato.jeez.jp/wordpress/post/rails/693 https://github.com/ctran/annotate_models ### 10.timecop について 一時的に日時を任意に、設定する https://github.com/travisjeffery/timecop ### 11.guard-rspec について https://github.com/johnbintz/guard-rails Guardfileの定義情報をもとに、ファイル更新の都度テストを実行させる。Guardfileは基本的に`guard init`コマンドで自動生成したもの。`cmd: 'spring rspec -f doc'` となっているところだけ変更している状態。 そのためこのファイルでguardを動かすためには以下の2つの条件が必要です。 1. rspecを入れている 2. springを入れている ```ruby:Guardfile # A sample Guardfile # More info at https://github.com/guard/guard#readme guard :rspec, cmd: 'spring rspec -f doc' do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } watch('spec/spec_helper.rb') { "spec" } # Rails example watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } watch(%r{^spec/support/(.+)\.rb$}) { "spec" } watch('config/routes.rb') { "spec/routing" } watch('app/controllers/application_controller.rb') { "spec/controllers" } # Capybara features specs watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" } # Turnip features and steps watch(%r{^spec/acceptance/(.+)\.feature$}) watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } end ``` ### 12.factory_girl_rails について データの生成をフィクスチャからファクトリーに変更する。 https://github.com/thoughtbot/factory_girl ### 13.database_rewinder について `database_rewinder` はテスト毎にデータをクリアすることに対応するためのGem https://github.com/amatsuda/database_rewinder ***** ### ぼくの「.pryrc」 参考のページを見つつ取り入れられるところは取り入れて「.pryrc」を作っている。 「.pryrc」にはpryの設定と、関連するライブラリの連携設定などを記述。 【参考】 https://github.com/JuanitoFatas/dotpryrc/ https://github.com/pry/pry/wiki/Pry-rc ```ruby:.pryrc ## Settings Pry.config.color = true Pry.config.editor = "vim" Pry.config.prompt = proc do |obj, level, _| prompt = "" prompt << "#{Rails.version}@" if defined?(Rails) prompt << "#{RUBY_VERSION}" "#{prompt} (#{obj})> " end ## Alias Pry.config.commands.alias_command "lM", "ls -M" # Ever get lost in pryland? try w! Pry.config.commands.alias_command 'w', 'whereami' # Clear Screen Pry.config.commands.alias_command '.clr', '.clear' ## Below others cooperation # refs: https://github.com/pry/pry/wiki/FAQ#wiki-awesome_print if defined? AwesomePrint begin require 'awesome_print' Pry.config.print = proc { |output, value| Pry::Helpers::BaseHelpers.stagger_output("=> #{value.ai}", output) } # Pry.config.print = proc { |output, value| output.puts value.ai } #ページングなし rescue LoadError => err puts "no awesome_print :(" puts err end end # refs: https://github.com/pry/pry/wiki/FAQ#wiki-hirb if defined? Hirb Hirb::View.instance_eval do def enable_output_method @output_method = true @old_print = Pry.config.print Pry.config.print = proc do |output, value| Hirb::View.view_or_page_output(value) || @old_print.call(output, value) end end def disable_output_method Pry.config.print = @old_print @output_method = nil end end Hirb.enable end # refs: https://github.com/deivid-rodriguez/pry-byebug#matching-byebug-behaviour if defined?(PryByebug) Pry.commands.alias_command 'c', 'continue' Pry.commands.alias_command 's', 'step' Pry.commands.alias_command 'n', 'next' Pry.commands.alias_command 'f', 'finish' end ``` 以上。 ところどころ参考サイトへのリンクを貼らせていただきました!ありがとうございます。 |
|
| 42位 |
|
|||
|
18:42:33 |
(Simplearchitect 所属) |
|
このドキュメントでは、Chefを実行して、インフラを作成したい人が、既存のレシピがあるのを前提に、Chefの概要を理解するためのドキュメントです。Chef-soloの構成のみに対応した記述になっています。理解が間違えているところとかあればご指摘ください。
1. Chefの概要 --- ### 1.1. Chefとは シェフは、インフラストラクチャーをコードに変換するための自動化プラットフォームです。仮想環境でも、物理環境でも、クラウドでも使う事ができます。インフラストラクチャを自動化することで、プロダクトのマーケット投入を早めたり、スケールや複雑さに対応したり、システムを安全に保ちます。 ### 1.2. Chefの仕組み Chefはサーバーをセットアップして、希望の状態にするための「クックブック」「ノードオブジェクト」というDSL(設定ファイルっぽいもの)をローカルのワークステーションで作成します。それらのDSLをローカルのバーチャルマシンや、クラウド、物理サーバーに反映させると、サーバーのセットアップが完了します。  ### 1.3. Chefで使われる用語 Chefで使われる代表的な用語は次のような物です。chefはServer-Client形式のものと、単独で使うものがありますが、本ドキュメントでは後者を取り扱います。 #### 1.3.1. cookbookとreceipe cookbookというのは、お料理本の意味で、複数のreceipe(レシピ)を含みます。一つのcookbookが一つの料理の料理方法が載っています。例えばカレーです。receipeは、カレーでも、様々なカレーがあるので、そのカレーの種類だけ作り方があり、それがreceipeにまとまっているイメージです。 技術的にいうと、MySQLとかApacheとか毎にcookbookがあり、その中にいくつかreceipeがあります。例えばMySQLのMasterとか、Slaveとかです。具体的なreceipeを適用すると、そこで設定されたとおりに、実際のサーバーが出来上がります.receipeには、こうなってほしいという望まれるサーバーの状態が定義されています。 #### 1.3.2. knife chefは料理の例えになっています。料理をするには包丁とかが必要になります。[knife](http://docs.opscode.com/chef/knife.html)は、実際にcookbookをサーバーに適用する時に使うコマンドです。 ### 1.4. Chefの考え方 #### 1.4.1. Immutable Infrastructure 最近は仮想化の発達により、クラウドや、仮想マシンにより、カンタンにサーバーのインスタンスを作ったり、壊したりということができるようになりました。また、アプリケーションのインストール等も従来は、手で手順を作って、その手順に従ってコマンドを打って環境を作っていました。最近は、サーバーへのインストールや設定、そしてインスタンスの起動や停止といったことまで「プログラム」で実行できるようになりました。この動きをInfrastracture as a codeといいます。この流れで出てきた考えがImmutable infrastructureという考えです。通常のサーバーは一度セットアップのあとは、ライブラリのバージョンアップ、設定変更にあわせて、サーバーで操作を行い、サーバーの状態を変更してきました。ところが、こういう事をすると、サーバーにより、差異がでてきたり、インストールしているパッケージの整合性が取れなくなったり、挙動がおかしくなったり、操作ミスしたりといったようにいろいろな問題が発生してきます。 ですので、一度作成したサーバーのレシピは、サーバー側では設定変更を行わず、あくまで、Chefのようなプロビジョニングツールの方で、レシピ等を変更するようにします。つまり、サーバーの状態はいつも同じ(不変)な状態をキープできるようになります。そうすると、どこでも、何回セットアップしたとしても、まったく同じ状態のサーバーがでてきます。 ポイントは、サーバー側で設定変更等をしないことです。あくまでレシピを変更しましょう。 #### 1.4.2. 冪等性 レシピを作るときには、冪等性という性質をもっている事が重要視されます。冪等性は、Chefにおいては、何回レシピを適用しても、結果(サーバーの状態)が同じになるような性質です。例えば、最初にApacheのレシピを作ったときに、まず、Aapcheをインストールするところまで作ったとして、レシピを流してサーバーを作りました。次に、設定ファイルの変更をレシピに追加して、再度レシピを同じサーバーに流れた時にも、1からインスタンスを上げて同じレシピを流した時でも同じ結果が得られるような作りにしましょうということです。 #### 1.4.3. 捨てやすくすること Chefのようなツールがあれば、サーバーがおかしくなったらいつでも、それを捨てて、すぐにクラウドでインスタンスを立ち上げて、Chefを流して、セットアップするというのが非常に短時間でできます。 だから、一度作成したインスタンスをずっと使おうという発想ではなくて、今まで使っていたインスタンスはいつでも捨ててChefを流したら同じ状態になるような設計にしましょう。 2. Chefの実行方法 --- ### 2.1. chefのインストール 次のコマンドもしくは、Gemfileに記述しましょう ``` % gem install chef % gem install knife-solo ``` 尚、このGemが使える前提条件は次の通りです。 + Unix, Linux, MacOSX, MS Windows + MacOSXの場合はXCodeをインストールする + githubのアカウントが必要なのでgitもインストールしておく + サーバーにアクセスできるようにする(SSHで) + サーバーのドメイン名か、IPAddressが事前にあること [Install Chef 11.x on Workspation](http://docs.opscode.com/install_workstation.html) ### 2.2. 初期セットアップ #### 2.2.1. リポジトリの設定 chefのファイルは、GitHub等のレポジトリで管理するのが望ましいでしょう。1つのプロジェクトに対して1つのchef用リポジトリというレベルの粒度で作成しましょう。 knifeコマンドを使ってリポジトリを初期化します。knifeの設定ファイル(~/.chef/knife.rb)等が作成されます。 ``` % knife solo init chef ``` ### 2.3. ディレクトリの構造 適当ですが、私のプロジェクトのchefディレクトリ以下は次のような構成になっています。 ``` $ tree -L 2 . |-- Berksfile |-- Berksfile.lock |-- Gemfile |-- Gemfile.lock |-- cookbooks | |-- Berksfile.lock | |-- apache2 | |-- apt | |-- aws | |-- build-essential | |-- chef_handler | |-- database | |-- homebrew | |-- iptables | |-- logrotate | |-- mysql | |-- openssl | |-- pacman | |-- postgresql | |-- windows | |-- xfs | `-- yum |-- data_bags |-- environments |-- nodes | |-- vagrant.json | `-- mysql01.json |-- roles | |-- all_in_one.json | |-- app_server.json | |-- mysql_master_server.json | |-- mysql_slave_server.json | `-- varnish_server.json |-- site-cookbooks | |-- info-base | |-- mysql55 | |-- mysql56 | `-- varnish `-- vendor `-- bundle ``` #### 2.3.1. 一般的なディレクトリ 各ディレクトリについて説明していきます。 | ディレクトリ | 名称 | |:-----------|------------:| | cookbooks | インターネットから取得したcookbookが複数格納されます | | data_bags | ドメインデータが格納されます | | environments | staging/production等環境固有の状態が格納されます | | nodes | 個別のノードの状態が格納されます | | roles | 役割(web-server/db-server等)の状態が格納されます | | site-cookbooks | 自作のcookbookが作成されます | ##### 2.3.1.1. cookbooks インターネットから取得したcookbookが格納されます。このセクションは自分で触らない方がよいと思います。中身を読むだけにしましょう。Berksというツールを使って、このcookbookを取得します。[Opscode Community](http://community.opscode.com/cookbooks)に存在するcookbookを取得して使う事ができます。 cookbookの具体的な中身に関してはこの後解説いたします。 ##### 2.3.1.2. data_bags このセクションには、OSでセットアップするユーザ等、OSセットアップに関する「ドメイン」のデータを格納します。 具体的にはSSHの鍵のデータもここで保持されたりします。 ##### 2.3.1.3. environments staging/production/develop等の環境の違いに関する設定が記述されます。 ##### 2.3.1.4. nodes 個別の具体的なnodeに対する設定や状態が記述されます。 ##### 2.3.1.5. roles web-server, db-server等、サーバーの種類別の設定や状態が記述されます。 ##### 2.3.1.6. site-cookbooks cookbooks以外の、自分たちで作ったcookbookをここに格納します。 ``` $ tree -L 3 |-- chef | |-- Berksfile | |-- Berksfile.lock | |-- Gemfile | |-- Gemfile.lock | |-- cookbooks | | |-- Berksfile.lock | | |-- apache2 | | |-- apt | | |-- aws | | |-- build-essential | | |-- chef_handler | | |-- database | | |-- homebrew | | |-- iptables | | |-- logrotate | | |-- mysql | | |-- openssl | | |-- pacman | | |-- postgresql | | |-- windows | | |-- xfs | | `-- yum | |-- data_bags | |-- environments | |-- nodes | | |-- vagrant.json | | |-- info_ec2.json | | `-- mysql01.json | |-- roles | | |-- all_in_one.json | | |-- app_server.json | | |-- mysql_master_server.json | | |-- mysql_slave_server.json | | `-- varnish_server.json | |-- site-cookbooks | | |-- info-base | | |-- mysql55 | | |-- mysql56 | | `-- varnish | `-- vendor | `-- bundle ``` ### 2.3. Chefのファイルの読み方 chefの設定を読み解くためには、nodesディレクトリにある、nodeオブジェクトファイル(jsonファイル)からたどってみるのがいいと思います。 ``` $ tree -L 2 . |-- Berksfile |-- Berksfile.lock |-- Gemfile |-- Gemfile.lock |-- cookbooks | |-- Berksfile.lock | |-- apache2 | |-- apt | |-- aws | |-- build-essential | |-- chef_handler | |-- database | |-- homebrew | |-- iptables | |-- logrotate | |-- mysql | |-- openssl | |-- pacman | |-- postgresql | |-- windows | |-- xfs | `-- yum |-- data_bags |-- environments |-- nodes | |-- vagrant.json | `-- mysql01.json |-- roles | |-- all_in_one.json | |-- app_server.json | |-- mysql_master_server.json | |-- mysql_slave_server.json | `-- varnish_server.json |-- site-cookbooks | |-- info-base | |-- mysql55 | |-- mysql56 | `-- varnish `-- vendor `-- bundle ``` #### 2.3.1. nodes/nodeファイル ちなみに、nodes/roles/environmentのディレクトリのファイルは、書き方は同じになっています。これらは設定を整理するために分割されているだけです。後述の特定のルールによって設定が上書きされます。例えば今のnodeファイルを見てみましょう。 これは、rolesにあるall_in_oneというのを実行(run_list)する事だけが示されています。 ``` # vagrant.json {"run_list":["role[all_in_one]"]} ``` じゃあ、次にRoleのall_in_one.jsonを見てみます。 #### 2.3.2. rolesのファイル 文法は先ほどのものと同じです。 こちらのファイルはいろいろ定義されていますね。このroleは、Vagrantで作ったローカルの仮想マシンに、全てのプロダクトをぶち込むファイルになっています。 読むポイントは次の要素です。 | ディレクトリ | 名称 | |:-----------|------------:| | default_attributes | レシピの設定内容を定義します | | description | ファイルの目的を記述します | | run_list | 実行するレシピを順に記述します | | override_attributes | レシピの設定内容を強制的に上書きします | ``` { "name": "all_in_one", "chef_type": "role", "json_class": "Chef::Role", "default_attributes": { "mysql": { "server_root_password" : "password", "server_repl_password" : "password", "server_debian_password" : "password" }, "varnish": { "backend_host" : "127.0.0.1", "backend_port" : "8080", "listen_port" : "80" } }, "description": "For vargrant environment. All in one package", "run_list": [ "base::packages", "info-base", "ruby", "ruby::passenger", "apache2", "supervisor", "varnish", "yum::remi", "mysql::server" ], "env_run_lists" : {}, "override_attributes": { "yum" : { "remi" : { "includepkgs" : "compat-mysql* mysql*" } }, "apache": { "listen_ports" : ["8080"] } } } ``` 基本的に大きなポイントはrun_listと、attributesの設定です。run_listは、実行するレシピ、attributeは、サーバーの設定ファイルに書かれるような設定値を定義します。この設定値は、cookbook/role/environment/nodeで定義されたものが、下記のルールの従って上書きされます。最終的な上書きされた結果がサーバーに反映されます。多分サーバーを作る時は、cookbookには、デフォルトの値が書かれて、roleで、webサーバー等のサーバー種別毎に決まった値が設定されます。environmentには、staging/production毎に違うような値のみを記述します。最終的にnodeでnode毎に違う情報(ホスト名等)のみを上書きします。こうしておくと、設定値がDRYになるのでオススメです。 この上書きルールですが、下記の図がわかりやすいかと思います。   具体的には例えば次のページにかいてあります。 [About Roles](http://docs.opscode.com/essentials_roles.html) 他にもenvironment等も設定される可能性がありますので、書き方が同じなので省略します。次に、具体的なcookbookを見てみましょう。単純なのがよいので、私の作成したvarnishのモノを見てみましょう。 #### 2.3.3. cookbookの読み方 cookbookを自分で作りたい時は次のコマンドで実行します。 ``` $ knife cookbook create varnish -o site-cookbooks ``` site-cookbooksのvarnishディレクトリに行くと次のようなファイルができています。読む側とすると重要なのは、attributes, receipes, templatesの3つです。それぞれ次のような意味です。 | ディレクトリ | 名称 | |:-----------|------------:| | attributes | デフォルトの設定情報を定義します | | files | アップロードしたいファイルを格納します | | receipes | レシピの実行内容を記述します | | templates | 設定ファイルのテンプレートを記述します | ``` $ tree . ├── CHANGELOG.md ├── README.md ├── attributes │ └── default.rb ├── definitions ├── files │ └── default ├── libraries ├── metadata.rb ├── providers ├── recipes │ └── default.rb ├── resources └── templates └── default ├── default.vcl.erb └── varnish.erb ``` 最初に読みたいのは、receipe/default.rbです。cookbookがrun_listで実行されたらまずコレが呼ばれます。細かい事は説明しませんが、書いてあることだけを説明します。 ``` remote_file "/tmp/#{node['varnish']['file_name']}" do source "#{node['varnish']['remote_uri']}" end rpm_package node['varnish']['rpm_package_name'] do action :install options "--nosignature" provider Chef::Provider::Package::Rpm source "/tmp/#{node['varnish']['file_name']}" end package "varnish" do action :install end template "/etc/varnish/default.vcl" do source "default.vcl.erb" mode 644 owner "root" group "root" notifies :restart, "service[varnish]" action :create end template "/etc/sysconfig/varnish" do source "varnish.erb" mode 644 owner "root" group "root" notifies :restart, "service[varnish]" action :create end service "varnish" do supports :status => true, :restart => true, :reload => true action [ :enable, :start ] end ``` ちなみにvarnishのインストール手順 (CentOS)はこんな感じです ``` % rpm --nosignature -i http://repo.varnish-cache.org/redhat/varnish-3.0/el6/noarch/varnish-release/varnish-release-3.0-1.el6.noarch.rpm % yum install varnish ``` これは、インストールだけですが、設定ファイルとして、/etc/varnish/default.vclと、/etc/sysconfig/varnishを設定する必要があります。 尚、package等のアクションは、"chef package"などでgoogleすれば、一撃でリファレンスがでてきます。 例えばここです [package - Chef docs](http://docs.opscode.com/resource_package.html) ##### 2.3.3.1. remote_file リモートからファイルをとってきて格納するための記述です ##### 2.3.3.2. rpm_package これは、rpmコマンドに相当する記述です。 ##### 2.3.3.3. package yum, apt等のパッケージインストールのためのコマンドです。OSによって発行されるコマンドが異なります。 ##### 2.3.3.4. template 設定ファイルを作成したり、書き換えたりします。notifiesという設定をすると、このファイルが書き換えられた後のアクションが記述できます。ここでは、varnishの設定ファイルが書き換えられたら再起動になっています。 ##### 2.3.3.5. service サービス化と自動起動の設定をします。 ##### 2.3.3.6 bash この他によく使われる物としてbashというアクションがあります。これは、サーバー側でコマンドを発行するためのものです。出来るだけbashを使わないようにします。というのも、他のアクションは、冪等性が考慮されているので、何回実行しても同じ結果になるようになっています。ところが自分でこのbashを作ると、冪等性のコーディングをしないといけませんし、OS毎の挙動の違いも場合によっては自分で書かないといけません。これは面倒です。 ##### attributes さて、default.rbでは、node['varnish']['file_name']といったような記述が見受けられます。これらの変数の具体的な定義がattributesに書かれています。この値はRole/Environment/Nodeで上書きされる可能性があります。 ``` default['varnish']['versions'] = "3.0-1.el6" default['varnish']['file_name'] = "varnish-release-3.0-1.el6.noarch.rpm" default['varnish']['remote_uri'] = "http://repo.varnish-cache.org/redhat/varnish-3.0/el6/noarch/varnish-release/varnish-release-3.0-1.el6.noarch.rpm" default['varnish']['rpm_package_name'] = "varnish-release" default['varnish']['backend_host'] = "127.0.0.1" default['varnish']['backend_port'] = "80" default['varnish']['listen_port'] = "6081" ``` ##### templates templateアクションで設定ファイルを作る事を選択した場合は、templatesディレクトリの下に定義ファイルを作りましょう。上記のattributesで設定した値を使う事ができます default.vcl.erb ``` backend default { .host = "<%= node['varnish']['backend_host']%>"; .port = "<%= node['varnish']['backend_port']%>"; } acl backend_apps { "127.0.0.1";} sub vcl_recv { if (client.ip ~ backend_apps && req.http.X-REFRESH) { set req.hash_always_miss = true; } } ``` 他のcookbook等もこれで一通り読み込むことができます。 ### 2.4. Chefの実行 さて、Chefのnodeオブジェクトを実行して、実際にサーバーを望ましい状態にしてみましょう。 #### 2.4.1. Berksfile そのまえに、まず、プロジェクトで使うcookbookを取得してみましょう。自分で作るもの以外は既存のものを使用して、attribute部分のみをrole/environment/nodeで書き換えることができます。Berksは、rubyのGemfileのcookbook版みたいな感じです。ここに欲しいcookbookを書いておきましょう。 ``` site :opscode cookbook 'mysql', '~> 4.0.20' cookbook 'database' cookbook 'apache2' cookbook 'yum' ``` そして、実行します。 [Berkshelf](http://berkshelf.com/) ``` % gem install berkshelf % berks install ``` berkshelfのgemはGemfileに書いておくといいでしょう。これによりcookbooksディレクトリ以下にcookbookが取得されます。 #### 2.4.2. レシピの実行 さて、遂にレシピを実行する時がきました。読み込んで理解したレシピを流してみましょう。 ここでは、解説しませんが、Vagrant等をつかって、仮想マシンを用意しておきましょう。 ##### 2.4.2.1. 事前設定 レシピを流したいサーバーに、`% ssh server_name` でログイン出来るように設定しておきましょう。[vargrant](http://www.vagrantup.com/)を使っていない場合は必要ありませんが、vargantの環境の場合は次のようなコマンドが有効です。sshの設定は周りの人に聞いてみてください。 ``` % vagrant ssh-config --host server_name >> ~/.ssh/config ``` ##### 2.4.2.2. Why-Run 実際にレシピを流す前に、サーバーにレシピを流したら一体どうなるか?(Why-Run)を確認するコマンドがあります。実行はnode以下のファイルを指定するといいでしょう。-WオプションがWhy-Runのオプションです。これを実行すると、サーバーで実行される予定の内容がプリントされます。もしエラーが発生したら、上記のファイルを修正します。 ``` bundle exec knife solo cook server_name nodes/vagrant.json -W ``` ##### 2.4.2.3. knife-solo [knife solo](http://docs.opscode.com/chef/knife.html)コマンドのサブコマンドcookで実際にサーバーをレシピの状態に反映させてみます。Why-Runで動作確認したら、-Wオプションをとってコマンド実行しましょう。 ここでは、server_nameというsshでログインできるサーバーにnodes/vagrant.jsonのnodeオブジェクトから呼ばれるレシピが実行されます。 ``` bundle exec knife solo cook server_name nodes/vagrant.json ``` これで、無事サーバーがセットアップされるはずです。 4. 参考文献 --- [Chef](http://docs.opscode.com/) [入門 Chef Solo](http://www.amazon.co.jp/%E5%85%A5%E9%96%80Chef-Solo-Infrastructure-as-Code-ebook/dp/B00BSPH158) **Chef Fundamentals Module 1** (下のアイコンをクリックすると、チュートリアルが見れます!) [](https://learnchef.opscode.com/screencasts/fundi-webinar-week-1/) ちなみに、同じところに沢山のChefのVideoチュートリアルがあります. 他にも[Youtube Opscode](http://www.youtube.com/user/Opscode)でもいろいろな講演をみることができます。 以上です |
|
| 43位 |
|
|||
|
13:58:41 |
(株式会社Bizcast 所属) |
|
Twitter, GitHub, Qiita などのように root/(username) でユーザーページをルーティングするところが増えてきている. このルーティングを採用し, help などのユーザー名を許可すると, root/help が奪われてしまう. そこで, 登録時に validate で, ある程度排除するのが習わしになっていると思うが, 急に root 直下に置きたいページが増えたときなどに取得されていると悲しいことになる.
また, サブドメインを利用するサービスだと, api などをうっかり取られてしまうケースが後を絶たない. http://api.hatenablog.com/ みたいに取られることによる面白みもあるが, おおむねつらい. 実際, twitter では search アカウントが取られていて, TweetDeck では twitter.com/search~~ という URL をうまく解釈できず, search ユーザーの情報を表示する挙動となり, つらさの伝播が観測される. そこで, 必要になりそうな名前をリザーブリストとして共有しておき, 悲しみを防ぎたい. 適当に思いついたのまとめただけだから, あとはたのんだ. なお, 複数形については意図的に除外しているので注意してほしい. (2014/02/18 1:29追記: 色々ジャンル分け等崩壊してきたのでどうにかしたい) ### 便利情報 - 同様の趣旨の rubygem: [rono23/reserved_word](https://github.com/rono23/reserved_word) - [Twitter の non_username_paths](https://dev.twitter.com/docs/api/1.1/get/help/configuration) - 差別用語等のリスト: [shutterstock/List-of-Dirty-Naughty-Obscene-and-Otherwise-Bad-Words](https://github.com/shutterstock/List-of-Dirty-Naughty-Obscene-and-Otherwise-Bad-Words) - 名前空間について: [これからの名前の話をしよう](http://www.slideshare.net/tadsan/ss-31387472) ### 基本 index, home, top, help, about, security, contact, connect, support, faq, form, mail, update, mobile, phone, portal ### 紹介 tour, tutorial, navigation(navi), manual, doc, company, store, shop, topic, news, information(info), howto, pr, press, release, sitemap, ### 課金 plan, price, business, premium, member ### 規約 term, privacy, rule, inquiry, legal, policy ### リソース icon, image(img), photo, css, stylesheet(style), script, src, js, javascript, dist, asset, source, static, file, flash, swf, xml, json, sag, cgi ### ページ account, user, item, entry, article, page, archive, tag, category, event, contest, word, product, project, download, video, blog, diary, site, popular, i (my, me), owner, profile, self, old, first, last, start, end, special, design, theme, purpose, book, read, organization, community, group, all ### 機能 status(state), search, explore, share, feature, upload, rss, atom, widget, api, wiki, bookmark, captcha, comment, jump, ranking, setting, config, tool, connect, notify, recent, report, system(sys), message(msg), log, analysis, query, call, calendar, friend, graph, watch, cart, activity ### 認証 password, auth, session, register, login, logout, signup, signin, signout, forgot, admin, root, secure ### 編集 get, show, create, edit, update, post, destroy, delete, remove, reset, error, new, dashboard ### 求人 recruit, join, offer, career, corp ### 特定のアプリケーションで使われるもの - Apache: cgi-bin, server-status, balancer-manager, ldap-status, server-info, svn ### SQL 等で使う単語 - 2 字: as, by, if, is, on, or - 3 字: add, dir, off, out, put - 4 字: case, else, find, then, when - 5 字~: count, order, select, switch ### その他 school, developer(dev), test, bug, code, guest, app, maintenance, roc, id, bot, game, forum, contribute, usage, feed, ad, service, official, language, repository, spec, license, [asct](https://www.google.co.jp/search?q=asct+%E7%89%B9%E5%AE%9A), dictionary(dict), version(ver), gift, alpha, beta, tux, year, public, private, default, request(req), data, master, die, exit, eval, issue, thread, diagram ### 怖いやつ undef, nan, null, empty, 0 ## サブドメイン ### 基本 www ### その他 - [国別コードトップレベルドメイン](http://ja.wikipedia.org/wiki/%E5%9B%BD%E5%88%A5%E3%82%B3%E3%83%BC%E3%83%89%E3%83%88%E3%83%83%E3%83%97%E3%83%AC%E3%83%99%E3%83%AB%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3) |
|
| 44位 |
|
|||
|
00:56:02 |
(Consensus Base Inc. 所属) |
|
随時更新します。 ## 対象の人 * 使ったことないけど、軽く理解はしておきたい人 * 本格的に勉強する前に、大まかな概要を知りたい人 ## 特徴 * サーバーサイドのJavaScript * 非同期 (I/Oの処理結果を待たず処理を進める) * ノンブロッキングI/O (I/Oの結果を待たないで処理をすすめる。I/O処理が終了したらコールバック関数を実行。) * イベントドリブン (何かしらのキッカケで動き始める) * シングルスレッド (メモリ消費が少ない、仕事切り替えが少ないので速い) * JavaScript エンジンが Google の V8 で速い ### Node.jsで作られたアプリの特徴 * 小さいデータや小さい計算の場合、速い * メモリ消費量が少ない * ローカルとリモートとのデータのやり取りをシームレスに行うフレームワーク(Meteor)などが出てきている * サーバ側、クライアント側で処理の共通化ができる (入力値チェックなど) * サーバサイドのコード変更した場合、Node.jsアプリの再起動が必要(な場合が多い) * リアルタイムなアプリケーションに合う * シングルページアプリケーションに合っている * WebSocketを利用するアプリに合っている ## node.js は何を解決する? * C10K問題 (クライアント1万台接続問題)を解決する * 従来のWebサーバは、1万接続もあるとパフォーマンスが落ちる。ので、その問題を解決する ## どのように解決する? * ノンブロッキングI/O * I/Oの待ちをなくすことで、沢山の接続を速くこなすことができる 例えば、ハードディスクの入出力のスピードは、CPUやメモリーから見ると非常に遅い。書き込み完了を待っているのは非常に時間の無駄なので、ハードディスクに書き込む命令を出して、書き込めたかの結果を待たず次の処理をどんどん進めてしまうので非常に処理を早くすすめられる。書き込めた場合にコールバックが呼ばれる。 ## フレームワーク? * Node.jsは、フレームワークではありません。ライブラリでもありません。 * JavaScript アプリケーションのプラットフォーム * Node.jsでは、Webサーバとアプリケーションが同じ PHPのWebサイトで無理やり例えると、Apache + mod_php + Webアプリ です。なので、Node.js の Hello Worldのサンプルは、HTTPを受付け、200とヘッダーと内容を返す、というのが多い ## サーバーサイドJavaScript * ファイル読み書きは、fsモジュールを使う ## node.js 上の有名なフレームワーク * Express (シンプルなフレームワーク) * Backbone.js * Sails (Railsライクなフレームワーク) * Meteor 訂正) AngularJS (Angular2)を、node.jsのフレームワークと記載しておりましたが、ご指摘によりJavaScriptのフロントエンドのフレームワークということで削除いたしました。 ## 基礎用語 * コールバック関数(非同期の処理が終わったら呼ばれる関数) * CommonJS (共通化しようとしている規格) * libuv (libev, libeio) 非同期I/Oライブラリ, イベントループ・ライブラリ * DIRT (data-intensive real-time) DIRTy アプリ * npm (node package manager) * 沢山のモジュールとそのバージョンやモジュールの依存性を管理する ## クライアントサイド JavaScript との違い * ホストオブジェクト: ブラウザの globalな window オブジェクト * require * モジュール: ファイルI/Oなど * C/C++プラグインで拡張可能 * npm: パッケージマネージャー ## 参考 [Node js 入門](http://www.slideshare.net/SatoshiTakami/intoroduction-nodejs "Node js 入門") |
|
| 45位 |
|
|||
|
19:46:27 |
(サイバーエージェント 所属) |
|
最近聞かれたので、 自分がよく使う`Vim`の移動コマンドをまとめてみました。 プラグインいらずの素の`Vim`ですが、 プラグインなしで十分高速に移動できます。 特に一行一文字検索の`f`コマンドとか、 変更してきた箇所をもどれる`g;`コマンドとかが使えてくると本当に便利です。 あっ、この記事は主にファイル内の移動に限っています。 ##基本移動 初心者はまず、矢印キーを脱却して、`h, j, k, l`へ。 `10j`のように数字を先に打つと、一気に数字分移動できる | コマンド | 移動内容 | |:----:|:----:| | h | 左へひとつ | | j | 下へひとつ | | k | 上へひとつ | | l | 右へひとつ | #####ステップアップ 表示上の行移動 | コマンド | 移動内容 | |:----:|:----:| | gj | 下へひとつ | | gk | 上へひとつ | ``` 115 abcdefg hijklmn 116 abcd ``` 115行目のbにカーソルがある場合、 通常の`j`移動だと、`b➡b` 表示行の`gj`移動だと、`b➡i` ##行移動 | コマンド | 移動内容 | |:----:|:----:| | 0 | 行の先頭へ(インデント無視して先頭へ) | | ^ | 行の先頭へ | | $ | 行の末尾へ | | + | 下の行の先頭へ | | - | 上の行の先頭へ | #####ステップアップ 前述同様`g`をつけると表示上の移動へ(直感的) ##ページ移動 ガッと移動したい場合 | コマンド | 移動内容 | |:----:|:----:| | ctrl + u | 半画面分 上へ | | ctrl + d | 半画面分 下へ | | ctrl + b | 一画面分 上へ | | ctrl + f | 一画面分 下へ | | { | 段落毎に上へ | | } | 段落毎に下へ | | gg | そのファイルの先頭へ | | G | そのファイルの末尾へ | 他には`:100`で行指定で移動できる。 この場合だと、100行目に移動。 ##単語移動 | コマンド | 移動内容 | |:----:|:----:| | w | 単語の先頭に進む | | b | 単語の先頭に戻る | | e | 単語の末尾に進む | | ge | 単語の末尾に戻る | 単語分を進むか戻るか、さらに大文字もある。 #####ステップアップ | コマンド | 移動内容 | |:----:|:----:| | W | 単語の先頭に進む | | B | 単語の先頭に戻る | | E | 単語の末尾に進む | | gE | 単語の末尾に戻る | 違いは次のような単語を一語とみるかどうか `abc.def.ghi` 小文字なら5単語 `abc . def . ghi` 大文字なら1単語 `abc.def.ghi` `a➡i`に行きたい場合、 `e`移動なら5回 `a➡c➡.➡f➡.➡i` `E`移動なら1回 `a➡i` ##一行一文字検索 これホント便利。多用してます。 | コマンド | 移動内容 | |:----:|:----:| | f | その行の順方向に一文字検索 | | F | その行の逆方向に一文字検索 | | t | その行の順方向に一文字検索(カーソルは文字の手前) | | T | その行の逆方向に一文字検索(カーソルは文字の手前) | `abcdefg hijklmn` `a`にカーソルがある場合、 `fi`で一気に`i`に移動。 #####ステップアップ これとfキーの組み合わせが、本当に便利。 | コマンド | 移動内容 | |:----:|:----:| | ; | 順方向に繰り返し検索 | | , | 逆方向に繰り返し検索 | `abcdefg abcdefg abcdefg` `a`から三つ目の`e`に行く場合、 `fe` `;` `;` そこから二つ目の`e`に戻る場合 `,` ##検索移動 一般的なページ内文字列検索 | コマンド | 移動内容 | |:----:|:----:| | / | 順方向に文字列検索 | | ? | 逆方向に文字列検索 | | n | 順方向に繰り返し検索 | | N | 逆方向に繰り返し検索 | `abcde abced abcedf` この場合、`/edf`で末尾の`edf`に移動。 複数行にわたって検索可能。 ##括弧移動 これは`d`の削除とか`y`のコピーと合わせて使うと便利。 | コマンド | 移動内容 | |:----:|:----:| | % | 対となる括弧へ移動 | `(abc)` カーソルが`(`から`%`コマンドで`)`へ移動 ##TIPS ####ジャンプリストを辿る ファイル内も移動できるけど、ファイル間の移動に便利。 直前に編集していたファイルに戻りたいとか。 今回の記事にはそぐわないかな。。 | コマンド | 移動内容 | |:----:|:----:| | ctrl + o | ジャンプしてきた箇所に戻る | | ctrl + i | ジャンプしてきた箇所に進む | ####変更リストを辿る 自分がしたファイルの変更履歴をたどれる。便利。 | コマンド | 移動内容 | |:----:|:----:| | g; | 変更してきた箇所に戻る | | g, | 変更してきた箇所に進む | ####マーク移動 あんまり使っていないから、要望があるまでカット。 ##最後に この移動と組み合わせて削除とかコピーとかいろいろできることが、 vimの移動の便利なところですね。 # 外部アカウント 技術情報のみつぶやくアカウント作成しました。VimやNeoVimの最新情報も発信していきます。 [Twitterはこちら] (https://twitter.com/takeharumikami) [Feedlyのフォローはこちら] (http://cloud.feedly.com/#subscription%2Ffeed%2Fhttp%3A%2F%2Fqiita.com%2Ftakeharu%2Ffeed) |
|
| 46位 |
|
|||
|
10:16:34 |
(Moneyforward 所属) |
|
## Rubyの開発効率を高めたい!
vimでRubyやRails開発するときに、デフォルトの状態では非力ですよね。 開発効率を高める9つのプラグインを紹介します。 - **alpaca_tags** ctagsの非同期生成 - **neosnippet** Rails/sinatra/rspec等の補完 - **swtich.vim** `.present?`:`.brank?`など対応するキーワードを切り替える - **rubocop** 文法エラー、スタイルチェック - **vim-rails** Railsプロジェクト用プラグイン - **unite-rails** Railsプロジェクト用Unite-source - **vim-endwise** `if...end`など対応するキーワードの自動補完 - **ruby-matchit** `%`を拡張して、`def...end`等のキーワードを移動出来るようにする。 - **vim-ref** Ruby/Gemsのリファレンスを引く ## インストール vimのプラグイン管理には[NeoBundle]( http://qiita.com/items/1c32d3f24cc2919203eb )を使います。 [一括インストールはコチラ]( https://gist.github.com/6695766 ) ## [AlpacaTags](https://github.com/alpaca-tc/alpaca_tags) Vim + Ctagsってかなり便利ですよね。 tagジャンプの機能を使えば、プロジェクト内のメソッドやクラスの探索を一瞬で行えます。 ただし、IDEと違って手動で生成するのが面倒。。 そう思っていた時期が僕にもありました。 **自動化すればいいじゃないか** Gemfile更新のタイミング、ファイル保存のタイミングで自動的にコマンドを実行すれば、 いつでも新鮮なtagsを使うことが出来ます。  - `:AlpacaTagsUpdate` - Git配下のtagsを生成します。 - `:AlpacaTagsBundle` - Gemfileからgemのtagsを生成します。 - `:AlpacaTagsSet` - 生成したtagsを読み込みます。 - `Unite tags` - uniteを使ってtagを操作します [オススメの設定](https://gist.github.com/6581417) ## [neosnippet](https://github.com/Shougo/neosnippet.vim) snippets補完は、入力中の文字に対して予め容易された一連の処理を展開する補完です。 これは、一度使ってみると癖になります。  [ オススメの設定 ]( https://gist.github.com/4521425 ) Railsを覚えたてのころ、僕は本気でsnippetsを書いて **全てを補完** することにしました。 View/Controller/Route/Migration/Rspecを補完したneosnippetに死角はありません。(無駄は多い) Railsで動的に読み込むsnippetを変える場合は、後述するvim-railsを使いましょう。 [Snippetsダウンロード(書き換えて使ってください)](http://cl.ly/3o471d471T3a/download/ruby_snip.zip) ## [switch.vim](https://github.com/AndrewRadev/switch.vim) **入れておいて、絶対損は無い** と言い切りましょう。 `:Switch`をいうコマンドを実行すると、 [`true`,`false`]や[`.blank?`,`.present?`]といった定義されたキーワードの切り換えを行えます。  正規表現やキーワードの定義が可能です。 僕は、Railsのcontrollerで記述するstatusをSwitchで切り替えています。 `[404, ':not_found']`などと設定しておくと、結構便利ですよ。 [オススメの設定はコチラ](https://gist.github.com/6696152) ## [rubocop](https://github.com/bbatsov/rubocop) rubocopを使うと、Rubyのコードがコーディングルールに遵守されているかチェック出来ます。チームで開発する場合には必須ですね。 詳細は[コチラの記事](http://qiita.com/yuku_t/items/0ac33cea18e10f14e185)が参考になります。 ## [vim-rails](https://github.com/tpope/vim-rails) railsを書くときに、移動をスマートにしたり、vimからコマンドを実行する事が出来ます。 私がvimを使う理由は、vim-railsによってコードが何よりも書きやすいからに他なりません。 `:R`でview - controllerを移動したり、`:A`でテストコードとを行き来出来ます。 [ Youtube ]( http://www.youtube.com/watch?v=30P8DSNOZuU ) [ オススメの設定 ]( https://gist.github.com/4521441 ) ちなみに、`autocmd User Rails/spec/models/*`といった構文はもう使えなくなりました。 未だにこちらを使い人は、[コチラを参照]( https://gist.github.com/5975234 )してください また、NeoSnippetと併用する場合は下記のように記述します。 ```vim:.vimrc autocmd User Rails.view* NeoSnippetSource ~/.vim/snippet/ruby.rails.view.snip autocmd User Rails.controller* NeoSnippetSource ~/.vim/snippet/ruby.rails.controller.snip autocmd User Rails/db/migrate/* NeoSnippetSource ~/.vim/snippet/ruby.rails.migrate.snip autocmd User Rails/config/routes.rb NeoSnippetSource ~/.vim/snippet/ruby.rails.route.snip ``` ## [unite, unite-rails](https://github.com/basyura/unite-rails) `:Unite rails/<Tab>`で、Unite上にviews/controllersなどを一覧表示させる事が出来ます。 これをキーマップに指定して、vim-railsと組み合わせればRails内の移動を最速で行う事が出来ます。 僕はRailsの開発時に *必ず2ステップ* 以下で移動出来るようにしています。 Gemfileやrspecやroutes.rbなども一瞬です。素敵。 オススメの設定は[ こちら ]( https://gist.github.com/4521467 ) ## [vim-endwise](https://github.com/tpope/vim-endwise) `if...end`や`def...end`など、 **対になる文字を自動で書き出してくれるプラグイン** です。 `if`まで押して、`<CR>`すると自動で`if...end`まで書き出してくれます。 ruby以外にも、幾つかの言語で対応しているのでオススメです。 neocompleteと一緒に使う場合は、neocompleteのdocをちゃんと読むと良いでしょう。 [オススメ?の設定](https://gist.github.com/4521460) ## [vim-ref](https://github.com/thinca/vim-ref) [vim-ref-ri](https://github.com/yuku-t/vim-ref-ri) vim-refはvim上からリファレンスを引く事が出来る便利なプラグインです。 [Uniteと組み合わせて使うことも出来ます]( http://blog.remora.cx/2010/12/vim-ref-with-unite.html ) `Ref ri link_to`でrails内のlink_toのリファレンスを見ることが出来るようになります。 - w3mやlynxをインストールする リファレンスの閲覧に必要なのでhomebrewなどでインストールしましょう。 `brew install w3m` or `brew install lynx` - vimの設定を追加する 設定が無くても使えますが、あればより使いやすくなります。 riのPATHは各々書き換えてください。 [ オススメの設定 ]( https://gist.github.com/4521470 ) ## おまけ[matchit](https://github.com/vim-scripts/ruby-matchit) vimには、対になる文字の上で`%`を押す事で、対応する()や{}を移動する機能が備わっています。 matchitを有効にすれば、rubyでよく使う`def...end`なども%で移動出来るようになります。 ```vim if !exists('loaded_matchit') " matchitを有効化 runtime macros/matchit.vim endif ``` ## 独り言 コードばかり書いてたら、いつの間にか街に就活生が溢れていました。 この記事を見ている学生エンジニアは、きっとRuby系の会社へ就活しに行くんですね。。。 ファイト! |
|
| 47位 |
|
|||
|
01:38:19 |
|
|
git でファイルを無視するには、通常は `.gitignore` や `.git/info/exclude` を使います。
しかし、既に git 管理下にあるファイルは、これらの設定があっても無視されません。 以下の方法を使えば、git 管理下にあるファイルをあえて無視することが可能です。 方法 ==== 次の2つの方法があります。どちらを使っても、ファイルの変更を無視できます。 方法(1) assume-unchanged ------------------------ ``` git update-index --assume-unchanged [ファイル名] ``` この設定を取り消すには次のようにします。 ``` git update-index --no-assume-unchanged [ファイル名] ``` 方法(2) skip-worktree --------------------- ``` git update-index --skip-worktree [ファイル名] ``` この設定を取り消すには次のようにします。 ``` git update-index --no-skip-worktree [ファイル名] ``` 設定の確認 ========== 上記の設定がされているファイルを確認するには、次のようにします。 ``` git ls-files -v ``` * assume-unchanged 設定のファイルは、状態が小文字で表示されます。 * skip-worktree 設定のファイルは、状態が S と表示されます。 assume-unchanged と skip-worktree の違い ======================================== この2つは、以下の点が違います。 * assume-unchanged : そのファイルが作業ツリー上で変更されているときでも、git はその変更を無視して変更されていないとみなします。 * skip-worktree : そのファイルが作業ツリー上で変更されているときには、git はその変更を保ちます。 これは、以下のような状況で動作の違いとしてあらわれます。 作業ツリー上でもリポジトリ上でもファイル更新があって、それをマージするとき -------------------------------------------------------------------------- * assume-unchanged : 作業ツリー上の更新は破棄されて、リポジトリの内容が取り込まれます。 * skip-worktree : 作業ツリー上の更新は保持されて、リポジトリの内容は取り込まれません。 作業ツリー上でファイル更新があって、`git reset --hard` を実行したとき --------------------------------------------------------------------- * assume-unchanged : 作業ツリー上の更新は破棄されます。 * skip-worktree : 作業ツリー上の更新は保持されます。 何のために使うのか ================== そもそも、何のためにファイルを無視したいのでしょうか。 更新確認しなくていいファイルを無視することで git status などを高速化したい -------------------------------------------------------------------------- 例えば、ネットワークドライブを使っていてファイルアクセスが遅い場合などがそうです。 このために用意されたのが、assume-unchanged 設定です。 この設定がされていると、ファイルをチェックしなくなるので高速になるというわけです。 git 管理しているファイルがビルド時に更新されてしまうが普段は無視したい ---------------------------------------------------------------------- 開発環境によっては、こんな困ったファイルがあったりします。 * ビルド時に自動生成するファイルではないので git 管理したい * ビルド時にそのファイルがあると日付だけ更新される あるいは、ビルドで生成するファイルを git に入れている場合もあるかもしれません。 こういったファイルは、作業ツリー上では頻繁に更新しますが、git には触ってほしくありません。 このときには、skip-worktree 設定が使えます。 まとめ ====== git 管理下のファイルを無視する方法について書きました。 git は様々な細かいニーズに答えて開発されていると思います。 |
|
| 48位 |
|
|||
|
01:42:00 |
(Increments inc. 所属) |
|
アプリケーションエンジニアの人には「なんか重い」という状況に遭遇したらインフラの人にタスクを投げる、という人もいるかも知れません。けど、その重さのどこに原因があるのか。CPUか、ネットワークか、IOかくらいの診断はできた方がアプリ開発においても有益です。
「せっかくつくったシステムがなんか重い」 そんな時にアプリケーションエンジニアとしてできることを書きます。 本職のインフラの人にはぬるい内容だと思います。何を隠そう僕自身がアプリ寄りの人間なので、突っ込んだ話はできないのです。あしからずご了承ください。 # なんかサーバが重いなー ## まずはロードアベレージを調べる サーバが重いと思ったら、まず真っ先にすべきことは対象ホストにSSH接続してロードアベレージを調べることでしょう。ロードアベレージとは **実行されずに待たされているプロセスの数** のことで、多すぎるとやばいと認識しておきましょう。ロードアベレージだけでは何が原因で重くなっているのか判断することはできないけれど、検査のとっかかりとして行います。 ロードアベレージがどれくらいの数値がヤバいのかは、CPUのコアの数とかに依存する(らしい)もので、一概に「Xを超えていたらまずい」と言い切ることができません。なので、日頃からロードアベレージをちょくちょく確認し「あれ、いつもよりロードアベレージ高いな」と言えるようになることが重要なのだろうと思います。 - 使うコマンド: top、uptime topはロードアベレージの他にも今走ってるプロセスを占有してるメモリ量とかでソートしたり色々できる便利コマンドです。uptimeはほぼロードアベレージだけを表示してくれるコマンドです。 topの方やれることが多いので普段はこちらを使えばいいですが、本当に切迫しているサーバ上だと、topの実行すらままならないという状況もあり得るので、uptimeの存在も頭の片隅においておくべきでしょう。 top使うと右上にload averageに続いて小数が3つ表示されます。これがロードアベレージで、左から過去1分5分15分の間の数値を表しています。強いていうなら **1分と5分のやつだけ高かったら、最近始めたプロセスが原因だと推測できる** というくらい。 ### ロードアベレージが低かった場合 ロードアベレージが低いのに重い、ということは原因は外部にあうことが疑われます。ネットワークの問題や、リモートホスト(例えばWebサーバとDBサーバが別ホストにあって、Webサーバのロードアベレージが低いなら、DBサーバを疑ってみるみたいな)に原因がある可能性があります。なのでサーバのネットワークIOの様子を見てみるといいでしょう。 - 使うコマンド: dstat、netstat dstatとnetstatは両方statで終わってますが、実は他にもvmstat、iostat、ifstatなんかもあります。このdstatというのは、これら乱立したXXXstatコマンドをひとまとめにし、かつ、使いやすくしよう、という目的で作られた後発ツールです。なので、使うならdstatがいいと思います。 例えば `dstat --tcp` とやるとTCPコネクションの様子を見たり `dstat --net` とやるとネットワークの様子を見ることができますが、、、はっきり言ってアプリの人間には意味不明だと思いますので、調べても分からない場合は素直にインフラに強い人に尋ねましょう。 ### ロードアベレージが高かった場合 この場合はホストの中に問題があると想像できます。続いてCPUとIOのどちらに原因があるか探ります。 ## CPU使用率とIO待ち状態を調べる - 使うコマンド: sar、dstat、vmstat `sar -u` や `dstat -a` などを実行します。sarの場合で話をすると、見るべきは `%user` `%system` `%iowait` の項目です。 ### %userが高い場合 ユーザプログラムがボトルネックになっていると考えられます。どのプログラムに原因があるか調べましょう。 - 使うコマンド: top、dstat、pidstat `top` はプロセスの一覧をCPU使用率やメモリ占有率などでソートする機能があります。例えば`top`を実行してからおもむろに `m` と打つとメモリ順でソートしてくれます。`dstat --top-cpu` とすることで最もCPUを使っているプロセスを表示することもできます。 `pidstat` は特定のプロセスの統計情報を調べるのに使います。問題のプロセスがある程度しぼれたら `pidstat -u -p PID,PID,PID` をします。PIDには怪しいプロセスのPIDを入れます。-u はCPU使用率を表示するオプションです。 ここで、CPUに負荷がかかっているのは、ディスクI/Oやメモリなどが適切に働いているか、プログラムが暴走しているかのどちらかです。後者の場合は完全にアプリケーションエンジニアのデバッグの仕事です。前者かつスループットが上がらない、という場合は、アプリケーションエンジニアとしてはアルゴリズムの改善などで対処できますが、インフラの人とサーバの増設などを検討することも必要かも知れません。前者でかつスループットが高い状態というのは、リソースを余すことなく使えているということなので、むしろ理想的な状態であるといえますが、将来的にリソースが足りなくなる可能性が高いので、早めにリソース増強の算段を建てたほうがいいかも知れません。 ### %systemが高い場合 これはカーネル空間の処理に時間を取られていることを意味します。この場合に考えられるのは、I/O待ちでCPU時間を消費しているか、大きいプロセスをforkしまくっている等(らしい)です。なので %system が高い場合 %iowait が高い場合も多いようです。 この場合はI/O待ち状態のプロセスを探します - 使うコマンド: top、dstat `dstat --top-bio` で最もブロックI/Oをしているプロセスが、`dstat --top-io`で最もI/Oをしているプロセスが表示されます。 ### %user も %system も低いのに %iowait が高い場合 何かのプロセスでスワップが発生している可能性があります。 - 使うコマンド: top、free、sar、dstat、vmstat `sar -W` や `dstat --swap` はスワップインとスワップアウトの状況、`free` はシステム全体のメモリ使用状況が調べらることができます。`top` でメモリ使用量でソートすればメモリを沢山使っているプロセスを発見することができます。 スワップが確認された場合、アプリケーションエンジニアとしてはメモリリークがないか調べ、問題箇所を修正することで対処できます。そうでない場合は、純粋に搭載メモリが足りていないのでメモリの増設などを検討しましょう。 ## まとめ - 問題があったらまずはロードアベレージを調べましょう - ロードアベレージが低い場合は、外部に問題がある可能性があります。 - ロードアベレージが高い場合は、内部に問題がある可能性があります。CPU、IOのどこに問題があるのか調べましょう。 - `dstat` 便利です |
|
| 49位 |
|
|||
|
10:52:41 |
(Increments inc. 所属) |
|
「vim入門」系記事で解説されないためか、意外と使い方が知られていないvimgrep。
- ファイルを開いては検索、開いては検索ってしてる? - grepするためにvimから出てる? - grep結果を見て改めてvimで開き直してる? それ、vimgrep使えば256倍早くなる(かも)よ。 # 簡単なまとめ vimgrepは… - ファイルをまたいで検索できる - grepやgit-grepよりは遅いので巨大プロジェクトでは検索対象を絞ったほうがいい - ワイルドカード使うと簡単に絞り込める - 繰り返し同じ対象から検索する場合はargument listを使うと捗る - gitリポジトリではgit-ls-filesと組み合わせる - 該当箇所に素早く移動&編集できる - quickfix-windowと組み合わせると更に捗る ## この記事読むと分かること - `:vimgrep`コマンドの使い方 - `:cwindow`コマンドの使い方 - `:args`コマンドの使い方 - gitリポジトリで便利に使う方法 ## この記事読んでも分からないこと - vimの正規表現 - Quickfixの詳細 - `:grep`コマンドの使い方 ## 関連するvim-help より詳しいことは以下のヘルプを読むこと。 - quickfix.txt - :vimgrep - :cwindow - :args - cmdline-special - pattern-overview - wildcards # vimgrepの使い方 vimgrepは名前に *grep* が含まれていることからも分かるように、パターンに一致する箇所をファイルから検索するためのコマンドだ。書式は以下のとおり。(蛇足だと思うが`:vim[grep]`は`[grep]`が省略可能である、つまり`:vim`でもよいという意味。) ```vim :vim[grep] {pattern} {file} ... ``` 要するに`{file} ...`で指定したファイルの中から`{pattern}`で一致する箇所を検索する 。 `{pattern}`には正規表現も使える。vimの正規表現については`:help pattern-overview`参照。 `{file}`にはいくつかの特殊文字があり、ひとつひとつ指定しなくてもワイルドカードで特定のディレクトリ以下のファイル全て、のように指定することなどもできる。いくつか後述するが、より詳しくは`:help cmdline-special`参照。 このとき検索にヒットした箇所をQuickfixのerror listという形式で管理する。これがどういう風に使えるかは後述する「Quickfixと組み合わせる」を参照。 ちなみにvimには`:grep`もある。こちらはシステムのgrepコマンドをvimから呼び出すためのもの。詳細は`:help :grep`を参照。 ## vimgrepと/の違い ところでvimで検索と言えば`/`(スラッシュ)もあるが一体何が違うのか。 | / | vimgrep -----------------------|------------------|---------------------- 正規表現使える | yes | yes 検索対象 | カレントバッファ | 引数で指定する ヒット件数 | 分からない | 分かる 次の検索結果へ移動する | `n` | `:cn[ext]` 前の検索結果へ移動する | `N` | `:cN[ext]` `:cp[revious]` Quickfix使える | no | yes `/`(スラッシュ)を使うと現在開いているファイルの中で検索できることは、vimmerなら当然知っているだろう。この現在開いているファイルをvimではカレントバッファ(current buffer)と呼ぶ。 `/`ではヒット件数は分からないが、vimgrepを使うと`(1 of 5)`のように表示されるので何件ヒットしたのかが一目で分かる。この場合は5ヶ所ヒットした内で1つ目の結果を表示していることを意味する。 `:cnext`を実行すればカーソルが次の位置に移動し`(2 of 5)`と表示される。 私はいちいち`:cnext`とか打つのがだるいので次のように設定している。qはQuickfixのqのつもり。 ```vim:.vimrc nnoremap [q :cprevious<CR> " 前へ nnoremap ]q :cnext<CR> " 次へ nnoremap [Q :<C-u>cfirst<CR> " 最初へ nnoremap ]Q :<C-u>clast<CR> " 最後へ ``` ## カレントバッファに対してvimgrepする vimgrepはファイルを横断して検索できるのが便利だが、カレントバッファを検索するために`/`の代わりに使うのも有りだ。カレントバッファを対象にするには`{file}`に`%`と指定する。 つまり`/hoge`の代わりに`:vim hoge %`を実行すれば、現在開いているファイルを対象にしたvimgrepになる。 ```vim " カレントバッファを対象にする :vim {pattern} % ``` ## ワイルドカードで複数指定する `{file} ...`で複数のファイルを指定できるとは言っても、一つ一つ列挙するなんてやっていられないだろう。それに特定のディレクトリ以下のファイルを対象に検索したい、ということが大半だ。 こういうときはワイルドカードを使う。以下の例を見ればだいたい雰囲気はつかめるだろう。より詳しくは`:help wildcards`参照。 ```vim " カレントディレクトリ以下のあらゆるファイルを対象にする :vim {pattern} ** " app/views以下のあらゆるファイルを対象にする(ディレクトリを再帰的に検索) :vim {pattern} app/views/** " app/views/users内のファイルを対象にする :vim {pattern} app/views/users/* " app/views以下のerbファイルを対象にする :vim {pattern} app/views/**/*.erb " app/views以下で_で始まるerbファイルを対象にする :vim {pattern} app/views/**/_*.erb ``` また`{file}`はカレントディレクトリからの相対パスで指定する。カレントディレクトリは`:pwd`で確認可能。また`:cd`でvim起動後に変更することも可能。 ワイルドカードで出来る限り絞り込むのはパフォーマンス的に重要だ。vimgrepで対象となったファイルは全て一旦vimによってメモリに載せられたのちに検索が実行される。不要なファイルを外しておくことでメモリ消費を抑えることができる。またvimgrepは通常のgrepコマンドやgit-grepよりは遅い。 ## 何度も同じ検索対象を使う場合 同じ`{file}`に対して`{pattern}`を変えながら何度も検索する、という場合を考えてみる。例えば特定のディレクトリ内にアプリケーションコードが格納されており、その中のファイルだけを対象に検索したい、というような感じだ。愚直にやるなら次のような感じでコマンドを実行することになる。vimが多少は補完してくれるとは言っても、何度も何度もpath/to/search/dir/**と入力するのはつらすぎる。 ````vim :vim foo path/to/search/dir/** :vim bar path/to/search/dir/** :vim baz path/to/search/dir/** :vim hoge path/to/search/dir/** :vim piyo path/to/search/dir/** ``` こういう場合は`:ar[gs]`コマンドを使う。さきほどの例は次のように置き換えられる。つまるところ`:args`で入力された値を`##`で指定できるわけだ。ちなみに`:args`はvimが内部で管理しているargument listを置き換えるためのコマンドで、argument listは最初vim起動時に`vim`コマンドに引数で渡されたファイルが入っている。 ```vim :ar path/to/search/dir/** :vim foo ## :vim bar ## :vim baz ## :vim hoge ## :vim piyo ## ``` この例を見れば分かるように`:args`にはワイルドカードを指定できる。これはこれで極めて便利だが、シェルコマンドを指定してその標準出力を受け取ることも可能だ。これを利用するには`を使う。これは工夫次第で更に便利になる。後述する「Gitリポジトリで使う場合」などはその典型だ。 ```vim :ar `find . -name \\*.rb` ``` ## Gitリポジトリで使う場合 Gitプロジェクトで下記を実行するとインデックスされていないファイルも含めて全て検索される。 ```vim " gitリポジトリのルートでこれを実行してはいけない :vim {pattern} ** ``` Gitで管理しているプロジェクトではインデックスされているファイルだけを対象にしたいことが大半だ。 そういうときはgit-ls-filesと`:args`を組み合わせる。git-ls-filesについて詳しくは`man git-ls-files`を参照してほしいが、簡単な話がGitでインデックスされているファイルの一覧を標準出力する。パスを指定することで絞り込むこともできることも合わせて言っておく。こうすれば以後そのvimプロセス内では `##` で必要なファイルに対して検索ができる。 標準出力を使ったファイルの指定は `:vimgrep`の`{file}`にも指定できるので、一回だけならわざわざ`:args`を使う必要はない。 ```vim " インデックスされている全てのファイルを対象にする :vim {pattern} `git ls-files` " appディレクトリ内でインデックスされているファイルを対象にする :vim {pattern} `git ls-files app` " appディレクトリ内でインデックスされている.htmlファイルを対象にする :vim {pattern} `git ls-files app/**/*.html` ``` こうすればgit-grepするためにいちいちvimから出て行く必要はなくなる。 ## バッファされている全てのファイルに対して検索する 現在バッファに乗っているファイルを対象に検索したいという場合もあるだろう。そういうときは`:bufdo`と`:vimgrepa[dd]`を組み合わせる。`:bufdo`は開いているバッファに対して後続のコマンドを実行して回るコマンド。`:vimgrepadd`は新しいQuickfixのリストを作るのでなく追加する。 ```vim :bufdo vimgrepa {pattern} % ``` 複数ウィンドウを開いていて、それらを対象にしたいなら`:bufdo`の代わりに`:windo`を使う。また`:vimgrepadd`は追加するものであることから、連続実行すると前の検索結果が残ってしまう。一旦Quickfixをリセットするには`:cex[pr]`コマンドに`””`を渡す。 ```vim :cex "" ``` # Quickfixと組み合わせる vimgrepの基本的な使い方は掌握されたことだろう。 ところでvimgrepは検索結果を一覧表示できたら嬉しいとは思わないだろうか?`:cnext`を連打して一つ一つ見て回るのは、見たいのが検索結果の一部だった場合は特に、不毛だ。 ここで使うのが`:cw[indow]`コマンドだ。実行すると新しいウィンドウが作られ、そこにvimgrepの検索結果の一覧が表示される。一行が一件の検索結果に対応し、それぞれファイルのパス、ヒットした箇所のそのファイル内での位置、その行全体が表示される。 このウィンドウ上で特定の行へ移動しEnterを押すとその検索位置が表示されてカーソルがそこに移動する。このとき作られたウィンドウは消えずに残っているので、Ctrl-wを使って移動して改めて他の検索結果を選択することもできる。 vimgrepしてから`:cwindow`したのでは二回コマンドを実行しなければならず若干面倒くさい。次のように一回で書いてしまうのがよい。以下を実行するといきなり検索結果一覧のウィンドウが表示され、カーソルがそのウィンドウの最初の行に移動する。 ```vim :vim {pattern} {file} | cw ``` ちなみにここで作られたウィンドウはvim用語ではquickfix-windowと呼ばれる。 QuickfixというのはとあるCコンパイラが出力するコンパイルエラー形式のこと(らしく)、それをファイルに保存してvimから読み込んでエラー箇所を簡単に飛べると嬉しいよね、という動機で作られた機能の総称。vimgrepの検索結果は自動的にこの形式で読み込まれるため、Quickfixの結果を一覧表示するquickfix-windowで一覧表示できる、というわけだ。 詳しくは`:help quickfix`を参照。あと`:help 30.1`なんかも読むとよい。 それとQuickfixは`+quickfix`なvimでないと使えないので注意。 ## 自動的にquickfix-windowを開く `| cw` を書くのが面倒くさい場合はvimがQuickfixにフックするためのイベントを用意しているので、それを使うとよい。 下記設定の場合`:vimgrep`に加えて`:grep`、`:Ggrep`でも自動的にquickfix-windowを開くようになる。 ```vim:.vimrc autocmd QuickFixCmdPost *grep* cwindow ``` # まとめ vimgrepを使えばvimから出ること無くファイルを横断して検索することができる。もうCtrl-Zで抜けてfgで戻るなどという人生の浪費をしなくてよいのだ。 |
|
| 50位 |
|
|||
|
20:14:32 |
(シナプス株式会社 所属) |
|
# 予備知識
PHPはフォームから送信された値などをコード実行開始に自動的に変数として使えるようにしてくれる非常に便利なプログラミング言語です.しかし,それをそのまま用いるとエラーが発生したり,脆弱性になってしまったりするケースがたくさんあります.使う前には適当なチェック処理が必要です. ## どういった変数が対象になるか 以下に挙げられた変数は,ユーザーが勝手に値や構造を書き換えたり,送信をそもそも行わずにアクセスしたりすることが可能な<ins>信用できない変数</ins>だと思ってください.例え,ラジオボタンで選択肢を限定していたり,隠し要素として埋め込んでいたりしたとしても,これに該当してしまいます. ### `$_GET` - アクセスされたURLの `?` 以降のクエリーストリングに含まれる情報. - `method` 属性の値が `get` であるフォームから送られた情報. ### `$_POST` - `method` 属性の値が `post` であるフォームから送られた情報. ### `$_COOKIE` - Webブラウザから送信されるクッキー. ### `$_REQUEST` - `$_GET` `$_POST` `$_COOKIE` の内容が合成されて作られたもの. なお, `$_SESSION` はサーバー側に保存される変数なので,このチェック対象にはなりません.チェックすべきはセッションの有効期限が切れていないか,つまり `$_SESSION` が空になってしまっていないかどうかです. ```php:セッションの有効期限切れをチェック session_start(); if (!$_SESSION) { /* セッション有効期限切れに対応する処理 */ } ``` これらの特殊な変数に関する詳細は [「リクエストパラメータ・セッションに関するまとめ」](http://qiita.com/mpyw/items/7852213f478e8c5a2802) で詳しく解説しています. ## PHPのエラーの種類 PHPには主に以下のようなエラーがあります. (これで全てではありません) ### **E_PARSE** (別名: Parse Error, Syntax Error, 文法エラー) コードが文法的に誤っているときに発生します.一切の処理を行いません. ### **E_ERROR** (別名: Fatal Error, 致命的なエラー) 処理を継続することが不可能になってしまった場合に発生します.発生したところで処理を停止します.これが発生するリスクを持つコードを書いてはいけません. ### **E_WARNING** (別名: Warning, 警告) 処理を継続することは可能ですが,想定外の問題が起こったときに発生します.これが発生するリスクを持つコードは可能な限り書かないでおくべきです. ### **E_NOTICE** (別名: Notice, 通知) 処理を継続することは可能ですが,想定内の問題が起こったときに発生します.これは手を抜いて無視する人と丁寧に対応する人に二分されますが,<ins>対応の仕方を知らないまま手抜きをするようなプログラマになってはならない</ins>と私は考えるので,ここでは徹底的に対応することにします. ## エラー発生による弊害 - 本番環境でエラーメッセージが表示されてしまうと,サーバーを攻撃しようと企む人にヒントを与えてしまう事になります. - ページのデザインが崩れて不格好になります. このため,エラーを表示させるのはデバッグ環境だけにすることが殆どです.但し,エラーを非表示にしたとしても… - 処理速度が低下します. - php.iniの設定によってはエラーログが溜まります. - php.iniの **error_reporting** の設定によっては表示されないエラーもありますが,処理速度の低下は防げません. - **[エラー制御演算子](http://www.php.net/manual/ja/language.operators.errorcontrol.php) `@`** を使えば表示はされなくなりますが,処理速度の低下は防げません.またデバッグ環境で何らかのバグが発生してしまった状態でも,バグに関わるエラーメッセージが全て表示されなくなってしまいます.デバッグ作業がより難航することになるので,可能な限りこの演算子に頼ることは避けたほうが無難でしょう. このようにさまざまなデメリットがあるので,可能な限りエラー発生を防ぐように書きましょう. ## 全てのエラーを表示 php.iniを以下のように編集すれば全てのエラーが表示されるようになります. ```ini:php.iniでの恒久的な設定 error_reporting = -1 display_errors = On ``` もしphp.iniの書き換えが出来ない場合,使用しているサーバーがApacheであれば以下のように **.htaccess** ファイルを設置することで対応できます. ```apache:.htaccessでの恒久的な設定 php_value error_reporting -1 php_flag display_errors On ``` それも不可能な場合や一時的に有効にしたい場合は,スクリプトの先頭で以下のコードを実行すればそれ以降は全てのエラーが表示されるようになります.但し **E_PARSE** はコードを読み取る段階で発生するエラーなので,これには対応することが出来ません. ```php error_reporting(-1); ini_set('display_errors', 'On'); ``` ## 比較演算子 `==` と `===` の比較 入門書などでは `==` が主体的に使われていることが多いですが,私は `===` に全て統一して使うことを推奨します.前者は **型の相互変換** を勝手に行います.PHP以外の言語出身者からすれば,これほど気持ち悪いものは無いでしょう. - [PHP Manual - 比較演算子](http://us1.php.net/manual/ja/language.operators.comparison.php) ## [htmlspecialchars](http://www.php.net/manual/ja/function.htmlspecialchars.php) 関数によるエスケープ処理 HTML中で特殊な意味を持つ文字列は必ずエスケープしなければなりません.ユーザーから入力された値に対してこの処理を怠ると **[クロスサイトスクリプティング](http://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B5%E3%82%A4%E3%83%88%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0)** に関する脆弱性が発生してしまいます. この処理を行うタイミングは<ins>表示する直前</ins>にしましょう.それ以外の場所でこの処理を行うべきではありません.例えばデータベースやファイルに格納するときにあらかじめエスケープしてしまうのは,ソフトウェア構成上の誤りだと言えます. ### `$string` エスケープしたい文字列を渡します. ### `$flags` フラグを指定します.省略した場合のデフォルト値は `ENT_COMPAT` となります.実際には `ENT_HTML401` との論理和となりますが,この関数の挙動には影響を及ぼさないのでここでは触れないことにします.詳しくは **[「htmlspecialchars関数やhtmlentities関数で使用されるフラグの検証」](http://qiita.com/mpyw/items/939c526dd5de6619b565)** を参照してください. #### <font color="silver">ENT_COMPAT でエスケープされる文字</font> `<` `>` `&` `"` 属性値を全てダブルクオーテーションで括っているのであればこれで問題はありません.但し,エスケープすれば安全だと思って安易に属性値として埋め込むのは避けましょう.例えば以下のケースではリンクをクリックしたときにJavaScriptが実行されてしまいます. ```php:不十分なXSS対策 // $_POST['url'] = "javascript:alert('XSS')"; とする $url = htmlspecialchars($_POST['url']); echo '<a href="' . $url . '">リンク</a>'; ``` #### <font color="silver">ENT_QUOTES でエスケープされる文字</font> `<` `>` `&` `'` `"` これを指定しておくのが一番無難ですが,こちらも上記と同様に埋め込む対象となる属性によってはリスクが発生するので細心の注意を払ってください. ### `$encoding` エンコーディングを指定します. PHP5.4以降とPHP5.3以前でデフォルト値が異なり,前者では `UTF-8` ,後者では `ISO-8859-1` となっています.前者であれば指定を省略することが可能ですが,バージョンによって挙動が異なってしまうのも問題なので,省略せずに指定することがマニュアルでも強く推奨されています. <ins>ここまでは省略できない必須パラメータだと思ってください</ins>. ### `$double_encode` `&` `<` `>` のように既にエスケープされている文字をもう一度エスケープして `&amp;` `&lt;` `&gt;` とするかどうかを論理値で指定します.デフォルトは `true` です. ### ラクに記述できる方法 表示するときに毎回 ```html+php <p><?php echo htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); ?></p> ``` と書くのは非常に煩わしいので, ```php function h($str) { return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); } ``` のような関数を作っておくと ```html+php <p><?php echo h($str); ?></p> ``` として簡潔に書くことが出来ます.ここから末尾のセミコロンは省略して ```html+php <p><?php echo h($str) ?></p> ``` とすることができ,更に **echo短縮構文** を使えば何と ```html+php <p><?=h($str)?></p> ``` ここまで短く出来てしまいます.非常に便利な構文なのですが,これがデフォルトで使えるのは **PHP5.4以降** のみで,それより古いバージョンの場合はphp.iniで `short_open_tag` が有効化されている場合にしか使えません.もしphp.iniの書き換えが出来ない場合,使用しているサーバーがApacheであれば以下のように **.htaccess** ファイルを設置することで対応できます. ```apache:.htaccess php_flag short_open_tag on ``` # チェック処理の概要 ## [isset](http://www.php.net/manual/ja/function.isset.php) ### エラーの発生するケース PHPでは,原則的に未定義の変数や配列インデックスの値を表示または取得しようとしたとき, **E_NOTICE** レベルのエラーが発生します. - 変数が未定義 → **Notice: Undefined variable** - 配列インデックスが未定義 → **Notice: Undefined index** つまり,手抜きコーディングでよくある以下のようなチェック処理は不適当です. ```php if ($_POST['email'] == '') { $errors[] = 'Eメールアドレスが入力されていません'; } ``` 実際にフォームから送信してそのページに遷移した場合にはエラーは発生しませんが,<ins>直接そのページにアクセスした場合には</ins>**`$_POST['email']`**<ins>は未定義</ins>となるので,以下のエラーが発生してしまいます. ```txt:エラー Notice: Undefined index: email ``` なお,この手抜きのケースでは `===` を用いることは出来ません. 未定義の値を **E_NOTICE** レベルのエラーを発生しながら強引に取得しようとしたときその値は `NULL` と見なされるので, **型の相互変換** により `NULL` を `""` として判定させなければ正しい動作をすることが出来ないからです. ### エラーの発生を防ぐ方法 [isset](http://www.php.net/manual/ja/function.isset.php) は,変数が **未定義でない** かつ **NULLでない** ことをエラーを発生させずにチェックすることが出来ます.但し,外部からの入力を格納した変数が `NULL` を取るケースは存在しないので,このような使い方をする上では単に未定義でないかどうかのチェックを行うものとして考えていただいて構いません. ```php if (!isset($_POST['email'])) { $errors[] = 'Eメールアドレスが送信されていません'; } elseif ($_POST['email'] === '') { $errors[] = 'Eメールアドレスが入力されていません'; } ``` 「送信されていない」と「入力されていない」を区別する必要がない場合,以下のようにまとめてしまっても構いません. ```php if (!isset($_POST['email']) || $_POST['email'] === '') { $errors[] = 'Eメールアドレスが入力されていません'; } ``` ## [is_string](http://www.php.net/manual/ja/function.is-string.php) 関数<br />[is_array](http://www.php.net/manual/ja/function.is-array.php) 関数 最初に紹介した [isset](http://www.php.net/manual/ja/function.isset.php) によるチェックだけで自然な利用方法でも発生してしまう大半のエラーは防ぐことが出来ます.しかし,厳密に全てのエラーを潰すにはまだ不十分です.外部からの入力で受け取るものには **文字列** と **配列** の2種類があるからです. 例えば,こういうコードがあったとします. ```php:http://example.com/test.php if (isset($_GET['var'])) { echo $_GET['var']; echo htmlspecialchars($_GET['var'], ENT_QUOTES, 'UTF-8'); echo $items[$_GET['var']]; echo 1 + $_GET['var']; } ``` 一見これで十分なように思えますが,エラーを完璧に潰すにはまだ不十分です. `http://example.com/test.php?var[]=hoge` 正確にURLエンコードすれば `http://example.com/test.php?var%5B%5D=hoge` のようなクエリを受け取った時に, `$_GET['var']` は以下のような **配列** になってしまいます. ```php [0 => 'hoge'] ``` 故にこの例では上から順に ```txt:エラー Notice: Array to string conversion Warning: htmlspecialchars() expects parameter 1 to be string, array given Warning: Illegal offset type Fatal error: Unsupported operand types ``` のようなエラーが発生してしまいます. - 配列が **E_NOTICE** レベルのエラーを発生しながら文字列に強引に変換されたとき, `"Array"` という扱いになる. - 入力値をそのままPHPの組み込み関数に渡している場合, **E_WARNING** レベルのエラーを発生してしまうケースが多い. - 配列のオフセットにスカラー値または `NULL` 以外を指定したとき, **E_WARNING** レベルのエラーが発生する. - 計算不可能なオペランド構成で `+` `-` などの演算子を使用したとき, **E_ERROR** レベルのエラーが発生する. こういった事態を防ぐためには,厳密に **文字列であるかどうか** もしくは **配列でないかどうか** をチェックする処理が必要です.先ほど述べたように,可能性は **文字列** と **配列** の2種類しかないので,以下のどちらのパターンを採用しても構いません.意味が分かりやすいと思ったほうを選んでください. ```php:文字列であることをチェックしたい場合 is_string($var) !is_array($var) ``` ```php:配列であることをチェックしたい場合 !is_string($var) is_array($var) ``` ここでは [is_string](http://www.php.net/manual/ja/function.is-string.php) 関数の返り値の否定を用います. ```php if (!isset($_POST['email'])) { $errors[] = 'Eメールアドレスが送信されていません'; } elseif (!is_string($_POST['email'])) { $errors[] = 'Eメールアドレスが不正送信されました'; } elseif ($_POST['email'] === '') { $errors[] = 'Eメールアドレスが入力されていません'; } ``` 「送信されていない」と「不正送信された」と「入力されていない」を区別する必要がない場合,以下のようにまとめてしまっても構いません. ```php if (!isset($_POST['email']) || !is_string($_POST['email']) || $_POST['email'] === '') { $errors[] = 'Eメールアドレスが入力されていません'; } ``` 配列の場合は各要素ごとのチェックも必要です. ```php if (!isset($_POST['params']) || !is_array($_POST['params'])) { $errors[] = 'パラメータが送信されていません'; } else { foreach ($params as $key => $value) { if (!is_string($value)) { $errors[] = "パラメータ{$key}が不正です"; } } } ``` ## [empty](http://www.php.net/manual/ja/function.empty.php) [empty](http://www.php.net/manual/ja/function.empty.php) は,変数が **未定義である** または **型の相互変換により`false`と等しいと見なされる値である** ことをエラーを発生させずにチェックすることが出来ます.つまり以下のセットはそれぞれ同じ意味となります. ```php:空と見なす場合 !isset($var) || $var == false !isset($var) || !$var empty($var) ``` ```php:空でないと見なす場合 isset($var) && $var == true isset($var) && $var !empty($var) ``` `""` は `false` と見なされる値の1つなので,最初の [isset](http://www.php.net/manual/ja/function.empty.php) だけを用いた例はこのように書くことも出来ます. ```php if (empty($_POST['email'])) { $errors[] = 'Eメールアドレスが入力されていません'; } ``` 2番目に紹介した [is_string](http://www.php.net/manual/ja/function.is-string.php) 関数によるチェックも織り交ぜるなら以下のようになります. ```php if (empty($_POST['email']) || !is_string($_POST['email'])) { $errors[] = 'Eメールアドレスが入力されていません'; } ``` 但し,`""` だけでなく `"0"` も `false` と見なされてしまい, **「入力したのに入力していないと言われた」** と利用者から文句が飛んでくるかもしれません.これぐらいの手抜きは場合によっては許されると思いますが,厳密な処理に拘る場合には避けるべきでしょう.著者はかなり気にします. # フィルタ関数の活用 ここからは 1. スクリプトの先頭のほうで送信されてきた値をチェックする.有効なものであれば適当なローカル変数にその値を代入し, **未定義** もしくは **想定外の型** であれば `""` `NULL` `false` などを代入する. 2. そのローカル変数を用いて条件分岐を行い,適宜エラーに該当するかどうかをチェックする. という処理フローを想定して「1.」の部分のコードを提示します.こちらの方が全体的な見通しが良くなり,入力フォームを再表示するときの利便性も向上するからです. ## 文字列のみを許可する ```php if (!isset($_POST['name'])) { $name = null; } elseif (!is_string($_POST['name'])) { $name = false; } else { $name = $_POST['name']; } ``` **[filter_input](http://www.php.net/manual/ja/function.filter-input.php)** 関数を利用すれば,これと等価な処理をもっと美しく書くことが出来ます. ```php $name = filter_input(INPUT_POST, 'name'); ``` 文字列として送信されてきた場合のみ何か処理を行いたい場合は ```php $name = filter_input(INPUT_POST, 'name'); if (is_string($name)) { /* 文字列として送信されてきた場合のみ実行したい処理 */ } ``` と書けば良いでしょう. ## 文字列を強制する ```php if (!isset($_POST['name']) || !is_string($_POST['name'])) { $name = ''; } else { $name = $_POST['name']; } ``` 分岐パターンと代入先が1種類しかないので, **[三項演算子](http://www.php.net/manual/ja/language.operators.comparison.php#language.operators.comparison.ternary)** を利用することが出来ます. ```php:&&を使う場合 $name = isset($_POST['name']) && is_string($_POST['name']) ? $_POST['name'] : ''; ``` ```php:||を使う場合 $name = !isset($_POST['name']) || !is_string($_POST['name']) ? '' : $_POST['name']; ``` [filter_input](http://www.php.net/manual/ja/function.filter-input.php) 関数と文字列へのキャストを利用すれば,これらと等価な処理をもっと美しく書くことが出来ます. ```php $name = (string)filter_input(INPUT_POST, 'name'); ``` ## 1次元配列を強制する ```php if (isset($_POST['items'])) { $items = $_POST['items']; if (!is_array($items)) { $items = [$items]; } foreach ($items as $key => $value) { if (!is_string($value)) { unset($items[$key]); } } } else { $items = []; } ``` **配列へのキャスト** と **[array_filter](http://www.php.net/manual/ja/function.array-filter.php)** 関数を利用すれば,これと等価な処理をもっと美しく書くことが出来ます. ```php $items = isset($_POST['items']) ? (array)$_POST['items'] : []; $items = array_filter($items, 'is_string'); ``` 配列から文字列への強引なキャストでは **E_NOTICE** レベルのエラーが発生してしまいますが,<ins>文字列から配列へのキャストではエラーが発生しません</ins>.キー `0` に対する値を1つ持つ配列としてエラー無しにキャストされます. ## 指定したキーに限定して1次元配列を強制する ### テキストの配列を対象とする場合 ```html <form method="post" action=""> A: <input type="text" name="texts[a]"><br> B: <input type="text" name="texts[b]"><br> C: <input type="text" name="texts[c]"><br> <input type="submit" value="送信"> </form> ``` テキストボックスは<ins>入力内容が無くても空文字列として送信されます</ins>が,不正なリクエストに備えるには結局冗長に書かざるを得ません. ```php foreach (['a', 'b', 'c'] as $i) { $texts[$i] = isset($_POST['text'][$i]) && is_string($_POST['text'][$i]) ? $_POST['text'][$i] : '' ; } ``` ### チェックボックスの配列を対象とする場合 ```html <form method="post" action=""> <input type="checkbox" name="checks[a]" value="1">A<br> <input type="checkbox" name="checks[b]" value="1">B<br> <input type="checkbox" name="checks[c]" value="1">C<br> <input type="submit" value="送信"> </form> ``` チェックボックスに関しては,<ins>チェックしていないものは送信されません</ins>.また,今回のように値を実際には必要とせず,単純にチェックされたかされなかったかのみを判定したい場合は - チェックされたもの → `true` - チェックされなかったもの → `false` として存在させておいた方が何かと扱いやすいと思います.これを実現するには以下のようにします. ```php foreach (['a', 'b', 'c'] as $i) { $checks[$i] = isset($_POST['checks'][$i]); } ``` ## 指定した値に限定して1次元配列を強制する 先ほどのチェックボックスの例に関して,今度はキーではなく値に着目し,想定されない値は含まないようにします. ```html <form method="post" action=""> <input type="checkbox" name="checks[]" value="a">A<br> <input type="checkbox" name="checks[]" value="b">B<br> <input type="checkbox" name="checks[]" value="c">C<br> <input type="submit" value="送信"> </form> ``` **[array_intersect](http://www.php.net/manual/ja/function.array-intersect.php)** 関数で値に注目したときの共通項を計算します. `0` から始まる数字添え字配列にした方が綺麗なので,必要に応じて最後に **[array_values](http://www.php.net/manual/ja/function.array-values.php)** 関数を通しておきます. ```php $checks = isset($_POST['checks']) ? (array)$_POST['checks'] : []; $checks = array_values(array_intersect($checks, ['a', 'b', 'c'])); ``` # 更なるステップアップ ## [可変変数](http://www.php.net/manual/ja/language.variables.variable.php) の利用 [filter_input](http://www.php.net/manual/ja/function.filter-input.php) 関数を利用するとかなりコードを短くできますが,それでも項目数だけ繰り返しコールする必要がありました. ```php $name = (string)filter_input(INPUT_POST, 'name'); $email = (string)filter_input(INPUT_POST, 'email'); $comment = (string)filter_input(INPUT_POST, 'comment'); ``` これを **[可変変数](http://www.php.net/manual/ja/language.variables.variable.php)** という機能を利用することで,どれだけ項目が増えても1つ分の記述だけで済ませることが出来ます.但し,<ins>変数名 </ins>**`$v`**<ins> と一致する </ins>**`'v'`**<ins> が配列内に存在すると正しい処理が行えなくなる</ins>ので十分注意してください. ```php foreach (['name', 'email', 'comment'] as $v) { $$v = (string)filter_input(INPUT_POST, $v); } ``` もちろん,以下のように特定の配列やオブジェクトの中に格納することも可能です.お好みに応じて適当に使い分けてください. ```php:配列のインデックスを定義する foreach (['name', 'email', 'comment'] as $v) { $p[$v] = (string)filter_input(INPUT_POST, $v); } ``` ```php:オブジェクトのプロパティを定義する $p = new stdClass; foreach (['name', 'email', 'comment'] as $v) { $p->$v = (string)filter_input(INPUT_POST, $v); } ``` ## 自分でフィルタリング用の関数を作る 複雑な配列をフォームから受け取る場合などには,上記の工夫だけでは間に合わないかもしれません.そういったときには自分でフィルタリング用の関数を作って処理をするのが適当です.以下に私が自作したとても便利な関数を紹介しておきます. - [Qiita - filter_input_simple](http://qiita.com/mpyw/items/5a7242269f51dfabc973) - [Qiita - filter_input_array_recursive](http://qiita.com/mpyw/items/c39b9ee695a5c2e74627) ## 具体的な値に関するバリデーションを行う ここまでは 1. 未定義ではないか 2. 想定外の型ではないか という処理のみに着目してきましたが,実際には 1. 未定義ではないか 2. 想定外の型ではないか 3. **誤った形式の値ではないか** という処理になることがほとんどでしょう.EメールアドレスやURLなどのチェックがこれに該当します.詳しくは以下のまとめにて紹介しています. - [Qiita - PHPで各種バリデーション](http://qiita.com/mpyw/items/346f1789ad0e1b969ebc) ## 推薦記事 - [Qiita - 【PHP超入門】HTTP(GET・POST)について](http://qiita.com/7968/items/4bf4d6f28284146c288f) by @7968 |
|
| 51位 |
|
|||
|
16:50:41 |
(Increments 所属) |
|
Objective-C のプロパティの属性を指定するとき従うべきガイドラインをまとめた。
## できる限り `nonatomic` を指定する `atomic` にしてもパフォーマンスが悪化するだけでほとんどメリットがない(参考:[StackOverflow - Atomic vs nonatomic properties](http://stackoverflow.com/questions/588866/atomic-vs-nonatomic-properties))。 ```objc @property (nonatomic) id value; ``` `nonatomic` と `atomic` の使い分けの指針は次のとおり: > - 参照型: メモリアドレスのみの書き込みなので、常にnonatomicでよい - プリミティブ型: - int, BOOL等ワンステップでの書き込みが可能: 常にnonatomicでよい - 単一のスレッドからしかアクセスされない: 設計に気をつけつつnonatomic推奨 - 複数のスレッドからのアクセスがあり、long,構造体などサイズの大きい値: atomic推奨 ([thx to](#comment-bcdf4e94f134605184e8) @takasek) 複数のスレッドから同時に読み書きが行われる可能性があるプロパティには `atomic` を指定する。 `atomic` を指定することで getter と setter が排他的に実行されるようになり、値の書き込みに複数のメモリ操作命令が必要な型(構造体など)が中間状態で読み出されることがなくなる。 `atomic` を指定してもスレッドセーフになるとは限らない点に注意すること。たとえば、 `obj.atomicInt++` を複数スレッドで同時に実行すると、実行した回数より値が小さくなることがある(参考:[Objective-Cでatomicな宣言プロパティがatomicであるとは限らない話|スマホとアドテク開発記](http://ameblo.jp/tomotaken/entry-10818354164.html))。 スレッドセーフ性を保証したいときは、 `atomic` よりも `@synchronized` や `NSLock` などの排他制御のほうが適切なこともあるので、よく検討すること。 ## `strong` と `assign` は省略してもよい オブジェクト型のプロパティは `strong` が、それ以外の型のプロパティは `assign` がデフォルトであるため、省略することができる。 `weak` や `copy` のプロパティと桁をそろえるために、あるいは既存のコードとスタイルを一致させるために、明示してもよい。 注:Xcode 4.2.xまでは、オブジェクト型プロパティの `strong` の省略は認められない。 ```objc // 省略 @property (nonatomic) id object; @property (nonatomic) NSInteger primitiveValue; // または明示 @property (nonatomic, strong) id object; @property (nonatomic, assign) NSInteger primitiveValue; ``` ## Outlet には基本的に `weak` を指定する Outlet が参照するオブジェクトは、大抵は何らかのビューのサブビューである。サブビューはそのスーパービューによって保持されるので、 outlet は `weak`で構わない。 `weak` にすることで意図しない循環参照を予防できる。ただし、次のようなオブジェクトを参照する outlet は `strong` にしなければならない: - Nib のトップレベルオブジェクト(ウィンドウ、他のビューに属さないビュー、 `NSObjectController` などビュー以外のオブジェクト) - サブビューのうち、 `-removeFromSuperview` を呼ぶなどして一時的にビュー階層から取り除かれる可能性があるもの (参考:[Managing the Lifetimes of Objects from Nib Files](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW6)、[[iOS5] ARC : Outletにはweakプロパティを使おう](http://blog.natsuapps.com/2011/11/ios5-arc-weakproperty-for-outle.html)) ```objc @property (nonatomic, weak) IBOutlet NSView *view; @property (nonatomic, strong) IBOutlet NSWindow *window; @property (nonatomic, strong) IBOutlet NSView *removableView; ``` ## デリゲートプロパティには `weak` を指定する あるオブジェクトをそのオブジェクト自身のデリゲートにするような状況では循環参照が発生するため、デリゲートプロパティを `strong` にするとオブジェクトが解放されなくなる。特別な事情がなければデリゲートプロパティは `weak` にすべきである。 Cocoa のデリゲートプロパティは、 `NSURLConnection` や `CAAnimation` などの例外を除き `weak` か `unsafe_unretained` になっている。 ```objc @property (nonatomic, weak) id delegate; ``` ## `weak` 参照してはいけないクラスに注意する OS X では、 UI に関係するクラスのうちのいくつかが `weak` 参照に対応していない(参考:[ weak 参照してはいけない Cocoa のクラス一覧](http://qiita.com/uasi/items/ee71a476ec3ef9c3c10d))。これらのクラスを弱参照したい場合は `unsafe_unretained` 参照を使うこと。 ## Mutable なサブクラスがある型には `copy` を指定する `NSString` 型などの、対応する `NSMutable...` 型があるプロパティには `copy` を指定する。Mutable なオブジェクトの `copy` メソッドは immutable なオブジェクトを返すため、プロパティにセットされる値が immutable であることを保証できる。 Immutable オブジェクトの `copy` メソッドは、オブジェクトをコピーせずインスタンス自身を返すため、パフォーマンスが悪化することはない。 ```objc @property (nonatomic, copy) NSString *string; ``` ## `readonly` と対応するプロパティには `readwrite` を明示する あるプロパティをクラス外部からは `readonly` にする場合、クラス内での定義には `readwrite` を明示する: ```objc /*** Foo.h ***/ @interface Foo @property (nonatomic, readonly) id value; @end /*** Foo.m ***/ @interface Foo () @property (nonatomic, readwrite) id value; @end ``` `readwrite` はデフォルトであり省略可能だが、クラス外に `readonly` で公開するプロパティだけ `readwrite` を明示するルールにすると分かりやすい。 ## あるプロパティをヘッダと実装の両方に書くときはオーナーシップ属性を一致させる 下の例のようにオーナーシップ属性が一致していないと、 "ARC forbids synthesizing a property of an Objective-C object with unspecified ownership or storage attribute" とエラーが出る。 ```objc // 誤った指定の例 /*** Foo.h ***/ @interface Foo @property (nonatomic, readonly) id value; // 暗黙的に `strong` を指定 @end /*** Foo.m ***/ @interface Foo () @property (nonatomic, copy, readwrite) id value; // `copy` を指定 @end ``` ## ブロック型のプロパティには `copy` を指定する 関数本体の中に記述されたブロックリテラルの記憶領域はスタックに確保される。ブロックオブジェクトをプロパティにセットするためには、 `copy` メソッドを呼んで記憶領域がヒープに確保されたブロックを作らなければならない。 ```objc typedef void (^MyCallback)(void); @property (nonatomic, copy) MyCallback callback; ``` ブロック型のプロパティにオーナーシップ属性を指定しないと、暗黙的な `strong` にはならず、 "ARC forbids synthesizing a property of an Objective-C object with unspecified ownership or storage attribute" とエラーが出る。明示的に `strong` を指定しても警告は出ないが、そうすべきではない。 ## BOOL 型のプロパティには is で始まる名前の getter を指定する getter をメソッドとして呼び出すときは名前が is で始まるほうが自然。 ```objc @property (readonly, getter=isBlue) BOOL blue; ``` ```objc if (color.blue) { } if (color.isBlue) { } if ([color isBlue]) { } ``` (参考:[Adopting Modern Objective-C](https://developer.apple.com/library/ios/releasenotes/ObjectiveC/ModernizationObjC/AdoptingModernObjective-C/AdoptingModernObjective-C.html#//apple_ref/doc/uid/TP40014150-CH1-SW4)) # まとめ ```objc /*** Foo.h ***/ @interface Foo // strong や assign は省略可 @property (nonatomic) id object; @property (nonatomic) NSInteger primitiveValue; // Outlet は weak にする(トップレベルオブジェクトは strong) @property (nonatomic, weak) IBOutlet NSView *view; @property (nonatomic, strong) IBOutlet NSWindow *window; // Delegate は weak にする @property (nonatomic, weak) id delegate; // Mutable なサブクラスがある型は copy にする @property (nonatomic, copy) NSString *possiblyMutable; // 後述 @property (nonatomic, readonly) id internallyWritable; @property (nonatomic, copy, readonly) id internallyWritableByCopy; // ブロック型は copy にする typedef void (^MyCallback)(void); @property (nonatomic, copy) MyCallback callback; // BOOL 型の getter 名は is で始める @property (nonatomic, getter=isBlue) BOOL blue; @end /*** Foo.m ***/ @interface Foo () // 外部から readonly にするプロパティには readwrite を明示する @property (nonatomic, readwrite) id internallyWritable; @property (nonatomic, copy, readwrite) id internallyWritableByCopy; @end ``` # 属性の順序について 属性を並べる順番は任意だが、既存のコードにプロパティを追加するなら周りのスタイルに合わせるべきである。新規に書く場合、筆者は以下の順番を推奨する: ```objc @property (nonatomic, strong, readonly, getter=foo, setter=setFoo:) id foo; ``` `nonatomic` はほぼ常に指定されるので、 `@property (nonatomic` までを定型文としてとらえる。 `strong` や `weak` も(デフォルト値を省略しないなら)常に必要なので2番目に書く。 `getter` と `setter` は最後に指定する。仮にその後に属性を書くと、 getter/setter 名が長い場合に目で追いづらくなるだろう。 |
|
| 52位 |
|
|||
|
23:15:06 |
|
|
JavaScriptフレームワークに興味あるし、Angular.jsを使ってみようかな・・・
そんな純真無垢なあなたを混沌の世紀末に引きずり込むのが、ほかでもない[Tutorial](http://docs.angularjs.org/tutorial/)なのです。 TutorialではほぼControllerしか出てこないので、素直にこの通り書いているとまず間違いなく3カウントでControllerにコードが集中するいわゆるFat Controllerになり、せっかくMVCフレームワークも地獄の荒野になります。 実は、Angular.jsでまず目を通すべきなのは[Developer GuideのConceptual Overview](http://docs.angularjs.org/guide/concepts)です。これを読めばどう処理を分割するかがきちんと書かれていますが、以下ではそれ+経験をもとにAngular.jsで正しくMVCを使用するためのポイントをまとめました。 # Angular.jsの3原則 ## 1.ControllerはイベントハンドリングとData Bindingに集中する これはAngular.jsに限らず、あらゆるフレームワークで言えることです。JavaScriptフレームワークにおけるControllerの役割は以下2つで、それに集中していることが望ましいです。 * バインディング(Model -> View) Controller内で管理するModel(またはそのリスト)の変更を、Viewに反映します。具体的には`ng-bind`/`ng-model`、`ng-repeat`の設定に該当します。Angular.jsではこれらを設定するだけで自動的に変更が反映されます。 * イベントハンドリング(View -> Model) イベントの発生を検知し、対応する動作(関数)を呼び出します。具体的には`ng-click`等の設定に該当します。 呼び出す関数は、Controllerに直接実装する以外に、Modelクラス、Serviceによる実装などの選択肢があります。この関数の実装をどのように分割するのかが、Fat Controllerを避けるポイントとなります(後述)。 ## 2.Viewが複雑になったらDirective・Filterを使う サイト内で共通で使用するデザインなどは、これを別途templateにしdirectiveとしてコンポーネント化することができます。 下記では、`template.html`にデザインを分離し、それを`myTemplate`というdirectiveにして使用しています。 ``` <div ng-repeat="m in models"> <my-template></my-template> <!-- Directiveを呼び出し --> </div> <script> var myApp = angular.module("myApp",[]); ・・・ myApp.directive("myTemplate",function(){ //template.htmlへ切り出し return { restrict: 'E', templateUrl: 'template.html' }; }) </script> ``` directiveについての詳細は[公式ドキュメント](http://docs.angularjs.org/guide/directive)、また手前味噌ですが[こちらの記事](http://qiita.com/icoxfog417/items/59a07c9a74084114fe23)を参照ください。 これでデザインをコンポーネント化できるほか、表示用のロジックをControllerから分離することができます。 FilterはいわゆるHelperのように使用することができ、数値を通貨で表示したりカンマ区切りで表示したりといった、細かいところに使えます。 例としては、以下のような感じです。`date`は最初から組み込まれているFilterなので、何にも書かないでもこれだけで動作します。Filterへは、`:`で引数を渡すことができます。 ``` <div ng-app> {{1288323623006 | date:'yyyy/MM/dd'}}<br> </div> ``` また、こちらも組み込まれている`filter`(紛らわしいですが、filterというFilterです)を配列に適用することで絞り込みもできます(この使い方のイメージのほうが強いかもしれません)。 ``` <div ng-app ng-init="names = ['john','risa','mike','jhonasan','kensiro']"> <input type="text" ng-model="searchText" /> <div ng-repeat="n in names | filter:searchText" >{{n}}</div> </div> ``` ## 3.Serviceによるコンポーネント化を行う `ng-click`で呼び出される関数が数百行あって、Controller内には特定のイベントでしか使わないグローバル変数があふれる・・・という事態になっては、JavaScriptフレームワークを利用するメリットがありません。ここは適切にコンポーネント化を行いましょう。そのためにAngular.jsに用意されている仕組みが、Serviceになります。 ここでいったん用語を整理しておきますが、Angular.jsを構築するオブジェクトは以下2つに分けられます。 * Service ユーザーがAPIを自由に決められるもの。なお、すべてsingletonとして扱われます(インスタンスは一つしか作成されず、使いまわされる)。 * Specialized object controllerや directiveなど、Angular.jsによりAPIが定められているもの。 そしてServiceの作成方法(recipe)には以下の種類があります。 ### Value 値を定義するServiceを作成するrecipeです。具体的にはサーバーサイドのAPIのURLなど、アプリケーション全体の定数管理などに利用します。 ``` var myApp = angular.module('myApp', []); myApp.value('clientId', 'a12345654321x'); ``` ### Factory Valueだけでは処理を記述できないので、引数を取った関数を記載できるrecipeです。 先程の`clientId`をFactoryベースで書き直すと、以下のようになります。 ``` myApp.factory('clientId', function clientIdFactory() { return 'a12345654321x'; }); ``` 当然、下記のように関数も記載できます。 ``` myModule.factory('greeter', function($window) { return { greet: function(text) { $window.alert(text); } }; }); ``` このように`factory`は単一の値だけでなく、関数やオブジェクトなども返却でき、その意味では最も汎用的に利用できるrecipeとなります。 ### Service JavaScriptクラスを生成するrecipeとなり、「ServiceをService recipeで作る」と、そうした意味になります。このため、Angular.js界隈で「Service」といった場合、どちらの文脈で話しているか判断する必要があります。 ``` function Greeter(clientId) { this.clientId = clientId; this.greet = function() { return "Hello " + this.clientId; } } myApp.service('Greeter', ["clientId", Greeting]); ``` ValueやFactoryと異なり、指定(return)した値がそのまま利用されるのでなく、オブジェクトがnewされて利用されます(上記の場合、new Greeterが行われます。ただ、シングルトンとして扱われるためnewされるのは1回のみです)。 既存資産がオブジェクト指向に則りJavaScriptクラスを利用して設計されているなら、Serviceとの相性が良くなります。 ### Provider Providerは一番汎用的なrecipeで、他のrecipeはすべてProviderの簡易記法に該当します。 Factory/Serviceと異なり、`config`でも利用できるという特性がありますが、逆に言うと`config`でどうしても利用したい場合を除き使うべきでないようです。 ### Constant Valueと同様ですが、`config`でも利用できるという特性があります。見た感じとドキュメントの位置的なものから、Providerとセットで使うような印象を受けます。普通の定数/パラメーター管理ならValueで十分でしょう。 上記の詳細は、公式の[Providers](https://docs.angularjs.org/guide/providers)を参照ください。かなり詳しく書かれています。 過去、FactoryとServiceどちらを使えばよいのか、ということがたびたび議論されてきましたが、`1.2.7`以降のドキュメント整備と`Value`recipeの登場により、明確に境界を引くことができるようになりました。 **値の管理ならValue、関数ならFactory、JavaScriptクラスならServiceを利用する。** 定数管理はValue、それ以外はFactoryを利用して記述しておき、Factoryで書いていたけれども一定のまとまりが出てきたもの、あるいはconstructorによる初期化処理が必要になったなどしてクラス化したくなった場合、Serviceにするというのが素直かなと思います(既に手元にJavaScriptクラス資産がある場合この限りでない)。 Controllerを除くSpecialized object(directiveやfilterなど)もFactoryで実装されており、全体としてFactoryで実装するというのは筋にかなっているかなと思います。 Serviceにする場合でも、上記のとおりrecipeの切り替えによる記載の変更はわずかであり、切り替えの負担はそれほどないと思います。 Service recipeを利用した処理のモジュール化の実例として、[サンプル](http://jsfiddle.net/icoxfog417/DG24c/)を作ってみました。 Serviceに切り出す対象の処理は、「Modelにメソッドとして実装するのも、Controllerに実装するのも適切ではない処理」ということなのでどんなサンプルにするか悩みましたが、こんな形のゲームっぽいものにしました。 1秒ごとに草がはえていき、牛を投入すると草を食べてくれるが5回食べるとおなか一杯で死んじゃうというものです。 Modelでは牛の状態(今までどれだけ草を食ったか)、Controllerでは経過秒数を管理しています。そして、経過秒数による状態の評価(草&牛の数の計算)をServiceとして切り出しました。 これをControllerで実装していたらまさに「ロジックがControllerに混在している」状態となり、かといって、牛Modelで扱うには不適当・・・なので、まさにServiceの出番なはず。 こんな視点でServiceをうまく使うことができれば、ゲームなどModelの状態変化が複雑に絡み合う場合でもうまく実装できると思います(そしてAngular.jsの場合単体テストが簡単になるというのもうれしいポイント)。 上記の3原則を覚えておけば、お前のAngular.jsはもうMVCではない・・・と言われないハズ。 |
|
| 53位 |
|
|||
|
21:03:09 |
|
|
JavaScriptフレームワークは[数多い](http://codebrief.com/2012/01/the-top-10-javascript-mvc-frameworks-reviewed/)が、結局どれを選んだらよいのかよくわからん、ということはよくある。
今回は、メジャーどころの三大フレームワークを使ってまったく同じ機能のTodoアプリを作成してみました。 そのソースコードは、下記の通りjsFiddleで参照&稼働させてみることができます。これで(きれいごとではない)クセやコード量などを感じてみてもらえればよいと思います。 | FW | jsFiddle | 所感 | |:-----------|:------------|:------------| | Backbone.js | [Todo①](http://jsfiddle.net/icoxfog417/pfxP5/) |大規模開発向きか。簡単なアプリなら割に合わない| | Knockout.js | [Todo②](http://jsfiddle.net/icoxfog417/sujqa/) | わかりやすい。面倒な面はあるが、不可解でドツボにはまることがあまりない | | Angular.js | [Todo③](http://jsfiddle.net/icoxfog417/nLC3g/) | 便利。ただ、自由度が高すぎて統制をかけたい場合は向かないかも| **Todoリストの機能** 1.テキストボックスから、Enterで追加できる 2.登録したTodoはダブルクリックで編集可能になり、Enterで編集確定できる 3.登録されているTodoの総件数がフッターに表示される 4.完了したTodoがある場合、それらをリストから消すボタンが表示される 5.全選択/解除を行うチェックボックスがある ## 個人的な結論 趣味開発で使うならAngular.js・仕事で使うならKnockout.jsをお勧めしたい。 まず、フレームワークを選択する際は、以下3つの選択基準を持つとよいと思う。 **1.開発の規模** 大規模ならBackbone.jsはお勧めできる。 書き方が決まっていて、チュートリアルに目を通せば(面倒なのは置いておいて)何を作らなければならないかは簡単に理解できる。そこそこの人数で長い時間の開発を行うなら、UIチームはアプリケーションとView、サーバーサイドはModelとCollection、という感じで分業もしやすいと思う。UIバインディング型のフレームワークは性質上画面DOMありきなので、バインディング部分のモジュール化は結構頭を使う必要があると思う。 **2.フレームワークに求めるもの** コードを構造化したいのか、生産性を上げたいのか、両方の場合どちらの優先度が高いのかを考えるとよいと思う。 生産性の向上ならUIバインディング型のKnockout.jsやAngular.jsがお勧めだ。 これらは、普通に使ってて便利!と思える瞬間が多い。個人で開発する場合構造化に迫られる人はあんまりいないと思うので、そういう場合にも良い。 **3.趣味** 使い始めるとその後拘束されることになるので、なんだかんだ言って第一印象は重要だ。今回は実際に書いたコードを掲載しているので、参考になれば。 以下は、所感の詳細。 ## Backbone.js 確かにこれを使えばJavaScriptはきれいに書ける気がする。が、手間はかなりかかる。 今回Todoリストを表示するのに、まずTodoのModel、これを表示するためのView、リスト表示するためのCollection、そして最後にアプリケーション、と4つもオブジェクトを作る必要がある。 将来的な使いまわしを考えるならこれが活きてくるのかもしれないが、そうするとそこそこ大規模でないと割に合わないと思う。あと「軽い」とよく言われるがその分他ライブラリ(jQuery/Underscore.js etc)に依存しているので、そこは要注意(特にjQueryはすでにプロジェクトで使っているバージョンがあったりするので)。 ## Knockout.js わかりやすい、というのが第一印象。DOMとJavaScript側ViewModelの対応関係は見通しがよく、理解しやすい。 イベント/テンプレートバインディングなど効果の実感できる機能がすぐに使え、JavaScriptフレームワークを初めて使う、またチームで展開してみたい、という時にお勧めできる。 独自タグがないのでHTMLはHTMLとして完成されるのもポイントが高い(他のフレームワークは、そんな属性ない警告や不明タグ警告がそこそこ出る)・・・が、その分煩雑な記述が要求されることも多い(特にIF文)のは注意。 ## Angular.js 形としてはKnockout.jsに近いが、より簡単にかける。 具体的に簡単な点としては、Knockout.jsだと単純に値を書きたい場合でも```<span data-bind="text:hoge"></span>```みたいな形でDOMを経由しないといけないが、Angular.jsでは```{{hoge}}```と一発でかける(ただ、逆にng-click="function(){xxx}"みたいにはかけないので、一長一短?)。 今回のTodoアプリでは、選択しているのが1つなら"item"、複数なら"items"にするというようなしちめんどくさい機能があるのだが、こういう細かい実装があるときこの差が結構出てくる。 ただ、自由に書ける反面統制がききにくそうで、そこは注意が必要と感じた。チームで使うなら、Model定義とtemplateについては要ルール整備と思う→カオスにならないためということで、[まとめました](http://qiita.com/icoxfog417/items/2ac773c33a8b34288551)。 また、Backbone.jsにおけるUnderscore.js、Knockoutにおけるko.utilsみたいなサポートライブラリがないので、「配列から画面で選択された要素を削除する」みたいな簡単な処理でも書くのが面倒なことがある(今回のTodoリストでも・・・)。 |
|
| 54位 |
|
|||
|
23:42:52 |
(Gaiax Co.Ltd. 所属) |
|
(興味深い手法があれば随時追加していきます at 2013/04/11) ネットを検索すると、色々な手法が出てきますが、自分としては[「WEB+DB PRESS plus 開発ツール徹底攻略」](http://www.amazon.co.jp/dp/4774156167?tag=tetsujijp-22)p.71 に載っていた以下の手法がシンプルで良く理解できました。 本家リポジトリの例として、実際にGitHubに存在する練習用リポジトリ `git@github.com:DQNEO/Renshu.git` を使います あなた (*youraccount*) が既にForkしているRenshuリポジトリをcloneします。 $ git clone git@github.com:youraccount/Renshu.git Cloning into 'Renshu'... remote: Counting objects: 109, done. remote: Compressing objects: 100% (70/70), done. remote: Total 109 (delta 15), reused 109 (delta 15) Receiving objects: 100% (109/109), 12.34 KiB, done. Resolving deltas: 100% (15/15), done. $ cd Renshu ブランチは以下のような状態になっているはずです (他にもあるかもしれません)。 $ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/master リモートリポジトリとして、オリジナルのリポジトリを **upstream** という名前で設定します。 $ git remote add upstream git://github.com/DQNEO/Renshu.git 以後、このリポジトリは **upstream** という名前で本家リポジトリを参照します。一度設定すれば再設定の必要は無いようです。 ブランチを確認すると、以下のように remotes/upstream/master が加わったことが分かります。環境によっては後述の `git fetch upstream` の操作を行った後で出てくる場合もありますので、出てこない場合には `git fetch upstream` を試した後に再度 `git branch -a` で確認してみてください。 $ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/master remotes/upstream/master 本家リポジトリの変更を取り出すためには **fetch** と **merge** の手順を取ります。 まずfetch $ git fetch upstream remote: Counting objects: 1, done. remote: Total 1 (delta 0), reused 1 (delta 0) Unpacking objects: 100% (1/1), done. From git://github.com/DQNEO/Renshu * [new branch] develop -> upstream/develop * [new branch] master -> upstream/master そしてmerge $ git merge upstream/master Updating fe579f8..a1e70ae Fast-forward 変更が無い場合には "Already up-to-date." と表示されて終わりです。今回は差分があったので、コミットIDがfe579f8からa1e70aeまでの差分がmergeされました。 詳細は[当該書籍](http://www.amazon.co.jp/dp/4774156167?tag=tetsujijp-22)を参照ください。 |
|
| 55位 |
|
|||
|
06:17:06 |
(None 所属) |
|
自分用のまとめです。
## 色彩の三属性 色は彩度・明度・色相の三属性(三要素とも呼ぶ)から成り立っている。 ### 1.色相 - 色味の違いのこと。 - 同じ赤でも赤紫・黄みの赤などがあるように、色相の数は無数に存在。 #### 色相環  *via [色相 - Wikipedia](http://upload.wikimedia.org/wikipedia/commons/d/db/HLSColorSpace.png)* - 赤、黄、緑、青、紫といった色を、順序立てて並べた図。 - それぞれ色の反対側に補色がくるように作られている。 - 虹と同じ。 #### 類似色 - 色相環の隣り合う色のこと。 #### 補色 - 色相環において、反対側に存在する色同士のこと。 - 補色同士の関係は、お互いを引き立てる効果がある。 - 純色や、明度の同じ補色同士を組み合わせると、お互いが主張しすぎてハレーションを起こし見づらくなるので注意する。 ### 2.彩度 色の鮮やかさの度合い。  *via [配色パターンからWebデザインを考える | Webクリエイターボックス](http://www.webcreatorbox.com/wp-content/uploads/2010/03/saturation.jpg)* | 彩度 | 特徴 | |:----|-------------------| | 高い | 派手・華やか・目立つ | | 低い | 地味・おだやか・上品 | #### 無彩色 - 白と黒との混合で得られる色(白と黒自体も含む)の総称。 - 白・黒・さまざまな濃度の灰色が含まれる。 - 彩度が0であることを表す。 - 無彩色でない色は、有彩色である。  - 写真技術と光への道でフォトディレクション-Jimdo撮影編") *via [写真技術 色と陰影(手代木さんblogより) - 写真技術と光への道でフォトディレクション-Jimdo撮影編](http://u.jimdo.com/www56/o/se6b3834a1c29b2ed/img/icab110824ab7059f/1307344953/std/image.gif)* #### 有彩色 - 白、黒、灰色以外の色味を持った色のこと。 - 有彩色にはさらに、純色、清色、中間色に分類される。 以下のような図を、 **等色相面** と言う。  *via [純色、清色、中間色|株式会社ノイエデザイン](http://www.noiedesign.com/contents/wp/wp-content/uploads/2013/07/e2c20c4d6b295ae0b02886223dc0f230.jpg)* ##### 純色 - 各色相において、最も彩度が高い色。 - ただし、色空間によっては純色以外にも彩度が最大になる色がある。 ##### 清色 - 清色はさらに、明清色、暗清色に分類される。 - 清色にはくすんだ印象がなく、濁りがない色という特徴がある。 ###### 明清色 - 純色に白だけを加えた色。 - ピンクや水色など ###### 暗清色 - 純色に黒だけを加えた色。 - 紺色や茶色など ##### 中間色(濁色) - 純色に灰色を加えた色。 - ベージュやオリーブグリーンなど ### 3.明度 色の明るさの度合い。  *via [配色パターンからWebデザインを考える | Webクリエイターボックス](http://www.webcreatorbox.com/wp-content/uploads/2010/03/value.jpg)* | 明度 | 特徴 | |:----|-------------------| | 高い | 明るい、さわやか、爽快、清潔感のある | | 低い | 暗い、重厚、落ち着いた、高級感 | #### Tips:コントラストを意識する  *via [色を色で見ないで|1 pixel|サイバーエージェント公式クリエイターズブログ](http://stat.ameba.jp/user_images/20140219/10/ca-1pixel/d3/4a/p/o0600042912851200552.png)* >モチーフの『構造』を理解し、『本質』を見抜く。 *via [色を色で見ないで|1 pixel|サイバーエージェント公式クリエイターズブログ](http://ameblo.jp/ca-1pixel/entry-11770516521.html)* #### 色によって感じる明るさの違い - 暖色系(黄色等)の色は明るく見え、寒色系(紺等)の色は暗く見える。  *via [配色が苦手でも、これを読めばスキルアップできるおすすめスライド6選。 | Handy Web Design](http://image.slidesharecdn.com/random-110825054720-phpapp01/95/slide-9-728.jpg?cb=1314269361)* ###### ナチュラルハーモニー - 配色のうち明るい方に黄色に近い色を採用し、暗い方に青色に近い色を採用すること。 ###### コンプレックスハーモニー - 配色のうち明るい方に青色に近い色を、暗い方に黄色に近い色を配色すること。 - コンプレックスハーモニーは、違和感を与える配色になるので気をつけて使う必要があるが、モダンな印象を与えることも。 ## 色調(トーン)  *via [日本色研事業株式会社 HomePage](http://www.sikiken.co.jp/pccs/fig/pccstone.jpg)* - 3属性の組み合わせ - 配色とは、トーンとトーンの組み合わせのこと。 - 色相、彩度、明度のうち、彩度と明度から色を分けたもの。 - 一つの色に対して、彩度や明度の差で様々な色のパターンが作れる。(「色調/トーンが違う」と表現) - 色調から色の組み合わせを選択する場合、明るめの色と暗めの色を選択するとコントラストの差によって視認性が高くなる。 - 逆にコントラストの差が少ないものを選択すると、視認性が低くなる。 ## 寒色、暖色、中性色 ### 寒色 - 見た時に冷たさを感じる色 - 青、青緑、青紫 - 陰気、鎮静 - 後退色(奥に引っ込んで見える) ### 暖色 - 見た時に温かみを感じる色 - 赤紫・赤・オレンジ・黄色 - 陽気、興奮、リラックス - 進出色(前に出て見える)  *via [12色の色が与える印象について。配色に困った時のいろいろ。 | Handy Web Design](http://handywebdesign.net/wp-content/uploads/2012/07/070909.jpg)* ### 中性色 - 黄緑、緑、紫 #### 中間色 >一方、『中間色』とは、考えのまったく異なる色を指す名前で、【グレー(灰色)を含んだ鈍い色調の一連の色】を指しています。したがって、グレーを加えていけば、暖色系も寒色系も中性色も『中間色』となるのです。 *via [色彩 Question and Answer:中性色と中間色の違いは? - livedoor Blog(ブログ)](http://blog.livedoor.jp/questions2/archives/52438341.html)* ## 混色 - 混色には大きく分けて加法混色と減法混色がある。 - 色には光の3原色(RGB)と色の3原色(CMY)がある。 - WEBデザインはRGBで、DTPはCMYKを使って配色を決めるのが一般的。  *via [日本色研事業株式会社 HomePage](http://www.sikiken.co.jp/colors/fig-c/konshoku-1.jpg)* ### 加法混色 加法混色には、同時加法混色、併置加法混色、継時加法混色がある。 #### 同時加法混色 - 加法混色の3原色 = RGB(レッド・グリーン・ブルーの3色) - 基本は黒でそこに色を足していくことで特定の色を表す。 - 全てが重なった状態が白になる。 - テレビなどの電子機器に使われる。 - RGB色空間は可視光空間なので色域が広く、彩度が高い。(放っておくと高彩度の配色になりやすく、難易度が高い) #### 併置加法混色 - 小さな色点を高密度で並べることで起きる混色。 - モザイク画や点描画、織物などに使われる。 - 離れたところから見ると混色された絵として見ることができる。  *via [混色│色彩学講座](http://rock77.fc2web.com/main/color/color1-6.files/heiti1.gif)* #### 継時加法混色(回転混色) - 塗り分けた円板を高速で回転させると異なる一つの色に見える。  *via [Illustrator基礎講座-カラー](http://illustrator-ok.com/illustrator_koza/color/color_asset/ostwasd_kaitenkongo.gif)* ### 減法混色 - 減法混色の3原色 = CMY(シアン・マゼンタ・イエロー) - 基本は白でそこに色を足していくことで特定の色を表す。 - 全てが重なった状態が黒になる。 - 印刷物等に使われる。 #### CMYK >CMYの3色で作った黒は完全な黒とは色味が違うため、別にK(ブラック)という黒専用のものが用意されています。これは印刷の基本となるプロセスカラー(4つの色)と呼ばれていて、CMYKと表現します。ちなみにブラックがBじゃなくてKなのは、BはBlue(青)を表すためです。 *via [12色の色が与える印象について。配色に困った時のいろいろ。 | Handy Web Design](http://handywebdesign.net/2012/07/12colors-give-you-the-impression/)* ## 色立体 - 色相、明度、彩度の全ての要素の色の変化を表す3次元の図。  *via [5.13 マンセル色立体](http://gc.sfc.keio.ac.jp/class/2005_14630/slides/05/img/110.png)* ## 表色系 - 色立体に色を配置するときの基本ルール。 - 表色系には、混色系と顕色系がある。 ### 混色系 - 色や光の三原色を混ぜ合わせて表現。 - RGB(HSB/HSV)やCMY、XYZ表色系など - 機械向き ### 顕色系 - 色相、彩度、明度で表現。 - マンセル表色系やPCCSなど - 人間向き #### マンセル色空間 - 顔料ベースの減法混色。 - 必然的に彩度がほどよく抑えられるため、調和のとれた色を選びやすい。 - 向かい合わせになっている色同士が物理補色の関係になっている。 - つまり、色を混ぜ合わせれば無彩色になる。 #### PCCS(日本色研配色体系) - 色彩調和を目的に作られた(配色向き) - Hue(色相)+ Tone(トーン)で色を表す = **ヒュートーンシステム** - 向かい合わせになっている色同士が心理補色の関係になっている。 - つまり、互いに残像として見ることのできる補色対。 ## 和色 >和風なデザインにする場合、黒・白・赤を基準に彩度の低い色がプラスされることが多いです。「和風にしたいからとりあえず彩度下げとけー」って考えもあながち間違っていないかも。。 *via [配色パターンからWebデザインを考える | Webクリエイターボックス](http://www.webcreatorbox.com/tech/web-design-color-scheme/)* ## 視認性を高める  *via [12色の色が与える印象について。配色に困った時のいろいろ。 | Handy Web Design](http://handywebdesign.net/wp-content/uploads/2012/07/070907.jpg)* ## 誘目性を高める - 赤〜黄色の彩度の高い色 - 明度差をつける ## 対比現象と同化現象 - 色の組み合わせよっては、単色で見たときと色が違って見えることがある。 - 目の錯覚を利用した錯視画像はこの現象を利用している。 ## 色の選び方 伝えたいテーマに基づいたメインカラーを決め、そこを基準にベースカラー、アクセントカラーを決定する。  *via [配色が苦手でも、これを読めばスキルアップできるおすすめスライド6選。 | Handy Web Design](http://image.slidesharecdn.com/random-131004063351-phpapp01/95/slide-35-638.jpg?cb=1380890929)* ### 1.メインカラーを決める - 明度の低い色が扱いやすい(視認性が高い) #### 色の意味から考える - 色にはその色から自然と連想するキーワードがある。 - 意味を理解せずに色の選択はできない。 - 目的に合った色を選択しなければ、見る人に適切な意味を伝えることができず、誤った意味を伝えてしまう場合もある。 - 色彩設計でもっとも重要なのは、「何を伝えたいか。」 | 色 | 代表的な意味 | |:---|-----| | 赤 | 情熱、活発、積極的、外向性、興奮、勇気、欲望、革命、力、勇敢、活気、注意 | | 橙 | 友情、食欲、活力、行動、冒険、陽気、成功、活動的、発展的、好意的、善意、安定、幸福、太陽 | | 黄 | 楽しい、元気、上昇志向、知性、好奇心、洞察力、決断力、想像力、快活、協力 | | 黄緑 | 若々しい、調和、知恵、勇気、希望、順応 | | 緑 | 自然、安定、秩序、平和、健康、調和、地球、静か、安全、安心、信頼、若者 | | 青緑 | カジュアル、洗練、冷静、治癒 | | 青 | 清潔、冷静、安定、信頼、誠実、知恵、神秘、調和、知性、落ち着き | | 紺 | 革新的、豊かさ、専門的、信頼、知識、法則、論理 | | 紫 | 気品、高貴、神秘的、想像 | | 桃 | 可憐、好意、慈愛、愛情、尊敬、感謝、女性的、絶妙、快活、幸福、友情、優しい | | 白 | 純白、清潔、高貴、洗練、平和、明快、献身、単純 | | 黒 | 高貴、神秘、洗練、不安、優雅 | | 灰 | 調和・憂鬱・不安・過去・薄暗い・思い出 | | 銀 | 冷たさ・金属・洗練・硬い・上品 | | 金 | 成功・高級・富・頂点・輝き・豪華 | ### 2.ベースカラーを決める - 背景に使用される。 - 明度が高く、彩度が低い色や、無彩色が多い。 ### 3.アクセントカラーを決める - 部分的に強調するアクセントとなる色。 - 彩度の高い色や、メインカラーと捕色関係に当たる色を選ぶとより効果的。 - ただし補色を使った配色は扱いが難しい。色の扱いに不慣れな人は色相差を小さめに。 ### 4.色の割合を考える [70:25:5]の法則 - ベースカラーを70%、メインカラーを25%、アクセントカラーを5%で割り振るとバランスよく見える。  *via [配色が苦手でも、これを読めばスキルアップできるおすすめスライド6選。 | Handy Web Design](http://image.slidesharecdn.com/random-110825054720-phpapp01/95/slide-16-728.jpg?cb=1314269361)* ### 5.カラーを分割する - それぞれの割合の中で使用する色を増やす。 - 色相が同じでトーンが違う色 - トーンが同じで色相が違う色  *via [配色が苦手でも、これを読めばスキルアップできるおすすめスライド6選。 | Handy Web Design](http://image.slidesharecdn.com/random-110825054720-phpapp01/95/slide-31-728.jpg?cb=1314269361)* ## ジャッドの色彩調和論 - 20世紀半ばまでの先人たちの色彩調和論を"4つの原理"にまとめたもの。 ### 1.秩序の原理 - 一定の法則によって規則的に選ばれた色は調和する - 規則的に色を選ぶ(補色、色相から等間隔で選ぶなど)  *via [「色相環」とか「トーン」ってどう使うの?配色のコツは「ジャッドの色彩調和論」 | Design | WebNAUT](http://webnaut.jp/design/img/img_design_131017_01.jpg)* #### 同一調和 / アイデンティティ(Identity) - 同じ色相のみを使った配色 - 非常に調和が取れる一方、単調になりやすい。 - 明度や彩度差をもたせてコントラストを取ると良い。 #### 類似調和 / アナロジー(Analogy) - 二つの色相差が25から43度の類似を使った配色法。 - 色の違いもわかり、強すぎない。ソフト。 - 明度や彩度に大きくコントラストをとるとうまくいきやすい。 - 失敗しにくい配色。 #### インターミディエート(Intermediate) - 色相環で90度の位置にある色同士を使った配色 - バランスよく安定感がある。 #### 対比調和 / ダイアード(Dyad) - 色相環の正反対にある色同士の配色。 - 派手。主張が強い。失敗すると目が痛い。 - どちらかの明度を上げた場合、補色である色の明度は落とすのがポイント。 - コンプリメンタリー(Complementary)とも呼ばれる。 #### オポーネント(Opornent) - インターミディエートとダイアードの間での2色配色。 #### スプリットコンプリメンタリー(Split Complementary) - 基準色とその補色の類似色を使った配色。 - カラフル。 #### トライアド(Triad) - 色相環を三等分した位置にある3色での配色。 - バランスの取れた配色の組み合わせ。 #### テトラード(Tetrad) - 色相環を5つに分けた色 - もしくはトライアドに白と黒を加えた5色の配色。 #### ヘクサード(Hexad) - 色相環を正六角形分けた6色 - もしくはテトラードに白と黒を加えた6色の配色。 ### 2.馴染みの原理 - 目に馴染んでいる色を選ぶ(グラディエーション、影などの陰影、自然に存在する色など) - 実際に自然に存在する配色は人間にとって受け入れやすい。 - 真っ黒というものは自然界には存在しないので、うっすらと色を含ませたほうが馴染むことも。 - ナチュラルハーモニーを利用する  *via [「色相環」とか「トーン」ってどう使うの?配色のコツは「ジャッドの色彩調和論」 | Design | WebNAUT](http://webnaut.jp/design/img/img_design_131017_02.jpg)* ### 3.類似の原理 - 共通性のある色を選ぶ(色相が近い色、彩度、明度を揃えた配色)  *via [「色相環」とか「トーン」ってどう使うの?配色のコツは「ジャッドの色彩調和論」 | Design | WebNAUT](http://webnaut.jp/design/img/img_design_131017_03.jpg)* #### ドミナントカラー配色 色相を統一して、トーン(明度・彩度)を変化させた配色。 #### トーンオントーン配色 ドミナントカラーよりも明度差を大きくした配色。 #### ドミナントトーン配色 トーン(明度・彩度)を統一して、色相を変化させた配色。 #### トーンイントーン配色 ドミトナントトーンよりも、明度差の小さい配色。 #### カマイユ配色 - 色相・トーン差がほとんどなく、区別がつかないほど微妙な色調の違いによる配色。 - 素材の異なるものの組み合わせで、微妙に色が変化があるのもカマイユ配色の一種。 #### フォ・カマイユ配色 カマイユ配色に対し色相・トーンもやや変化をつけた配色。 #### グラデーション配色 - 3色以上の多色を色相・彩度・明度を一定の法則に従って段階的に変化させる配色。 - リズム感が生まれ立体感や遠近感などの視覚効果を得られる。 ### 4.明瞭性の原理 - コントラストの効いた配色をする  *via [「色相環」とか「トーン」ってどう使うの?配色のコツは「ジャッドの色彩調和論」 | Design | WebNAUT](http://webnaut.jp/design/img/img_design_131017_04.jpg)* #### トリコロール配色 - フランス国旗に代表される、明快なはっきりとした三色配色。 - トリプル・カラー・ワークとも呼ばる。 - 間に白などの低彩度色や無彩色を挟んで調和させるセパレーションカラーを置く事が多い。 #### セパレーションカラー - 低彩度色や無彩色を色と色の間に挟んで調和させる技法。 - ハレーションが生じたり、隣接した為ぼやけた色を引き締める。 #### ビコロール配色(ダイアード配色) - 明快なはっきりとした二色配色。 - 隣接する色によっては、ハレーションが生じたり、ぼやける事があるので注意が必要。 #### コントラスト配色 - 反対色でつくる配色 - くっきりとしメリハリがあり力強い印象になる。 - コントラストには、色相・彩度・明度それぞれのコントラストがある。 - 秩序の原理のダイアードもこの配色 ## 配色ツールを使う ###カラースキームいろいろ [Color wheel | Color schemes - Adobe Kuler](https://kuler.adobe.com/create/color-wheel/) [[ HUE / 360 ] The Color Scheme Application](http://hue360.herokuapp.com/) [colourcode - find your colour scheme](http://colourco.de/) [Color Scheme Designer 3](http://colorschemedesigner.com/) [0to255](http://0to255.com/) [NIPPON COLORS - 日本の伝統色](http://nipponcolors.com/) [配色の見本帳 | キーカラーで選ぶ配色パターン](http://ironodata.info/) ## 色覚テスト [Online Color Challenge](http://www.xrite.com/online-color-test-challenge) >程度こそ違えど女性では255人に1人。男性では12人に1人の割合で何かしらの色覚異常を持っているとのこと。Wikipedia「色覚異常」での記載では、日本人だともっと低くて、女性で500人に1人、男性で20人に1人の割合くらいみたいですね。 男性に多い理由は、ご存知の方も多いかと思いますが、X染色体の異常による遺伝的な要因が強いため。 *via [あなたは色がどれだけ見えていますか? グラデーションで色の正確さを計測できる「Online Color Challenge」 : ギズモード・ジャパン](http://www.gizmodo.jp/2014/03/online_color_challenge.html)* ## その他 [iOS 7 colors](http://ios7colors.com/) [新旧の標準カラーパレットのまとめ「Colors」:phpspot開発日誌](http://phpspot.org/blog/archives/2014/03/colors.html) ## 参考サイト [色 - Wikipedia](http://ja.wikipedia.org/wiki/%E8%89%B2) [配色を考えやすくするための様々なルール - YATのBLOG](http://wp.yat-net.com/?p=4036) [配色パターンからWebデザインを考える | Webクリエイターボックス](http://www.webcreatorbox.com/tech/web-design-color-scheme/) [配色に自信がなくても!Webデザインが好きになる配色ツールと使い方 | Webクリエイターボックス](http://www.webcreatorbox.com/tech/color-tool-web-design/) [12色の色が与える印象について。配色に困った時のいろいろ。 | Handy Web Design](http://handywebdesign.net/2012/07/12colors-give-you-the-impression/) [暖色と寒色 ≫ 色の色々な話](http://colorstalk.jp/warm-color-cool-color/) [色彩 Question and Answer:中性色と中間色の違いは? - livedoor Blog(ブログ)](http://blog.livedoor.jp/questions2/archives/52438341.html) [配色が苦手でも、これを読めばスキルアップできるおすすめスライド6選。 | Handy Web Design](http://handywebdesign.net/2013/12/slide-share-about-color/) [「色相環」とか「トーン」ってどう使うの?配色のコツは「ジャッドの色彩調和論」 | Design | WebNAUT](http://webnaut.jp/design/645.html) [日本色研事業株式会社 HomePage](http://www.sikiken.co.jp/colors/index.html) [色相環 マンセルとPCCSのちがい | 発信~色*駅 - 楽天ブログ](http://plaza.rakuten.co.jp/sachiki/diary/200904300000/) ## 参考文献 [Amazon.co.jp: A・F・T色彩検定公式テキスト3級編: A・F・T公式テキスト編集委員会, 社)全国服飾教育者連合会(A・F・T): 本](http://www.amazon.co.jp/A%E3%83%BBF%E3%83%BBT%E8%89%B2%E5%BD%A9%E6%A4%9C%E5%AE%9A%E5%85%AC%E5%BC%8F%E3%83%86%E3%82%AD%E3%82%B9%E3%83%883%E7%B4%9A%E7%B7%A8-A%E3%83%BBF%E3%83%BBT%E5%85%AC%E5%BC%8F%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E7%B7%A8%E9%9B%86%E5%A7%94%E5%93%A1%E4%BC%9A/dp/4901257188) |
|
| 56位 |
|
|||
|
14:55:43 |
(Increments inc. 所属) |
|
オバマ大統領の再選に大きく寄与したことで大きな注目を集めているA/Bテスト。A/Bテストを導入した、することを検討している、という開発現場も多いのではないだろうか。
そんな中、Web上で次のような議論を見つけた。 - [20 lines of code that will beat A/B testing every time](http://stevehanov.ca/blog/index.php?id=132) - [Why multi-armed bandit algorithm is not “better” than A/B testing](http://visualwebsiteoptimizer.com/split-testing-blog/multi-armed-bandit-algorithm/) 一言でまとめると「A/Bテストよりバンディットアルゴリズムの方がすごいよ」「いやいやA/Bテストの方がすごいし」ということだ。 で、バンディットアルゴリズムとは一体何者なのか? そこで[Bandit Algorithms for Website Optimization (O'REILLY)](http://www.amazon.co.jp/gp/product/B00AM86Y0K/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=B00AM86Y0K&linkCode=as2&tag=qiita-taka84u9-22)を読んでみた。その結果分かったことを踏まえてざっくりと解説する。(O'REILLYのサイトに行けば日本語版も売っている)  # 要約 バンディットアルゴリズムとは… - 実データに基いてWebサイトなどの改善を行う手法の総称 - 活用(exploit)と探索(explore)の2つから成る - さまざまなアルゴリズムが考案されている ## この記事を読むと分かること - A/Bテストの概要 - バンディットアルゴリズムの概要 - Epsilon-Greedy Algorithm - 狭義のA/BテストはEpsilon-Greedy Algorithmにおいて`ε = 1`の場合に等しい ## この記事を読んでも分からないこと - Epsilon-Greedy Algorithmより洗練された手法の詳細 - Softmax Algorithm - UCB Algorithm - バンディットアルゴリズムを実世界で利用する方法 # そもそもA/Bテストとは何か オバマ大統領の選挙キャンペーンで用いられ、再選に大きく寄与したことで一躍有名になったA/Bテストは、これまで人間の主観に依存していた様々な事柄の改善に対する意思決定方法を置きかえる、データに基づいた工学的なアプローチだ。 と、説明するとなんだか仰々しい感じがするが、その中身は気が抜けるほど単純である。 N種類のデザインを作ってユーザをランダムに振り分け、一番良い結果を導き出した法を採用する、という手法のことだ。A/Bテストという言葉はAとBという2種類のデザインを用いたために付けられた名前だが、別にNは2である必要は無い。ここでいう良い結果、というのは例えばコンバージョン率であったり、サイト滞在時間のような数値として計測できるものを用いる。オバマ大統領の選挙戦では選挙資金の寄付額などを用いたことが知られている。 大事なことは人の主観ではなくデータに基いて最適なデザインを模索するという点だ。データではなく主観に頼った場合、得てして発言力のある人の意見(CEOとかプロデューサーとか)が反映されることから、このような意思決定方法をHIPPO(Highest Paid Person's Opinion - 高給取りの意見)と呼ぶ。 しかしながら、A/Bテストという言葉は「人の意見ではなくデータに基いて判断する方法の総称」として扱われていることも多い。特に非学術的ニュースメディアではその傾向が強いように思う。下記に挙げる記事はその一例だ。 - [A/Bテストがビジネスルールを変えていく(あるいは、ぼくらの人生すらも?)<< WIRED.jp](http://wired.jp/2012/12/29/abtest_vol5-2/) - [【WEBマーケター必見】ABテスト最新事例19選 - NAVER まとめ](http://matome.naver.jp/odai/2130457591894883201) どちらが正しくて、どちらが間違っている、という話はここではしない。ここで言いたいのは、「A/Bテスト」という単語を見たときそれが広義の意味で使われているのか、狭義の意味で使われているのか、読者が文脈に応じて読み分ける必要がある、ということだ。 本稿は狭義の意味で用いている。 # バンディットアルゴリズムとは そんなA/Bテストと同じ文脈で語られる言葉に **バンディットアルゴリズム** (Bandit Algorithms)がある。これもA/Bテスト同様にデータに基づいた意思決定手法である。 これもA/Bテスト同様に複数のデザインを用意し、ユーザを振り分けて実際にテストして、その結果から最適なデザインを探すのだが、この個々のデザインを歴史的な経緯から **腕** (arm)と呼ぶ。 バンディットアルゴリズムは、もともとギャンブルに対する最適な戦略を考えることから始まった。 目の前に5つのスロットマシーンがある。それぞれのマシーンの当たる確率とその時の報酬は異なる。さて、手元に1000枚のコインがあるとき、そのコインをどのように配分すれば得られる報酬を最大化できるだろうか? 一般的なスロットマシーンには腕と呼ばれる棒がついており、コインを投入してその棒を引くとドラムが回転し始める。つまりここでやっていることは最も効率的な腕の引き方を考えていることと同じだ。 そういった訳で、バンディットアルゴリズムでは一つ一つのデザインを腕と呼ぶ。実際、Wikipediaでは *[Multi-armed bandit](http://en.wikipedia.org/wiki/Multi-armed_bandit)* という名前の項目でバンディットアルゴリズムは説明されている。 そして、今の喩え話をWebアプリケーション開発の文脈に置きかえると、5つのデザインと1000人のユーザがいるとき、どのようにユーザを分配すれば最も効果的かを考える、ということになる。 ## バンディットアルゴリズムとA/Bテストは何が違うのか それは既知の知識を活用するかどうかだ、と説明されることが多いようだ。 重要な要素として、バンディットアルゴリズムは **活用** (exploitation)と **探求** (exploration)の2つから構成される。活用は現時点で最もよいとされている腕を使うことであり、探求は候補の腕の中でどれが最も優れているかをテストすることだ。A/Bテストは全てのユーザを探求に割り振り、活用は行わない。 この立場からすれば「ユーザのX%でA/Bテストを実施する」というのは間違った表現だ、ということになるだろう。 ## 何故バンディットアルゴリズムは"活用"を行うのか 何故バンディットアルゴリズムで活用を行うのか。 その理由を一言で言うなら **リスクマネジメントの為** である。 バンディットアルゴリズムの目的は最良の腕を見つけることであることは間違い無いのだが、別に我々は研究目的でWebサイトを運営しているのではない。本質的には **収益を最大化すること** が目的なのだ。その意味でこれまでに一定の結果を残してきた腕と、どんな結果をもたらすか分からない腕を同列に扱うことは、短期的な収益減のリスクを伴う。そこで、ある程度のユーザには現時点でもっともよいと思われるものを提示し、残りのユーザに対してテストを行うのだ。 ## A/Bテスト支持派の主張 多くの場合この"活用を行なう"という点においてバンディットアルゴリズムの方が優れていると説明されることが多いのだが、面白いことに、全く同じ理由でA/Bテストの方が優れていると主張する意見もある。つまり"活用が無い"からA/Bテストは優れている、というのだ。 何故A/Bテスト支持者が活用は不要だと考えるかというと、それは、スピードが損なわれるためだ。 ユーザを活用に回すとその分探求が遅れる。A/Bテスト支持派の意見は、多少の犠牲を払ってでも素早く最適な腕を見つけることが大切だ、というところに集約されるように思う。また、探求が早いということは直ぐに腕の良し悪しの区別がつくので、悪い腕を引き続けて損失が膨らむような愚は犯さない、ということでもある。 だが果たして本当にそうなのだろうか?例えば自動的に探求と活用の割合を調整するようなバンディットアルゴリズムであれば、ここでA/Bテスト支持派が行なっている指摘は意味を成さなくなるだろう。 バンディットアルゴリズムを評価する上でどのような評価軸があるのだろうか。 ## バンディットアルゴリズムを評価するための6つの指標 [Bandit Algorithms for Website Optimization](http://www.amazon.co.jp/gp/product/B00AM86Y0K/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=B00AM86Y0K&linkCode=as2&tag=qiita-taka84u9-22)ではバンディットアルゴリズムを評価する6つの指標として下記のものが列挙されている。 指標 | 意味 ---------------------------------|------------------------------------------------------------------------- Curiosity | 探求から得られた知見を利用するか。一つ一つのテストは独立しているか否か。 Increased Exploitation over Time | 自動的に探求の割合が減っていくか。 Strategic Exploration | 何を最大化するか。利益か、得られる知見か、それともその両方か。 Number of Tunable Parameters | 何個のパラメータを調整しなければならないか。(少ないほど良い) Initialization Strategy | 各腕に対する初期値の割り当て方 Context-Aware | ユーザと腕の相性などを考慮するか。例えば男性、女性を区別できるか、など。 ## Epsilon-Greedyアルゴリズム バンディットアルゴリズムは活用と探求を行う、と言われてもピンとこないかも知れない。そこで最も単純なバンディットアルゴリズムである **Epsilon-Greedyアルゴリズム** を紹介する。 Epsilon-Greedyアルゴリズムの考え方は極めてシンプルで、`0 ≦ ε ≦ 1`を満たす`ε`に対して、全てのユーザの内`1 - ε`は活用、`ε`は探求を行う。そして探求に割り当てられたユーザをテストしたい腕に改めて割り振る。この`ε`の値を調整することで、活用と探求の割合を増減させることができるわけだ。 - `ε = 0`の場合探求は一切行われないのでこれはバンディットアルゴリズムを使っていないのと同じ状況だ。 - `ε`を少しずつ増やしていくと、次第に探求の割合が増えていく。 - `ε = 1`の時、活用は一切行われず全てのユーザが探求に回される。 そんなEpsilon-Greedyアルゴリズムだが、先ほど紹介した6つの評価指標に照らしてみると、いくつもの欠点があることが分かる。 指標 | Epsilon-Greedyアルゴリズムの評価 ---------------------------------|------------------------------------------------------------------------- Curiosity | 常に全ての腕が選ばれる確率が等しい。 Increased Exploitation over Time | `ε`の値は一定なので、探求の割合は減少しない。 Strategic Exploration | 何を最大化するか。利益か、得られる知見か、それともその両方か。 Number of Tunable Parameters | 調整するパラメータは`ε`の1つ Initialization Strategy | 全ての腕が同じ評価 Context-Aware | コンテキストは一切考慮しない # Epsilon-Greedyアルゴリズムを改良する Epsilon-Greedyアルゴリズムより優れたアルゴリズムは勿論存在する。[Bandit Algorithms for Website Optimization](http://www.amazon.co.jp/gp/product/B00AM86Y0K/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=B00AM86Y0K&linkCode=as2&tag=qiita-taka84u9-22)で紹介されているのは下記のものだ。(残念ながら各アルゴリズムの詳細を説明するだけの気力がないので、詳しく知りたい人は本書を実際に読んで欲しい。) ## Softmaxアルゴリズム Epsilon-Greedyアルゴリズムの大きな問題点の1つがCuriosity性が無いことだ。つまり、既にダメだと分かっている腕も等確率で選択されてしまい、損失が膨らむのだ。 Softmaxアルゴリズムは特にこの点においてEpsilon-Greedyアルゴリズムを改良したものになっている。探求が進む内に得られた期待値に応じて腕を重み付けし、それに基いて腕を選択する。また`ε`の値を探求が進むに連れて自動的に増加させる、より発展的なアルゴリズムも紹介されている。これによってSoftmaxアルゴリズムは放置しておくだけで勝手に一番いい腕だけを使うようになるわけだ。 ## UCBアルゴリズム Softmaxアルゴリズムは、各腕を引くことでどれだけ報酬が得られるか、という情報は使っているが、 **各腕についてどれだけ知っているか** は考慮されていない。つまり、本当は悪いのに良いと勘違いされた腕は、そのうち勘違いが是正されるが、本当は良いのに悪いと勘違いされた腕は無視されるのでいつまでも勘違いされたままになる可能性があるのだ。 UCBアルゴリズム(Upper Confidence Bound Algorithm)は名前からも分かるように、得られた評価値の信頼性を自動的に高めようとする。つまり、あまり良くわかっていない腕は積極的に試していくのだ。そのため本の中でも、Softmaxアルゴリズムに評価実験で劣るという結果が出た。だが、重要な点としてUCBはSoftmaxアルゴリズムと違って「Initialization Strategy」が自動化されるという強みがある。 また「Context-Aware」性を持ったUCBとしてLinUCBとGLMUCBという2つの改良版が紹介されている。LinUCBは線形回帰、GLMUCBは一般化線形回帰を用いて腕の価値を再評価する。(これらについては本書の中でも名前が紹介されている程度なので、詳しくは引用されている論文を当たらなければならない。) # 現実的にバンディットアルゴリズムは使えるのか 最後に、このような理論を考えるときに重要なことは、その理論を現実の問題に応用出来るのか、ということだ。 が、この点についても7章で詳しく論じられているので詳しくはそちらを参照して欲しい。 # 結局バンディットアルゴリズムはA/Bテストより優れているのか まず前提として、ここでいうA/Bテストとは「全てのユーザをランダムに振り分けてテストする」ものである、ということをご確認いただきたい。 この場合のA/Bテストは`ε = 1`なEpsilon-Greedyアルゴリズムと等しい。故に「A/Bテストとバンディットアルゴリズムを比較すること」は「ゴールデンリトリバーと犬を比較すること」のようなもので、特に意味が無い。 A/BテストとEpsilon-Greedyアルゴリズムの比較という意味では、一長一短な面があると言えるだろう。Epsilon-Greedyアルゴリズムは非常にナイーブな実装であり、いくつもの問題を抱えているためだ。 その一方でEpsilon-Greedyアルゴリズムの問題点を改善した手法がいくつも提案されている。本稿とりあげたSoftmaxアルゴリズムやUCBアルゴリズムなどがその一例だ。これらとA/Bテストを比較するなら、バンディットアルゴリズムに軍配が上がると言えるのではないだろうか。 だが現実問題としてどれだけ実際のシステムの改善に応用出来るか、という話になると、実装コストなどさまざまな点も考慮しなければならなくなり、それらを勘案すると一概に「A/Bテストよりバンディットアルゴリズムの方が優れている」と言い切れないのではないだろうか。 # 合わせて読みたい - [20 lines of code that will beat A/B testing every time](http://stevehanov.ca/blog/index.php?id=132) バンディットアルゴリズムの方が優れているよ、という趣旨のブログ記事。 - [Why multi-armed bandit algorithm is not “better” than A/B testing](http://visualwebsiteoptimizer.com/split-testing-blog/multi-armed-bandit-algorithm/) 上の記事に対する反論という形でA/Bテストの優位性を主張しているブログ記事。実際にA/Bテストとバンディットアルゴリズムの比較を行なっているが、ここで用いているのはEpsilon-Greedyアルゴリズムであることに注意。 - [Bandit Algorithms for Website Optimization (O'REILLY)](http://www.amazon.co.jp/gp/product/B00AM86Y0K/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=B00AM86Y0K&linkCode=as2&tag=qiita-taka84u9-22) バンディットアルゴリズムについての入門書籍。おそらくここまでしっかりまとまった本は他に無いのではないだろうか。洋書だが英文は簡単で比較的読みやすかった印象。古くからある知見に加えて、最近発表された学術論文の内容も取り扱われており、今後より深く勉強したくなった人のための文献紹介という面でも有用だと感じた。 - [強化学習 (森北出版)](http://www.amazon.co.jp/dp/4627826613) 翻訳和書。第2章の29ページ分がバンディット問題に関する、様々なアルゴリズムの効果と解説。 |
|
| 57位 |
|
|||
|
01:15:16 |
|
|
# Summary
普段使い用 PowerShell メモ # 思惑 個人的には、 PowerShell はスクリプトをゴリゴリ書くよりも、 ちょこちょこ作業するのに向いていると思っている。 ただ、しばらく使っていないと忘れたりするので、そういうものをまとめてみる。 Linux で、grep とか使って、よくやりそうな感じのやつ。 共用パソコンなどの自分の環境以外でも使えることを想定しているので、 できるだけデフォルトの状態で、できるものに限定する。 また、普段使い想定なので、タイプ数を減らすのを優先していたりするので、 PowerShell の文法的には微妙なところも多少ある。 # grep それ `Select-String` でできるよ ```ps1 Select-String -Path .\foo.txt -Pattern "bar" ``` # find -name hogehoge それ(以下略 ```ps1 ls -r | where { $_.Name -match "hogehoge" } ``` # cat ```ps1 Get-Content .\foo.txt ``` 最初から `cat` がエイリアスに設定されてるはずなので、 `cat` でも可 # sort, uniq ```ps1 Get-Content .\test.txt | Sort-Object | Get-Unique # デフォルトのエイリアスを使って、↓みたいにも可能 cat .\test.txt | sort | uniq ``` # wc -l ```ps1 $(cat .\test.txt).Length ``` # diff ```ps1 Compare-Object $(cat .\a.txt) $(cat .\b.txt) ``` ただし、表示形式は微妙。 unified で見たい・・・ # sed ```ps1 Get-Content .\foo.txt | % { $_ -replace "posh","PowerShell" } ``` # tail ```ps1 # PowerShell 2.0 Get-Content .\error.log -Wait # PowerShell 3.0+ Get-Content .\error.log -Wait -Tail 5 ``` # wget ```ps1 (new-object net.webclient).DownloadFile("URL", "保存先の絶対パス") ``` 新しめの PowerShell だと ↓ みたいにも可能 ```ps1 Invoke-WebRequest http://files.kaoriya.net/goto/vim74w64 -OutFile .\vim.zip ``` # curl ```ps1 Invoke-RestMethod -Uri "http://localhost:1234/poshcomplete/" -Method POST -Body "text=test" ``` # unzip ```ps1 # .NET 4.5+ # 末尾のセミコロンが無いと表示されないので付けています.付けていてもコードとしては動きます(どうみてもバッドノウハウw [System.IO.Compression.Zipfile]::ExtractToDirectory("圧縮ファイルの絶対パス", "解凍先の絶対パス"); ``` # xargs ```ps1 ls .\ | % { cat $_.FullName } ``` # man ```ps1 man Foo-Bar ``` # その他便利そうなものを少し ## なんとなくループ回したいときとか ```ps1 "PS", "posh", "PowerShell" | % { echo $_ } 0..255 | % { echo $_ } cat .\filepathlist.txt | % { cat $_ } # テキストファイルの中身を 1 行ずつループで回す ``` ## 応答なし ```ps1 kill -name iexplore ``` ## 最終更新日時とかで絞り込みたいとき ```ps1 # 30日以内に更新した1KB以上のファイル一覧 ls | where { ($(Get-Date) - $_.LastWriteTime).Days -lt 30 } | where { $_.Length -gt 1KB } ``` where で絞り込めるので、ファイルサイズとかで絞り込んだりする。 ## タイマー ```ps1 $n = 3 timeout $($n * 60); shutdown -h ``` n 分後にハイバネーション ## PowerShell as a "電卓" ```ps1 1 + 2 + [Math]::Pow(2, 10) ``` 普通に演算子を使って、単純な計算をしたり、 `System.Math` を使って、いろいろしたり ## ディレクトリの中の合計サイズ調べるとき ``` ps1 gci .\ | where { $_.PSIsContainer } | % { Write-Host -NoNewline ($_.Name + "`t"); (gci $_.Name -Recurse | measure -Property Length -Sum).Sum; } ``` ## .NET の確認 unzip とか、電卓の Math とかみたいに、.NET も呼べるので、ちょっとメソッド確認したいときとかに使える。 読み込まれていないものを使いたいときは、 `Add-Type -AssemblyName System.HogeHoge` みたいにして読み込んで使う。 ## 履歴からスクリプトを作る元を出力する インタラクティブに実行していって、それを出力して編集してスクリプト作る ```ps1 Get-History | % { Out-File -InputObject $_.CommandLine -Encoding default -Append ~/foobar.ps1 } ``` ## jq jq みたいなことできるコマンドも、比較的新しいバージョンならできる。 オブジェクトで返ってくるので便利。 csv とかも、オブジェクトで返ってくるコマンドレットがあるので便利。 説明が面倒なので、下記のリンク先を見るとか、ググるとかしてください。 [PowerShell で JSON をファイル入出力 する - tech.guitarrapc.cóm](http://tech.guitarrapc.com/entry/2013/08/10/220803) ## eject ```ps1 (New-Object -com WMPlayer.OCX.7).cdromCollection.Item(0).Eject() ``` (☝ ՞ਊ ՞)☝ウイーン # おわりに ここまで書いて気がついたけど、オライリーのクックブックに、似たようなこと書いてた(分量が全然違うけど 他にも、見なおしてみたら、役に立ちそうなこと書いてあったから、再読するべき。 この手のシェルを使って仕事するのと、マウスでクリッククリックしまくって手作業で仕事を処理するのとでは、 効率が全然違うので、面倒な仕事は、さっさと終わらせて、本質的な作業に時間を割けるようにしたいですね。 # see also * [CD-ROMトレイを取り出せるPowerShell関数、「Dismount-CDDrive」作った - PowerShell Scripting Weblog](http://winscript.jp/powershell/258) * [PowerShellでtail -fを再現する - tech.guitarrapc.com](http://tech.guitarrapc.com/entry/2013/04/15/220408) |
|
| 58位 |
|
|||
|
02:10:03 |
|
|
##スマフォwebページのスライドメニュー
アプリでは当たり前のように実装されているスライドメニューですが、webページではなかなか使い心地のよいスライドメニューが実装されているのは見かけません。 スマートフォンのブラウザではjavascriptでのアニメーションはどうしてもガタガタになってしまうし、ちらつきやスクロール制御のめんどくささからもうwebページでネイティブアプリ並のスライドメニューを実装するなんて無理と思っていました。 Facebookのwebページですらボタンの反応は悪いしアニメーションも動かないし最悪です。 ##一方Google先生はパーフェクトなスライドメニューを実装していた  **さすがGoogle先生! 俺達に出来ないことを(** Googleのスライドメニューは以下の点でパーフェクトです。 1. スライドのアニメーションがとても滑らか・ちらつかない 2. ボタンの反応にストレスを感じない 3. メインコンテンツ部分のどこをさわってもメニューが閉じる 4. 横スクロール対策がしっかりされている 5. メニューだけスクロールできる **今回はこれらがどのように実装されているのか調査しながら、jQueryのプラグインとしてスライドメニューを実装しました。** **具体的なソースはあまり載せていないので、この記事の最後に貼ってあるサンプルページとgithubを見てみてください。** ##アニメーションについて GoogleといえばFlash使わずjavascriptでなんでもやっちゃうイメージが強かったので、当然**スライドのアニメーションもjavascriptを使っている**と思ってソースを眺めていると、CSSにこんな記述がありました。 ``` -webkit-transform: translateX(0); -webkit-transition: .2s -webkit-transform ease-in-out; ``` CSS3のアニメーション...だと...! [参考 CSS3 - transform](http://www.htmq.com/css3/transform.shtml "") **スマートフォンではjavascriptではクラスの切り替えのみを行い、アニメーションはCSSに任せるのが定石**のようですねー。 また、Googleは`translateX`を使っていますが、よくある実装としては`translate3d`を使い**GPUアクセラレーター**を有効にすることでにぬるぬる描画を実現していることが多いです。 あとソースを読んでいて気づいた細かいテクニックとしては ####実際に動かすのはコンテンツ部分 メニューはz-indexに負の値をもって奥に隠れているだけです。 横スクロール対策にもなっている ####目立たない要素はアニメーション時はhiddenにする アニメーションを軽くするための努力 ####はじっこのちらつきは背景色でカバーする 実装してみてわかってのですが、メニュークローズ時にbodyの色がちらつきます。 Googleではメニューとちらつく部分の背景色を同じにすることで対策しているようです。 ##イベントについて Facebookのスライドメニューの反応が悪いように感じるのは、おそらく`onclick`でアニメーションを発火しているからです。 スマートフォンでは`onclick`の発生が遅いので、スマフォ特有の`ontouchstart`や`ontouchend`を使うことでストレスなく操作できます。 **メニューオープンはメニューボタンのontouchend、クローズはコンテンツ部分のontouchstart**にするのがよさそうです。 ##メニューのスクロールについて 今回実装する上で一番苦労したがこれでした。 実際にさわってみるとわかりますがGoogleのスライドメニューはネイティブアプリと同じようにメニュー部分だけがスクロールでき、項目が多い場合でも対応できます。 なぜ苦労したか、**結論から言うとスクロールの処理を自前で書いていたからです。** まず考えたのはメニュー部分を`overflow: scroll`などでスクロール可能にし、`event.preventDefault()`でページ全体にイベント伝播しないようにする方法でしたが、結局うまくいかず。 困って再度Googleのソースを見てみるとメニューを囲っているdivに怪しい記述が... ``` style="-webkit-transform:translate3d(0px, 0px, 0px)" ``` さきほども登場した`translate3d`ですね! Googleのjsは当然 圧縮難読化されているので実際に確認してはいないのですが、どうやら**メニュー部分の`ontouchmove`を拾って、styleにy軸方向の値を設定した`translate3d`を連続的に書き込むこと**でスライドを実装しているようです。 実際この実装方法でうまくいきました。 ## 今回作成したプログラムのソース & サンプルページ 今回作ったものを載せておきます [サンプルページ](http://encircle.jp/sp/ "") ※iPhone5でのみ動作確認してあります ※PCでは動きません(ontouch系が使えないので) githubにもおいてあります [github - jSlideMenu](https://github.com/ukitaka/jSlideMenu "") ## まとめ #### ・javascriptでなくCSS3のアニメーションを使おう #### ・ちらつくならtranslate3dを使おう #### ・ontouch系を使おう ## 最後に #### CSS3すげぇ 角丸くできるだけかと思ってました。ごめんなさい。 #### Googleすげぇ とても参考になりました。 |
|
| 59位 |
|
|||
|
21:37:25 |
(レジュプレス株式会社 所属) |
|
### 寝ている時間以外はVimとRailsを触っている僕が選ぶVim Tips10選を紹介します。
Vimを使い始めて3ヶ月ぐらいの人にオススメです。 ※ あんまりRailsは関係ありません。 # diw (ノーマルモード) #### カーソル上の単語を消す こいつがなくてはVimを使う意味が無いというほどよく使うコマンド。 ノーマルモードで ` hoge ` という文字列上の、どこかにカーソルがある状態で`diw`とタイプすると`hoge`という単語が消えます。 この何を単語とするのか、その判定が非常に秀逸で、直感にマッチしていて便利です。 たとえば、`|`をカーソル位置だとして、 `ho|ge bar` -> `bar` `ho|ge.bar` -> `.bar` `"ho|ge"` -> `""` `<di|v>` -> `<>` このように、うまい感じに特殊記号等を避けて、単語を削除してくれます。 `ciw`とタイプすることで、`hoge`を削除後、挿入モードになることも可能。 # di" (ノーマルモード) #### ""(ダブルコーテーション)で囲まれた文字列を消す `diw` と似たようなコマンドで、`""`で囲まれた文字列を消してくれます。 このコマンドの便利なところは、カーソル位置が`""`内に無くても良いところ。 例えば、カーソル位置を`|`だとして、 ` hoge = "f|oo bar"`という状態で、`di"`とタイプすると、当然`hoge = "|"`になります。 しかし、なんと、 `ho|ge = "foo bar"` という状態で、`di"`とタイプしても`hoge = "|"`となります つまり現在位置が`""`内に含まれて無くても、その行内で今あるカーソル位置よりも後ろに`""`で囲まれた文字列があれば、そこまで移動して消去してくれるのです。 # * (ノーマルモード) #### カーソル位置にある単語を検索する `*` のお陰で自分で検索する文字列を打つことが半分以下に減りました。 `*`は今あるカーソル位置の単語を検索してくれます。 `diw` と同じように、単語の判定が秀逸。 `hoge fo|o bar` -> `foo`を検索 `fo|o.bar` -> `foo`を検索 ControllerやModelで、変数がどこで使われているのかを素早く検索するときに便利です。 # C-a, C-x (ノーマルモード) #### 数値をインクリメント、デクリメントする ノーマルモードで `3` にカーソルがあったとして、`C-a`(Ctrl+a) とタイプすると `4`になります。 また、`C-x`(Ctrl+x)とタイプすると、逆に`2`になります。 これだけ見ると全く使いどころの無いコマンドに見えますが、ある2つのテクニックを知っているだけで、 CSSを書くのに非常に便利なコマンドになります。 ##### 数値と組み合わせる ただ`C-a`とタイプするのではなく、`3C-a`, `10C-a`のようにコマンドの前に数値を入力します。 Vimでは基本的に、先に数値を入力しておくことで、その数値回数だけそのあとのコマンドを繰り返します。 つまり、`3C-a` は `C-a` を3回繰り返す、 `10C-a` は `C-a` を10回繰り返すという意味です。 これによって、例えばCSSで ` margin: 8px `という記述があり、`15px`に直したいというときも `7C-a` で修正できます。 ##### 数値にカーソルを置かないで使う この `C-a`, `C-x` について、実はなんと数値上にカーソルがなくても使えます。 先に紹介した `di"` のように、その行において、カーソルのある位置から後ろに数値があれば、それをインクリメントしてくれます。 したがって、 `ma|rgin: 8px` こんな状態だとしても、`5C-a` とタイプすることで `margin: |13px` と、インクリメントすることができ、更にカーソル位置も数値の場所に移動します。 CSSを書くときはこの`C-a`(Ctrl+a)と`C-x`(Ctrl+x)がないとやってられません。 # Unite #### ファイラ( https://github.com/Shougo/unite.vim ) 唯一Railsに関係するTipsな気がします。Uniteの説明は「vim unite」でググれば色々と出てくることでしょう。 ※ :help unite でも簡単なexampleや説明があります。 今回はuniteはファイラとして利用する場合について説明します。 とりあえずキーバインドとして僕は以下のように `.vimrc` に書いています。 ```vim NeoBundle 'Shougo/unite.vim' let g:unite_enable_start_insert = 1 let g:unite_enable_split_vertically = 0 let g:unite_winwidth = 40 nnoremap <silent> ,uf :<C-u>UniteWithBufferDir -buffer-name=files file file/new<CR> nnoremap <silent> ,um :<C-u>Unite file_mru <CR> nnoremap <silent> ,urc :<C-u>Unite file_rec/async:app/controllers/ <CR> nnoremap <silent> ,urfc :<C-u>Unite file file/new -input=app/controllers/ <CR> nnoremap <silent> ,urm :<C-u>Unite file_rec/async:app/models/ <CR> nnoremap <silent> ,urfm :<C-u>Unite file file/new -input=app/models/ <CR> nnoremap <silent> ,urv :<C-u>Unite file_rec/async:app/views/ <CR> nnoremap <silent> ,urfv :<C-u>Unite file file/new -input=app/views/ <CR> nnoremap <silent> ,urs :<C-u>Unite file_rec/async:app/assets/stylesheets/ <CR> nnoremap <silent> ,urfs :<C-u>Unite file file/new -input=app/assets/stylesheets/ <CR> nnoremap <silent> ,urj :<C-u>Unite file_rec/async:app/assets/javascripts/ <CR> nnoremap <silent> ,urfj :<C-u>Unite file file/new -input=app/assets/javascripts/ <CR> nnoremap <silent> ,uro :<C-u>Unite file_rec/async:config/ <CR> nnoremap <silent> ,urfo :<C-u>Unite file file/new -input=config/ <CR> nnoremap <silent> ,url :<C-u>Unite file_rec/async:lib/ <CR> nnoremap <silent> ,urfl :<C-u>Unite file file/new -input=lib/ <CR> nnoremap <silent> ,urr :<C-u>Unite file_rec/async:spec/ <CR> nnoremap <silent> ,urfr :<C-u>Unite file file/new -input=spec/ <CR> ``` 実際に `,urc` 等をノーマルモードでタイプしてみると、どんな動きをするのか理解できると思います。 以前は `rails.vim` をファイラとしては利用していたのですが、ファイル数の増加に伴い、 `:RView hoge.rb` のような指定方法は、補完を使ったとしても難しくなってしまい、Uniteに移行しました。 この設定によって、rails.vim以上のファイル移動速度を発揮しています。 また、これは実行しているVimのカレントディレクトリを、Railsプロジェクト直下にしておく必要があります。 # g; (ノーマルモード) #### 前に変更した場所へカーソルを移動する Vimは他のエディタとは違い、いつどこで変更を行ったのかを記録しています。 それをさかのぼって、前に変更したところにカーソルを持って行ってくれるのが、この `g;` です。 このコマンドの存在によって、例えば、大変な行数があるファイルを編集しているときに、そのファイルの先頭を参照し、またさきほど変更していたところから編集し直す、といったことが非常に容易になります。 あんまり知られてないけど、一度使い始めたら、無いと困っちゃうコマンドです。 # C-h (いつでも) #### backspace(delete)キー Vimはまるで関係ありませんが、 基本的に`C-h` はBackSpaceキー(OSXならばdelete)に相当します。 backspaceはキーボードの右上にあり、これを使っていると右小指が痛くなってしまうので、手にやさしい `C-h` を使うことにしています。 # C-m (いつでも) #### Enterキー こいつもVimに全く関係ありませんが、基本的に `C-m`はEnterキーに相当します。 私は普段、日本語キーボードを利用しておりEnterキーは非常に遠く、また右小指が痛くなってしまうので、これからは一生 `C-m` を使うことに決めました。 # dap (ノーマルモード) #### パラグラフを消去 ```rb end def hoge foo hoge bar bar end def ... ``` この状態で `def hoge ... end` の中にカーソルがあるとき、 `dap` とタイプすることで ```rb end def ... ``` `def hoge ... end` のパラグラフが消去されます。 どんな要素を持ってパラグラフなのかは、詳しくは調べてはいませんが、「改行のない連続した行」な気がします。 つまり、 ```rb hoge hoge foo foo foo foo bar bar bar bar bar bar ``` このような状態ならば、 `hoge` のパラグラフ、 `foo` のパラグラフ、 `bar` のパラグラフの3つのパラグラフが存在します。 例に上げたとおり、メソッドをまるまる削除するのに便利です。 # C-^ (ノーマルモード) #### 前に開いたファイルを開く そのウィンドウで以前開いていたファイルを開きます。 `A.rb`を開いた後、`B.rb` を開いたところで `C-^` とタイプすると `A.rb` が開きます。 このコマンドの良い点は、 `C-^` によって `A.rb` が開かれた状態で、また `C-^` とタイプすると、`B.rb` に戻ってくる点です。 往々にして、他のエディタはこのようなコマンドは、どんどん前のファイルを開いていってしまい、結局元のファイルを開くことが難しくなってしまうことが多いのですが、このコマンドならば、ちょっと参照するために `A.rb` を開き、またすぐ `B.rb` に戻ってくる、みたいなことが簡単にできるのです。流石。 # タブ機能 #### Vimにもタブ機能があったんです。 僕はVimを使い出して、1年ぐらいしてから知ったのですが、VimにもChromeのようなタブ機能があったんです。 [vimのすごい便利なのにあまり使われていない「タブページ」機能](http://qiita.com/wadako111/items/755e753677dd72d8036d) 以前僕が書いた記事を参照してみると良いでしょう。  以上です。知っていた事項もあるかもしれませんが、少なくとも僕が上記の事項に気づくのには、Vimを使い出してから1年ぐらいかかりました。 皆様のVimライフがより良いものになれば幸いです。 P.S. [STORYS.JP](http://storys.jp)、 [coincheck](https://coincheck.jp) などのサービスを運営、開発しています。興味のある方はぜひ [和田](http://qiita.com/wadako111)まで連絡を! |
|
| 60位 |
|
|||
|
15:02:53 |
|
|
#課題 最近良く名前を聞くようになったソケット通信が速度が早くて良い的な話をしてたものの、ソケットってそんなに最近出てきたようなことだっけか?と曖昧になったので調べなきゃ寝れない!ということで調べてみた #プロトコルの種類 通信といえばTCP/UDPだろうと再度確認 |プロトコル|概要|備考| |----|-------------------|----------------| |TCP/IP|信頼性が高い|データグラム(電文)が届いたかどうかを誤りチェックしていて、届いていない場合は再送要求をする。<br>到着順序が保証されていて、到着したデータは並べ替えられる。<br>それらが失敗した場合はエラーになるようになっている| |UDP/IP|基本的な通信|データグラム(電文)は一方的に送信され、相手に届いたかどうかのチェック等はしない。<br>エラーの検知はしない。変な受信データもただ破棄されるのみのため、その分単純で高速。| ただ、ソケットの話はここでは出てこない #ソケットはどこの話か? ソケットのタイプ  OSI参照モデルではセッション層部分。TCP/UDPより上のレイヤーだった。 #あれ?ソケットって普通の通信じゃね? ソケットにも種類があるのか調べてみた。 |ソケット|概要|特定方法|通信箇所| |---|---|---|---| |INETドメイン|ネットワーク上でマシンを越えてのプロセス間通信|IPアドレス+ポート番号|他マシンと通信可| |UNIXドメイン|同じマシン上で動いているプロセスが通信を行うためのソケット。<br>アドレス・名前空間としてファイルシステムを使用している。<br>ファイルシステム内のinodeとしてプロセスから参照される。|ファイル名で一致|自マシンのみ| なるほど、最近ソケット通信、ソケット通信と言ってるのはUNIXドメインソケットの事か! #UNIXドメインソケットって何がいいの? [Performance Analysis of Various Mechanisms for Inter-process Communication](http://osnet.cs.binghamton.edu/publications/TR-20070820.pdf)に素晴らしい検証があった。 > It was hypothesized that pipes would have the highest throughtput due to its limited functionality, since it is half-duplex, but this was not true. For almost all of the data sizes transferred, Unix domain sockets performed better than both TCP sockets and pipes, as can be seen in Figure 1 below. > On some machines, Unix domain sockets reached transfer rates as high as 1500 MB/s.  UNIXドメインソケットは、TCPソケット(INETドメインソケット)よりも遥かにスループットが優れてるらしい。Solaris(UltraSPARC processor)ではアーキテクチャの違いからUNIXドメインソケットのほうが遅いらしい。 よし、これで寝れる [Redis コア開発者 @pnoordhuis のツイート](https://twitter.com/pnoordhuis/status/195614999283109888) > TIL that "an abstract socket address is distinguished by the fact that sun_path[0] is a null byte ('\0')". Anybody knows why this exists? えーと、抽象名前空間ソケット??まだ、UNIXドメインソケットにも種類があるのか? #UNIXドメインソケットの種類 [unix - sockets for local interprocess communication](http://man7.org/linux/man-pages/man7/unix.7.html) |種類|概要| |---|---| |ファイルシステムパス名(pathname)|きっと一番知られている一般的な手法。[bind(2)](http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/bind.2.html)を使用して、NULL終端(\0)されたファイルシステム上のパスに結びつけることができる。ファイルを使用しているため、パーミッションを調整する必要がある。サーバプロセスが終了するときには、[unlink(2)](http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/unlink.2.html)で名前を削除し、場合によってはそれが参照しているファイルも削除するのがお作法。 |無名(unnamed)|[bind(2)](http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/bind.2.html)を使ってパス名に結び付けることができないストリーム型のソケットは名前を持たない。[socketpair(2)](http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/socketpair.2.html)を使うと、名前のついていない CONNECTED なソケットのペアが生成される。fork してファイルディスクリプターを繋ぎ直してあげれば、pipe で実現するのと同じような双方向のプロセス間通信ができる。| |抽象名前空間(abstract)|sun_pathにファイルシステムのパスではなく、NULLバイト(\0)であることで区別される。NULLバイト以降に名前(文字列)を渡すが、ファイルパス名とは関係ない。ファイルシステムと紐付いていないので、 ファイルシステムのパーミッションなどに関係なくソケット通信できてしまう。Linuxでのみ利用可能。[unlink(2)](http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/unlink.2.html)で終了する必要がない。| #どのUNIXドメインかを見極めるには `$netstat -al --protocol=unix` で確認する ###ファイルシステムパス名 I-Node path部分にファイルパス名が表示されている ``` Active UNIX domain sockets (only servers) Proto RefCnt Flags Type State I-Node Path unix 2 [ ACC ] STREAM LISTENING 154823 /var/lib/mysql/mysql.sock ``` ###無名 I-Nodeは割り振られるが、pathは表示されない ``` Active UNIX domain sockets (only servers) Proto RefCnt Flags Type State I-Node Path unix 3 [ ] STREAM CONNECTED 175322 ``` ###抽象名前空間 I-Node path部分に@がついた名前が表示される ``` Active UNIX domain sockets (only servers) Proto RefCnt Flags Type State I-Node Path unix 2 [ ACC ] STREAM LISTENING 6653 @/com/ubuntu/upstart ``` #UNIXドメインソケットをサポートしているサービス |ミドルウェア|概要|種類|使用方法|連携ミドルウェア| |---|---|---|---|---| |Nginx|web server|pathname|[server divective](http://wiki.nginx.org/HttpUpstreamModule)|unicorn, gunicorn, php-fpm, etc... | |Unicorn|ap server|pathname|[Public Instance Methods](http://unicorn.bogomips.org/Unicorn/Configurator.html) |Mysql|db server|pathname|||| |Redis|KVS|pathname|[How fast is Redis?](http://redis.io/topics/benchmarks)||| |Memcached|KVS|pathname|[Using memcached](http://docs.oracle.com/cd/E17952_01/refman-5.6-en/ha-memcached-using.html) |upstart|deamon|abstract||| |sytemd|deamon|abstract||| |OS?|kernelプロセス?|unnamed||| #まとめ ソケット通信は今度からUNIXドメインソケット通信と言うこと(どのマニュアルにもちゃんと書いてあったけど笑)。TCPで接続することばっかりやってきたので、気にもしてなかったがKVSとかの接続はUNIXドメインソケット通信にしたらもう少し効率よく使えるのではないかと。まだまだ、調べたらいろいろありそうなので継続調査。 もう寝ます笑 |
|
| 61位 |
|
|||
|
16:46:38 |
(フリープログラマ 所属) |
|
==============================================
追記(14.02.07) -------------- [Pandoc User’s Guide](http://johnmacfarlane.net/pandoc/README.html)を日本語に全訳しました。 よろしければご参照ください。 * [Pandoc ユーザーズガイド 日本語版](http://sky-y.github.io/site-pandoc-jp/users-guide/) はじめに -------- 今回は、ドキュメント作成Tips Advent Calendar 2012の1日目 ([マインドマップから全てを紡ぎ出す - XMind+Pandocのドキュメント作成術 -](http://qiita.com/items/b92e9ce4b941545c8af5)) で少し紹介した**Pandoc**というツールについて、もう少し深く掘り下げて紹介したいと思います。 ### MarkdownとかreStructuredTextとか、流行ってますよね いわゆる軽量マークアップ言語が最近流行しています。特にMarkdownは猫も杓子もMarkdownな状態ですね。 一方で、Word文書、LaTeX、PDFなどの形式が提出文書として指定される場合も多々あります。 MarkdownやreSTで書いた文書を色々なフォーマットに変換できたら嬉しいですよね。 ### 電子書籍(EPUB)も流行ってますよね Kindleの上陸により、電子書籍を作成する需要が高まっています。 一方で、HTMLで書いた文書がしこたま溜まっていたり、 (特に学術系の方は)LaTeXで作成した本や論文がいっぱいあったりして、 それらを電子書籍化したい、という方も多いと思います。 HTMLやLaTeXがEPUBに変換できたら嬉しいですよね。 ### それらを実現するのがPandocです Pandocは**Markdown, HTML, LaTeX, reStructuredTextなどを 様々な形式(Markdown, reST, HTML, LaTeX, Word, PDFなどなど)に変換出来る強力なツールです。** Pandocが日本であまり知られていないのは、非常に残念に思います。 ここでは、ドキュメント作成の強い味方となるPandocについて、入門編として紹介します。 Pandocとは ---------- Pandocは、Haskell製のドキュメント変換ツールです。 この手のツールとしては、reStructuredTextを用いる[Sphinx](http://sphinx-users.jp/)が有名で、 他にも特定のマークアップ言語用のツールが多数存在します。 しかしながら、Pandocはこのようなツールとは大きく違う点があります。 それは、**対応フォーマットが恐ろしく豊富な点**と、**開発が活発な点です**。 ### 長所 #### とにかく対応フォーマットが豊富 まずは、対応フォーマットを見て下さい。 ##### 入力 入力フォーマットは、以下に対応しています: * Markdown * Textile * reStructuredText * HTML * LaTeX * DocBook XML * OPML (追記 14.05.09) * Emacs Org-mode (追記 14.05.09) * Literate Haskell (Markdown,reST,LaTeX) ##### 出力 出力フォーマットは以下の通りです(一部省略しています): * Plain Text * Markdown * reStructuredText * HTML * XHTML * HTML 5 * HTMLスライドショー (Slidy, Slideous, DZSlides or S5) * DocBook XML * LaTeX * プレゼン用スタイルファイル beamer も出力可 * RTF * ODT * Word docx * MediaWiki markup * EPUB * Emacs Org-Mode * OPML (追記 14.05.09) * Adobe InDesign ICML (追記 14.05.09) * PDF (LaTeXをインストールしている場合のみ) 詳細はこちらをご覧ください。 * [Pandoc User's Guide](http://johnmacfarlane.net/pandoc/README.html) #### 開発が活発 Pandocは2012年12月現在、開発が比較的活発です。 その証拠に、Pandocの開発用グループである [pandoc-discuss - Google グループ](https://groups.google.com/forum/?fromgroups#!forum/pandoc-discuss) で、2012年12月1日〜12月22日に更新されたトピック(スレッド)は33件ありました。 ##### 開発が比較的容易 (以下、推測ですが)開発が活発な要因としては、開発が比較的容易であることが考えられます。 ###### 機能ごとにモジュール化されている Pandocは書き出しモジュール (Writer)、読み込みモジュール (Reader)などのように、 うまくモジュール化されています。 そのため、部分ごとの拡張が比較的容易になっています。 ###### HaskellのパーサコンビネータParsecをうまく利用している また、PandocはHaskellで書かれています。 Haskellには、構文解析を行うためのパーサコンビネータParsecがあり、Pandocはこれを有効活用しています。 Parsecは正規表現よりも強力(LL(n)文法が解析可)なため、HTMLなどタグを囲むような文法を容易に解析できます。 しかもParsecは単なるHaskellのライブラリなので、Haskellの文法知識だけで理解できます。 そのため、特別な文法を覚える必要があるyaccなどのパーサジェネレータに比べて、 Parsecの方が分かりやすく簡潔に書けます。 以上の理由から、Pandocは開発が容易であることが考えられます。 ##### 最近、Org-mode用読み込みモジュールの開発がスタート (以下、Emacs Org-modeユーザ向けになりますが) ~~これまで書き出ししか対応していなかったOrg-mode文書に対して、 読み込みモジュール(Reader)の開発が始まっているそうです。~~ * [Org-Mode Reader · Issue \#476 · jgm/pandoc](https://github.com/jgm/pandoc/issues/476) ~~これが完成すれば、さらにPandocの世界が広がること間違い無しです。 楽しみにしましょう。~~ Emacs Org-modeの読み込みモジュールが完成しました!(14.05.09現在) ### 短所 一方で、Pandocにも短所があります。 #### 独自の読み込み・書き込みエンジンにより処理するため、文書の仕様をカバーできていない場合がある Pandocは独自の読み込み・書き込みエンジンにより処理を行っています。 そのため、文書の仕様に沿っていない部分もあります。 また、内部的にはHTMLで表現できる形式で処理されているため、その表現能力を超えるようなことはできません。 この欠点は、特にLaTeX(使った事無いですが、おそらくreSTも)に影響します。 Pandocのインストール -------------------- ダウンロードは以下のページからできます: [Pandoc - Installing](http://johnmacfarlane.net/pandoc/installing.html) ### プログラマの方 パッケージマネージャに慣れている方や、現在Haskellを扱っている・今後Haskellを触ってみたい方には、 Haskell Platform (cabal) によるインストールを強くおすすめします。 こちらの方が、アップデートが手軽にできます。 #### 手順 ##### 1. Haskell Platformをインストール * [The Haskell Platform](http://www.haskell.org/platform/) ##### 2. 端末・コマンドプロンプトで以下を実行 ```Bash $ cabal update $ cabal install pandoc ``` ### 一般の方 * Windows, MacにはPandoc単体のインストーラがあります。 * Linuxはパッケージマネージャ(apt, yumなど)に入っている場合があります。 基本的な使い方 -------------- ここでは例としてMarkdownを入力形式とし、HTMLを出力してみます。 もちろんHTML, LaTeX, reStructuredTextなどを入力としてもOKです。 ### MarkdownからHTMLに変換 #### 手順 ##### 1. Mac, Linuxの方はターミナル(端末)を、Windowsの方はコマンドプロンプトを開きます ##### 2. 公式サイトの[README](http://johnmacfarlane.net/pandoc/demo/README)(Markdown形式)を適当な場所にダウンロードします ```bash # Mac, Linuxの方はwgetやcurl -OでもOK $ wget http://johnmacfarlane.net/pandoc/demo/README ``` ##### 3. READMEのある場所に移動(cd)し、READMEをinput.mdに名前変更します ```bash # Mac, Linux $ cd ~/hoge $ mv README input.md ``` ##### 4. 以下のコマンドを入力: ```bash $ pandoc input.md -s -o output.html ``` * input.md: 入力するMarkdown文書の名前 * -o output.html : 出力するHTMLファイル名 * -s: standalone(完結したフォーマットで出力) * HTML文書、LaTeX文書を出力する場合は、基本的に -s (standalone) オプションを付けましょう * .bashrc/.zshrc のaliasに指定しておいてもいいかも: * `alias pandoc="pandoc -s"` #### そのままブラウザでプレビューするとき ##### Mac ```bash $ pandoc input.md -s -o output.html && open output.html ``` ##### Linux ```bash $ pandoc input.md -s -o output.html && gnome-open output.html $ pandoc input.md -s -o output.html && firefox output.html ``` * Linux使いならニュアンスは分かると思うので、適当にいじってください #### 形式を明示的に指定することもできます ```bash $ pandoc -f markdown -t html input.md -s -o output.html ``` * -f 入力形式 * -t 出力形式 形式の明示は、ファイルを指定せずにパイプで処理する際に必要です。 * 出力ファイル(-o)を指定しない場合は、-tオプションで指定した形式で標準出力に出してくれます(一部形式を除く) おすすめ使い方ベスト5 --------------------- ここからは、おすすめの使い方を紹介します。 ### ベスト5まとめ 1. MarkdownをEmacs Org-mode文書、reStructuredTextに変換する 2. Markdownをdocx/odt文書に変換する 3. MarkdownをLaTeX文書に変換する 4. Markdownをプレゼン文書に変換する 5. LaTeX文書をEPUBに変換する ### 1. MarkdownをEmacs Org-mode文書、reStructuredTextに変換する 出力ファイル(-o)の拡張子を変えるだけです。 #### Org-mode文書 ```bash pandoc input.md -o output.org ``` * ファイル形式を指定する場合は `-t org` #### reStructuredText ```bash pandoc input.md -o output.rst ``` * ファイル形式を指定する場合は `-t rst` 簡単ですね。 ### 2. Markdownをdocx/odt文書に変換する #### Word文書(docx) ```bash pandoc input.md -o output.docx ``` * ファイル形式を指定する場合は `-t docx` #### LibreOffice Writer文書(odt) ```bash pandoc input.md -o output.odt ``` * ファイル形式を指定する場合は `-t odt` ### 3. MarkdownをLaTeX文書に変換する ```bash pandoc input.md -s -o output.tex ``` #### 注意 Macの場合、Emacsで開くと濁点が分離します。 (MacのUnicodeが、NFDという形式を標準としているためです。NFC形式に変換する必要があります。) ##### Emacs上で直す方法 こちらを参照して下さい: - [Emacs小ネタ:MacでPDFからコピーすると濁点が分離する問題を直す](http://d.hatena.ne.jp/sky-y/20120805/1344169124) ##### 端末上で直す iconvコマンド(後述)を使うと、以下で直ります: ```bash iconv -f UTF-8-MAC -t UTF-8 output.tex ``` - 標準出力に出るので、パイプで適当に処理して下さい ### 4. Markdownをプレゼン文書に変換する #### 準備 公式サイトのSLIDESをダウンロードします。 * [SLIDES](http://johnmacfarlane.net/pandoc/demo/SLIDES) * または `wget http://johnmacfarlane.net/pandoc/demo/SLIDES` #### LaTeX Beamer ```bash pandoc -t beamer SLIDES -o output_beamer.pdf ``` #### HTMLスライド (Slidy) ```bash pandoc -t slidy SLIDES -s -o output_slidy.html ``` * -sオプションの付け忘れに注意! ### 5. LaTeX文書をEPUBに変換する Pandocを使うと(少しの手間はありますが)LaTeX文書をいとも簡単にEPUB形式の電子書籍を作れます。 #### 準備 公式サイトのexample4.texをダウンロード * [example4.tex](http://johnmacfarlane.net/pandoc/demo/example4.tex) * または `wget http://johnmacfarlane.net/pandoc/demo/example4.tex` #### コマンド ```bash pandoc example4.tex -o output.epub ``` #### EPUBのプレビュー ##### Mac Murasakiがおすすめです: [Murasaki](http://genjiapp.com/mac/murasaki/index.html) ##### Linux, Windows Calibreが良いそうです: [Calibre](http://calibre-ebook.com/) #### 注意 * LaTeXで `\input` を使っている場合は、 参照先ファイルをすべて元のTeXファイルに埋め込んでください。 * Pandocが `\input` に対応していないためです。 * 画像ファイルは全てpng形式にしてください。 * EPS画像しか無い場合、ImageMagickのmogrifyコマンドで変換するのが一番楽ちんです。 日本語文書をPDFで出力する方法 ----------------------------- Pandocには、LaTeXを使ってPDFを出力する機能があります。 これを使えると便利ですが、日本語文書(特にMacの場合)ちょっとややこしいです。 というのも、Pandoc標準の設定では、日本語の通らないpdfTeXを使用するためです。 そこで、日本語に対応し、Pandocがサポートしている**LuaLaTeX**を使用します。 ### 準備 #### LuaLaTeX 単体でインストールするのは難しいので、TeX Live 2012というパッケージをインストールします。 (これ以外のインストール方法は知らないです・・・。) * [TeX Live - TeX Wiki](http://oku.edu.mie-u.ac.jp/~okumura/texwiki/?TeX%20Live) TeX環境が古くなっている方は、これを機にTeX環境を一新するとよいと思います。 (ただし、卒論や原稿提出前の方は、やめておいたほうがよいと思われます・・・(汗)) #### iconv (Mac) Macの場合は、iconvという文字コードを変換するツールをインストールしておきます。 HomebrewかMacportsでインストールできると思います。 ### コマンド #### Mac以外のOSの場合 ```bash pandoc input.md -o output.pdf -V documentclass=ltjarticle --latex-engine=lualatex ``` #### Macの場合 ```bash iconv -f UTF-8-MAC -t UTF-8 input.md | pandoc -f markdown -o output.pdf -V documentclass=ltjarticle --latex-engine=lualatex ``` 長いので、よく使う場合はaliasかシェルスクリプトにしておくことをおすすめします * 以下を.bashrc/.zshrcに追加 ```bash alias pandocpdf="pandoc -V documentclass=ltjarticle --latex-engine=lualatex -t pdf" ``` その他のデモ ------------ こちらにいっぱいあります: * [Pandoc - Demos](http://johnmacfarlane.net/pandoc/demos.html) まとめ ------ PandocはMarkdown, HTML, LaTeX, reStructuredTextなどを様々な形式に変換出来る強力なツールです。 パイプ処理などをうまく使ったりすると、さらに便利に使えるかもしれません。 また、開発も進んでいるため、今後新たな可能性が広がるかもしれません。 しかし、まだ日本であまり知られておらず、非常にもったいなく思います。 まずはPandocを試してみて下さい。 そして感銘を受けた方は、ぜひ友人にどんどん布教してください! |
|
| 62位 |
|
|||
|
18:30:52 |
(フリーランス 所属) |
|
※お願い:最近時間がなかなか取れず、Rails5.xの時代になったというのに未だに5.xでの確認ができておりません。どなたか、5.xでも本記事の内容がうまくいった、と確認されました方はコメント欄にてご一報をいただけますと大変嬉しいです。
(本記事は今の所Rails 3.x〜4.2 対応です) ([Homebrew編](http://qiita.com/emadurandal/items/e43c4896be1df60caef0)も公開しました) ## はじめに:Railsをローカルインストールするという発想 今さらですが、Mac環境でrbenvを使って、Ruby・Rails環境を構築するための記事をまとめてみました。 bundlerでgemをRailsプロジェクト内にローカルインストールすることで、ruby環境を汚さずにRailsプロジェクトを生成できることを目的とします。 Rails自体もローカルインストールすることで、その都度好きなバージョンのRailsプロジェクトを生成し、共存させることができます(←ここ重要。古い書籍のRailsコードを試す時とか便利です)。 突っ込みどころがありましたら、遠慮なくコメントくださいませ。 (MacPorts使っているのは私の好みの問題なので、Homebrew使ってない奴は負け組とかそういうコメントはご勘弁ください^^; Homebrewの人でも参考になる内容だとは思います。) →[Homebrew編](http://qiita.com/emadurandal/items/e43c4896be1df60caef0)公開しました! あと、Markdown初心者なので、記事のレイアウトが見にくいかもしれません。徐々に改善していたいと思います。 →tadsan様が、全文のMarkdownを修正して、レイアウトを綺麗にしてくださいました。ありがとうございます! ## 前提環境 * OSはMacを前提とする。(OSX Mountain Lion/Marvericks/Yosemiteで動作確認) * Railsのバージョンは3.x〜4.1.xを前提とする。 * Rubyのビルドに必要なパッケージの導入にはMacPortsを使う(Gemに関してはもちろんBundler)。 Macを前提としているが、パッケージ管理ソフト以外の部分に関しては、Linux等でも使えるノウハウである。 Homebrewを使っている人でも、殆どの部分は使えるノウハウである(CONFIGURE_OPTS環境変数に指定するパスだけ気をつけてください)。 ## 本マニュアルが目指すこと * Ruby環境は、rbenvを使って、複数のバージョンを切り替えられるようにして導入する。 * rbenv環境下のRuby環境に追加インストールするGemはbundlerのみ。 * Railsもローカルにインストールすることで、Ruby環境を特定のバージョンのRailsで汚さずに、複数のバージョンのRailsプロジェクトを作成・共存できるようにする。 * ほとんどのGemは、Railsプロジェクト内に閉じた形でインストール・管理する(つまり、Railsプロジェクトのvender/bundleディレクトリ内にGemをインストールする) * 構築したRuby環境は、MacPortsに依存しない。 ## rbenvとは Ruby環境のバージョン切り替えツールである。Ruby(に限らないが)はバージョンの差違によって、ソフトウェアが正常に動いたり動かなかったり、といったことが往々にしてある。 そこで、特定のバージョンのRubyをドノーマルにインストールしてそれを使い続けるのではなく、バージョン切り替えツールでインストール・管理することで、使いたいソフトウェアや開発するプロジェクトに応じて複数のRuby環境を使い分けるのが最近のトレンドになっている。 Ruby環境のバージョン切り替えツールとしては、以前はRVMというものがあった(使っていらっしゃる方、過去形にしてすみません汗)が、以下のような特徴があり、 * ヘビーウェイト・多機能。 * cdを独自のものに置き換えてしまう。 * Bundlerなどと相性が悪い。 ちょっと使うのに抵抗があるという人が多かったのも事実。そうした状況を受けて(?)、あのRuby on Railsで有名な37signalsが作った、より軽量な代替ツールがrbenvである。 以下のような特徴を持つ。 * 動作がRVMより速い。 * 既存コマンドを置き換えるような怖いことをしない。 * 環境変数によって自動的にRubyバージョンを切り替え可能。 * gemの管理はBundlerで行うことが前提。 * rubyをOSのシステム深部でなく、自分のホームディレクトリ以下(`.rbenv`ディレクトリ)にインストールできるので、OSの環境を汚さないという点でも安心感がある。 rbenvとRVMのより詳細な比較については、以下のサイト様のページをご覧ください。 「[rbenv と RVM との違い](http://passingloop.tumblr.com/post/10512902196/difference-between-rbenv-and-rvm)」(passingloop様のサイトより) それぞれ設計思想が違うので、どちらを使うかは好みの問題でもあるが、しかし今後のトレンドとしてはRVMよりrbenvになりそうな感じなので、本記事ではrbenvを使う。 ## rbenv、Rubyのインストール rbenvとそのプラグインruby-buildをインストールする。 ``` $ git clone git://github.com/sstephenson/rbenv.git ~/.rbenv $ mkdir -p ~/.rbenv/plugins $ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build ``` rbenv用の以下の設定を `.bash_profile` に設定する。 ``` export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)" ``` 書き換えた `.bash_profile` の設定を読み込む。 ``` $ source ~/.bash_profile ``` MacPortsをインストールする。そしてportを最新の状態にする。 ``` $ sudo port selfupdate $ sudo port sync ``` > (なぜ、今流行のHomebrewでなく、MacPortsを使うのか。以下の利点があるからである。 > * Homebrewによって、/usr/localディレクトリをいじられるのが嫌な人にとっては、/optに全てをインストールするMacPortsは安心感がある。 > * 環境を再構築したくなったら、/optディレクトリを消すだけで良い。消すのではなく、リネームして、後で戻すことも可能。 > * 複数台のMacで共通の環境を作りたい場合は、/optディレクトリをrsyncするだけで実現できる。 > * 昔のMacPortsはソースからビルドしていたので遅かったが、最近のバージョンではバイナリからインストールしてくれるため、すでに「MacPortsは遅い」という定評は過去の物となった。 OpenSSLをインストールしないとrbenv環境での `bundle install` が失敗するらしいので、MacPortsでOpenSSLをインストールする。(参照:pygo様:http://d.hatena.ne.jp/CortYuming/20120618/p1 ) ``` $ sudo port install openssl ``` readlineとiconvがないと動かないgemがあるらしいので、MacPortsでこの二つをインストール。 (参照:tech.portalshit.net様:http://tech.portalshit.net/2011/11/15/i-am-new-with-rbenv/ ) ``` $ sudo port install readline $ sudo port install libiconv ``` インストール可能なRubyのバージョンを調べる。 ``` $ rbenv install -l ``` MacPort で インストールした openssl、readline、iconv のパスを教えて、rbenv でRubyをインストール。 ``` $ CONFIGURE_OPTS="--with-openssl-dir=/opt/local --with-readline-dir=/opt/local --with-iconv-dir=/opt/local" rbenv install 2.0.0-p195 # ←バージョン番号は読み替えてね $ rbenv rehash ``` 以下のコマンドで、インストールしたRubyのバージョンのリストを確認できる ``` $ rbenv versions ``` インストールしたRubyを有効にする ``` $ rbenv global 2.0.0-p195 ``` ## SSL証明書のインポート ここまでの状態のRubyだと、SSL通信を行う処理をしようとした時に、 ``` /Users/hoge/.rbenv/versions/2.0.0-p195/lib/ruby/2.0.0/net/http.rb:918:in `connect': SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (Twitter::Error::ClientError) ``` というエラーが起きます。そこで、対策として証明書をセットアップします。 まずは、証明書のパスを確認し、 ``` $ ruby -ropenssl -e "p OpenSSL::X509::DEFAULT_CERT_FILE" "/opt/local/etc/openssl/cert.pem" ``` 返ってきたパスに対して、証明書をダウンロードします。 ``` $ sudo curl "http://curl.haxx.se/ca/cacert.pem" -o /opt/local/etc/openssl/cert.pem ``` (参考ページ:[rubyでSSL利用時に証明書エラーが発生する場合の対応](http://k-shogo.github.io/article/2013/08/22/ruby-ssl-connect-error/)) これでOKです。 ## Bundlerのインストール Railsで利用するGemは各プロジェクト毎にbundlerで管理するようにしたいので * GEMでRuby環境にbundlerのみをインストール * bundlerを使って一時的にrailsをローカルにインストール (Railsプロジェクトを作成するためだけに使用。railsすらローカルディレクトリにインストールする) * ローカルのrailsでプロジェクト(今回の例では”example”)を作成 * railsをローカルインストールするために使ったbundlerの環境を削除 * exampleプロジェクト内で必要なgemをbundlerでローカルインストール という手順を踏む。でははじめよう。 GEMでRuby環境にbundlerのみをインストールする。 ``` $ rbenv exec gem install bundler $ rbenv rehash ``` ちなみに現在有効なrubyにインストールされたgemの確認は以下のコマンドで行う。 ``` $ rbenv exec gem list ``` インストールしたgemパッケージの保存場所を調べるには以下のコマンドを使う。 ``` $ rbenv exec gem which bundler ``` ## RailsのローカルインストールとRailsプロジェクトの作成 **(新規Railsプロジェクトを作る時は、常にこのステップから行う)** bundlerを使って一時的にrailsをローカルにインストール。(Railsプロジェクトを作成するためだけに使用。railsすらローカルディレクトリにインストールする) まず、Railsプロジェクトを作りたいディレクトリに移動して、そこで `Gemfile` を作る。 ``` $ cat << EOS > Gemfile source "http://rubygems.org" gem "rails", "4.1.1" # ←ローカルインストールしたいRailsのバージョンを指定。指定しなければ最新版が入る。 EOS ``` railsを `vender/bundle` ディレクトリ以下にインストールする。 ``` $ bundle install --path vendor/bundle ``` (このように、 `bundle install` する際に `--path vendor/bundle` をつけることで、gemのインストール先がRuby環境でなく、 ローカルの`vendor/bundle` ディレクトリ以下になる) インストールしたgemを確認したい場合は、以下を実行。 ``` $ bundle list ``` railsでプロジェクト(今回の例では”example”)を作成。 ``` $ bundle exec rails new example --skip-bundle ``` ( `vendor/bundle` に入ったgemを使ってコマンドを実行したい場合は上記のように `bundle exec 〜` のようにする) ( `--skip-bundle` の指定を忘れないように!そうでないと `bundle install` が発動し、Ruby環境にgemがインストールされてしまう!) railsをローカルインストールするために使ったbundlerの環境と、ローカルのrailsを削除する。 ``` $ rm -f Gemfile $ rm -f Gemfile.lock $ rm -rf .bundle $ rm -rf vendor/bundle ``` ## Railsプロジェクトの環境セットアップ 必要に応じて、example内のGemfileの内容を編集する(Rspecとか、Guardとか、色々入れるよね)。 次に、example内に移動し、Gemfileに書かれたGemのインストールを行う。 この際も `--path vendor/bundle` オプションをつけて `bundle install` することでプロジェクト内に閉じた状態でGemをインストールすることができる。 ``` $ cd example $ bundle install --path vendor/bundle ``` (この `bundle install --path vendor/bundle` で、再びrails(及び関連Gem)が今度はRailsプロジェクトの `vender/bundle` ディレクトリ以下にインストールされる) 以下の2つの理由から、Gitの管理対象から `vendor/bundle` ディレクトリを外す。 * `vendor/bundle`ディレクトリにあるGemsは容量を食うのでGitリポジトリに入れたくない。 * GemfileやGemfile.lockファイルはGitの管理対象なので、cloneした人はそれをもとに`bundle install --path vendor/bundle`すれば、簡単に同じGem環境を導入できる。 ``` $ echo '/vendor/bundle' >> .gitignore ``` ## Railsプロジェクトの起動 以下のようにして、Railsの起動に成功したら、この記事の目的は達成された。おめでとう! ``` $ bundle exec rails server ``` # 以下、Tips ## gemのインストール先の判断(Ruby環境下 or bundlerでのローカルインストール)について 基本的には、そのプロジェクトでのみ使いたいgemであれば、bundlerで`bundle install --path vendor/bundle`して、プロジェクトに閉じた形でgemをローカルインストールし、`bundle exec ~ `で実行する形を取るのがよい。 ただし、`bundler`や`pry`など、 * どのプロジェクトでも共通して使いたい。 * 複数のバージョンを使い分ける必要性が基本的になく、常に最新バージョンであればよい。 という性質のgemであれば、bundlerでローカルインストールするのでなく、`rbenv exec gem install ~ `としてRuby環境に直接インストールしてしまった方が利便性が高い。 gemの特性と自分の利用シーンに合わせて、gemのインストール場所は使い分けよう。 ## 間違えてRuby環境の方にいろんなGemが入ってしまった場合の対処 以下で全てのgemをアンインストールする。その後、標準でインストールされていたgemやbundlerを入れ直すこと。 ``` $ rbenv exec gem list | awk '{print "rbenv exec gem uninstall " $1}' | sh -xv ``` あるいは、以下の方法でRuby環境ごと削除して、rubyを導入しなおしても良い。 ## 不要になったバージョンのRuby環境を削除する ```bash $ rbenv uninstall -f 2.0.0-p195 $ rbenv rehash ``` ## rbenvを最新版に更新する rbenvのインストール方法によるが、今回はgit cloneでインストールしたため、普通にgit pullすればよい。 ``` $ cd ~/.rbenv/ $ git pull $ cd ~/.rbenv/plugins/ruby-build/ $ git pull ``` ## rbenvで、1.9.3-p125より古いrubyをビルド・インストールする 1.9.3-p125 より前のバージョンのrubyはllvm-gccではなくGCCでコンパイルされていたようです。よって、Xcode4.2以降がインストールされている場合、GCCがないので(/usr/bin/gccはllvm-gccへのシンボリックリンクとなっている)、ビルドに失敗します。 以下のような感じで失敗します。 ``` $CONFIGURE_OPTS="--with-openssl-dir=/opt/local --with-readline-dir=/opt/local --with-iconv-dir=/opt/local" rbenv install 1.8.7-p370 ERROR: This package must be compiled with GCC, but ruby-build couldn't find a suitable `gcc` executable on your system. Please install GCC and try again. DETAILS: Apple no longer includes the official GCC compiler with Xcode as of version 4.2. Instead, the `gcc` executable is a symlink to `llvm-gcc`, a modified version of GCC which outputs LLVM bytecode. For most programs the `llvm-gcc` compiler works fine. However, versions of Ruby older than 1.9.3-p125 are incompatible with `llvm-gcc`. To build older versions of Ruby you must have the official GCC compiler installed on your system. TO FIX THE PROBLEM: Install the official GCC compiler using these packages: https://github.com/kennethreitz/osx-gcc-installer/downloads You will need to install the official GCC compiler to build older versions of Ruby even if you have installed Apple's Command Line Tools for Xcode package. The Command Line Tools for Xcode package only includes `llvm-gcc`. BUILD FAILED ``` この対策としては、MacPortsでapple-gcc42をインストールすることです(Homebrewでも同じものがあるようです)。 ``` $ sudo port install apple-gcc42 ``` すると、 `/opt/local/bin/gcc-apple-4.2` ができるので、同階層にgcc-apple-4.2へのシンボリックリンクをgccという名前で作ります。 さらに、ビルドにautoconfが必要なので、それもインストールします。 ``` $ sudo port install autoconf ``` そうして、rubyをもう一度ビルドしてみましょう。今回、ビルドを通すため、さらにいくつかのオプションを追加しています。 ``` $ CONFIGURE_OPTS="--with-arch=x86_64 --with-openssl-dir=/opt/local --with-readline-dir=/opt/local --with-iconv-dir=/opt/local --without-tk" CFLAGS="-arch x86_64" LDFLAGS="-arch x86_64" rbenv install 1.8.7-p370 ``` # 謝辞 以下のサイト様の情報をかなり使わせていただきました。ていうかかなりの部分をコp(ry * [吾輩のメモである](http://d.hatena.ne.jp/clmind/20111109/1320814435)(clmind様) 以下のサイト様からも情報をいただきました。 * [pygo](http://d.hatena.ne.jp/CortYuming/20120618/p1)(CortYuming様) * [tech.portalshit.net](http://tech.portalshit.net/2011/11/15/i-am-new-with-rbenv/)様 # こちらの記事もどうぞ と思ったんですが、以下の記事はもう古いので参考にしないでください。そのうち更新します。すみません。 ~~Ruby、Rails環境のセットアップをマスターしたら、今度は「ぼくがかんがえたさいきょうのRailsてすとかんきょう」を手に入れましょう。以下の記事をどうぞ。~~ ~~* [だれがなんといおうとぼくこそさいきょうのRails4テストかんきょう(ry](http://qiita.com/emadurandal/items/1062364905f89910297d)~~ |
|
| 63位 |
|
|||
|
09:15:51 |
|
|
プログラミングの原作本はオープンソースになって公開されていることがあります。
例えば、 ・[C++ GUI Programming with Qt 4 (First Edition)](http://www.qtrac.eu/marksummerfield.html) ・[C++ GUI Programming with Qt 3](http://www.qtrac.eu/marksummerfield.html) ・[The C++ Hackers Guide](http://www.oualline.com/books.free/free-books.html) ・[Teachers' Guide to Practical C++ Programming](http://www.oualline.com/books.free/teach/intro.html) ・[PHP Programming](http://en.wikibooks.org/wiki/PHP_Programming) ・[Programming Scala](http://www.artima.com/pins1ed/) ・[Real World Haskell――実戦で学ぶ関数型言語プログラミング](http://book.realworldhaskell.org/read/) ・[Ruby Best Practices](http://majesticseacreature.com/rbp-book/pdfs/rbp_1-0.pdf) ・[7 Free JavaScript E-Books and Tutorials](http://www.readwriteweb.com/hack/2010/12/6-free-javascript-e-books.php) ・[プログラマが知るべき97のこと](http://programmer.97things.oreilly.com/wiki/index.php/Contributions_Appearing_in_the_Book) ・[ソフトウェアアーキテクトの知るべき97のこと](http://97things.oreilly.com/wiki/index.php/97_Things_Every_Software_Architect_Should_Know_-_The_Book) Amazonで気になる本を調べて、”原作者 言語 free ebook”と検索すれば出てきます。 調べれば調べるほどたくさんあるんですねw 他に良書があれば教えて下さい。 【追記】 著作権的に怪しいのは削除しました。原作者のホームページに行けばだいたい書いています。 |
|
| 64位 |
|
|||
|
19:57:39 |
(Akatsuki Inc. 所属) |
|
##概要
仮想環境(VirtualBoxなど)上への仮想マシンの立ち上げ、操作が可能。 ##用語 ###プロバイダ 仮想環境 eg.VirtualBoxやVM Ware、EC2など ###プロビジョニング ミドルウェアの設定やインストールを行うツール eg.シェルスクリプト、Chef(chef-solo, chef-client)、Puppetなど ###Boxファイル 仮想マシン起動の際にベースとなるイメージファイルのこと。 仮想環境ごとに必要。 通常はOSイメージから作成する。 Vagrant利用の上で最低限必要な設定(Vagrantユーザの作成、sshdの起動、プロビジョニングツールのインストール)のみを行っておくのが普通。 ###Vagrantfile 構築する仮装マシンのスペックやプロビジョニングツールの指定など、仮想マシンの構成を記述する。 Rubyベース。 基本的に、本ファイルとプロビジョニングツールの設定ファイルの2つがあれば、どこでも同じ環境を再現できる。 ##コマンド ###基本 ``` # バージョン確認 $ vagrant -v # ヘルプ $ vagrant -h ``` ###仮想環境のベースとなるBoxファイルを準備 ``` $ vagrant box add NAME URL ``` ```sample # sample $ vagrant box add centos64 http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130427.box (Vagrantfileのconfig.vm.boxディレクトリで指定する。) ``` BoxファイルはVeeweeで作成することも出来る。 http://www.ryuzee.com/contents/blog/6614 ###Box一覧の確認 ``` $ vagrant box list ``` ###Vagrantfileの作成 ``` $ vagrant init BOX_NAME ``` ``` # sample $ vagrant init centos64 ``` ###Vagrantfileの設定 ```sample.rb $ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box="centos64" //ネットワークの設定 config.vm.network :private_network, ip:"192.168.33.10" //GUIモードの設定 config.vm.provider :virtualbox do |vb| vb.gui = true end end ``` ※ホストオンリーネットワーク →ホストOSとゲストOS間でのみ通信できるネットワーク。 上記では、ゲスト側に192.168.33.10(任意)を割り当てている。 以下は古い書き方。 ``` 誤)config.vm.network :hostonly, "192.168.33.10" ``` ``` 正)config.vm.network :private_network, ip:"192.168.33.10" ``` ###仮想マシンの起動 ``` $ vagrant up ``` ※Vagrantfileと同じディレクトリで実行する GUIモードにしているとVirtualBoxが起動するので、id/passともに"vagrant"でログイン ###sshでログイン ``` $ vagrant ssh ``` ※仮想マシンにてsshdが起動している必要あり。 ###通常の方法でssh出来るようにする。 Chef Soloをknife-soloで動かすことを見越して、通常のsshアクセス、つまり ``` $ ssh 192.168.33.10 ``` でsshできるようにする。 ``` # ~/.ssh/config Host 192.168.33.* IdentityFile ~/.vagrant.d/insecure_private_key User vagrant ``` もしくは ``` $ vagrant ssh-config --host melody ``` で仮想サーバへのssh設定を吐き出してくれるため、 ``` $ vagrant ssh-config --host melody >> ~/.ssh/config ``` とすることで ``` $ ssh melody ``` でログインできるようになる。 ###sshログイン時の設定確認 ``` $ vagrant ssh-config ``` ###status確認 ``` $ vagrant status ``` ``` Current machine states: default running (virtualbox) (仮想マシン名) (status) ``` ###仮想マシンの停止 ``` $ vagrant halt ``` ###仮想マシンの削除 ``` $ vagrant destroy ``` ###仮想マシンのエクスポート ``` $ vagrant package ``` →package.boxという名前のBoxファイルが生成される。 それを配布し、受け取った側は ``` $ vagrant box add new_box package.box ``` で利用できる。 ## vagrantをより便利に ### sahara saharaを用いると、簡単に仮想マシンの状態保存・ロールバックが出来ます。 saharaでVagrantの状態管理 http://qiita.com/kidachi_/items/ba365905b2a770c72be1 ### chef関連 サーバ構成管理ツールといえばchef。 Chefの基本 http://qiita.com/kidachi_/items/9d569b8673e70ef93f0e knife-soloによるChefの実行 http://qiita.com/kidachi_/items/b222fb2892e6108c46d5 |
|
| 65位 |
|
|||
|
11:42:26 |
(株式会社ずんシステム 所属) |
|
# 課金ポイントは3つ
そんなに難しいことはないと思いますが **課金ポイントは3つ** あります。 ##ストレージ容量 単純に保存容量に対して課金されます。 低冗長化ストレージを指定すると2割くらい安くできます。 ログだとか家族写真の保存だとかメインだとデータ転送よりここにお金がかかってきます。(容量でかいけど古いやつは殆どアクセスしないようなのはライフサイクル設定でGlacierに移動する手もあります) ##データ転送 課金されるのは送信だけです。受信(S3へのアップロード)は無料です。 また、`インターネットへの送信`と`別のAWSリージョンまたはCloudFrontへの送信`で別料金が設定されてますが、小~中規模のシステムならサーバ群は1リージョンに纏まってることが多いでしょうから、CroudFront利用時くらいにしかその料金は発生しないと思います。しかもCroudFront利用時は殆どのトラフィックはCroudFrontで処理されるから転送量はそっちに計上されて、S3からの送信は殆ど無視出来る感じになるでしょう。 あと __同一リージョン内の(AZ非依存な)AWSサービスとの通信は無料__ です。例えば同一リージョンのEC2とS3間の通信は送信も受信も無料。 なので特殊な用途以外では、 __一番高いインターネットへのデータ送信だけ考えておけばよい__ と思います。 ##リクエスト数 多分これがあるせいでS3の料金が分かりにくいって言われるんでしょうね。 S3ではGET/PUT/POST/LIST/COPYなどのリクエスト数に対しての課金もあります。 - GET は安い($0.0037/10,000req、OPTION/HEADとかも、↓以外は全部この料金) - PUT/POST/LIST/COPY は高め($0.0047/1,000req、GETの12.7倍) - DELETE は無料 例えば1日100万GETされると月に3千万リクエストで課金は$11.1になります。 データ転送コストの一部という感覚です。 # 適当に計算してみた感じ CDN用途で使ったとして、キャッシュとか考えずに適当に計算してみると↓こんな感じ。 ※2014/07/04に東京リージョンで計算した値です(前の価格覚えてないけど消費税UPで変わったかもしれないので再計算)。 ※2014/12/01にAWS初の[データ転送量値下げ](http://aws.amazon.com/jp/blogs/aws/aws-data-transfer-price-reduction/)キタ!後で再計算予定だけどとりあえず<font color="red">下の表の転送料金部分を25%OFFくらいで見ておけ</font>ばOK! ※数字の単位は K=1024, G=1024*1024, T=1024^3, P=1024^4 です。 | GETリクエスト数 | 平均ファイルサイズ | データ転送量 | 転送料金 |リクエスト料金 | 合計料金 | |--------------:|----------------:|-----------:|--------------:|--------------:|------------:| | 3千万(100万/日) | 1KB | 28.6GB | $5 .55 | $11 .10 | $16 .65 | | 3千万(100万/日) | 1.95KB | 55.8GB | **$11 .01** | **$11 .10** | $22 .11 | | 3千万(100万/日) | 50KB | 1.4TB | $287 .33 | $11 .10 | $298 .43 | | 3千万(100万/日) | 100KB | 2.8TB | $574 .86 | $11 .10 | $585 .96 | | 3千万(100万/日) | 1MB | 28.6TB | $5,068 .03 | $11 .10 | $5,080 .13 | | 3億(1千万/日) | 1KB | 286.1GB | $57 .31 | $111 .00 | $168 .31 | | 3億(1千万/日) | 1.95KB | 557.9GB | **$111 .94** | **$111 .10** | $222 .94 | | 3億(1千万/日) | 50KB | 14.0TB | $2700 .33 | $111 .10 | $2,811 .33 | | 3億(1千万/日) | 100KB | 27.9TB | $4,960 .54 | $111 .10 | $5,071 .54 | | 3億(1千万/日) | 1MB | 286.1TB | $40,258 .35 | $111 .10 | $40,369 .35 | | 30億(1億/日) | 1KB | 2.8TB | $574 .86 | $1,111 .00 | $1,684 .86 | | 30億(1億/日) | 1.95KB | 5.4TB | **$1,121 .18** | **$1,111 .00** | $2.231 .18 | | 30億(1億/日) | 50KB | 139.7TB | $21,113 .33 | $1,111 .00 | $22.223 .33 | | 30億(1億/日) | 100KB | 279.4TB | $39,386 .31 | $1,111 .00 | $40,496 .31 | | 30億(1億/日) | 1MB | 3.1PB | お問い合わせ | $1,111 .00 | お問い合わせ | - 平均ファイルサイズが2KB以下だと、`リクエスト数` > `データ転送` > `ストレージ容量` - 平均ファイルサイズが2KB以上だと、`データ転送` > `リクエスト数` > `ストレージ容量` という順番でお金がかるっぽいです。 なので分割されたJS/CSSはconcatして小さい画像はCSSスプライトも活用してリクエスト数を減らしてやりましょう、サイトパフォーマンスが上がるだけじゃなく財布にも優しくなります。 それからやっぱ転送量が一番金がかかるのでクライアントキャッシュ出来るものはどんどんキャッシュさせて圧縮できるものは圧縮しときましょう。 ちなみにS3の前にCloudFrontを挟むとS3のリクエスト数とデータ転送量は大幅に減ります。ですが減った分はCloudFront側で計上されるのでゼロになるわけじゃありません、データ転送料金はCloudFrontの方が多少安く設定されています。特にデータ転送量が巨大になってきた場合はCloudFrontを経由したほうが、ボリュームディスカウントが効いてきたり、またリザーブド契約をすることで大幅な割引を受けたりも出来るので是非検討しましょう。特に年間とおして月平均10TB以上使う場合はCloudFrontの営業に連絡を取るべきです。 #月額計算機(Amazon Web Services Simple Monthly Calculator) とりあえずAWSの課金で悩んだら以下のページから見積もりできるので、実際の数字を入れて見てみるのが理解への近道です。 [Amazon Web Services Simple Monthly Calculator](https://calculator.s3.amazonaws.com/calc5.html?lng=ja_JP) S3の料金なら、左メニューからAmazon S3をクリックして、リージョン選択をしてとりあえずは、ストレージ>ストレージ、データ転送>データ転送、リクエスト>GET、の値を入力しておけば十分でしょう。 あとこの計算機は、デフォで **右上の分かりにくいところにある初年度無料枠のチェックボックス** がONになってるので長期的な見積もりをしたい場合は外しておきましょう。 # 値下げについて - 容量に対する課金は過去に何度も値下げがされています。きっと今後も定期的に値下げがあることが期待できます。 - <strike>データ転送量と</strike>あとコール回数依存の課金(S3の場合はリクエスト数)に関しては、僕の記憶が確かなら全AWSサービスに対しても一度も値下げが無いと思います。特にデータ転送に関わる部分は高額になりがちなので安くなると嬉しいなーと毎回思うけどビクともしません、聖域なんですかね? - 2014年12月1日から、多分AWSで初の[データ転送量の値下げ](http://aws.amazon.com/jp/blogs/aws/aws-data-transfer-price-reduction/)キター!!全体的に大体25%くらいは安くなるぞぉぉぉおーー! #関連リンク [Amazon S3 料金表](http://aws.amazon.com/jp/s3/pricing/) [Amazon Web Services Simple Monthly Calculator](https://calculator.s3.amazonaws.com/calc5.html?lng=ja_JP) |
|
| 66位 |
|
|||
|
08:00:17 |
(None 所属) |
|
 *via [Impress Japan: Web制作者のためのSublime Textの教科書 今すぐ最高のエディタを使いこなすプロのノウハウ](http://www.impressjapan.jp/books/1113101106)* サンプルPDF4章の目次より ※説明は私の調べたものなのでかなり適当です。 ## SideBarEnhancements サイドバー右クリックのメニューを拡張 [titoBouzout/SideBarEnhancements](https://github.com/titoBouzout/SideBarEnhancements) ## LineEndings 改行コードの変更。 [SublimeText/LineEndings](https://github.com/SublimeText/LineEndings) ※現状、SublimeText3で動かないです。143Pのコラムで動かし方について言及がある模様。 ## TrailingSpaces 行末の半角スペースを削除。 [SublimeText/TrailingSpaces](https://github.com/SublimeText/TrailingSpaces) ## Focus Last Tab >Sublime Text 2 では、Chrome などのタブブラウザーと同じように command + 1 〜 9 で切り替えができます。 唯一違うのは、command + 9 は最後のタブではなく、9番目のタブに切り替わること。 つまり9個以上タブを開いていない場合には機能しません。 この挙動を Chrome などと揃えるために Focus Last Tab というパッケージを入れてみました。 *via [Sublime Text 2 のタブ選択をブラウザーと同じようにする // understandard](http://www.understandard.net/tool/tool010.html)* [eproxus/focus_last_tab](https://github.com/eproxus/focus_last_tab) ## Quick File Open >よく使うファイルをすぐに呼び出せるようにする Package *via [よく使うファイルをすぐに呼び出せるようにする Sublime Text Package // understandard](http://www.understandard.net/tool/tool013.html)* [gsingh93/sublime-quick-file-open](https://github.com/gsingh93/sublime-quick-file-open) ## GotoRecent >GotoRecent 最近開いたファイルを簡単に ショートカット「Ctrl+shift+R」 *via [Sublime Text 2を導入をしてみた。参考サイトなど | 零のメモ張](http://shirura.jugem.jp/?eid=144)* [paccator/GotoRecent](https://github.com/paccator/GotoRecent) ## RecentActiveFiles >直近で利用したファイル一覧を表示可能になります。 ⌘ + k + ⌘ + t のキーバインドで表示します。 *via [Rails - オレオレ Sublime Text 環境まとめ - Qiita](http://qiita.com/fakestarbaby/items/bd72acb2eda25b5523d0)* [jugyo/SublimeRecentActiveFiles](https://github.com/jugyo/SublimeRecentActiveFiles) ## Zip Browser Zipアーカイブの中身を表示 ※目次では'Zip Browse'となってるけどたぶんタイポでこれのことだと思う。 [klorenz / SublimeZipBrowser / ソース / — Bitbucket](https://bitbucket.org/klorenz/sublimezipbrowser/src/11b667184e5d?at=default) ## Local History >GitやSVNがなくても使える簡易バージョン管理ができる *via [【追記】【寄せあつめ】今さらだけどSublime Text 2の基本とカスタマイズ【&Vim化】【4日目】 | Developers.IO](http://dev.classmethod.jp/tool/html-corder-sublime-text-2-customize/)* [vishr/local-history](https://github.com/vishr/local-history) ## SublimeOnSaveBuild >ファイル保存時に自動でビルドさせる *via [Y.A.M の 雑記帳: Sublime Text 2 でファイル保存時に自動でビルドさせる](http://y-anz-m.blogspot.jp/2012/11/sublime-text-2.html)* [alexnj/SublimeOnSaveBuild](https://github.com/alexnj/SublimeOnSaveBuild) ## AdvancedNewFile >新規のフォルダやファイルをダイアログから素早く作成する。 *via [Sublime Text: AdvancedNewFile で、フォルダやファイルを素早く作成する (Nettuts+ Fetch も) | deadwood](http://www.d-wood.com/blog/2013/11/08_4994.html)* [skuroda/Sublime-AdvancedNewFile](https://github.com/skuroda/Sublime-AdvancedNewFile) ## Terminal >ターミナルを起動させる *via [SublimeTextからMacのターミナルを起動させるプラグインで作業効率ガチ上げ! | メモ帳代わりのブログ](http://www.absolute-keitarou.net/blog/?p=292)* [wbond/sublime_terminal](https://github.com/wbond/sublime_terminal) ## BoundKeys キーバインド設定一覧を表示 [CodeEffect/BoundKeys](https://github.com/CodeEffect/BoundKeys) ## SuperSelect >Cmd+D(Ctrl+D)を拡張する。任意の単語を順番に、スキップしながら、反転するなど。使い方は公式サイトを参照。 *via [Sublime Text 2 Packages.](https://gist.github.com/konitter/4652460)* [codecarson/SublimeSuperSelect](https://github.com/codecarson/SublimeSuperSelect) ## BufferScroll スクロールやキャレット位置を記憶して復元 [titoBouzout/BufferScroll](https://github.com/titoBouzout/BufferScroll) ## Nettuts+ Fetch >外部リソースを簡単なコマンドで手元にpullする事ができます。 例えば、普段からよく使うjQueryやリセット用のCSS、 常用するフレームワークや、Wordpress等のCMS等、 設定さえしておけば、コマンド一発(正確には三発)でダウンロード・展開してくれます。 *via [Sublime TextとNettuts+ Fetchで音速のスタートアップ – Mach3.laBlog](http://blog.mach3.jp/2012/01/10/sublime-text-and-nettuts-fetch.html)* [weslly/Nettuts-Fetch](https://github.com/weslly/Nettuts-Fetch) ## cdnjs >様々な有名jsライブラリを公開しているCDNサービスから、 使いたいライブラリを選択してscriptタグとして挿入できるパッケージ *via [Sublime Text2の作業効率を加速させるパッケージ12個 | WEB EGG](http://leko.jp/archives/453)* [dafrancis/Sublime-Text--cdnjs](https://github.com/dafrancis/Sublime-Text--cdnjs) ## BracketHighlighter 開始/終了タグの強調 [facelessuser/BracketHighlighter](https://github.com/facelessuser/BracketHighlighter) ## SublimeCodeIntel >ユーザー定義変数/関数の補完や定義元ジャンプができるようになる。 *via [[SublimeText2]SublimeCodeIntelで更にIDE化 | DevAchieve](http://wada811.blogspot.com/2013/02/sublime-text-2-sublime-code-intel.html)* [SublimeCodeIntel/SublimeCodeIntel](https://github.com/SublimeCodeIntel/SublimeCodeIntel) ## SublimeLinter >文法チェックをリアルタイムでしてくれる *via [noanoa 日々の日記 : Sublime Text 3 での SublimeLinter の設定](http://blog.livedoor.jp/noanoa07/archives/1961271.html)* [SublimeLinter/SublimeLinter3](https://github.com/SublimeLinter/SublimeLinter3) ## All Autocomplete >標準の自動補完(オートコンプリート)機能を強化し、マッチする文字列があればサジェストする。 *via [不肖者の人生実験](http://fusyomono1.blogspot.jp/2013/03/sublime-text2-sublime-text2-sublime.html)* [alienhard/SublimeAllAutocomplete](https://github.com/alienhard/SublimeAllAutocomplete) ## Alignment >選択範囲の整形。 *via [[SublimeText2]「第0回 Sublime Text 2 勉強会」で紹介されたプラグインまとめ - The Powerful Code](http://powerful-code.com/blog/2012/11/plugins-for-st2/)* [wbond/sublime_alignment](https://github.com/wbond/sublime_alignment) ## Tag >閉じタグ </ 打つと補完してくれるので地味に便利! *via [SublimeText2/3 環境設定とか(基本3) - Qiita](http://qiita.com/tatsuoSakurai/items/cd9e22a9dc4e10ea6deb)* [SublimeText/Tag](https://github.com/SublimeText/Tag) ## CSScomb >CSSのプロパティをソートしてくれる便利ツールです。またソートするプロパティの順番も自分でカスタマイズ可能!ものぐさな自分にとって痒いところに手が届くマストなツールです。 *via [【便利ツール】CSScomb for sublime text 2で、CSSプロパティを整理・整頓【16日目】 | Developers.IO](http://dev.classmethod.jp/tool/csscomb/)* [csscomb/csscomb-for-sublime](https://github.com/csscomb/CSScomb-for-Sublime) ## Smart Delete キャレット位置前後のスペースを一発で削除する [mac2000/sublime-smart-delete](https://github.com/mac2000/sublime-smart-delete) ## Goto-CSS-Declaration >htmlを編集中に、ショートカットキー一発でcssの該当idやclass、要素にジャンプできる *via [じっくり解説!Sublime Text 2 プラグイン 「Goto-CSS-Declaration」 - フロントエンドとかPCとかスマホとか](http://d.hatena.ne.jp/keyword/css)* [rmaksim/Sublime-Text-2-Goto-CSS-Declaration](https://github.com/rmaksim/Sublime-Text-2-Goto-CSS-Declaration) ## View in Browser HTMLをブラウザでプレビュー [adampresley/sublime-view-in-browser](https://github.com/adampresley/sublime-view-in-browser) ## Browser Reflesh >ファイルを保存すると同時にブラウザのプレビューを更新してくれる *via [SublimeTextで保存した瞬間にブラウザプレビューを更新してくれるパッケージ「Browser Refresh」の使い方 | iPhone・Macの情報発信ブログ "NUMBER333"](http://number333.org/2013/11/07/sublime-text-browser-refresh/)* [gcollazo/BrowserRefresh-Sublime](https://github.com/gcollazo/BrowserRefresh-Sublime) ## LiveReload >ファイル保存のたびにブラウザを自動リロードしてくれる *via [Sublime Text 2 に LiveReload を導入する【Windows】 | weblog](http://weblog.masakiyoshida.com/tools/sublimetext2-livereload/)* [dz0ny/LiveReload-sublimetext2](https://github.com/dz0ny/LiveReload-sublimetext2) ## Live Style ブラウザプレビューのリアルタイム更新。 [emmetio/livestyle-sublime](https://github.com/emmetio/livestyle-sublime) ## Sass Sassのシンタックスハイライトとコード補完 [nathos/sass-textmate-bundle](https://github.com/nathos/sass-textmate-bundle) ## SCSS SCSSのシンタックスハイライトとコード補完 [MarioRicalde/SCSS.tmbundle](https://github.com/MarioRicalde/SCSS.tmbundle) ## SASS Build >SulimeText内でSassをコンパイルすることができます。 *via [SublimeText(Windows、Mac)をSassで使いやすい設定にする | Web制作者のためのSassの教科書 - 公式サポートサイト](http://book.scss.jp/about/c7/editor/st.html)* [jaumefontal/SASS-Build-SublimeText2](https://github.com/jaumefontal/SASS-Build-SublimeText2) ## SASS Snippets >関数などのスニペットが入っています。 *via [SublimeText(Windows、Mac)をSassで使いやすい設定にする | Web制作者のためのSassの教科書 - 公式サポートサイト](http://book.scss.jp/about/c7/editor/st.html)* [npostulart/SCSS-Snippets](https://github.com/npostulart/SCSS-Snippets) ## Compass >Compassのスニペットと、Compassをコンパイルすることができます。 *via [SublimeText(Windows、Mac)をSassで使いやすい設定にする | Web制作者のためのSassの教科書 - 公式サポートサイト](http://book.scss.jp/about/c7/editor/st.html)* [whatwedo/Sublime-Text-2-Compass-Build-System](https://github.com/WhatWeDo/Sublime-Text-2-Compass-Build-System) ## LESS LESS のシンタックスハイライト。 [danro/LESS-sublime](https://github.com/danro/LESS-sublime) ## LESS-build LESSのコンパイル。 [berfarah/LESS-build-sublime](https://github.com/berfarah/LESS-build-sublime) ## Stylus Stylusのコンパイルとシンタックスハイライト。 [billymoon/Stylus](https://github.com/billymoon/Stylus) ## Haml Hamlのシンタックスハイライト。 [phuibonhoa/handcrafted-haml-textmate-bundle](https://github.com/phuibonhoa/handcrafted-haml-textmate-bundle) ## Jade Jadeののシンタックスハイライト。 [github.com](https://github.com/davidrios/jade-tmbundle) ## Jade Build Jadeのコンパイル。 [mutian.github.io](http://mutian.github.io/Jade-Build/) ## Ruby Slim Slimのシンタックスハイライト。 [slim-template/ruby-slim.tmbundle](https://github.com/slim-template/ruby-slim.tmbundle) ## Handlebars Handlebarのシンタックスハイライト。 [daaain/Handlebars](https://github.com/daaain/Handlebars) ## Twitter Bootstrap Snippets Bootstrapのスニペット。 [devtellect/sublime-twitter-bootstrap-snippets](https://github.com/devtellect/sublime-twitter-bootstrap-snippets) ## Bootstrap 3 Snippets Bootstrap3のスニペット。 [JasonMortonNZ/bs3-sublime-plugin](https://github.com/JasonMortonNZ/bs3-sublime-plugin) ## Bootstrap 3 Jade Snippets Bootstrap-Jadeのスニペット。 [rs459/bootstrap3-jade-sublime-plugin](https://github.com/rs459/bootstrap3-jade-sublime-plugin) ## Foundation 5 Snippets Foundation5のスニペット。 [zurb/foundation-5-sublime-snippets](https://github.com/zurb/foundation-5-sublime-snippets) ## Wordpress Wordpressのコード補完。 [purplefish32/sublime-text-2-wordpress](https://github.com/purplefish32/sublime-text-2-wordpress) ## Search Wordpress Codex WordPress Codexを検索できるようにする。 [welovewordpress/SublimeWordPressCodex](https://github.com/welovewordpress/SublimeWordPressCodex) ## Search Stack Overflow Stack Overflowを検索できるようにする。 [ericmartel/Sublime-Text-2-Stackoverflow-Plugin](https://github.com/ericmartel/Sublime-Text-2-Stackoverflow-Plugin) ## Can I Use 選択した要素の各ブラウザ対応状況を調べる [Sublime-text-caniuse by Azd325](http://azd325.github.io/sublime-text-caniuse/) ## Emmet Emmetを使えるようにする。 [sergeche/emmet-sublime](https://github.com/sergeche/emmet-sublime) ## Hayaku Hayakuを使えるようにする。 [hayaku/hayaku](https://github.com/hayaku/hayaku) ## Inc-Dec-Value >テキスト中に含まれる数字と#fffffなどを増減できます。 option + 上下矢印キーで動かせるようになります。忘れがちですが地味に便利です。 *via [SublimeText2を3ヶ月使ってみて利用を決めたPackageまとめ](http://news.ko-zu.com/package/)* [rmaksim/Sublime-Text-2-Inc-Dec-Value](https://github.com/rmaksim/Sublime-Text-2-Inc-Dec-Value) ## AutoFileName >htmlやcssを書いているときに、ファイルを指定する事よくあります。 そんな時にめちゃ便利なプラグインです。 例えば、htmlのimgタグを入力中に、別の階層にある画像ファイルを指定する場合、ディレクトリを構造を移動しながら、ファイルを選択して指定することができ、さらに、画像の width heightも自動的にセットしてくれます。 *via [じっくり解説!Sublime Text 2 プラグイン 「AutoFileName」 - フロントエンドとかPCとかスマホとか](http://charlie.hateblo.jp/entry/2013/04/19/112120)* [BoundInCode/AutoFileName](https://github.com/BoundInCode/AutoFileName) ## Colorpicker カラーピッカー。 [weslly/ColorPicker](https://github.com/weslly/ColorPicker) ## image2Base64 >画像ファイルをSublime Textにドロップした時に 自動的にbase64エンコードした文字列に変換してくれるようになります。 *via [Sublime Text2の作業効率を加速させるパッケージ12個 | WEB EGG](http://leko.jp/archives/453)* [tm-minty/sublime-text-2-image2base64](https://github.com/tm-minty/sublime-text-2-image2base64) ## JavaScript Console Javascriptのスニペット。 [caiogondim/js-console-sublime-snippets](https://github.com/caiogondim/js-console-sublime-snippets) ## JavaScript Patterns JavaScriptパターンのスニペット。 [caiogondim/js-patterns-sublime-snippets](https://github.com/caiogondim/js-patterns-sublime-snippets) ## jQuery jQueryのスニペット。 [SublimeText/jQuery](https://github.com/SublimeText/jQuery) ## AndyJS2 JavaScript と jQuery の入力補完。 [agibsonsw/AndyJS2](https://github.com/agibsonsw/AndyJS2) ## Underscorejs snippets Underscore.jsのスニペット。 [AntouanK/sublime-underscorejs-snippets](https://github.com/AntouanK/sublime-underscorejs-snippets) ## Backbone.js Backbone.jsのスニペット。 [tomasztunik/Sublime-Text-2-Backbone.js-package](https://github.com/tomasztunik/Sublime-Text-2-Backbone.js-package) ## AngularJS AngularJSのスニペット。 [angular-ui/AngularJS-sublime-package](https://github.com/angular-ui/AngularJS-sublime-package) ## Jasmine Jasmineを利用できるようにする。 [gja/sublime-text-2-jasmine](https://github.com/gja/sublime-text-2-jasmine) ## Mocha Snippets Mochaのスニペット。 [jfromaniello/sublime-mocha-snippets](https://github.com/jfromaniello/sublime-mocha-snippets) ## Better CoffeeScript >coffee scriptはjsに変換する必要があります。 保存と同時にビルドしてくれるSublime Textプラグインをご紹介。 *via [Better coffeeScriptでキマリ | はらぺこ屋](http://harapeko.wktk.so/blog/blog-web/js-blog-web/2013-07-12/1246)* [aponxi/sublime-better-coffeescript](https://github.com/aponxi/sublime-better-coffeescript) ## Better TypeScript TypeScriptのシンタックスハイライト。 [lavrton/sublime-better-typescript](https://github.com/lavrton/sublime-better-typescript) ## Dart Dartのシンタックスハイライト。 [dart-lang/dart-sublime-bundle](https://github.com/dart-lang/dart-sublime-bundle) ## JsMinifier jsファイルのミニファイ。 ※とてもまぎらわしい似たような名前のがあります。 [cgutierrez/JsMinifier](https://github.com/cgutierrez/JsMinifier) ## Python Auto-Complete Pythonのコード補完。 [eliquious/Python-Auto-Complete](https://github.com/eliquious/Python-Auto-Complete) ## Python Flake8 Lint Python用のLintツール? [dreadatour/Flake8Lint](https://github.com/dreadatour/Flake8Lint) ## DocBlockr コメントブロックの自動補完。 [これ](http://koizuss.hatenablog.com/entry/2013/01/14/112350)とか[これ](http://qiita.com/cocottejs/items/98ba0fd5093871d9d121)への対策書いてあるかな。。 [spadgos/sublime-jsdocs](https://github.com/spadgos/sublime-jsdocs) ## Markdown Preview Markdownをブラウザでプレビュー。 [revolunet/sublimetext-markdown-preview](https://github.com/revolunet/sublimetext-markdown-preview) ## Dotfiles Syntax Highlighting .gitignoreや.bash_profile等のシンタックスハイライト。 [mattbanks/dotfiles-syntax-highlighting-st2](https://github.com/mattbanks/dotfiles-syntax-highlighting-st2) ## ApacheConf.tmLanguage >Apache Confファイルの文法ハイライト。 *via [おすすめプラグイン - Sublime Text2 Wiki*](http://wikiwiki.jp/sublimetext2/?%A4%AA%A4%B9%A4%B9%A4%E1%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3)* [colinta/ApacheConf.tmLanguage](https://github.com/colinta/ApacheConf.tmLanguage) ## sublime-github Githubを利用できるようにする。 [bgreenlee/sublime-github](https://github.com/bgreenlee/sublime-github) ## sublimeGit Gitを利用できるようにする。 [SublimeGit](https://sublimegit.net/) ## SublimeServer >Sublime Serverは、Sublime Text 2で動作する簡易Webサーバープラグインです。 *via [すぐに使える簡易サーバーでHTMLをチェックする3つの方法 – Mach3.laBlog](http://blog.mach3.jp/2012/09/21/check-html-with-simple-server.html)* [learning/SublimeServer](https://github.com/learning/SublimeServer) ## Sublime SFTP SFTPを利用できるようにする。 [Sublime SFTP – an FTP, FTPS & SFTP Package for Sublime Text by wbond](http://wbond.net/sublime_packages/sftp) # 感想 半分くらいは知らなかった。 使い方わからない/日本語のドキュメントが一切無いのも多いし、 結局本書を買わざるをえないという罠でした。 ### あとプラグイン探しはパッケージブラウザが便利です [Package Control - the Sublime Text package manager](https://sublime.wbond.net/) |
|
| 67位 |
|
|||
|
10:57:00 |
(フリーランス 所属) |
|
複数人数で開発を行っている場合、新しい人が参加した時にこのアプリだけは入れておいてね!!ってことはありますよね? 大体の場合はwikiとかにさらっと ``` #事前にインストールするもの hoge ``` みたいなこと書かれてると思いますが これがいっぱいあると # ダルいしメンテナンスかったるい。。。 のでosxで一般的になってる[homebrew](http://brew.sh/)の拡張[homebrew-cask](https://github.com/phinze/homebrew-cask)を使用して コマンド一発で開発に必要なアプリインストールできるよ!!ってできることがわかったのでメモ [homebrewのパッケージをGemfileみたく管理したい -> brewdlerがあった…](http://qiita.com/futoase/items/9a52ddd8bb19863372d0)で存在を知ったので感謝感謝 ※brewdlerは今[Homebrew/homebrew-bundle](https://github.com/Homebrew/homebrew-bundle)になっています ## 環境 * macosx10.8(ホストOS) ## 参考にしたサイト * [homebrewのパッケージをGemfileみたく管理したい -> brewdlerがあった…](http://qiita.com/futoase/items/9a52ddd8bb19863372d0) * [homebrew-cask](https://github.com/phinze/homebrew-cask) ## 事前準備に必要なもの * [homebrew](http://brew.sh/) ※入れていない方は何も考えずに```/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"```と打とう ## インストール手順 本家のままですぐ使えます(ターミナル開いて打ってね) ``` brew install caskroom/cask/brew-cask ``` ### 使いたいアプリを探す 下記のコマンドで探せます ``` brew cask search caffeine wireshark google-chrome mysql-workbench virtualbox …. …. ``` ### homebrew-caskでインストールしたアプリ一覧を見る パッケージ名を引数で指定するとパッケージの詳細な情報が出ます ``` brew list ansible chrome-cli libyaml openssl wget autoconf curl nkf pkg-config brew-cask git nmap pyenv ``` ### Caskでインストールする コマンド一発です ``` brew cask install google-chrome ``` 簡単ですね!!! ### brew-caskのインストールディレクトリについて brew-caskでインストールしたアプリはデフォルトだと ``` ~/Applications ``` にはいります。 これだとLaunchpadでインストールしたアプリがでてきません。 そんなときは ``` .zshrc ``` もしくは ``` .bashrc ``` に下記を記載するとインストール先ディレクトリを変更できます。 ```.zshrc export HOMEBREW_CASK_OPTS="--appdir=/Applications" ``` ### brew-caskのアプリの管理方法について [homebrew/homebrew-bundle](https://github.com/Homebrew/homebrew-bundle)を使ってGemfileみたいに管理することができます。 以下homebrew-bundleのインストール方法 (公式ほぼ引用です) ``` # brewにbundleコマンドが使えるようにする brew tap Homebrew/bundle # homebrewのGemfileみたいなファイルを作る touch Brewfile ``` 以下Brewfileサンプル ```Brewfile cask_args appdir: '/Applications' tap 'caskroom/cask' tap 'telemachus/brew', 'https://telemachus@bitbucket.org/telemachus/brew.git' brew 'emacs', args: ['with-cocoa', 'with-gnutls'] brew 'redis', restart_service: true brew 'mongodb' brew 'sphinx' brew 'imagemagick' brew 'mysql' cask 'google-chrome' cask 'java' unless system '/usr/libexec/java_home --failfast' cask 'firefox', args: { appdir: '/Applications' } ``` ### 【補足】使いたいアプリがない場合 公式の[ココ](https://github.com/caskroom/homebrew-cask/blob/master/CONTRIBUTING.md#adding-a-cask)にcaskの追加方法が記載されています。 |
|
| 68位 |
|
|||
|
14:00:14 |
(Increments 所属) |
|
# はじめに
このガイドでは、はじめて Zsh を使う人や Zsh の便利な使い方を知らない人に向けて、いくつかの便利な設定と操作方法を紹介します。また、 Zsh についての疑問を素早く解決できるよう、マニュアルの調べ方や他のドキュメントへのリンクも盛り込んであります。 このガイドでカバーしきれていない設定や分かりやすいドキュメントをご存知でしたら、ぜひ編集リクエストやコメントでお知らせください。 # 設定ファイル ここでは主に普段のキー入力数を大幅に減らせるような設定を紹介します。 ## .zshrc `~/.zshrc` は Zsh のインタラクティブシェル(ユーザーがコマンドを入力する画面)が起動した際に読み込まれる設定ファイルです。 Zsh スクリプトを実行したり、 `zsh -c 'command...'` でコマンドを実行したりしたときには読み込まれません。このファイルには主に Zsh の操作に関する設定を記述します。 ```zsh:.zshrc # Emacs ライクな操作を有効にする(文字入力中に Ctrl-F,B でカーソル移動など) # Vi ライクな操作が好みであれば `bindkey -v` とする bindkey -e # 自動補完を有効にする # コマンドの引数やパス名を途中まで入力して <Tab> を押すといい感じに補完してくれる # 例: `cd path/to/<Tab>`, `ls -<Tab>` autoload -U compinit; compinit # 入力したコマンドが存在せず、かつディレクトリ名と一致するなら、ディレクトリに cd する # 例: /usr/bin と入力すると /usr/bin ディレクトリに移動 setopt auto_cd # ↑を設定すると、 .. とだけ入力したら1つ上のディレクトリに移動できるので…… # 2つ上、3つ上にも移動できるようにする alias ...='cd ../..' alias ....='cd ../../..' # "~hoge" が特定のパス名に展開されるようにする(ブックマークのようなもの) # 例: cd ~hoge と入力すると /long/path/to/hogehoge ディレクトリに移動 hash -d hoge=/long/path/to/hogehoge # cd した先のディレクトリをディレクトリスタックに追加する # ディレクトリスタックとは今までに行ったディレクトリの履歴のこと # `cd +<Tab>` でディレクトリの履歴が表示され、そこに移動できる setopt auto_pushd # pushd したとき、ディレクトリがすでにスタックに含まれていればスタックに追加しない setopt pushd_ignore_dups # 拡張 glob を有効にする # glob とはパス名にマッチするワイルドカードパターンのこと # (たとえば `mv hoge.* ~/dir` における "*") # 拡張 glob を有効にすると # ~ ^ もパターンとして扱われる # どういう意味を持つかは `man zshexpn` の FILENAME GENERATION を参照 setopt extended_glob # 入力したコマンドがすでにコマンド履歴に含まれる場合、履歴から古いほうのコマンドを削除する # コマンド履歴とは今まで入力したコマンドの一覧のことで、上下キーでたどれる setopt hist_ignore_all_dups # コマンドがスペースで始まる場合、コマンド履歴に追加しない # 例: <Space>echo hello と入力 setopt hist_ignore_space # <Tab> でパス名の補完候補を表示したあと、 # 続けて <Tab> を押すと候補からパス名を選択できるようになる # 候補を選ぶには <Tab> か Ctrl-N,B,F,P zstyle ':completion:*:default' menu select=1 # 単語の一部として扱われる文字のセットを指定する # ここではデフォルトのセットから / を抜いたものとする # こうすると、 Ctrl-W でカーソル前の1単語を削除したとき、 / までで削除が止まる WORDCHARS='*?_-.[]~=&;!#$%^(){}<>' ``` →[その他のオプションの調べ方](http://qiita.com/uasi/items/c4288dd835a65eb9d709#1-5) ## .zshenv `~/.zshenv` は Zsh が起動した際に必ず読み込まれる設定ファイルです。ここには主に環境変数などの、操作に関係のない設定を記述します。 ```zsh:.zshenv # PATH の設定(お好みで) export PATH="/usr/local/bin:$PATH" # PATH の内容と同期している配列変数 path も使える path=( ~/bin $path ) # もし .zshenv を複数のマシンで共有していて、 # あるマシンには存在するが別のマシンには存在しないパスを PATH に追加したいなら、 # パスの後ろに (N-/) をつけるとよい # こうすると、パスの場所にディレクトリが存在しない場合、パスが空文字列に置換される # 詳細は `man zshexpn` の Glob Qualifiers を参照 path=( /machine1/only/bin(N-/) $path ) ``` # よく使うキー操作(Emacs キーバインドの場合) |キー|意味| |----|----| |**カーソル操作**|| |Ctrl-F|カーソルを1文字進める| |Ctrl-B|カーソルを1文字戻す| |Ctrl-A|カーソルを行頭に移動| |Ctrl-E|カーソルを行末に移動| |**文字削除**|| |Ctrl-W|1単語削除| |Ctrl-K|カーソルから行末まで削除| |Ctrl-Y|直前に削除した文字列を貼り付け| |**コマンド履歴**|| |Ctrl-P|履歴を1つ戻る| |Ctrl-N|履歴を1つ進める| |Ctrl-R|履歴を新しい順に検索(検索中に Ctrl-R/Ctrl-S を押すと1つ古い/新しい候補を表示)| |Ctrl-G(履歴表示中)|履歴表示をキャンセル| |**その他**|| |Esc を押して Q|現在入力中のコマンドをスタックに退避してプロンプトを空にする(何か別のコマンドを実行すると、退避したコマンドがスタックから取り出される)。コマンド入力中に別のコマンドを実行したくなったとき便利| ## その他のキーバインドの調べ方 キーに割り当てられているアクションの一覧は `bindkey -L` で表示できます: ``` % bindkey -L bindkey "^@" set-mark-command bindkey "^A" beginning-of-line bindkey "^B" backward-char ... ``` "^@" は Ctrl-@ を表し、続く set-mark-command はそのキーを押すと実行されるアクションの名前です。 Zsh ではアクションを定義する実体(組み込み関数または任意のユーザー定義の関数)のことをウィジェットと呼びます。ウィジェットの説明を読むには `man zshzle` の中をウィジェットの名前で検索します。 # 便利なコマンドや記法 - [Zsh - zmvコマンドで複数ファイルの一括リネーム - Qiita [キータ]](http://qiita.com/usamik26/items/fcb720ba646e5b0b4684):`zmv *.hoge *.fuga` で拡張子を一括で付け替えたりできる - [zsh で find を使わずに簡単にファイルを絞り込む - Qiita [キータ]](http://qiita.com/mollifier/items/1c4a4930a89aa75e5ced):`echo *(/)` でディレクトリにだけマッチするなど Zsh Advent Calendar にはたくさんの tips が集められています。 - [zsh Advent Calendar 2013 - Qiita [キータ]](http://qiita.com/advent-calendar/2013/zsh) - [zsh Advent Calendar 2012 - Qiita [キータ]](http://qiita.com/advent-calendar/2012/zsh) # マニュアルの調べ方 |マニュアル|内容| |----|----| |man zsh|zsh のマニュアルの一覧と zsh の概要| |man zshmisc|スクリプトの構文、リダイレクトやパイプの書き方、特殊関数の一覧、などなど| |man zshexpn|glob や変数展開の記法| |man zshparam|特殊変数の一覧や変数の添字展開の記法| |man zshoptions|setopt でセットできるオプションの一覧| |man zshbuiltins|ビルトインコマンドの一覧| 上級者向けのマニュアルは省略してあります。すべてのマニュアルを見るには `man zsh` を参照してください。[オンラインでマニュアルを読むこともできます](http://zsh.sourceforge.net/Doc/Release/zsh_toc.html)。 検索しづらい記号については以下のページが参考になります。 - [zsh の分かりにくい記号、用語のまとめ - Qiita [キータ]](http://qiita.com/mollifier/items/26a9ddcb7470f539e575) - [zsh の分からない用語を man から素早く探す - Qiita [キータ]](http://qiita.com/mollifier/items/14bbea7503910300b3ba) |
|
| 69位 |
|
|||
|
23:58:50 |
|
|
gitは、とにかくトピックブランチを作成して作業する。だいたい機能追加とかバグ修正とかの単位でブランチを作って作業します。(ちゃんとやってますよね?)
なので、作業の途中で別の修正を優先してお願いっ!なんて言われたときは、別のブランチに切り替えて作業をする必要がでてくる。そんな時に変更を一時的に退避しておくことのできる機能、それがstashである。 では、早速使い方 まだcommitしていない状態の変更ファイル(addしてる or add していない)が存在する状況で、次のコマンドを実行すると変更ファイルを退避することができる。 ``` $ git stash save ``` ※saveは、省略することもできる。 これでファイルの退避完了!git statusとか見てみると変更状態であったファイルがなくなっている。この状態なら安心してブランチを切り替えることもできる。めでたしめでたし。 と、退避だけならこれで終わりだけど、次につかうときに変更ファイルを取り出したい。 + いまどんな変更を退避しているかを確認 2つの変更を保存している状態(2回stashを行った状態) ``` $ git stash list stash@{0}: WIP on sub: a0d2f1b add fourth line stash@{1}: WIP on sub: 1a61919 add second line ``` ``` <stash名>: WIP on <stashを行ったブランチ名>: <ハッシュ> <コミットコメント> ``` ※ハッシュ、コミットコメントはstashを行った時のHEADのもの 次のようにすると、さらに変更内容もみれるので便利!(git logのオプションが使える) ``` $ git stash list -p ``` + さらに各変更について詳しく見たい場合、変更内容に含まれるファイルの一覧が見れる。 ``` $ git stash show <stash名> ``` + 復活させたいstash名がわかったら次のコマンドで取り出すことができる。 ``` $ git stash apply stash@{0} ``` + stash applyで変更を復活した場合は、stashリストのなかに復活済みの変更が残されているこれを削除するには、次のコマンドを使用する。 ``` $ git stash drop <消したいstash名> ``` + 変更の復活と、削除を同時に行う。 ``` $ git stash pop stash@{0} ``` stashが溜まってくると、なんの変更だったか、どれが必要な変更だったかわからなくなるのでこまめに消 したり管理するのが大切ですよー ##応用 git apply <stash名>で、変更を復活して適用したんだけど、やっぱやめとこうってときにこんなこんなやりかたで取り消せます。前半はgit stash show -p でパッチ形式で出力、それをgit apply -Rでパッチを逆に適用しています。 ``` $ git stash show <適用したstash名> -p | git apply -R ``` ##終わりに とっても基礎的なこと書いたけど、とりあえずgitを使えるようになるには、読んでわかった気になる前に手を動かして、コマンドが馴染んでくるまで何回も素振りをするのが近道だと思ってます。 |
|
| 70位 |
|
|||
|
16:43:15 |
|
|
http://railsdoc.com/active_support まず、nil?とempty?はrubyのメソッドで、blank?とpresent?はrailsで拡張されたメソッド(つまりrubyでは使えない)ってことを覚えておく。 - #nilは存在しないという意味。 対して ""は「何も無い」が存在している。空白の存在。 ``` book.nil? ``` もしbook自体が存在しないなら、上記はtrueを返す。 #empty?は「入れ物」は存在するのが前提。 ``` book.empty? description.empty? ``` は、bookやdescriptionそのものは存在していることが前提で、その配列の中身が空か、文字列の中身が空の場合にtrueを返す。 配列や文字列そのもの(入れ物)が無い場合にはNoMethodErrorが発生する。空ですか?と聞いてるわけだから、少なくとも「入れ物」が存在しないとダメってことか。 #blank?は、nil?とempty?の合わせ技 では、中身が空か、または入れものそのものが存在しないときの判定はどうなるか。blank?が使える。empty?とnil?を足したようなものだ。 nilと空のオブジェクトを判定できる。 ``` unless description.blank? end ``` と書けば、descriptionの文字列が空か、そもそもdescription自体が存在しない場合に処理をスキップできる。 #present?は、!blank?と同じ意味 ちなみに上の処理は present?を使って ``` if description.present? end ``` と書ける。 - nil? すべてのオブジェクトに定義されている。nilのときのみtrueを返す。 empty? 文字列の長さが0のとき、または配列が空のときにTrueを返す。もちろん数値には定義されていない。 blank? railsの拡張。nil, "", " ", [], {} のいずれかでTrueを返す。 |
|
| 71位 |
|
|||
|
23:58:59 |
(codeTakt (ex- mixi) 所属) |
|
## ファイル編集がコンフリクトした場合 下記はよくある(忌々しい)コンフリクト画面ですね。  皆さんはコンフリクトのmergeはどんな方法でやっていますでしょうか? vimやemacsで直接編集している方が多いイメージですが、実際開いてみると、下記のように差分が表示されていると思います。  この画面を見ただけではどのようにmergeすればよいのかわかりません。(Objective-CのARC/MRC双方の開発経験がある人は目をつぶってください・・) gitにはこのようなコンフリクトのmergeを支援する`git mergetool`コマンドが搭載されています。  このままEnterキーを押すと下記のような画面が立ち上がります。  画面幅の都合でフォントが小さいのですが、ここで「mergeしたい差分が作られる直前の状態」と「mergeしたい差分」に注目してみます。  この2つを見比べると、@propertyの「*adapter」のところが「*webViewAdapter」に変更になっていることがわかります。 mergeしたい差分の内容は実際には上記の変数名の変更だったのです。 ですので、「merge結果として保存するファイル」の中でコンフリクトした箇所の上側(画面内だとHEAD側)で、adapterをwebViewAdapterに書きかえます。下側とコンフリクトマーカは忘れずに削除しましょう。 ちなみに、git mergeでコンフリクトした場合は、真ん中はbranchが枝分かれした時の状態で、左は現在のbranchに追加された差分、右はmergeしたいbranchに追加された差分ということになります(3-way merge)。 ですので、真ん中と左を比べると、現在のbranchがどのように進んだのかを確認することができます。 今回は、ARC対応でretainがstrongに、assignがweakになったことがわかります。  mergeが完了したら、保存してエディタを終了します。merge前の状態は、該当のファイル.origとして保存されます。自動的にgit addされているので、コミットしましょう。 ※なお、キャンセルする場合は1回も保存をせずにエディタを終了します。 ※ちなみに現状ではvimのみの対応となります・・emacsでも設定方法があるようなので、うまく言った方はよければコメント等ください。 ### (Xcode用の)GUIのdiffツールっぽいのが起動する場合 opendiffになっている人がちょくちょくいるみたいなので、下記のコマンドでvimdiffに切り替えてください。 ```bash: git config --global merge.tool vimdiff ``` ## (番外編)mergetoolを使わず似た方法でmergeする 宗教上の都合でvimが使えない方や画面が狭い方に朗報です。 通常のコンフリクトマーカだとmergetoolの真ん中の画面(mergeしたい差分が作られる直前の状態、BASE)が欠落していましたが、コンフリクトマーカにそれを表示するオプションがありました。 ```bash: git config --global merge.conflictstyle diff3 ``` コンフリクトマーカが下記のようになります。 ``` <<<<<<< HEAD merge前の状態 ||||||| merged common ancestors mergeしたい差分が作られる直前の状態 ======= mergeしたい差分 >>>>>>> branch ``` とりあえず設定しておいても損はしません。mergetool使うほどでかいconflictじゃないときにも重宝します。 ※ただし、コンフリクトしていない場所に加えられた変更を把握できないので、大きい差分にはmergetoolを使うか、git diffを組み合わせて使うほうが良さそうです。 参照: http://qiita.com/hchbaw/items/1191c2627307a4673b1b ## ファイル削除がコンフリクトした場合(path conflict) 削除したファイルが編集されていた時に起きます。 - Deleted by them: - mergeする差分で削除されています - 現在のbranchで変更されています - Deleted by us: - 現在のbranchで削除されています - mergeする差分で変更されています 多くの場合最終的に削除を採用することになると思いますが、mergeのためにファイルが残されているので、下記のようにします。 ```bash: git rm path/to/target/file ``` ※unmergedっぽいメッセージが出ますが、きちんと削除が反映されます。 ## 同じ差分を複数回mergeする場合 gitにはコンフリクトした差分のmerge結果を保存しておき再利用するrerere機能というものがあります。 メンテナとかリリース担当で、同じ差分で複数回mergeする場合に便利です。 下記のコマンドで有効にできます。 ```bash: git config --global rerere.enabled true ``` http://d.hatena.ne.jp/unpush/20091010/1255186302 |
|
| 72位 |
|
|||
|
10:49:25 |
|
|
Xcode の便利プラグインまとめ
# はじめに Xcode 8 からのいわゆる Xcode Source Editor Extension ではなく、あくまで非公式のプラグインを掲載します。 * インストール先は `~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/` * Alcatraz などのパッケージマネージャを使うと管理が楽 * Xcode の更新でプラグインが使えなくなる事もあるので注意 * "DVTPlugInCompatibilityUUID" を追記する事で対応出来る場合もある(後述) ---- # 管理 ## Alcatraz Xcode プラグインや Color Scheme などを管理するパッケージマネージャ。これを入れておけばこの記事を見なくてもだいたいのプラグインは参照できます。 `メニューバー > Window > Package Manager` から開く。 [Xcode 7.1 で使えなくなった場合](http://qiita.com/usagimaru/items/59745e26584a00ddfba3#dvtpluginmanagernonappleplugins-xcode-71-について) ### 対応環境 * Xcode 5, 6 * OS X 10.9以降 ### インストール ``` $ curl -fsSL https://raw.github.com/supermarin/Alcatraz/master/Scripts/install.sh | sh ``` Xcode を再起動すると Window メニュー内に "Package Manager" という項目が現れる。 https://github.com/mneorr/Alcatraz http://mneorr.github.io/Alcatraz/  ## JDPluginManager Xcode プラグインを管理するためのプラグイン。Alcatraz が使えない環境では代替でこちらが利用できる。 https://github.com/jaydee3/JDPluginManager 旧 https://github.com/jaydee3/JDListInstalledPlugins    # エディタ – 自動補完 ## AutoCoding NSCoding や -description の実装を自動生成する。 もう配布されていないようです。-description の自動生成は [XcodeGenerateDescriptionPlugin] で代替できます。 http://questbeat.hatenablog.jp/entry/2013/11/01/150141 ### 動作環境 * Xcode 5.0, 6.1  ## XcodeGenerateDescriptionPlugin 選択したプロパティ定義から、-description メソッドの実装を自動生成する。モデルクラスなどの実装に役立つ。 https://github.com/adamontherun/xCodeGenerateDescriptionPlugin   ## Auto Importer for Xcode `#import` ディレクティブを自動挿入する。 https://github.com/lucholaf/Auto-Importer-for-Xcode  ## Peckham `#import` ディレクティブをどこからでも挿入できるようにする。 https://github.com/markohlebar/Peckham  ## ImportSorter `#import` ディレクティブの順番を自動で整理してくれる。 https://github.com/manji602/ImportSorter http://qiita.com/manji602/items/25db3f65ee6fbd7b8d37   ## CATweakerSense CAMediaTimingFunction のアニメーションカーブを GUI で編集できるようにする。 https://github.com/keefo/CATweaker  ### ヘルパーツール  ## ColorSense NSColor / UIColor のコードを OS 標準のカラーパネルで編集できるようにする。 Xcode 8からは標準機能になったので不要と思われる。 https://github.com/omz/ColorSense-for-Xcode 動作の様子 http://www.youtube.com/watch?v=eblRfDQM0Go ## ColorSenseRainbow Swift に対応した NSColor / UIColor のコードを OS 標準のカラーパネルで編集できるようにするやつ。 Xcode 8からは標準機能になったので不要と思われる。 https://github.com/NorthernRealities/ColorSenseRainbow  ## FuzzyAutocompletePlugin Xcode のコード補完機能を “Open Quickly” と同じような部分一致にする。Xcode 7.3 以降では標準機能になったので不要と思われる。 https://github.com/chendo/FuzzyAutocompletePlugin  ## HOStringSense for Xcode NSString 編集機能を拡張する。`"` `\n` 等がより自然に入力できるようになる。 https://github.com/holtwick/HOStringSense-for-Xcode  ## IntelliPaste 良い感じにペーストできるようにする。 RGB値をNSColor/UIColor定義にしてくれる機能もある。 https://github.com/RobertGummesson/IntelliPaste-for-XCode  ## KSImageNamed-Xcode UIImage のファイル名を補完する。画像プレビュー機能つき。 Xcode 8からは標準機能になったので不要と思われる。 https://github.com/ksuther/KSImageNamed-Xcode 解説記事 http://cocoadays-info.blogspot.jp/2013/01/xcode.html  ## LicenseNotice_XcodePlugin MIT, Apache, GPL などのライセンス条文をソースコードファイルに挿入する。 https://github.com/xxhp/LicenseNotice_XcodePlugin  ## SCXcodeSwitchExpander switch 文の補完機能を拡張するプラグイン。enum 定義から自動判定して必要な case が補完されるようになる。 https://github.com/stefanceriu/SCXcodeSwitchExpander  ## UselessPlugin Objective-C++ 向けの補完機能を追加するプラグイン。 https://github.com/HoZanHoi/UselessPlugin  ## VVDocumenter “///”と打つとJavaDoc風のコメントを挿入できるようにする。 https://github.com/onevcat/VVDocumenter-Xcode  ## XCActionBar Alfred のような感じにアクションを即座に実行できるようにする。 https://github.com/pdcgomes/XCActionBar    ## XcodeBoost シンボルハイライト、メソッドの選択、メソッド定義のコピー、正規表現によるハイライト、インデントを考慮したペースト等の編集機能を追加する。 https://github.com/fortinmike/XcodeBoost     # エディタ – コード整形 ## BBUncrustifyPlugin-Xcode ソースコードを整形する https://github.com/benoitsan/BBUncrustifyPlugin-Xcode ## ClangFormat ClangFormat を使ってコードを整形する。 https://github.com/travisjeffery/ClangFormat-Xcode http://clang.llvm.org/docs/ClangFormat.html  ### ClangFormat-XcodeでObjective-Cのコードを整形してみた http://qiita.com/shzero5/items/9bb912c86d22a867bcb4 導入からカスタマイズまでの方法がまとまっています。 ## Helmet Apple のフレームワークに含まれるヘッダーファイルを誤って編集できないように編集不可にする。 https://github.com/brianmichel/Helmet ## XAlign コードを `=` で揃えたりする。 https://github.com/qfish/XAlign  # エディタ – 表示系 ## AdjustFontSize `⌘ +` `⌘ -` でフォントサイズを変更できるようにする。使ってみたが、動作が割と重い。 https://github.com/zats/AdjustFontSize-Xcode-Plugin ## AutoresizeMask for Xcode UIViewAutoresizingMask の記述にフォーカスを当てると、現在の状態を図で表示する。ただし、IB のようにクリックしてコードが反映される機能はない。 メニューバー `Edit` > `Show autoresizing masks…` で有効になる。 https://github.com/garnett/AutoresizeMask-for-Xcode  ## Backlight 現在行の背景色を描画する。 https://github.com/limejelly/Backlight-for-XCode  ## BlockJump ブロック単位でカーソル移動する。 https://github.com/tyeen/BlockJump  ## SCXcodeEditorInset エディタの最下端の領域に余白を作る。 https://github.com/stefanceriu/SCXcodeEditorInset  ## SCXcodeMinimap Sublime Text のようなプレビュー機能を追加する。 https://github.com/stefanceriu/SCXcodeMiniMap  ## TOCAssetCatalogBackground xcassets の編集画面の背景色を黒くすることができるスイッチを追加する。(白系のアイコンが見やすくなる) https://github.com/toco/TOCAssetCatalogBackground  ## Polychromatic プロパティ、ivars, ローカル変数ごとに色を変える事ができる。 https://github.com/kolinkrewinkel/Polychromatic 標準:  Polychromatic:  ## XToDo `TODO`,`FIXME`,`???`,`!!!!` がついたコメントを一覧表示する。 https://github.com/trawor/XToDo  ## Xcode4_beginning_of_line HOMEキーでホワイトスペースのない最初の行へ移動する。 https://github.com/insanehunter/XCode4_beginning_of_line # ローカライズ ## Lin NSLocalizedString の入力を楽する。 https://github.com/questbeat/Lin https://github.com/questbeat/Lin-Xcode5  ## Xcode Quick Localization NSLocalizedString の入力を楽にする。 https://github.com/nanaimostudio/Xcode-Quick-Localization ## Xiblingual xib, StoryBoard のローカライズを補助する。 https://github.com/hetima/Xiblingual ### 動作環境 * Xcode 5.1   # Vim ## XVim vim キーバインド https://github.com/JugglerShu/XVim ## viXcode vim キーバインド(SIMBL プラグイン) https://github.com/robertkrimen/viXcode ## Vicious for Xcode   http://viciousapp.com # Interface Builder ## RRConstraintsPlugin Interface Builder に AutoLayout 関連の機能を追加するらしい。 https://github.com/RolandasRazma/RRConstraintsPlugin ## MoarFonts カスタムフォントのプレビューができる。有料。 http://pitaya.ch/moarfonts/ 解説記事 http://d.hatena.ne.jp/shu223/20140315/1395050662  # コンソール ## BBUDebuggerTuckAway デバッグ中に文字入力を開始したらデバッガーパネルを自動で閉じるようにする。 [DBSmartPanels] にも類似機能がある。 https://github.com/neonichu/BBUDebuggerTuckAway  ## Xcode Auto Close Debug デバッグが終わったらデバッグパネルを自動で閉じるようにする。 https://github.com/larsxschneider/XcodeAutoCloseDebug ## XCAddedMarkup コンソールに画像を表示できるようにする。 https://github.com/mikr/XcAddedMarkup  ## XcodeColors コンソールに着色する。SIMBL プラグインとしても動作可能。 http://deepitpro.com/en/articles/XcodeColors/info/index.shtml https://github.com/robbiehanson/XcodeColors ```objectivec:使い方 NSString *string = @"文字列に着色"; NSLog(@"\033[" @"bg80,10,12;" @"\033[" @"fg255,255,255;" @"%@" @"\033[;", string); ``` ## XLog コンソールに着色する。 https://github.com/Maxwin-z/XLog  ## BetterConsole コンソールに出力されたファイルパスをクリックできるようにする。  https://github.com/cppforlife/BetterConsole ## DebugSearch コンソールの出力をフィルタリングできるようにする。 https://github.com/maranas/DebugSearch ## PluginConsole プラグインのデバッグ向けのコンソール拡張? https://github.com/AlexIzh/PluginConsole Xcode 4   Xcode 5  ## MCLog コンソールの出力をフィルタリングできるようにする。 https://github.com/yuhua-chen/MCLog  # ナビゲーション ## BBUFullIssueNavigator Issue Navigator のセルを展開して表示する。 https://github.com/neonichu/BBUFullIssueNavigator  ## CodePilot 検索・ナビゲーション機能拡張 http://codepilot.cc  ## Dash Plugin for Xcode option + click のときに Dash で検索できるようにする。Dash.app が必要。 https://github.com/Kapeli/Dash-Plugin-for-Xcode ## KKHighlightRecentPlugin 直近に開いたファイルをナビゲーター上でハイライト表示する。新しい順に色が濃くなる。 https://github.com/karolkozub/KKHighlightRecentPlugin  ## QuickFind 検索 `command` + `F` の動作を拡張する。実行した時点で選択文字列で検索を開始できるようにする。 Xcode とシステムのキーバインドの設定が必要。 https://github.com/qiaoxueshi/QuickFind  ## RegexSwitch 検索欄の正規表現を切り替えるボタンを追加する。 https://github.com/hetima/RegexSwitch  ## KSHObjcUML Objective-C クラスの依存関係を出力できるようにする。 http://qiita.com/kimsungwhee/items/bcee04fc6275823e5991  ### インストール Alcatraz 経由でインストールできる。 ## Xcode Explorer プロジェクト内の通知やイベントを監視して一覧表示してくれるプラグイン。 https://github.com/edwardaux/XcodeExplorer   ## XprobePlugin Objective-C メモリブラウザー。オブジェクトの中身をトレースして一覧で表示できる模様。 https://github.com/johnno1962/XprobePlugin   ## XcodeWay Xcode やプロジェクトに関連するディレクトリに簡単に飛べるショートカットをウインドウメニューに追加する。 https://github.com/onmyway133/XcodeWay  # CocoaPods ## CocoaPods for Xcode https://github.com/kattrali/cocoapods-xcode-plugin  ## CocoaPodUI https://github.com/Galeas/CocoaPodUI  # CocoaControls ## CocoaControls CocoaControls に登録されている情報を検索したりできる。 https://github.com/yeahdongcn/CocoaControlsPlugin  # Git ## GitDiff git の差分をハイライト表示できる。 該当行の行番号欄の右端とスクロールバーに色が付き、カーソルを重ねると以前のコードがポップアップされる https://github.com/johnno1962/GitDiff  ## ShowInGitHub https://github.com/larsxschneider/ShowInGitHub  # その他 ## Anglerfish シミュレーター一覧を最近使用した順に並び替える。 https://github.com/dealforest/Anglerfish  ## LessAnim Xcode のいくつかのアニメーションを無効化するプラグイン。 https://github.com/ShingoFukuyama/LessAnim-for-Xcode 詳しくはこちら:[XcodeのUIアニメーションを消すプラグインLessAnim](http://qiita.com/ShingoFukuyama/items/999d23fd782d43461656) ## DBSmartPanels デバッグパネル、ユーティリティパネル等の挙動を設定できる。 例えば、「文字入力を始めたらデバッグパネルを自動で閉じる」、「IBを起動したらユーティリティパネルを自動で開く」といったことが設定可能。 https://github.com/chaingarden/DBSmartPanels/  ## xcfui 未使用の import ディレクティブを警告表示する。 https://github.com/jcavar/xcfui  ## Injection for Xcode Method Swizzling により、実行中でもコード編集しながらデバッグができるようになる。 <iframe src="//player.vimeo.com/video/50137444" width="500" height="286" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe> <p><a href="http://vimeo.com/50137444">Code Injection for Xcode (Plugin Version)</a> from <a href="http://vimeo.com/user13435079">John Holdsworth</a> on <a href="https://vimeo.com">Vimeo</a>.</p> http://injectionforxcode.com https://github.com/johnno1962/injectionforxcode ## KPRunEverywhereXcodePlugin 複数の実機デバイス向けに一括ビルドができるようになる。 https://github.com/kitschpatrol/KPRunEverywhereXcodePlugin  ## Mini Xcode ツールバーを隠しても Scheme を表示できるようにする。 https://github.com/Daij-Djan/MiniXcode   ## SchemeShortcuts Scheme に対してショートカットを割り当てる。 https://github.com/cppforlife/SchemeShortcuts ## DerivedData Exterminator DerivedData(ビルド時に生成されるキャッシュ等が保存されているディレクトリ)を消去するためのボタンをウインドウ/ツールバーに追加する。 https://github.com/kattrali/deriveddata-exterminator  ## CopyMethodStyle メソッドやプロパティをいろいろな形式でクリップボードにコピーできる。 プロジェクトから Archive ビルドして .xcplugin ファイルを生成したら手動インストールする。 https://github.com/hetima/CopyMethodStyle  ## WindowFlex Xcode のウインドウ幅をより縮小できるようにする。 https://github.com/zenangst/WindowFlex  ## Open with Application ファイルのコンテクストメニューから「このアプリケーションで開く」指定が出来るようにする。 https://github.com/inquisitiveSoft/Open-with-Application  ## WakariyasuiCommitPopup コミット履歴のポップアップメニューの表示を判りやすくする。 https://github.com/hetima/WakariyasuiCommitPopup ### 動作環境 * Xcode 5.0  ## Xcode4 Fixins Xcode 4の UI の挙動を変更するプラグイン集。 ### 動作環境 Xcode 4専用 ### 内容 * **CurrentLineHighlighter**: 選択行強調 * **DisableAnimations**: UIアニメーション無効化 * **FindFix**: 検索時にオプション表示をデフォルトにする。エディタの表示がバグる? * **HideDistractions**: エディタだけ表示 * **InhibitTabNextPlaceholder**: Tab キーによるプレースホルダ移動を禁止 * **TabAcceptsCompletion**: Tab キーで補完候補を選択 * **UserScripts**: Xcode3 風の AppleScript メニューを追加 https://github.com/davekeck/Xcode-4-Fixins ## XLocation 地図から GTX ファイルを生成できる機能を追加する。 https://github.com/StefanLage/XLocation 解説記事 http://cocoadays-info.blogspot.jp/2014/05/gpx-xcode.html  ## Xcode Maven Plugin https://github.com/sap-production/xcode-maven-plugin ## Gradle Xcode Plugin https://github.com/openbakery/gradle-xcodePlugin # プラグインの雛形 ## Xcode4 Plugin Template Xcode 4 プラグインのひな形 https://github.com/kattrali/Xcode4-Plugin-Template ## Xcode5 Plugin Template Xcode 5 プラグインのひな形 https://github.com/kattrali/Xcode5-Plugin-Template # 更に詳しく ## DVTPlugInCompatibilityUUID Xcode の更新でプラグインが読み込まれなくなってしまったら、プラグインバンドルの Info.plist 内 `DVTPlugInCompatibilityUUIDs` アレイに、対象の `DVTPlugInCompatibilityUUID` を追記する事で対応出来る場合がある。これについては以下の記事でも解説されている。 |Xcode|UUID| |---|---| |8.0|8A66E736-A720-4B3C-92F1-33D9962C69DF| |8beta6|1637F4D5-0B27-416B-A78D-498965D64877| |7.3|ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C| |7.2.1|F41BD31E-2683-44B8-AE7F-5F09E919790E| |7.2|F41BD31E-2683-44B8-AE7F-5F09E919790E| |7.1.1|7265231C-39B4-402C-89E1-16167C4CC990| |7.1β|CC0D0F4F-05B3-431A-8F33-F84AFCB2C651| |7.0|0420B86A-AA43-4792-9ED0-6FE0F2B16A13| |6.4|7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90| |6.3|9F75337B-21B4-4ADC-B558-F9CADF7073A7| |6.2|A16FF353-8441-459E-A50C-B071F53F51B7| |6.1|C4A681B0-4A26-480E-93EC-1218098B9AA0| |6.0|C4A681B0-4A26-480E-93EC-1218098B9AA0| |5.1|A2E4D43F-41F4-4FB9-BB94-7177011C9AED| Xcode6で動かないプラグインを対応させる http://qiita.com/roana0229/items/c8517935b43096be4682 Xcode 5.1にupdateした後に、使えなくなったxcode plug-inを再び使えるようにする方法 http://qiita.com/roothybrid7/items/85bacb992a22aaad04cd [暫定対応]Xcode5.1で今まで使っていたプラグインが使えなくなってしまった場合の対応 http://qiita.com/hachinobu/items/d26b86454c2f8a4e175f DVTPlugInCompatibilityUUID http://qiita.com/kotowo/items/39caf5d451f0a538824c ## DVTPlugInManagerNonApplePlugIns-Xcode-7.1 について Xcode7.1にアップデートしてAlcatrazが消えたら http://qiita.com/redpanda/items/b2e6a1538bb3e71688b7 Alcatraz in XCode 7.1 http://stackoverflow.com/questions/33342969/alcatraz-in-xcode-7-1 ## プラグイン作成資料 Xcode Plugin が盛り上がっているらしい http://qiita.com/griffin_stewie/items/ffb53d0a5d03fe2d91a4 初めてのXcode 5 プラグイン開発 http://dev.classmethod.jp/smartphone/xcode5-plugin-1/ Creating an Xcode4 plugin http://www.blackdogfoundry.com/blog/creating-an-xcode4-plugin/ |
|
| 73位 |
|
|||
|
12:50:54 |
|
|
JavaScriptの`this`は同じソースコードでも呼び出し元次第で意味が違ったりして複雑だと思われがちだけど、一回覚えてしまえば簡単だ。 **JavaScriptにはthisが4種類ある** これだけをしっかり覚えておけば、後は必要な時に *4種類って何があるんだっけ?* と考えれば容易に思い出せる。 ちなみに、下記のコードはブラウザ上で実行することを想定している。(なので`window`を使う) # トップレベルのthis グローバルオブジェクトを指す。 ```js var hoge = "fuga"; window.foo = "bar"; // fuga+bar と表示される console.log(this.hoge + "+" + this.foo); (function(){ // 同じくfuga+bar と表示される console.log(this.hoge + "+" + this.foo); })(); ``` # コンストラクタ内のthis 作られるインスタンス自身を指す。 ```js var Hoge = function Hoge(msg) { this.hoge = msg; this.method = function () { console.log("method+" + this.hoge); } } var obj1 = new Hoge("Hello"); var obj2 = new Hoge("World"); // Hello と表示 console.log(obj1.hoge); // method+Hello と表示 obj1.method(); // World と表示 console.log(obj2.hoge); // method+World と表示 obj2.method(); // しまったnewを付け忘れたのでHogeの中のthisはグローバルオブジェクトだ! Hoge("newじゃねーよ!"); // ぎゃーっ! true! console.log(window.hoge == "newじゃねーよ!"); // ぎゃーっ! method+newじゃねーよ! window.method(); ``` # 何かに所属している時のthis 所属しているオブジェクトを指す。 ```js var hoge = {foo:"bar"}; hoge.print1 = function() { console.log(this.foo); }; // bar を表示 hoge.print1(); var func = function() { console.log(this.foo); }; // window.foo が参照されるのでundefined func(); hoge.print2 = func; // thisがhogeに変わったので bar と表示される hoge.print2(); // 一応prototypeも試してみましょうね。 var Hoge = function Hoge(msg) { this.message = msg; }; Hoge.prototype.print = function() { console.log("prototype+" + this.message); } var obj = new Hoge("Hello"); // prototype+Hello と表示 obj.print(); ``` # function#apply とか function#call とかで無理矢理変えられた時のthis thisを外部からの干渉で無理矢理書き換えられた場合。jQuery使っててclickイベントとか取るとthisが書き換えられている。そういうAPIなの。JavaScriptはNTR文化なの。 あと、`document.getElementsByTagName("div")` とかやると NodeList が取れる。そこで `Array.prototype.slice.call(document.getElementsByTagName("div"))` とかすると Array に変換できるハックがある。ってりんごが言ってた。 ```js var hoge = { foo: "bar", print:function(){ console.log(this.foo); } }; // 何かに所属している時のthisなので bar と表示 hoge.print(); var paramour = {foo:"NTR"}; // thisをすり替えられたので NTR と表示 hoge.print.call(paramour); var Hoge = function Hoge(msg) { this.message = msg; } Hoge.prototype.print = function() { console.log("Love " + this.message); } var obj1 = new Hoge("sarari-man"); // Love sarari-man と表示 obj1.print(); // アイエエエエ!?ニンジャ!?ニンジャナンデ!? obj1.print.call({message:"ninja"}); // thisを保護する方法もあるよ var ProtectedHoge = function ProtectedHoge(msg) { this.message = msg; // this を保存しておく var _this = this; this.print = (function(fn) { // fn に元々のthis.printが入ってる。 return function() { // 元々のthis.printを_thisに保存しておいた元々のthisを指定して実行しなおす。 fn.apply(_this, arguments); }; })(this.print); } ProtectedHoge.prototype.print = function() { console.log("Love " + this.message); } var obj2 = new ProtectedHoge("NinjaSlayer"); // ドーモ、ニンジャスレイヤーです。ニンジャ殺すべし。 obj2.print.call({message:"ninja"}); ``` ちなみにCoffeeScriptだとthisの保護も超カンタン `->` じゃなくて `=>` 使うだけ。[コンパイル後のコード](http://coffeescript.org/#try:class%20ProtectedHoge%0A%20%20constructor%3A%28%40message%29%20-%3E%0A%20%20print%3A%28%29%20%3D%3E%0A%20%20%20%20console.log%28%40message%29%0A%0Anew%20ProtectedHoge%28%22Fujikido%22%29.print%28%29%0A) ```coffeescript class ProtectedHoge constructor:(@message) -> print:() => console.log(@message) new ProtectedHoge("Fujikido").print() ``` # ねっ?簡単でしょう? 呼び出し元からthisを変えられる仕様にしたヤツを腹を切れ(#^ω^)ビキビキ |
|
| 74位 |
|
|||
|
23:56:05 |
(ever sense, Inc. 所属) |
|

gitを使い始めるとcommit, push, pullなどはある程度理解出来るようになりますが、<b>fetch</b>ってなんだ?ってなりますよね。 あまり馴染みにくいのは、pullが<b>fetch</b>と<b>merge</b>の両方を組み合わせたコマンドだからなんですね。 ``` pull = fetch + merge origin/master ``` # fetchとは gitの場合、リポジトリはリモートとローカルの2ヶ所あります。fetchとはリモートリポジトリから最新情報をローカルリポジトリに持ってくるコマンドです。 fetchをしても、pullのようにファイルが更新されるわけではありません。 あくまでもローカルリポジトリが更新されるだけです。 もっと詳しくいうと、例えばmasterブランチを使っているのであれば、 <b>origin/masterが更新される</b>ということです。 # masterとorigin/masterの違い masterは、例えばローカルのファイルを更新してコミットする場合にはmasterに入りますよね。 ローカルの作業ディレクトリと結び付いているのがmasterブランチです。 origin/masterとはリポジトリと結びついているブランチです。 とはいってもorigin/masterを参照するといっても、常に直接リモート側を見ているわけではないのです。 <b>git fetchをすることでorigin/masterが更新</b>されるのですね。 ローカルリポジトリの中にはmasterとorigin/masterの2つそれぞれの情報が置かれていることになります。 fetchを行ったときに新しい更新があったとするとorigin/masterが最新になり、masterはその分の更新がまだ行われていない事になります。 そこでmergeが必要になるんですね。 master ← origin/master 最新情報をマージするわけですね。 #git mergeへ という理解を持てないと、svnなどを使ってきた人にはどこのブランチをマージするのか?となりますよね。 リモートリポジトリの最新情報をローカルのmasterブランチにマージする必要があります。 ``` git merge origin/master ``` ここまで処理を行うことでローカルのファイルが最新状態に更新されます。 #git pull git pull は上記の工程を一気に行うコマンドでした。 便利なコマンドではありますが、これらの理解をした上で利用することでpullしたときにコンフリクトなどが発生してテンパらずに正しい理解で行動出来ますね。 |
|
| 75位 |
|
|||
|
02:57:50 |
(Wantedly, Inc. 所属) |
|
RspecのVersion2.11から ```ruby:old_spec.rb foo.should eq(bar) foo.should_not eq(bar) ``` を ```ruby:new_spec.rb expect(foo).to eq(bar) expect(foo).not_to eq(bar) ``` というように書くようになりました。 別にshouldを使った記法がなくなったわけではありませんが、 https://github.com/rspec/rspec-expectations のREADME.mdには、もう新しいSyntaxの説明しか載っていないし、今後はexpectの方を使っていくほうがいいでしょう。 http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax には、新しいSyntaxを導入した背景が説明されています。 簡潔に書くと、shouldだとBasicObjectを継承したクラスのテストを書くときに不具合が起こるみたいですね。 ## 移行方法 ### 基本 基本的には、上に書いたように、 - `foo.should` を `expect(foo).to` に - `foo.should_not` を `expect(foo).not_to` に 置き換えるだけです。 ### 比較演算子をとる ただし、 shouldの後に直接オペレータをとる記法は使えなくなりました。 具体的には、 ```ruby:old_operator_expectations.rb foo.should == bar "a string".should_not =~ /a regex/ [1, 2, 3].should =~ [2, 1, 3] foo.should < bar # <=, >, >= も同様 ``` を ```ruby:new_operator_expectations.rb expect(foo).to eq bar expect("a string").not_to match /a regex/ expect([1, 2, 3]).to match_array [2, 1, 3] expect(foo).to be < bar # <=, >, >= も同様 ``` のように変えます。 つまり、 - `==`の代わりに`eq` - 正規表現関係の`=~`の代わりに`match` - 配列の`=~`の代わりに`match_array` を使えばいいんですね。 shouldの後に比較演算子`<`をとる記法の代わりは、`be <`というように`be`を入れれば解決します。 `should(to) less than`より`should(to) be less than`の方が英語的に正しいので、これはこれでOKですね。 ### ブロックをとる また、このexpect記法だと、ブロックをとることができるので、 ```ruby:old_block_expectations.rb lambda { do_something }.should raise_error(SomeError) ``` を ```ruby:new_block_expectations.rb expect { something }.to raise_error(SomeError) ``` と書けるので、統一感があっていいですね。 ## 移行が終わったら ```ruby:config/initializers/rspec.rb RSpec.configure do |config| config.expect_with :rspec do |c| c.syntax = :expect # disables `should` end end ``` とすると、今後shouldを使った書き方ができなくなります。 ちなみにデフォルトでは ```ruby: c.syntax = [:should, :expect] # enables both `should` and `expect` ``` と設定したのと同じになっています。 ## 他のシチュエーションの書き方一覧 基本的に置き換えは上記の通り行うと済みますが、参照用に https://github.com/rspec/rspec-expectations に載っている他の書き方をそのまま転載しておきます。 ### Equivalence ```ruby expect(actual).to eq(expected) # passes if actual == expected expect(actual).to eql(expected) # passes if actual.eql?(expected) ``` Note: The new `expect` syntax no longer supports `==` matcher. ### Identity ```ruby expect(actual).to be(expected) # passes if actual.equal?(expected) expect(actual).to equal(expected) # passes if actual.equal?(expected) ``` ### Comparisons ```ruby expect(actual).to be > expected expect(actual).to be >= expected expect(actual).to be <= expected expect(actual).to be < expected expect(actual).to be_within(delta).of(expected) ``` ### Regular expressions ```ruby expect(actual).to match(/expression/) ``` Note: The new `expect` syntax no longer supports `=~` matcher. ### Types/classes ```ruby expect(actual).to be_an_instance_of(expected) expect(actual).to be_a_kind_of(expected) ``` ### Truthiness ```ruby expect(actual).to be_true # passes if actual is truthy (not nil or false) expect(actual).to be_false # passes if actual is falsy (nil or false) expect(actual).to be_nil # passes if actual is nil ``` ### Expecting errors ```ruby expect { ... }.to raise_error expect { ... }.to raise_error(ErrorClass) expect { ... }.to raise_error("message") expect { ... }.to raise_error(ErrorClass, "message") ``` ### Expecting throws ```ruby expect { ... }.to throw_symbol expect { ... }.to throw_symbol(:symbol) expect { ... }.to throw_symbol(:symbol, 'value') ``` ### Yielding ```ruby expect { |b| 5.tap(&b) }.to yield_control # passes regardless of yielded args expect { |b| yield_if_true(true, &b) }.to yield_with_no_args # passes only if no args are yielded expect { |b| 5.tap(&b) }.to yield_with_args(5) expect { |b| 5.tap(&b) }.to yield_with_args(Fixnum) expect { |b| "a string".tap(&b) }.to yield_with_args(/str/) expect { |b| [1, 2, 3].each(&b) }.to yield_successive_args(1, 2, 3) expect { |b| { :a => 1, :b => 2 }.each(&b) }.to yield_successive_args([:a, 1], [:b, 2]) ``` ### Predicate matchers ```ruby expect(actual).to be_xxx # passes if actual.xxx? expect(actual).to have_xxx(:arg) # passes if actual.has_xxx?(:arg) ``` ### Ranges (Ruby >= 1.9 only) ```ruby expect(1..10).to cover(3) ``` ### Collection membership ```ruby expect(actual).to include(expected) expect(actual).to start_with(expected) expect(actual).to end_with(expected) ``` #### Examples ```ruby expect([1,2,3]).to include(1) expect([1,2,3]).to include(1, 2) expect([1,2,3]).to start_with(1) expect([1,2,3]).to start_with(1,2) expect([1,2,3]).to end_with(3) expect([1,2,3]).to end_with(2,3) expect({:a => 'b'}).to include(:a => 'b') expect("this string").to include("is str") expect("this string").to start_with("this") expect("this string").to end_with("ring") ``` |
|
| 76位 |
|
|||
|
11:19:00 |
(株式会社キュリオシティソフトウェア 所属) |
|
# はじめに スマートフォンアプリ開発でAPIを介しWeb/APIサーバーとやりとりをする場合、「httpsを使っていれば通信はユーザーにバレない」なんてことはなく、Webアプリでツールを使ってできるのと同じようにユーザーには通信内容の確認や改竄などができます。 そのため、そのことを前提にアプリやサーバーAPIの設計と実装を行わない場合、アプリ利用者によるゲームスコア結果送信の改竄や、ソーシャルゲームにおけるレイドボスなどへのダメージ操作、ECサイトアプリでの購入操作なども可能になってしまいます。 また、最近自分は[「無料で音楽聴き放題!! - ネットラジオ」というアプリをリリースしたのですが](https://itunes.apple.com/jp/app/nettorajio-j-popmo-wu-liaode/id769979888?mt=8)、このアプリに導入するスタティックリンクライブラリが不明な外部サーバーへ通信していないか、SSLを使用しているつもりがそうでない通信をしてしまっていないかのチェックをするため、自分は[mitmproxy](http://mitmproxy.org/)というプロキシサーバーのツールを利用しています。 以降の内容はこんな感じです - mitmproxyとその導入について - 通信内容の確認 - 通信内容の改竄(リクエストの書き換え、レスポンスの書き換え) 必要なものは次の通りです * 有線LAN接続されたMac OSX(有線接続が必要なのはインターネット共有機能のためです) * homebrew # mitmproxyとその導入について mitmproxyはコマンドライン製のプロキシツールです。名前のmitmとはman-in-the-middleの略で通信用語のman-in-the-middle attack(中間者攻撃)などに使われる用語から来ているようです。 中間者攻撃というと過激ですが、自分で開発したアプリに対して検証をする際には便利なツールとなります。 イメージとしては次のようになります。  Mac OSX搭載の標準設定にあるインターネット共有(OSX自体がWiFiルータになる機能)を使い、通信をproxyで中継しiOS実機でそのWiFiを利用するというのが大雑把な仕組みです。 ## Pythonのインストール mitmproxyの実行には、まずPythonが必要になるのでhomebrewでPythonをインストールします ``` $ brew update $ brew upgrade $ brew install python ``` 次にpipをインストールします。easy_insallはbrewによるpythonのインストールと同時にインストールされているはずです。 ``` $ easy_install pip ``` ## mitmproxyのインストール pipからmitmproxyをインストールします ``` $ pip install mitmproxy ``` すでにインストールしていてアップデートしたい場合は次のようにオプション-Uを付けてアップデートしておきましょう。 ``` $ pip install mitmproxy -U ``` pipでインストールすることで関連するライブラリもインストールされます。 # mitmproxyの起動 実行は次の通り、オプション-pでポートを指定します。 ``` $ mitmproxy -p 8080 ``` 私はポート8080を指定していますが、これはiPhone側からのWiFi設定でも8080を指定が必要です。 iOSのWiFi設定からHTTPプロキシで手動を選択しサーバーのアドレスとポート番号を指定できるようになっています。  (図のsunnyとはMac OSXのマシン名で、WiFi名ですので気にしないでください) ## iPhone実機へのルート証明書のインストール またmitmproxyを実行すると~/.mitmproxy/にmitmroxy-ca.pemという証明書が作成されます。 OS XからiPhoneへこの証明書をメール送信し、iPhone側のメールクライアントから証明書を開くことでインストールすることが可能です。なお、この証明書はiOSアプリ開発者にはお馴染み[設定][一般][プロビジョニング]から確認できます。 # mitmproxyを使う mitmproxyの簡単な利用として、リクエストとレスポンスを見たり、それらを改竄、または通信を止めることが出来ます。 リクエストの改竄については、例えばすでに送信したリクエストからパラメータなどを変更することができ、変更したリクエストを再度送ることが出来ます。  ## インタラクティブにリクエスト/レスポンスデータを書き換え mitmproxyを起動した図の画面のリクエスト一覧から、[Enterキー]でGETしたリクエスト/レスポンスの詳細画面に移ります。 [eキー]でリクエスト(クエリ、パス、URL、ヘッダー、form、body、メソッド)を編集、 一覧に戻り[rキー]で編集したリクエストを再度GETすることができます。 また、気付きづらいのですが[Vキー]で編集をもとに戻すこともできます。これらのショートカットは[?キー]を押すことによって確認することができます。 このようにインタラクティブにリクエストを編集できる機能は、サーバー側にAPIコールをお気楽に試せるため、サーバーAPI側の仕様が不明な場合に検証やテストを行うのに最適です。課金アイテムのリクエストなどでパラメータをデタラメにした場合の異常系や、In-App Purchase時のサーバーへのレシートの送信もテストできるでしょう。 逆にこのインタラクティブな書き換えでは、リクエストを編集し再度GET/POSTなどしても、アプリ側の通信が完了済みのため、アプリ側で改ざんされたリクエスト/レスポンスを受けてのテストなどが出来る場合は少ないと思います。 ## スクリプトによるリアルタイムなデータ改竄 mitmproxyではインタラクティブな操作によるリクエスト/レスポンスの改竄ではなく、あらかじめ改ざんする内容を記載したPythonスクリプトファイルを指定することにより、通信ごとにスクリプトを適用することが出来ます。 例えばソーシャルゲームで多人数のユーザーが共通のボスと戦っているレイドボスと呼ばれるシステムがあったとします。 大抵の場合、流れは次のようになるでしょう - ユーザーAがボスに100ダメージ与えた - サーバーにユーザーAが100ダメージを与えたことをリクエスト送信 - サーバー側はユーザーAが100ダメージ与えたと認識 - ユーザーAに完了のレスポンス mitmproxyでダメージを変え、ダメージを与えたご褒美アイテムの数を変更することを考えると、次のようになります。 - ユーザーAがボスに100ダメージ与えた - リクエストをフックし9999ダメージ与えたことに変更 - サーバーにユーザーAが9999ダメージを与えたことをリクエスト送信 - サーバー側はユーザーAが9999ダメージ与えたと認識 - ユーザーAに完了のレスポンス - レスポンスをフックしご褒美アイテムの数を増やす スクリプトでリクエストレスポンスの改竄を行うことで、インタラクティブな書き換えではタイムアウトしてしまい出来なかった事ができるようになってしまいます。 ### スクリプトの指定 ``` $ mitmproxy -s examples/add_header.py ``` サンプルにあるadd_header.pyは次のようにシンプルな内容なので導入に持ってこいではないでしょうか。 ```python:add_header.py def response(context, flow): flow.response.headers["newheader"] = ["foo"] ``` responseメソッドは通信のレスポンスをHTTP(S)クライアントに届ける前に書き換えるメソッドです。このメソッドをオーバーライドし、レスポンスヘッダーに”newheader”を追加し、値をリストから文字列”foo”としています。 FlowクラスやResponseクラスについてはflow.pyに定義されているため、仕様を理解するにはそこを参照するしかないでしょう。 https://github.com/mitmproxy/mitmproxy/blob/master/libmproxy/flow.py スクリプトで特定のホストに対するリクエストかどうかの条件判断をするサンプルコードもありこれが一番参考になるでしょう。 https://github.com/mitmproxy/mitmproxy/blob/master/examples/redirect_requests.py redirect_requests.pyは”example.com"のホストへのリクエスト全てをオリジナルなレスポンスを作成して返し、"example.org”であれば"mitmproxy.org”へとリダイレクトします。 このサンプルコードを元に、自分の用意するホストへのリクエストの改竄を行い検証するのが手っ取り早いでしょう。 # mitmdumpについて mitmproxyをインストールするとmitmdumpというツールもインストールされます。mitmdumpはこれまで説明してきたmitmproxyと違い、結果を先述のコンソール画面のようなインタラクティブなリスト表示ではなく、標準出力へ出力するため、grepと併用するなどし結果を絞り込むことも出来るようです。これを利用することで特定ホストに対してhttpsを使っているつもりが、httpになっているかなどのチェックを効率的に行うことが出来るでしょう。 # 操作と用途まとめ - mitmproxyの基本操作でアプリ内課金で不正なリクエストにしてみる - mitmproxyの基本操作でアプリ内課金部分でアプリからニセ(使用済みまたは他のアプリ)のレシートをサーバーに送って何が起こるか簡単に試せる - mitmproxyのスクリプトで特定ホストへのリクエスト/レスポンスを改竄してみる - mitmdumpで特定ホストに対してhttp通信してしまっていないか確認できる # ここで書かなかったこと ここに書いた方法はポート8080にアクセスするようにwifiの設定を変更しています。ですが、透過型プロキシを使えば、実機がwifiのデフォルトである80ポートを使って通常と同じように通信をしているようにすることも可能です。 透過化型プロキシを使う方法は下記のサイトを参照すれば良いと思います。 MacでWifi共有で透過的にmitmproxy http://bagpack.hatenablog.jp/entry/2014/02/04/225553 # おわりに 世の中のWeb API設計/運営者やアプリ開発者が以上のことを正しく知っていないと、障害が起きた時の復旧や違反者の追跡など本来のコンテンツ運営とは違う時間がかかってしまいダメージは大きく計画の停滞感さえまねきます。発端はスマートフォンアプリでhttpsでの通信を行えば安心などの楽観的思考により設計が受け身になっていることだと思うので、まず通信の傍受/改竄方法を知ることが先決かなと思いました。 実際、AppleのGameCenterはplistでスコアを送信しているようで、mitmproxyによるインタラクティブなリクエストの改竄だけで、アプリ利用者のスコアを変更できるようです(現在は対策されているかもしれませんが)。 Setting highscores on Apple's GameCenter http://mitmproxy.org/doc/tutorials/gamecenter.html 具体的なセキュリティ対策に対しては、それぞれのコンテンツの要件と優先順位によって変わると思うので各自で考えましょう。もしくは[弊社に相談を頂ければ一緒になって考えたりもします](http://curiosity.co.jp/)。 ## 参考URL mitmproxy http://mitmproxy.org/ mitmproxyのスクリプトサンプル https://github.com/mitmproxy/mitmproxy/tree/master/examples mitmproxyを使ってSSL通信の中身を確認する http://ido.nu/kuma/2012/01/29/how-to-use-mitmproxy/ pyasn1 and PyOpenSSLの依存関係があるからpip使うようになった? https://github.com/mitmproxy/mitmproxy/issues/37 |
|
| 77位 |
|
|||
|
15:47:25 |
|
|
# はじめに 表題のとおり、いまさら聞けないgitの使い方だが、正直マイナーなコマンドはヘビーユーザではない限り、必要になるたびにGoogleで検索しているのが実情ではないだろうか。このページではその備忘録として、基本的な使い方+アルファのgitコマンドについてまとめる。 # 基本操作 ## リモートリポジトリからクローンする 大抵のgitプロジェクトでは、githubなどから自分の環境にクローンを作成するところからはじまる。基本文法は以下の通り ``` $ git clone <uri> ``` このuriの中に入るのは、http,httpsから始まるurlやsshプロトコルで指定するもの、gitoliteなどの他のソフトウェアと連携するものなどいくつかのパターンがある。 ### http, httpsを使う場合 だいたい、githubからcloneする場合はこのパターン。 ``` $ git clone https://github.com/YYYYYY/XXXXX.git ``` ウェブデザインが変化してからだいぶ経つが、httpsのurlはページ右サイドバーの、HTTPS clone URLのボタンをクリックするとクリップボードにコピーされる。 ### sshプロトコルから始まる場合 研究室や会社の一部門などの比較的ローカルなセクションで、gitを使っている場合はこのケースが多い。 ``` $ git clone ssh://user@hostname/path/to/repos ``` ユーザにはアクセスするユーザ名、hostnameはローカルマシンのホスト名またはIPアドレス、/path/to/reposには、gitリポジトリまでの絶対パスが入る。ホームディレクトリからの相対パスじゃないことに注意。 ### gitoliteなどの他のサードパーティのソフトとの連携 それぞれの内容に従ってください。 ``` $ git clone gitolite@<hostname>:<projectname> ``` 上記は、標準的にセットアップされたgitoliteのアクセス方法。詳しくは以下のページ参照。 * http://git-scm.com/book/ja/Git-%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC-Gitolite ## 自分用にローカルにgitリポジトリをつくる 自分用にリポジトリを作る場合は、プロジェクトのディレクトリの中で、ゴミファイルやビルド途中でできたファイルなどを削除し、以下を実行する。 ``` $ git init $ git add * $ git commit -m "initial import" ``` リポジトリを初期化し、現在あるファイルを全部登録して、コミットする手順を実行すると、初期インポートまでが完了する。 ## ローカルで開発するときに必要な作業 最も多用するコマンド群です。ローカルのみで作業している人は、「リモートリポジトリ・・・」の件は無視してよいです。 ### 変更のコミット いちいち、変更したファイルを登録したり、コメントをエディタを立ち上げて書くのが面倒なので、以下のコマンドに落ち着く ``` $ git commit -a -m "<comment>" ``` -aオプションは、既に登録されたファイルで変更点があるものをすべて指定するという意味、-mオプションはその後につづく文字列をコメントとして追加する。 ### 新しく出来たファイルの登録 開発途上でできたソースファイルや設定ファイル、ライブラリは以下のコマンドで追加する ``` $ git add <file> $ git add <directory> $ git add <wildcard. e.g. *.java> ``` addの後に続くものは、ファイルやディレクトリの他にワイルドカードも使えます。 ``` $ git add -A ``` プロジェクトディレクトリ中に追加されたすべてのファイルを追加する場合は上記が便利。ただし、コンパイルの中間ファイルなども登録されちゃったりするので、注意。 ### 変更したファイルのコミット git mvを用いず、エディタなどで変更したファイルの変更履歴をコミットする場合は以下を用いる. ``` $ git add --all <target> ``` オプションをつけない場合のgit addコマンドは、省略しないと ``` $ git add --ignore-removal <target> ``` に設定されていて、ワーキングツリーから削除されたファイルの追跡は行われないようになっているとのこと.ちなみに、疑わしき状況になるとwarningでしっかりと警告されます。以下、原文。 ``` * 'git add --ignore-removal <pathspec>', which is the current default, ignores paths you removed from your working tree. * 'git add --all <pathspec>' will let you also record the removals. Run 'git status' to check the paths you removed from your working tree. ``` eclipseはgitプラグインがちゃんとしていないので、手動でコミットしている人はたびたびお世話になるかもしれない. ### ファイル権限の編集 うっかり実行権限を付け忘れてコミットした場合は、以下のコマンドで権限を修正可能 ``` $ git update-index --add --chmod=<options> <target> # e.g. $ git update-index --add --chmod=+x <target> ``` ### 不要なファイルの削除 実装過程で不要になったファイルは以下のように削除できる ``` $ git rm <target> $ git rm -r <target dir> ``` ディレクトリ単位で削除したい場合は、-rオプションをつけると良い。中間ファイルなどを誤ってコミットしてしまった場合、--cachedオプションを使って削除すると、ローカルコピー上からは削除されず、git管理ファイルから外すことが出来る ``` $ git rm —cached <target> $ git tm -r —cached <target dir> ``` ## まだコミットしていない内容のコミット状態までのロールバック(svn revert相当) 編集中の内容からコミット済みのところまで戻りたい場合は以下のようにして、ロールバックできる。svn revertと一緒。 ``` $ git checkout <target file name> ``` ## リモートリポジトリとのシンク チームプログラミングして、自分のローカル環境と、リモートリポジトリの内容をシンクしたい場合には以下のコマンドを使います。 ### リモートリポジトリからローカルのリポジトリへ変更を反映 自分が寝ている間に他の小人さんがソースに加えた変更を自分のリポジトリに反映します。 ``` $ git pull ``` ### リモートリポジトリへローカルで編集した内容の反映 ある程度ローカルで編集した内容をリモートリポジトリに反映するときに使います。 ``` $ git push ``` ローカルのタグのpushは ``` $ git push --tags $ git push origin <tagname> ``` ### ブランチで作業しているときのpushとpull #### remoteのブランチ(への変更)を取得するとき ``` $ git pull origin <branchname> $ git checkout <branchname> ``` checkoutは、まだそのブランチに移行していないとき実行する。すでにそのブランチで作業しているときはいらない。 #### ブランチのremoteへのpush ブランチのpushは以下で実行する。 ``` $ git push origin <branchname> ``` 基本的には、 ``` $ git push/pull origin <tagname> ``` だということさえ覚えておけば、後の開発方法は一緒である。自分がいま、どのブランチで作業しているかは以下を実行して調べられる。 ``` $ git branch ``` # 知っておくと便利だけど、だいたい使わず忘れるコマンド ## アーカイブの作成 配布物をつくるときは以下のコマンドを使います。svn exportとだいたい同じ。 ``` $ git archive --format=tar --prefix=<dir/> HEAD | gzip > <path/to/repo.tar.gz> $ git archive --format=tar --prefix=<dir/> HEAD | bzip2 > <path/to/repo.tar.bz2> ``` prefixには、archiveが解凍されたときのディレクトリ名が、<path/to/repos.tar.gz|.tar.bz2>にはtarballのパスが入ります。 ## タグの作成とタグづけされた状態への移行 ### タグの一覧 以下のコマンドで一覧を見ることが出来ます。 ``` $ git tag ``` ### タグの作成 タグの作成。オプション付きだけど、以下のセットが便利です。 ``` $ git tag -a <tagname> -m <comment> ``` tagnameにはタグの名前(たいてい、"v0.0.X"のような文字列が入ります)。commentにはコメントが入ります。 ### タグの反映 クローンしてきたリポジトリをタグ付けされた状態に戻すときに使います。 ``` $ git checkout refs/tags/<tagname> ``` ## 管理されているファイルの一覧を見る 初期のインポート時に、全部インポートしたか確認するときなどに便利. ``` $ git ls-files ``` ## ブランチの作り方 ### ブランチの確認 ``` $ git branch ``` ### ブランチの作成 \<new branch name>という名前のブランチを作成します。 ``` $ git checkout -b <new branch name> ``` ### リモートブランチの取得 リモートブランチの取得は以下のコマンドから実行する。git pullするとすごいことになるので、checkoutでやったほうが良い。 ``` $ git checkout -b <same branch name with remote> origin/<remote branch name> ``` たとえば、developというリモートブランチがある場合、 ``` $ git checkout -b develop origin/develop ``` となる。 ### ブランチの切り替え \<branch name>というブランチに切り替えます(そのブランチ名がない場合は作成します) ``` $ git checkout <branch name> ``` ### ブランチの削除 \<the branch name to be removed.>という名前のブランチを削除します。 ``` $ git branch -d <the branch name to be removed.> ``` ### リモートリポジトリへの反映 リモートリポジトリにブランチ\<branch name to be updated.>をpushします。 ``` $ git commit -a -m “comments” … $ git push origin <branch name to be updated.> ``` githubのpullリクエストでよく使うので、覚えておきましょう。 ## githubとの密な連携 githubとの連携をするなら、github拡張のhubを利用すると良い。コマンドラインからpull requestを行うなど、より密にgithubと連携できる。 ``` ## OSX $ brew install hub ``` インストールはOSXなら上記のコマンドで、他のOSならバイナリが以下から入手できる. * https://github.com/github/hub/releases その後、.bash_profileに以下を設定すると、hubコマンドとしてgitを使用できる。 ``` eval "$(hub alias -s)" ``` 以下は新たに追加されるコマンドの一部 ``` ## clone $ git clone hoge/geho > git clone https://github.com/hoge/geho.git ## pull-request $ git pull-request ``` 詳細なコマンドは以下を参照のこと * https://github.com/github/hub |
|
| 78位 |
|
|||
|
17:56:47 |
(合同会社ユーキューブ 所属) |
|
1行目を
```bash #!/bin/bash -eu ``` とするか、または、 ```bash set -eu ``` を書いておく。以下解説。 ## `set -e` エラーがあったらシェルスクリプトをそこで打ち止めにしてくれる(`exit 0`以外が返るものがあったら止まるようになる)。「あっあれここでうまくいってないからデータ準備できてないのにあれあれっもうやめて!」ってなるのを防げる。 ## `set -u` 未定義の変数を使おうとしたときに打ち止めにしてくれる。Perlでいう`use strict 'vars';`的なもの。 ```bash rm -rf $TMEP_DIR/ ``` って気軽な気持ちで書いてしまって、「ん、やたら時間かかると思ったらスペルミスうわなにをするやめ」ってなるのを防げる。 ## 一部だけ例外にしたい はてなブックマークのコメントより > -e は command1 || command2 みたいなことが出来なくなるの使うことないな。-uは付けといて良いが。 確かにおっしゃるとおりですね。コマンドの失敗を考慮して書いている部分については(もしくはやたら`exit 0`以外するコマンドを呼ばないといけないときなど)、スクリプトの一部に限り `-e` を無効にしたいかもしれません。そんなときは以下のようにできます。こうするとエラーハンドリングを全部しっかり考えて作っていない限りは使わない意味はない気がします。 ```bash set -eu # : # do something # : # ここからは set -e を無効にする set +e command1 || command2 # ここからは再度有効にする set -e # : # do something # : ``` |
|
| 79位 |
|
|||
|
19:22:08 |
(フリーランス 所属) |
|
最近CSSの設計手法も進化してきたので、モダンCSS設計についての記事をまとめてみました。
## [MindBEMding(日本語訳)](https://gist.github.com/juno/6182957) CSSのモダンな記法、BEM記法について詳しくまとめたもの。 ## [Japanese Translations of BEM-Methodology (BEMの日本語訳)](https://github.com/juno/bem-methodology-ja) BEMについて詳しくまとめたもの。図解あり。上記のサイトと被る部分あり。 ## [General CSS notes, advice and guidelines(日本語訳)](https://github.com/kiwanami/CSS-Guidelines) CSS Wizardryの人がまとめたCSS規約。これは目を通す価値あり。 ## [en.ja | Translated Articles for Front End Developers](http://enja.studiomohawk.com/) 海外のフロントエンドエンジニアの記事を翻訳しているサイト。過去ログもざっと読むべし。 ## [Ja - Scalable and Modular Architecture for CSS](https://smacss.com/ja) Jonathan Snookが提唱したスタイルガイド(有料)。 KindleやiPadなどで読める形式でダウンロードできる。 ## [SMACSS 読んだ](http://chroma.hatenablog.com/entry/2013/07/22/120818) SMACSSについてのまとめ。 # 2014/05/28追記 ## [MCSS](http://operatino.github.io/MCSS/ja/) BEMとOOCSSの原理をもとにした、Multilayer CSS構成システムについての解説。 Odnoklassniki.ru(世界のソーシャルネットワークのTOP10)開発チームによって作り出された。 ## [FLOCSS](https://github.com/hiloki/flocss) 「OOCSSやSMACSS、BEM、SuitCSSのコンセプトを取り入れた、モジュラーなアプローチのためのCSS構成案」とのこと。日本人が作成。 |
|
| 80位 |
|
|||
|
01:20:20 |
(シナプス株式会社 所属) |
|
# 脆弱性について
## 参考リンク - [PHPにおけるファイルアップロードの脆弱性CVE-2011-2202](http://blog.tokumaru.org/2011/06/PHP-file-upload-bug-CVE-2011-2202.html) - [PHP 5.4.1リリースのポイント](http://d.hatena.ne.jp/rui_hi/20120429/1335664113) ## 上記に対する補足説明 - PHP **5.4.1**以降 - PHP **5.3.11**以降 どちらかを満たしているならば,脆弱性は(今のところ)無い.どちらも満たしていないと, - __*`$_FILES`*__ 変数の構造を崩す攻撃 - **`../`** をファイル名に含めて送信する攻撃 **(ディレクトリトラバーサル)** の何れか,もしくは両方の脆弱性を所持していることになるので要注意. 脆弱性対策と注意事項 ================= ## _`$_FILES`_ Corruption 対策<br />改竄されたフォームからの複数ファイル配列送信対策 脆弱性が修正された環境でも **改竄フォーム対策** も兼ねているのでこれは必須. ```php:BAD !isset($_FILES['upfile']) ``` ```php:GOOD !isset($_FILES['upfile']['error']) || !is_int($_FILES['upfile']['error']) ``` ## Directory Traversal 対策<br />空ファイル名対策 脆弱性が修正された環境でも **ファイル名を空にすることは可能** なので,このパラメータを使用せずに自前で一意なファイル名を生成するのが最善の策.もし使うにしてもファイル名として使用して問題ないフォーマットになっているかしっかり検証する. ```php:ファイルデータの重複を許さない(バイナリデータからハッシュ値を取る) $name = sha1_file($_FILES['upfile']['tmp_name']); ``` ```php:ファイルデータの重複を許す(乱数を使う) while (is_file($name = bin2hex(openssl_random_pseudo_bytes(32)))); ``` <ins>ユーザーがファイルを削除可能</ins> な場合には **重複を許す必要がある** ので注意.あるユーザーがファイルを削除したら他のユーザーのファイルも消えてしまった,なんてことがあってはいけない. もしユーザーがアップロードしたファイル名をそのまま使用したい場合,正規表現を使用して以下のものを防がなければならない. - 先頭または末尾が `.` であったり, `/` が含まれているもの.ファイル名として無効であったり,ディレクトリトラバーサルが可能なものであったりする. - 実行可能な拡張子. `.php` に加え, `.cgi` `.py` `.rb` なども実行可能な環境があるので注意. - `半角英数字` `.` `_` `-` 以外の文字.さまざまなエンコーディングが存在するためである.もしこれを許可したい場合, [mb_convert_encoding](http://www.php.net/manual/ja/function.mb-convert-encoding.php) 関数を用いてサーバー環境に合うよう変換しなければならないが,ここでの説明は割愛する. ```rb:これにマッチすれば安全,マッチしなければ危険と見なす /\A(?!\.)[\w.-]++(?<!\.)(?<!\.php)(?<!\.cgi)(?<!\.py)(?<!\.rb)\z/i ``` [move_uploaded_file](http://www.php.net/manual/ja/function.move-uploaded-file.php) じゃなくて [rename](http://www.php.net/manual/ja/function.rename.php) じゃだめ? ------------------- [rename](http://www.php.net/manual/ja/function.rename.php) 関数ではなく [copy](http://www.php.net/manual/ja/function.copy.php) 関数であれば問題ない.以下のリンクを参照して頂きたい. - [is_uploaded_file() / move_uploaded_file() の必要性?](http://qiita.com/mpyw/items/db12ce86b15f3b0b3c19) アップロード例外処理サンプル ======================== ```php:テキストとして結果を出力する例 <?php header('Content-Type: text/plain; charset=utf-8'); try { // 未定義である・複数ファイルである・$_FILES Corruption 攻撃を受けた // どれかに該当していれば不正なパラメータとして処理する if (!isset($_FILES['upfile']['error']) || !is_int($_FILES['upfile']['error'])) { throw new RuntimeException('パラメータが不正です'); } // $_FILES['upfile']['error'] の値を確認 switch ($_FILES['upfile']['error']) { case UPLOAD_ERR_OK: // OK break; case UPLOAD_ERR_NO_FILE: // ファイル未選択 throw new RuntimeException('ファイルが選択されていません'); case UPLOAD_ERR_INI_SIZE: // php.ini定義の最大サイズ超過 case UPLOAD_ERR_FORM_SIZE: // フォーム定義の最大サイズ超過 (設定した場合のみ) throw new RuntimeException('ファイルサイズが大きすぎます'); default: throw new RuntimeException('その他のエラーが発生しました'); } // ここで定義するサイズ上限のオーバーチェック // (必要がある場合のみ) if ($_FILES['upfile']['size'] > 1000000) { throw new RuntimeException('ファイルサイズが大きすぎます'); } // $_FILES['upfile']['mime']の値はブラウザ側で偽装可能なので // MIMEタイプに対応する拡張子を自前で取得する if (!$ext = array_search( mime_content_type($_FILES['upfile']['tmp_name']), array( 'gif' => 'image/gif', 'jpg' => 'image/jpeg', 'png' => 'image/png', ), true )) { throw new RuntimeException('ファイル形式が不正です'); } // ファイルデータからSHA-1ハッシュを取ってファイル名を決定し,保存する if (!move_uploaded_file( $_FILES['upfile']['tmp_name'], $path = sprintf('./uploads/%s.%s', sha1_file($_FILES['upfile']['tmp_name']), $ext ) )) { throw new RuntimeException('ファイル保存時にエラーが発生しました'); } // ファイルのパーミッションを確実に0644に設定する chmod($path, 0644); echo 'ファイルは正常にアップロードされました'; } catch (RuntimeException $e) { echo $e->getMessage(); } ``` PSR-7ベースの方法 (2016/07/24 追記) =============== 今からコードを書く場合,この方法が最も推奨される.これはZend Frameworkのコンポーネントだが,もしフレームワーク側で個別に提供されている場合はそういったものを利用しても構わない.しかし,(任意多次元の配列に対応できるのかなど)**それが信用に値するコードか**を自分自身で確認した上で実際に使うことが望まれる. - **[PSR-7のHttp/Messageを使ったファイルアップロード処理](http://qiita.com/asaokamei/items/d9194076e11931f5f3c0)** 上記の内容に従った例をPHPマニュアルのユーザノートに投稿した.[リンク](http://php.net/manual/en/features.file-upload.post-method.php#119643)をこちらに記載しておく. # 実装例 (:warning:少しコードが古めなので注意:warning:) 具体的にフォームを交えて実装した例を紹介する.この記事では **「リクエストを受ける専用のファイル」** であることを想定してコードを書いたが,以下の例はリクエストを受けるだけでなく **「ユーザーにフォーム入力させるファイル」** という役割を兼ねさせている.1ファイルで全ての処理を担当するということである. ## [画像アップロード処理サンプル集](http://qiita.com/mpyw/items/73ee77a9535cc65eff1e) - 単純な画像アップロード - 画像形式の変換 - 画像のリサイズ - 複数ファイルのアップロード ## [PHP+MySQLで簡易画像アップローダ](http://qiita.com/mpyw/items/117ab6a88fd58d911c34) (上のサンプル集に含めようと考えましたが,内容が多すぎる気がしたので記事を分けました) - アップロードした画像の **画像データ** をMySQLに挿入する - アップロードした画像の **サムネイルデータ** を同時に作成してMySQLに挿入する - 取り出してきた **画像データ** を<ins>画像ファイルとして直接出力する</ins> - 取り出してきた **サムネイルデータ** を<ins>データURIスキームを利用してHTML内に出力する</ins> - Internet Explorer のみで起こり得る **画像XSS** を防止する ## [ディレクトリ作成を許可してファイルをアップロード](http://qiita.com/mpyw/items/695f443674aef8d349c3) - ユーザーがアップロードしたファイルの名前 `$_FILES['upfile']['name']` をそのまま使用する - ユーザーに保存するディレクトリを指定する権限を与え,存在しないものに関しては自動的に生成させる ## [CSVアップロードからのMySQLへのデータ挿入](http://qiita.com/mpyw/items/caa2568284b69d270f8b) - CSVファイルをユーザーにアップロードさせて,それをそのままMySQLのテーブルにINSERTする |
|
| 81位 |
|
|||
|
11:56:18 |
|
|
# ファイル操作 | key | 内容 | |-----------|----------------| | ZZ | 上書き保存し、viを終了 | :w | 内容を保存 | | :q! | 保存せずに終了 | # モード | key | 内容 | |-----------|----------------| | i | 挿入モードへ | o | 新しい行を追加し挿入モードへ | R | 上書きモードへ | v | ビジュアルモードへ | Ctrl + v | 矩形選択のビジュアルモードへ | ESC | コマンドモードに戻る | ctrl + \[ | コマンドモードに戻る | ctrl + z | vim を一時停止する # カーソル移動 | key | 内容 | |-----------|----------------| w | 次の単語 (Word) b | 前の単語 f(文字) | カーソルがある行の(文字)に移動 (Find) F(文字) | カーソルがある行の(文字)に移動(逆向き) 0 | 行頭 ^ | 行頭 $ | 行末 % | 対応する括弧に移動 Ctrl + u | 半画面上 (Up) Ctrl + d | 半画面下 (Down) zz | カーソルが画面中央になるようにスクロール Ctrl + o | 古いカーソル位置に戻る。 (Old)| Ctrl + i | 新しいカーソル位置に進む。 | # 行移動 | key | 内容 | |-----------|----------------| gg | 最初の行 88G | 88行目 G | 最終行 H | 画面上の最初の行 (Home) M | 画面上の中央の行 (Middle) L | 画面上の最後の行 (Last) # 検索と置換 | key | 内容 | |-----------|----------------| | * | カーソル下の単語を検索 | # | カーソル下の単語を検索 (上方向に検索) :%s/hage/hoge/g | 単語の置換(hageをhogeへ置換)。`%` はファイル全体を表す。 # 編集 | key | 内容 | |-----------|----------------| . | 直前の変更を繰り返す u | Undo Ctrl + r | Redo # コピー&ペースト | key | 内容 | |-----------|----------------| yy | 今いる行をコピー (yank) p | カーソルの場所に、ペースト yy5p | 現在の行をコピーし、下に5行追加する gv | 直前の選択範囲を再選択 # 特殊文字 key | 内容 | -----------|----------------| Ctrl-v return | 改行文字の入力 Ctrl-v tab | Tab 文字の入力 # ウィンドウ | key | 内容 | |-----------|----------------| :vsplit | 画面を左右に分割する :e filename | 今いるWindowにファイルを開く (Edit) :e test\*.cc | ワイルドカードを指定してファイルを開く Ctrl +w h | 左のWindowへ移動 Ctrl + w l | 右のWindowへ移動 # バッファ | key | 内容 | |-----------|----------------| :buffers | 編集中のバッファ一覧を表示 :bn | 次のバッファに移動 :bd[elete] | バッファを削除 # 単語補完 | key | 内容 | |-----------|----------------| Ctrl + p | 単語補完(前方検索) # マクロ | key | 内容 | |-----------|----------------| q\<letter\> | マクロの記録開始 | q | マクロの記録停止 | @\<letter\> | マクロの実行 | # レジスタ | key | 機能 | |-------|-------| | :reg | レジスタの一覧表示 | | Ctrl + r " | ヤンクした文字列をペースト | # コマンドの意味 | key | 意味 | |-----------|----------------| | g | 繰り返し | | c | 1回毎に確認 | # その他 | key | 内容 | |-----------|----------------| :!command | 外部コマンドの実行 : | 範囲選択中に `:` を入力すると、選択領域の範囲指定( `’<,’>` )が自動で挿入された状態でコマンドモードに入る。これを置換に利用すると便利。 (例: :'<,'>s/old/new/g) Ctrl + a | 数値のインクリメント Ctrl + x | 数値のデクリメント # 参考文献 [Route 477 - Vim覚え書き](http://route477.net/w/?VimMemo) [Vim 基本操作まとめ - Archiva](http://archiva.jp/web/tool/vim_basic.html) [検索・置換に便利なヤンク(レジスタ)【Vimコマンド】にさん追加7/3 - YKMbPP](http://d.hatena.ne.jp/ykmbpp/20080527/1211850475) |
|
| 82位 |
|
|||
|
17:10:37 |
|
|
フロントエンドエンジニアにとって、JavaScriptは今や開発の中心にあると言ってもいいと思います。
JavaScriptに対していまいち自信のない人向けに、おすすめしたい本をまとめてみました。 これで、日本のJS界隈がもっと賑わって、仲間が増えると嬉しいです(今でもかなり賑やかですが)。 # 基礎 ## JavaScript本格入門 [](http://www.amazon.co.jp/exec/obidos/ASIN/4774144665/axross-22/) JavaScriptの本の中では一番易しいんじゃないかな、と思います。 これ以上に読者ターゲットのレベルを落とした本は、解説が不十分だったり、レシピ集のような側面が強かったりで、勉強には向いていません(網羅的でないということ)。 この本は解説のみで、深い仕様やテクニックには踏み込みません。それだけに読みやすく仕上がっていると思います。 この本でJavaScriptに興味をもつことができたら、よりディープな本に進んでみるといいと思います。 ## パーフェクトJavaScript [](http://www.amazon.co.jp/exec/obidos/ASIN/477414813X/axross-22/) 「JavaScript本格入門」よりも、少しレベルの高い、同系統の本と思ってもらってかまいません。 プログラミング自体が初めての人がこの本をいきなり読むには、なかなか大変な思いをするでしょうが、「JavaScript本格入門」を読んだ後に進む先としてはバッチリです。買ったことを後悔させません。 ## JavaScript 第6版 [](http://www.amazon.co.jp/exec/obidos/ASIN/4873115736/axross-22/) 通称「サイ本」、JavaScriptのバイブルです。 とにかくページ数が多く、読みごたえがあります。上記の2つの本よりもさらに網羅的で、対象読者としてもプログラミング経験者を意識した説明になっています。 なかなか手を出しにくいかもしれませんが、JavaScriptを扱うフロントエンドエンジニアにとっては必読書です。ぜひ読んで下さい。 # 発展 ## JavaScriptエンジニア 養成読本 [](http://www.amazon.co.jp/exec/obidos/ASIN/4774167975/axross-22/) 基礎は大事ですが、JavaScriptの言語仕様だけを学んでいても、大規模なアプリケーションの開発に役に立つことは少ないでしょう。 特に、Webフロントエンドの開発手法と開発手段は、進化の速い分野のひとつです。 JavaScriptがWebページを装飾するスクリプトに過ぎなかった頃もありました。しかしサナギから羽化した今、JavaScriptはWebページをコントロールするまでに至っています。JavaScriptを学ぶなら、JavaScriptの言語仕様だけに留まらず、よく使われるツールやライブラリ、フレームワークなどの周辺技術についても学ぶべきです。 そんなアプリケーション開発のためのノウハウを学ぶのにピッタリの1冊です。JavaScriptとフロントエンド開発をもっと好きになれるので、ぜひ読んでみてください。 ## 開眼! JavaScript [](http://www.amazon.co.jp/exec/obidos/ASIN/487311621X/axross-22/) JavaScriptは手軽ながら、レキシカルスコープ、クロージャ、プロトタイプ、第一級関数など、少し理解が難しい要素が存在します。 この本は、そういった部分に焦点を絞り、丁寧に解説してくれています。あなたがJavaScriptの仕組みや、既存のコードの書かれ方に少なからず疑問を持っている点があれば、それを解決する本だといえるでしょう。 ## サーバサイドJavaScript Node.js入門 [](http://www.amazon.co.jp/exec/obidos/ASIN/4048703676/axross-22/) Node.jsは、いまではフロントエンドエンジニアにとって避けることのできない、重要なものであるといえます。 gulpやGrunt、BrowserifyやWebpackだけではありません。いずれはサーバサイドJavaScriptのコードの一部を読み書きしなければならない時代が来るでしょう。また、想像もできないかもしれませんが、あなたが書いたクライアントサイド向けのJavaScriptを、Webサーバー上でも動かさなければならない時も来るでしょう。 今からでも遅くないので、少しでもNode.jsと仲良くしましょう。この本はそのための1冊です。 ## リーダブルコード [](http://www.amazon.co.jp/exec/obidos/ASIN/4873115655/axross-22/) 謝らないといけませんが、これはJavaScriptの本ではありません。 しかし、「JavaScriptを書く」ということ考えた時に、どうしても薦めたかったので、無理やり入れてしまいました。 JavaScriptは動的型付けの言語であり、プロトタイプベースであるために、明確な「クラス」も存在しません。すべての関数がコンストラクタになりえます。そして、プリミティブな値と、nullやundefinedなどの特殊な値、FunctionやArrayなどのネイティブクラスを除けば、すべてが拡張された連想配列であると表現できます。これは自由な代わりに、あいまいさが強いとも言えます。 そうした時に重要になるのが、「より読解コストの低いコードを書けること」だと思います。ドキュメントを書けばいくらか誤魔化せますが、バグは前もってドキュメントに書くことはできません。何よりコードが読みやすいことが一番です。また、読解コストを意識されたコードは、自然とバグの少ないコードにもなります。 この1冊は、あなたのそうした意識を目醒めさせます。同僚を殺さないために、読んでみてください。 |
|
| 83位 |
|
|||
|
16:36:07 |
(株式会社ロックオン 所属) |
|
オブジェクトの命名規約
==================== DB設計によく携わっていた頃に多くのプロジェクトで共通で規定されていた規約をまとめてみました。 ここでは **オブジェクト** として以下のものを対象としています。 (カラムはテーブルの一部ではありますが、別で切りだしています。) * テーブル * カラム * インデックス * 制約 ## 1.全般 ### 大文字を利用しない テーブル名、カラム名ともに大文字を利用しない。 (DBにより大文字小文字を区別するもの、しないものなどがあるため小文字で統一を図る) | 名前 | OK/NG | | --- | --- | | DOCUMENTS | x | | Documents | x | | **documents** | o | ### 複数単語の連携は *スネークケース* テーブル名、カラム名ともにスネークケースを利用する。 キャメルケース、キャメルバックはNG。 | 名前 | OK/NG | | --- | --- | | tableName | x | | TableName | x | | tablename | x | | **table\_name** | o | ### 英語表記 基本的にはローマ字ではなく、英語で。 ### 略名は利用しない | 名前 | OK/NG | | --- | --- | | start_dt | x | | maker_cd | x | | **start_date** | o | | **maker_code** | o | ## 2.テーブル編 ### 基本系 は **〜s(複数形)** | 名前 | OK/NG | | --- | --- | | document | x | | **documents** | o | | category | x | | categorys | x | | **categories** | o | ### n:nのテーブル **〜s(複数形)** + "\_" + **〜s(複数形)** | 名前 | OK/NG | | --- | --- | | userscategories | x | | user_categories | x | | **users_categories** | o | *@ryo88c さんの案に、今までのプロジェクトでのケースを加えさせていただきました。* ## 3.カラム編 ### **ある瞬間の状態(ステータス)を表す名前とする** | カラム名 | |----------------| | **name** | | **age** | | **is_working** | | ... | *ある任意のタイミングの情報を管理するというDBの特性から考えたら当然では有ります* ### 以下のカラムは必須 | カラム名 | 役割 | |------------|----------| | id | 主キー | | created_at | 登録日時 | | updated_at | 更新日時 | ### 他テーブルの主キーとジョインするカラムは *テーブル名(単数系)\_id* | カラム名 | OK/NG | | ---------- | ---------- | | *categories\_id* | x | | *category\_id* | o | ### flg/kbnなどの略名は利用しない **flag** は on/off の意味がわかりにくいため、できるだけ使用しない。 ( **delete_flag** は on で削除? off で削除?) | 名前 | OK/NG | | --- | --- | | delete\_flg | x | | delete\_flag | △ | | *is\_deleted* | o | ### 時間を表すカラムは *受動態\_on* 、*受動態\_at* [Railsの基礎知識 - - Railsドキュメント](http://railsdoc.com/rails_base) より。 DATE型のカラムには名前を「受動態\_on」 TIMESTAMP型のカラムには名前を「受動態\_at」 | 名前 | OK/NG | |------------|-------| | created | x | | created_at | o | | closed_day | x | | closed_on | o | ## 4.制約編 ### ユニーク制約は uq\_<テーブル名>\_NN users テーブルのユニークキーは以下のとおり。 (同一テーブルに複数ユニークキーが存在していた場合、 *01* の部分を2桁のゼロフィル連番値で加算していく。) * *uq\_users\_01* ## 5.インデックス編 ### インデックス は idx\_<テーブル名>\_NN users テーブルのインデックスは以下のとおり。 (同一テーブルに複数インデックスが存在していた場合、 *01* の部分を2桁のゼロフィル連番値で加算していく。) * *idx\_users\_01* ### ユニークインデックス は ui\_<テーブル名>\_NN users テーブルのユニークインデックスキーは以下のとおり。 (同一テーブルに複数ユニークインデックスが存在していた場合、 *01* の部分を2桁のゼロフィル連番値で加算していく。) * *ui\_users\_01* ----- こんな規約もあるよ!という意見お待ちしています。 ----- **2017-01-22(日)** 以下の方のご指摘を反映 * @Hirata-Masato ----- **2016-06-29(水)** 以下の方々の案を追記 * @shoooo さん * @fuzzball さん ----- **2016-01-08(金)** 以下の方々の案を追記 * @ryo88c さん * @scivola さん |
|
| 84位 |
|
|||
|
13:16:09 |
(Abby 所属) |
|
# Docker で開発環境を作る話
こんにちは、Docker 0.9 が出ましたね。 ちょっと Docker を触っていて幾つかアレな点があったので共有しておこうと思います。 その他も合わせてまとめてます。 [私の Docker TIPS ](http://qiita.com/mopemope/items/181cb6c6c6f7cf9bbaa9) # Docker を使って開発環境、および開発環境の土台を作る まあよくある Docker の使い方って nginx だの redis だのいろんなサーバーを構築する感じだと思いますが。 今回は開発環境を構築する話をしたいと思います。 よく dotfiles なんかを github においてーなんてことやってる方多いと思います。 もうここは思い切って Docker のイメージにしてしまいましょう。 ### 利点 - モテる - なんかイケてる感じがする - 案件、プロジェクト毎に個別環境をクリーンなまま維持できる - みんな同じ環境で作業することができる(ライブラリのバージョンなどが揃う) - ぶっ壊れても巻き戻し可能(commitしなければ) - docker さえあればどこでも仕事ができる - image を作ってる間に一休みできる ### 欠点 - docker である - docker である - docker である ということでよくプロジェクトをまたがったり、いろんな対応していると環境がごちゃごちゃになりがちなので 思い切って分けちゃおう!使い捨てちゃおうってのが今回の話です。 また環境をメンバーで揃えてしまえば「オメーの環境でしか起きねーから!それ!」といった事を防げるかも知れません。 では実際の私が使ってる Dockerfile の一部と共にハマりそうなとこを回避するTIPSを書いていこうと思います。 前提として ubuntu を土台にしてるケースです。 ## 0.9 での ssh server でトラブル みんなが踏むとこですね。現在は解消されているようです。 https://github.com/dotcloud/docker/issues/4605 開発環境のため、基本 ssh server を立ち上げてそこから作業を行うのですが、ログイン後、pty がねえよとか言われる奴です。 ログインするとプロンプトが出てこず壊したくなりますね。 workaround でも書かれてますが run する際にオプションをつけましょう。 ``` sudo docker run -t -p 40000:22 mopemope/base ``` ## ubuntu 13.10 の sshd の設定で即死 これも踏むやつでしょうか。 開発環境なので、新しいライブラリなども使いたい!そのため ubuntu 13.10を土台にーなんて方も多いんじゃ ないでしょうか? とりあえず sshd を普通にたて起動はできた!が! って事が起こります。 ログイン後、即切断されるってやつですね。 13.10 では pam を見るのでこの設定を書き換えておく必要があります。 ``` RUN sed -i 's/.*session.*required.*pam_loginuid.so.*/session optional pam_loginuid.so/g' /etc/pam.d/sshd ``` ## fuse で即死 これもみんな踏むとこでしょうか。 apt でいろいろ入れてると fuse 関連にひかかって死ぬパターンですね。 一応以下で回避できますがきっともっといい方法があるでしょう。 ``` RUN chmod go+w,u+s /tmp RUN apt-get install libfuse2 RUN mkdir /tmp/fuse && \ cd /tmp/fuse && \ apt-get download fuse && \ dpkg-deb -x fuse_* . && \ dpkg-deb -e fuse_* && \ rm fuse_*.deb && \ echo -en '#!/bin/bash\nexit 0\n' > DEBIAN/postinst && \ dpkg-deb -b . /fuse.deb && \ dpkg -i /fuse.deb && \ cd / && \ rm -rf /tmp/fuse /fuse.deb ``` ## git clone 時のknown_hosts dotfiles や ソースなど git clone してくる際に死ぬパターンです。 known_hosts がまっさらなので確認してくるのですがそこで止まってしまいます。 ssh/config などで確認が入らないようにしておきましょう。 ``` Host github.com StrictHostKeyChecking no Host bitbucket.org StrictHostKeyChecking no ``` ## Dockerfile とまあいろいろあるのですが、使っている Dockerfile をサンプルとして書いておきます。 土台になるとこなので tag は xxxx/base としておきます。 個別環境は別途作成し、FROM xxxx/base として 先ほどの tag を使えばよいでしょう。 このコンテナに ``` ssh -XC ma2@xxxxx -p xxxx ``` でログインして開発すればよいでしょう。 基本、ソースなどの類は VCS などで管理すると思いますが、作業内容の永続化が必要であれば run する際に -v をつけてマウントしておきましょう。 ``` FROM ubuntu:13.10 MAINTAINER mopemope yutaka.matsubara@gmail.com RUN apt-get -yq update && apt-get -yq upgrade RUN chmod go+w,u+s /tmp RUN apt-get install libfuse2 RUN mkdir /tmp/fuse && \ cd /tmp/fuse && \ apt-get download fuse && \ dpkg-deb -x fuse_* . && \ dpkg-deb -e fuse_* && \ rm fuse_*.deb && \ echo -en '#!/bin/bash\nexit 0\n' > DEBIAN/postinst && \ dpkg-deb -b . /fuse.deb && \ dpkg -i /fuse.deb && \ cd / && \ rm -rf /tmp/fuse /fuse.deb RUN dpkg-divert --local --rename --add /sbin/initctl && rm -f /sbin/initctl && ln -s /bin/true /sbin/initctl # install RUN apt-get install -y openssh-server language-pack-ja zsh tmux git mercurial subversion build-essential python-setuptools python-software-properties RUN apt-get install -y wget unzip curl p7zip-full mosh xterm tree grep RUN apt-get install -y emacs24 vim zsh emacs-mozc-bin fonts-inconsolata fonts-ipafont fonts-ipaexfont rlwrap RUN apt-get install -y sqlite3 libsqlite3-0 libsqlite3-dev RUN apt-get install -y firefox gimp mozc-server libreoffice libreoffice-l10n-ja RUN mkdir /var/run/sshd RUN echo 'root:root' |chpasswd RUN locale-gen en_US en_US.UTF-8 && dpkg-reconfigure locales RUN locale-gen ja_JP ja_JP.UTF-8 && dpkg-reconfigure locales # sshd config RUN sed -i 's/.*session.*required.*pam_loginuid.so.*/session optional pam_loginuid.so/g' /etc/pam.d/sshd RUN /bin/echo -e "LANG=\"ja_JP.UTF-8\"" > /etc/default/local # Add User RUN adduser --disabled-password --gecos "" ma2 \ && echo "ma2 ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \ && echo 'ma2:ma2' | chpasswd RUN chsh -s /bin/zsh ma2 # Setup USER ma2 WORKDIR /home/ma2 ENV HOME /home/ma2 RUN mkdir .ssh RUN chmod 700 .ssh ADD id_rsa /home/ma2/.ssh/id_rsa ADD id_dsa /home/ma2/.ssh/id_dsa ADD config /home/ma2/.ssh/config RUN git clone git@bitbucket.org:mopemope/dotfiles.git RUN zsh dotfiles/setup.sh # emacs RUN mkdir .emacs.d RUN cp dotfiles/init.el .emacs.d RUN cp dotfiles/mozc.el .emacs.d RUN cp -R dotfiles/malabar .emacs.d/ RUN cp -R dotfiles/malabar-lib .emacs.d/ RUN cp -R dotfiles/snippets/java-mode .emacs.d/snippets/ # vim RUN mkdir -p .vim/bundle RUN git clone https://github.com/Shougo/neobundle.vim ~/.vim/bundle/neobundle.vim RUN cd ~/.vim/bundle/neobundle.vim/bin && ./neoinstall # codebox RUN hg clone ssh://hg@bitbucket.org/mopemope/codebox EXPOSE 22 CMD /usr/sbin/sshd -D ``` |
|
| 85位 |
|
|||
|
17:57:21 |
|
|
## 1. git が入っていなかったらインストール ```bash $ sudo yum -y install git ``` ## 2. rbenv をインストール(clone)する ```bash $ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv ``` ## 3. 環境設定 ```bash # PATH に追加 $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile # .bash_profile に追加 $ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile # 上記設定の再読み込み $ exec $SHELL -l ``` ## 4. rbenvの確認 ```bash $ rbenv --version rbenv 0.4.0-74-g95a039a ``` ## 5. ruby-build を インストール(clone)する ```bash $ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build ``` ## 6. ruby の最新版を確認 install -- list でインストール可能なバージョン一覧が出力される ```bash $ rbenv install --list Available versions: 1.8.6-p383 1.8.6-p420 1.8.7-p249 : : 2.0.0-p195 2.0.0-p247 2.0.0-p353 ← こいつが2.0系の最新版っぽい 2.0.0-preview1 : ``` ## 7. ruby のインストール 先ほど調べたバージョンを指定する ```bash $ rbenv install -v 2.0.0-p353 ``` ## 8. 環境確認 再読み込み ```bash $ rbenv rehash ``` インストールされているruby一覧を確認 ```bash $ rbenv versions * system (set by /home/vagrant/.rbenv/version) 2.0.0-p353 ``` 先ほどインストールした最新版に設定 ```bash $ rbenv global 2.0.0-p353 ``` 確認 ```bash $ ruby -v ruby 2.0.0p353 (2013-11-22 revision 43784) [x86_64-linux] ``` おしまい。 |
|
| 86位 |
|
|||
|
22:21:49 |
|
|
12/4の記事([AngularJSを使ったWebアプリのアーキテクチャ設計](http://qiita.com/zoetro/items/46d2a8b57f2645bb5033))で書くと言ったまま放置していたので、AngularJSのMVCパターンについて書いてみたいと思います。
AngularJSのMVCについては、12/19の[お前のAngular.jsはもうMVCではない。と言われないためのTutorial](http://qiita.com/icoxfog417/items/2ac773c33a8b34288551)というすばらしい記事がありますが、本記事ではもう少し抽象的な内容を扱ってみようかと思います。 # MVW(Model-View-Whatever)パターンとは MVCパターンには、MVC2、MVP、MVVMなど数多くの派生パターンがあります。 目的は同じなのに派生パターンがたくさんあるのは、それぞれのプラットフォーム固有の問題(フレームワークの違いや、サーバサイドかクライアントサイドかの違いなど)によってMV*の*の役割が異なるからです。 AngularJSは公式ページで"Superheroic JavaScript MVW Framework"と名乗っているので、またMVWなんていう新しいパターンが出てくるのかとゲンナリするかもしれません。 でもこれ、MVWという新しいパターンが提唱されているわけではないようです。 AngularJSの開発者はこんなことを言っていました。([引用元](https://plus.google.com/+AngularJS/posts/aZNVhj355G2)) > Having said, I'd rather see developers build kick-ass apps that are well-designed and follow separation of concerns, than see them waste time arguing about MV* nonsense. And for this reason, I hereby declare AngularJS to be MVW framework - Model-View-Whatever. Where Whatever stands for "whatever works for you". 「MV*について議論するのは時間の無駄だから、そんな暇があったらコードを書け。MV*の*の部分なんて"Whatever"でいいんだ。」という主張のようです。 言いたいことは分かりますが、だからといってWhateverなんて名前をつけたらControllerに何でもかんでも詰め込んでFat Controllerになってしまいそうです。 というわけで、時間の無駄だと言われてしまうのかもしれませんが、AngularJSのMVWパターンについて解説したいと思います。 # Model・View・Whateverの役割分担 MV*パターン適用の目的は[プレゼンテーションとドメインの分離](http://capsctrl.que.jp/kdmsnr/wiki/bliki/?PresentationDomainSeparation)です。 そこで、プレゼンテーション層とドメイン層に大きく分けた上で、それぞれの関係を図示してみました。  以降で各役割について解説します。 ## Model Modelは他のMV*パターンと違いはありません。つまり、プレゼンテーションに関わらない部分すべてがModelになります。 アプリケーションにもよりますが、次のような役割を持つことが多いでしょう。 * ビジネスロジック * データの入れ物 * サーバーサイドとの通信 * ローカルストレージ * etc. 他のフレームワークとの違いがあるとすれば、基本的にイベント通知の処理を実装する必要がない(例外はありますが)ところでしょうか。PureなJavaScriptのオブジェクトが使えるので実装が楽になりますね。 また、AngularJSのDI機能を利用してControllerとModelの関係を疎結合にすることができます。可能なところはどんどんService化するのがよさそうです。 ## View Viewの役割は、レイアウトとプレゼンテーションロジックに分けることができます。 レイアウトを独自のDSLで記述したり、コードで記述するようなフレームワークもありますが、AngularJSはHTMLでレイアウトを記述します。 ただしAngularJSでは、Directiveという機能で新しいタグや要素を自由に追加することができるので、HTMLの形をしたDSLと言ってしまってもよいかもしれません。 DOMを操作するような複雑なプレゼンテーションロジックは、Directiveの中に閉じ込めてしまうので、HTMLのレイアウトは宣言的に記述することができ、可読性も高くなります。 ## Controller & Scope ### Scopeの役割 Scopeは画面を描画するために必要な状態をストアする役割です。 MVVMパターンを知っている人にとっては、ViewModelと同じような役割を担っていると言えば分かりやすいでしょう。 クライアントMVCフレームワークを使わなかった場合、画面への描画内容と、描画するための状態(プレゼンテーションステート)は区別せず、DOMに両方の役割を持たせるのが一般的だと思います。 しかし、その方法では状態を管理することが難しく、ソースコードの可読性も低くなってしまいます。 そこで、AngularJSでは、DOMは画面の描画のためだけに使い、状態の保持はScopeで行うように役割分担をしています。 これにより、データの操作方法は明確になり、同一のデータを複数の場所に描画したり、異なる方法で描画するといったことが行い易くなっています。 ### Controllerの役割 ControllerはScopeをセットアップするための役割で、次のような仕事をします。 * Scopeにサービスの呼び出しを結びつける * Scopeの保持するデータを初期化する * Modelのイベント通知をScopeに結びつける Controllerにはビジネスロジックもプレゼンテーションロジックも書けるし、何かと太りがちな存在です。 ビジネスロジックはModelへ、プレゼンテーションロジックはDirectiveやFilterへ移譲し、できるだけControllerが太らないように気をつけなければなりません。 ### Controllerの分割 画面に要素を追加していくと、Scopeのメンバが増え、Controllerに渡すServiceの数も増えていきます。 もしそれが多すぎるのだとしたら、1つのControllerに役割を持たせすぎている可能性があります。 1画面につき1 Controllerにする必要はないので、画面内の要素ごとに分割したり、階層構造を作ったり、ng-repeat内の1要素ずつにControllerを割り当てたり、必要に応じて分割しましょう。 例えば、次のようにサイドバーと入力フォームと一覧表示部分と全体の親をそれぞれ別のControllerに分割したりします。 ~~~html <div ng-controller="MainController"> <div ng-controller="SideBarController"> サイドバー </div> <div ng-controller="InputAreaController"> 入力フォーム </div> <div ng-controller="ListControlelr"> <div ng-repeat="item in items"> <my-directive data="item" ></my-directive> </div> </div> </div> ~~~ コントローラを分離するとコントローラ間でデータ共有をする必要がでてきますが、手段はいくつか用意されています。 * [Angular JS で複数のコントローラ間でモデル(状態や値)を共有する方法 3 種類](http://qiita.com/sunny4381/items/aeae1e154346b5cf6009) Controllerを再利用しないのであれば親子関係を作ってやりとりし、そうでなければイベントでやりとりするのがおすすめです。 共通のサービスを使ったり`$rootScope`を使ったりするのはグローバル変数と変わりないので、最終手段にするのがよいかと思います。 # 参考情報 本記事の内容は下記のスライドの考え方を参考にさせてもらっています。 XAML系プラットフォームの知識がなくても理解できる内容ですので、ぜひともあわせて読んでみてください。 * [GUIアーキテクチャパターンの基礎からMVVMパターンへ](https://www.slideboom.com/presentations/591514/GUI%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%AE%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89MVVM%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%B8) # おしまい AngularJS Startup Advent Calendar 2013みごと完走ですね。 参加された皆様、特に発起人で13本もの記事を書かれた@matsuzanさん、お疲れ様でした! 読んでくれた皆様もありがとうございました! 始まる前は登録者も少なくて完走できるかどうか心配だったのですが杞憂でしたね。 この調子でAngularJSが盛り上がっていって欲しいものです。 |
|
| 87位 |
|
|||
|
10:59:59 |
(Freelancer 所属) |
|
iOS SDK では Core Image の CIDetector クラスで簡単に顔認識をおこなうことができます。iOS 7 からは、[笑顔やまばたきの検出も可能に](http://d.hatena.ne.jp/shu223/20130924/1379990718) なりました。
が、CIDetector は `detectorOfType:context:options:` というメソッドの第1引数で Detector Type を指定できる設計になっているものの、 ```swift public let CIDetectorTypeFace: String public let CIDetectorTypeRectangle: String public let CIDetectorTypeQRCode: String public let CIDetectorTypeText: String ``` [Detector Types - CIDetector Class Reference](https://developer.apple.com/library/ios/documentation/CoreImage/Reference/CIDetector_Ref/Reference/Reference.html#//apple_ref/doc/constant_group/Detector_Types) と、4つしか定義されてないので、結局のところ **顔・矩形・QRコード・文字領域しか認識できません**。 ##車やロゴや犬やネコを認識する OpenCV には顔検出用の手法として、 **Haar分類器** というものが実装されています。 この手法の詳細はここでは省略しますが、この手法は、 **顔検出だけでなく、車やペット等、さまざまな対象を検出** するのに広く使用されています。 で、このHaar分類器は、オブジェクトを検出するために、 **あらかじめその対象の特徴を学習させて作成した特徴量データ** (正しくは識別器をカスケードさせたもの)の入っているxmlファイル(この記事ではカスケードファイルと呼ぶことにします)を使用します。 ###OpenCVに同梱されているカスケードファイル OpenCV を git clone (参考:[OpenCV for iOSの使い方](http://qiita.com/shu223/items/3ff48a8bd6edd910e780))すると、data/haarcascades ディレクトリに、下記のカスケードファイルが入っています(2013年12月2日現在)。 - 顔 - haarcascade_frontalface_default.xml - haarcascade_frontalface_alt.xml - haarcascade_frontalface_alt2.xml - haarcascade_frontalface_alt_tree.xml - haarcascade_profileface.xml - 目 - haarcascade_eye.xml - haarcascade_eye_tree_eyeglasses.xml - haarcascade_mcs_lefteye.xml - haarcascade_mcs_righteye.xml - haarcascade_lefteye_2splits.xml - haarcascade_righteye_2splits.xml - haarcascade_mcs_eyepair_big.xml - haarcascade_mcs_eyepair_small.xml - 耳 - haarcascade_mcs_leftear.xml - haarcascade_mcs_rightear.xml - 口 - haarcascade_mcs_mouth.xml - 鼻 - haarcascade_mcs_nose.xml - 胴体 - haarcascade_fullbody.xml - haarcascade_mcs_upperbody.xml - haarcascade_lowerbody.xml - haarcascade_upperbody.xml 結構いろいろありますが、Core Image の **CIDetector では検出できない、耳・鼻・胴体** といったオブジェクトを検出するためのカスケードファイルもあらかじめ入っていることがわかります。 ちなみに haarcascade_eye_tree_eyeglasses.xml は、 **メガネ着用時の目** を検出するものです。 ###カスケードファイルを自作する OpenCVにあらかじめ入っているものだけでも、Core Image よりも多くの対象を検出できることがわかりましたが、 **人間以外のもの** 、たとえば車やバイク、犬やネコを検出するためのカスケードファイルは入っていません。 その場合には、OpenCVに入っている、 **haartraining** というツールを使用して、カスケードファイルを自作する必要があります。 手順は以下の通り。 1. 学習したいオブジェクトの例からなるデータセット(対象物が入っている画像)を集める 2. createsamples ユーティリティを使用して、陽性サンプル(対象物が入っているサンプル)のベクトル出力ファイルを構築する 3. 同様に、陰性サンプルのベクトル出力ファイルを構築する 4. haartraining で学習を実行する ・・・とこう書くとわりとシンプルですが、手順 1 の **データセット収集のハードルが半端なく高い** です。 ある程度の精度を求めるなら、 **1,000〜10,000枚** もの画像を集める必要があり、さらに、それらすべてについて **いくつの対象物がどこにあるか(オブジェクト数, 左上隅の x座標, y座標, 幅, 高さ)** を指定する必要があります。 現実的には、そういった画像を自動収集するツール、対象物の位置特定を補助するようなツールを自作する、といった努力が必要になると思います。 ###Webに落ちているカスケードファイルを探す 前述したハードルにより、カスケードファイルを自作することはあきらめ、偉大な先人達が公開してくれているものを探すことにします。 まず、カスケードファイルのデータベースみたいなものがあるのではないか、と探してみましたが、どうやらないようでした(あったらぜひ教えていただけると幸いです)。 というわけで **人間以外の剛体を検出するためのカスケードファイル** を、個別にキーワードをちまちま入れて探してみました。以下、見つけたものを列挙していきます。 ##車 ### [Vehicle Detection, Tracking and Counting on Behance](http://www.behance.net/gallery/Vehicle-Detection-Tracking-and-Counting/4057777) - 記事内に、カスケードファイルを含むソース一式が含まれています。  ###[Computer Vision: OpenCV support for DARPA Autonomous Vehicle](http://www.cs.utah.edu/~turcsans/DUC/) - "The trained Haar cascades can be found here." という記述のところに貼ってあるリンクからカスケードファイルをDLできます ##犬・ネコ ### [Visual Geometry Group: Oxford-IIIT Pet Dataset](http://www.robots.ox.ac.uk/~vgg/data/pets/) - **"Oxford-IIIT-Pet dataset" というペットの特徴量学習用データ** を使用 - 学習用データはダウンロードできたものの、カスケードファイルは公開してないっぽい - 同ページから "Cats and Dogs" という詳細が書かれた論文をダウンロードできます  ###[Cat Head Detection](http://research.microsoft.com/en-us/um/people/jiansun/papers/ECCV08_CatDetection.pdf) (PDF) - 論文のみ ###[TRACKING ANIMALS IN WILDLIFE VIDEOS USING FACE DETECTION](http://www.cs.bris.ac.uk/Publications/Papers/2000186.pdf) - 論文のみ ##標識 ### [Computer Vision: OpenCV support for DARPA Autonomous Vehicle](http://www.cs.utah.edu/~turcsans/DUC/) - 一時停止の標識, 停止信号 - "The trained Haar cascades can be found here." という記述のところに貼ってあるリンクからカスケードファイルをDLできます - stop signs, stop lights, and pedestrians とあるのですが、実際にどういう画像を学習させたかが不明 ##ロゴ ###[Haar cascade logo detection](http://www.wirelust.com/2011/11/08/haar-cascade-logo-detection/) - コカコーラのロゴを検出 - カスケードファイルは公開されてない。100枚の画像で学習させたとのこと。 <iframe width="640" height="360" src="http://www.youtube.com/embed/1DTg3YzDA2g?feature=player_embedded" frameborder="0" allowfullscreen></iframe> ##人間の部位 **OpenCVにデフォルトで入っているhaarでは検出できない人間の部位** を検出するカスケードファイル。 ###肩 - [Need Haar Casscades for Nose, Eyes & Lips(Mouth)](http://stackoverflow.com/questions/9015498/need-haar-casscades-for-nose-eyes-lipsmouth) - "Try this one: mozart.dis.ulpgc.es/pub/Software/HaarClassifiers/… It's a set of haar cascades for face features." とあるコメントから、DLできるzipに、肩と頭 (Head and shoulders) を検出するカスケードファイルが入っている ###Oppai - [Oppai-Detect 2 @ CodereposCon#1](http://yusukebe.com/archives/20080725/083750.html) - 記事内に "cascade_oppai.xml" というカスケードファイルへのリンクあり - ただしリンクをから飛べなかったので、今は公開されていないのかもしれません ###笑顔 - [SMILEsmileD](https://github.com/hromi/SMILEsmileD) - これは [iOS7 から Core Image でできる](http://d.hatena.ne.jp/shu223/20130924/1379990718) - 学習用データセットもリポジトリに入っている ###手/ジェスチャー - [EHCI 6 degrees of freedom hand tracker](https://code.google.com/p/ehci/wiki/HandTracking) - ソース一式がDLできて、aGest.xml というカスケードファイルが入っている  ##ペン ###[All about openCV: Creating a haar cascade classifier aka haar training](http://opencvuser.blogspot.in/2011/08/creating-haar-cascade-classifier-aka.html) - Pen detector という **ペンを検出** するカスケードファイルをダウンロードできます  ##その他 見つけ次第随時追加していきます。ご存知の方はぜひコメント欄などでご連絡ください! ##実装サンプル:画像から車を検出する まず、 **OpenCV for iOS の導入方法** 等、基本的なところは下記をご覧ください。 [OpenCV for iOSの使い方](http://qiita.com/shu223/items/3ff48a8bd6edd910e780) 上記記事で使っている OpenCVHelper.mm に、次のようなメソッドを追加します。 ```` + (UIImage *)detect:(UIImage *)srcImage cascade:(NSString *)cascadeFilename { cv::Mat srcMat = [OpenCVHelper cvMatFromUIImage:srcImage]; // グレースケール画像に変換 cv::Mat grayMat; cv::cvtColor(srcMat, grayMat, CV_BGR2GRAY); // 分類器の読み込み NSString *path = [[NSBundle mainBundle] pathForResource:cascadeFilename ofType:nil]; std::string cascade_path = (char *)[path UTF8String]; cv::CascadeClassifier cascade; if (!cascade.load(cascade_path)) { NSLog(@"Couldn't load haar cascade file."); return nil; } // 探索 std::vector<cv::Rect> objects; cascade.detectMultiScale(grayMat, objects, // 画像,出力矩形 1.1, 1, // 縮小スケール,最低矩形数 CV_HAAR_SCALE_IMAGE, // (フラグ) cv::Size(40, 40)); // 最小矩形 // 結果の描画 std::vector<cv::Rect>::const_iterator r = objects.begin(); for(; r != objects.end(); ++r) { cv::Point center; int radius; center.x = cv::saturate_cast<int>((r->x + r->width*0.5)); center.y = cv::saturate_cast<int>((r->y + r->height*0.5)); radius = cv::saturate_cast<int>((r->width + r->height)*0.25); cv::circle(srcMat, center, radius, cv::Scalar(80,80,255), 3, 8, 0 ); } return [OpenCVHelper UIImageFromCVMat:srcMat]; } ```` 前述した "Vehicle Detection" のページからカスケードファイル"haarcascade_car_1.xml" をダウンロードしてきて(リンクはよく探してみてください)、 プロジェクトに追加します。 そして、先ほど実装した `detect:cascade:` メソッドを使用して、 ```` self.imageView.image = [OpenCVHelper detect:self.imageView.image cascade:@"haarcascade_car_1.xml"]; ```` とやると、次のように車を検出できるようになります。  ##サンプルダウンロード 基本的に上記の手順通りに実装すればOKです。でも自分でつくるの面倒なのですぐにビルドできるものが欲しい!という方向けに、サンプルをgumroadよりダウンロードできるようにしました。 https://gum.co/emov gumroadなので、 **有料** (102円)になります。サンプルはいらないけど記事が少しは役に立ったから寄付してやるか、というお気持ちもありがたく頂戴いたします。値段が中途半端なのは、gumroadの仕様のためです、スイマセン。繰り返しになりますが、 **上記手順通りにやればOK(サンプルのダウンロードは不要)** です。 ###注意点 - iOS 7.0 でのみ動作確認しております。 - コードのみの販売です。サポート等はご容赦ください。 ##参考書籍 Haar分類器の詳細や、OpenCVの各種学習ツールを使ったカスケードファイルの生成方法は、以下の書籍に詳しいです。 <div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873114136/22301-22/ref=nosim/" name="amazletlink" target="_blank"><img src="http://ecx.images-amazon.com/images/I/515yhxgFPDL._SL160_.jpg" alt="詳解 OpenCV ―コンピュータビジョンライブラリを使った画像処理・認識" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873114136/22301-22/ref=nosim/" name="amazletlink" target="_blank">詳解 OpenCV ―コンピュータビジョンライブラリを使った画像処理・認識</a> 広範囲な内容が640ページにわたってわかりやすく解説されていて、訳文もものすごく読みやすいので、超オススメです。 また、拙著でも、 **画像処理関係のレシピ** を多く取り上げております。下記記事にて内容をご確認いただき、もしよろしければ! [『iOSアプリ開発 達人のレシピ100』という本を書きました](http://d.hatena.ne.jp/shu223/20130528/1369714635)  |
|
| 88位 |
|
|||
|
13:34:53 |
|
|
# Add ```bash: # 部分的にaddする git add -p # add済みのfileをadd(deletedを消すときにも使う) git add -u ``` # Apply ## diffからworking treeに反映する ```bash: git diff <FILE> > pending.diff git apply pending.diff ``` # Archive ```bash: # zip形式でファイルを出力。 git archive <COMMIT> --format=zip -o <OUTPUT FILE> ``` # Bisect ```bash: # スクリプトで渡す git bisect run ./test_script #=> 成功 0, 失敗 1, スキップ 125で返すスクリプト ``` ## コミットを二分探索する ```bash: git bisect good v1.6.0 git bisect bad v1.6.2 git bisect good # 壊れてない git bisect bad # こわれている git bisect skip # スキップ git bisect reset # 終了 ``` # Blame ```bash: # どのcommitでそのファイルが追加されたかみる git blame <FILE> ``` # Branch ```bash: # リモートのbranch一覧をみる git branch -r # ブランチ名を変更する git branch -m <NEW_NAME> # ローカルブランチを削除 git branch -d <BRANCH> # ローカルブランチを強制削除 git branch -D <BRANCH> # リモートブランチを削除 git push origin :<BRANCH> # ローカルブランチをリネーム git branch -m <OLD_BRANCH_NAME> <NEW_BRANCH_NAME> ``` # Clean ```bash: # 管理されていない(.gitignoreにもない)ファイルを削除する -n は消去ファイル確認 git clean -n -f ``` # Clone ```bash: # submodule も一緒に初期化 git clone --recursive <URL> ``` # Checkout ```bash: # work treeの向き先を現在のcommitにする(detached HEAD) git checkout HEAD^0 # fileをコミットからもどす git checkout <COMMIT> file # fileをインデックスから戻す git checkout <FILE> # remoteのブランチをとってきてcheckout git checkout -t -b <BRANCH> origin/<BRANCH> # 一つ前のbrachにcheckoutする git checkout - # 強制上書き git checkout . git pull ``` # Cherry-pick ```bash: # 狙ったコミットをマージする git cherry-pick <COMMIT> ``` # Commit ```bash:commit.sh # 追加してcommitする git commit --amend ``` # Diff ```bash:diff.sh # 最新のコミットと、インデックスの間 git diff --cached # コミット間の変更ファイル一覧 git diff --stat # ファイル名と変更を表示 git diff --name-status # カラーモード git diff --color-words ``` # Fetch ```bash: # 全てfetchする git fetch <REMOTE> <BRANCH> # fetchしたcommitをmergeする git merge FETCH_HEAD ``` # Format-patch ```bash: # 差分をpatch形式としてファイルで出力する git format-patch <COMMIT..COMMIT> ``` # Log ```bash:log.sh # 出力するコミット数制限 git log -2 # 各コミットの変更をパッチ形式で出力 git log -p # ログメッセージの最初の1行を出力 git log --pretty=short # 内容に変更がある部分を探す git log --pretty=short -S 'class Abc def aa p "aa" end end' -- <FILE> # 全てのログを見る git log -g # onelineでみる git log --oneline # ある特定のファイルのlogをみる git log v1.0.0 -- path/to/file.c # v1.0.0タグ以降のfile.cのログを見る # ある特定のユーザーのコミットをみる git log --author="<USER NAME>" ``` # Ls-files ```bash: # マージが必要なfileを出力 git ls-files -u ``` # Merge ```bash: # マージする。デフォルトはff git merge <BRANCH> # ffでも<BRAHCN>の情報があったことを残してMergeする git merge --no-ff <BRANCH> # 公開したcommitを取り消す git merge -s ours <COMMIT> # 一つ前にいたbranchをmergeする git merge - ``` # Merge-base ```bash: # 共通のcommitオブジェクトを出力する git merge-base <COMMIT> <COMMIT> ``` # Push ```bash: # 2つ前のコミットでプッシュ先のmasterブランチを更新 git push REMOTE HEAD^^:master # ローカルmasterでremoteのmaster更新 git push REMOTE master # ブランチ削除 git push :temporary # 強制的にpushする。(過去のcommitを変えちゃうかもしれない) git push -f ``` # Reset ```bash: # indexから全て削除 git reset #indexからFILEを削除 git reset <FILE> # commitを削除 git reset HEAD^ # commitを削除しファイルももとに戻す git reset --hard HEAD # resetを取り消す git reflog git reset --hard <リセットの前のCOMMIT> # 直前の操作(commit, merge, rebase, reset等)を取り消す git reset --hard ORIG_HEAD ``` # Remote ```bash: # 現在Remoteとして設定されている一覧を取得 git remote -v # Remoteを登録する git remote add <REMOTE> # Remoteとして登録しているのを削除する git remote rm <REMOTE> # Remote の url を変更する git remote set-url <REMOTE> <URL> ``` # Rev-list ```bash: # 指定したディレクトリのコミットidを一覧表示 git rev-list HEAD^..HEAD -- DIR/ ``` # Revert ```bash: # 過去のcommitを打ち消すcommitをする git revert <COMMIT> # コミットを行わない git revert -n <COMMIT> ``` # Rm ```bash: # Add取り消し git rm --cached <FILE> ``` # Rebase ```bash: # インタラクティブリベースを取り消す git rebase --abort ``` ## 過去のcommitを修正する ```bash: git rebase -i HEAD~<戻したいコミットの位置> # editorが起動するので直すコミットの横にあるコマンドを edit に変更 # 実際に変更して git add で indexに追加 git commit --amend でcommit git rebase --continue ``` ## リベースそのものを無かったことにする ```bash: git log -g # rebase -i 前のコミットを探す git reset <COMMIT> ``` ## topicブランチ育て中にmasterブランチの変更を取り込む ```bash: git checkout feature git fetch origin git rebase origin/master ``` ## 以下でも一緒 ```bash: git checkout feature git pull --rebase origin master git checkout master git pull --rebase ``` # Shortlog ```bash: # コミットの(ユーザー毎の?)要約 git shortlog HEAD^..HEAD -- <DIR> # ユーザー毎のコミット数を集計 git shortlog <COMMIT>..<COMMIT> --summary ``` # Status ```bash: # 次のcommitに含まれるのを確認する git status ``` # Stash ## 現在の変更を一時的によける ```bash: git stash git checkout <Other BRANCH> # edit files … git add git commit git checkout <Working BRANCH> git stash pop ``` # Tag ## remoteのタグを消す ```bash: # localのタグを消す git tag -d <TAG_NAME> # それをremoteに反映する git push origin :refs/tags/<TAG_NAME> ``` |
|
| 89位 |
|
|||
|
17:01:35 |
|
|
JavaScript で日付・時間を扱っていて、次から次へと罠にはまったので、あとから来る人のために書き留めておく。 ## Date.parse が返すのは Date でなく整数 `Date.parse` は、世界協定時 1970 年 1 月 1 日 00:00:00 からのミリ秒を返す。 Date を得るには `new Date` に渡す。 new Date に直接文字列渡しても同じ挙動なので、こちらのが簡潔。 ```javascript msec = Date.parse("Thu, 06 Sep 2012 00:00:00 +0900"); // 1346857200000 date = new Date(msec); // Date date = new Date("Thu, 06 Sep 2012 00:00:00 +0900"); // Date ``` ## 年を得るのに getYear は使わない `Date.prototype.getYear` は 1900 年からの年数で非推奨。 `Date.prototype.getFullYear` を使う。 ```javascript date = new Date("Thu, 06 Sep 2012 00:00:00 +0900") date.getYear(); // 112 (2012 - 1900) date.getFullYear(); // 2012 ``` ## 月は 0 から始まる `new Date` の第 2 引数でも、`Date.prototype.getMonth` でも、月は 0-11で表す。 ```javascript new Date(2012, 8, 6); // Thu, Sep 06 2012 00:00:00 GMT+0900 (JST) new Date("Thu, 06 Sep 2012 00:00:00 +0900").getMonth(); // 8 ``` ## 日を得るのは getDay ではない `Date.prototype.getDay` は 0 を日曜とする曜日を返す。 日にちは `Date.prototype.getDate`。 月とは違いこちらは 1-31。 ```javascript date = new Date("Thu, 06 Sep 2012 00:00:00 +0900"); date.getDay(); // 4 (木曜日) date.getDate(); // 6 (6日) ``` ## getTimezoneOffset の符号は普通の表記と逆 `Date.prototype.getTimezoneOffset` は UTC からの時差を分で返すが、通常タイムゾーンを示す文字列表現とは符号が逆。 ```javascript date = new Date("Thu, 06 Sep 2012 00:00:00 +0900"); date.getTimezoneOffset(); // -540 (540分=9時間で +0900 のこと) ``` ## 比較するときは getTime 使う 同じ日時を示す Date でもオブジェクトが異なれば == や === は false になる。 ```javascript s = "Thu, 06 Sep 2012 00:00:00 +0900" new Date(s) == new Date(s) // false new Date(s) === new Date(s) // false ``` `Date.prototype.getTime` で 世界協定時 1970 年 1 月 1 日 00:00:00 からの経過ミリ秒が得られるのでこれで比較する。 ```javascript s = "Thu, 06 Sep 2012 00:00:00 +0900" new Date(s).getTime() // 1346857200000 new Date(s).getTime() == new Date(s).getTime() // true new Date(s).getTime() === new Date(s).getTime() // true ``` ちなみに Date 同士の比較でも `<` や `>` は期待通りに動作するが、ややこしいのでおすすめしない。 (減算したり単項演算子 + を使って数値にキャストして比較する方法もあるが、意図がわかりにくい上に[遅い](http://jsperf.com/comparing-date-objects)ので使う理由はない) ## 文字列表現は toISOString か toJSON で取得する ECMAScript 3 で定義されている、文字列表現を得るメソッドには、下記のものがある (括弧内は [EMCAScript 3 仕様書 (PDF)](http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf) の項番号)。 - toString (15.9.5.2) - toDateString (15.9.5.3) - toTimeString (15.9.5.4) - toLocaleString (15.9.5.5) - toLocaleDateString (15.9.5.6) - toLocaleTimeString (15.9.5.7) が、いずれも "The contents of the string are implementation-dependent" と書かれており、形式は実装依存。 試してみたら違うのは IE だけだったけど、規格がないので責めてはいけない。 ```javascript date = new Date("Thu, 06 Sep 2012 00:00:00 +0900"); date.toString() // Chrome21: "Thu Sep 06 2012 00:00:00 GMT+0900 (JST)" date.toString() // Firefox14: "Thu Sep 06 2012 00:00:00 GMT+0900 (JST)" date.toString() // Safari: "Thu Sep 06 2012 00:00:00 GMT+0900 (JST)" date.toString() // IE8: "Thu Sep 6 00:00:00 UTC+0900 2012" ``` ECMAScript 5 では文字列表現を[ISO8601 のサブセットに規定](http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15)しており [toISOString](http://ecma-international.org/ecma-262/5.1/#sec-15.9.5.43) で得られる。 ```javascript date = new Date("Thu, 06 Sep 2012 00:00:00 +0900"); date.toISOString(); // "2012-09-05T15:00:00.000Z" ``` `JSON.stringify` から呼ばれる [toJSON](http://ecma-international.org/ecma-262/5.1/#sec-15.9.5.44) も toISOString を使うので、JSON.stringify したときも ISO8601 になる。 ```javascript date = new Date("Thu, 06 Sep 2012 00:00:00 +0900"); JSON.stringify(date); // ""2012-09-05T15:00:00.000Z"" ``` また、[Date.parse](http://ecma-international.org/ecma-262/5.1/#sec-15.9.4.2) や [new Date](http://ecma-international.org/ecma-262/5.1/#sec-15.9.3.2) でも、ISO8601 (のサブセット) をパースできることを定めている。 ```javascript new Date("2012-09-05T15:00:00.000Z") // Thu Sep 06 2012 00:00:00 GMT+0900 (JST) ``` ## JSON にすると日付が変わることがある 上で述べたように JSON.stringify したときの文字列表現は ISO8601 になるが、タイムゾーンを無視して[常に UTC になってしまう](http://ecma-international.org/ecma-262/5.1/#sec-15.9.5.43)。 これにより、タイムゾーンと時刻によっては日付が変わる。 ```javascript // 日本時間で 6 日 0 時は UTC では 5 日 15 時 date = new Date("2012-09-06T00:00:00+0900"); JSON.stringify(date) // "2012-09-05T15:00:00.000Z" ``` Date.prototype.toJSON を自分で書き換えてタイムゾーンを含む文字列を返すようにすれば回避可能だが、規格から外れてしまうため、JSON.stringify する前に文字列にしてしまうほうが安全。 ## 日付をパースした場合と数値で指定した場合で時刻が一致しない new Date や Date.parse で時刻を含まない日付文字列をパースすると、UTC 00:00:00 になる。 ```javascript // UTC 0 時 = JST 午前 9 時 new Date("2014-11-10") // Mon Nov 10 2014 09:00:00 GMT+0900 (JST) ``` 一方で `new Date(2014, 10, 10)` などとしたときは地方時 00:00:00 となり、これと時刻が一致しない。 ```javascript new Date(2014, 10, 10) // Mon Nov 10 2014 00:00:00 GMT+0900 (JST) ``` 前者の挙動は、日付文字列でタイムゾーン文字列がない場合、UTC として扱うという ECMAScript 5 の仕様に基づく。 > The value of an absent time zone offset is “Z”. > [15.9.1.15 Date Time String Format](http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15) ただし、ECMAScript 6 draft (October 14, 2014) では、これを地方時にするとの記述がある。 > If the time zone offset is absent, the date-time is interpreted as a local time. > [20.3.1.15 Date Time String Format](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-date-time-string-format) ## ISO8601 は IE8 ではパースできない Date.parse がパースできる形式は ECMAScript 3 では定められていないため実装依存。 試してみたら、主要ブラウザでは RFC2822 なら OK、ISO8601 は IE8 ではだめ (IE9 は OK)。 ```javascript Date.parse("2012-01-01 00:00:00"); // IE8 だと NaN ``` 上で述べたように ECMAScript 5 では ISO8601 (のサブセット) をパースできるよう定めている。 |
|
| 90位 |
|
|||
|
15:59:36 |
(Wantedly, Inc. 所属) |
|
Javascriptはブラウザのクライアントサイドで動く唯一の言語と言ってもいいので、普段書かなくてもちょいちょい書くことになる。そんな時用に、他の言語使っていると忘れてしまうJavascriptの重要な法則をまとめておく。 基本的にリファレンスにしているのはMozilla Developer Network (MDN)のドキュメントの以下のページ。MDNはJavascript関連では一番ちゃんとしたドキュメントだと信じている。 - [Working with Objects - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects) - [継承とプロトタイプチェーン - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain) - [this - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/this) - [オブジェクトモデルの詳細 - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Details_of_the_Object_Model) ## プロトタイプベース言語 Javascriptはプロトタイプベースのオブジェクト指向言語で、クラスベースのオブジェクト指向言語(例: C++, Java)とは異なる部分が多々ある。 例えば、クラスベース言語はクラスとインスタンスという概念があるのに対し、プロトタイプベース言語はPrototypical Objectというオブジェクトがあるだけ。 >表8.1 クラスベース (Java) のオブジェクトシステムとプロトタイプベース (JavaScript) のオブジェクトシステムとの比較 > | クラスベース (Java) | プロトタイプベース (JavaScript) | |:--|:--| |クラスとインスタンスは異なる実体です 。| すべてのオブジェクトはインスタンスです。 | | クラス定義を用いてクラスを定義します。また、コンストラクタメソッドを用いてクラスをインスタンス化します。| コンストラクタ関数を用いてオブジェクトのセットを定義および作成します。 | | new 演算子を用いて単一のオブジェクトを作成します。 | 同じです。 | | 既存のクラスのサブクラスを定義するクラス定義を用いて、オブジェクト階層を構築します。 | コンストラクタ関数に結びつけられたプロトタイプとしてオブジェクトを割り当てることで、オブジェクト階層を構築します。 | | クラスチェーンに従ってプロパティを継承します。 | プロトタイプチェーンに従ってプロパティを継承します。 | | クラス定義が、クラスの全インスタンスの全プロパティを定義します。実行時に動的にプロパティを追加することはできません。 | コンストラクタ関数またはプロトタイプがプロパティの初期セットを指定します。個々のオブジェクトやオブジェクトのセット全体へ動的にプロパティを追加したり、それらからプロパティを削除したりできます。 | >[クラスベース言語とプロトタイプベース言語 - オブジェクトモデルの詳細 - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Details_of_the_Object_Model) ## 全てはオブジェクト Javascriptで全てはオブジェクトで、クラスベース言語でいうところのインスタンスのみだと思えば良い。 > JavaScript では、ほぼあらゆるものがオブジェクトです。 > null と undefined 以外のすべてのプリミティブ型はオブジェクトとして扱われます。 [すべてはオブジェクト - Working with Objects - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects/#Object_everything) ## オブジェクトはプロパティを持つ オブジェクトは、変数や関数をプロパティとして持つことができる。 > JavaScript のオブジェクトは、自身に関連付けられたプロパティを持ちます。オブジェクトのプロパティは、オブジェクトに関連付けられている変数と捉える事ができます。オブジェクトのプロパティは基本的に、オブジェクトに属するものという点を除いて通常の JavaScript 変数と同じようなものです。 > [オブジェクトとそのプロパティ - Working with Objects - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) ## オブジェクトの実装は、結局、HashMap(連想配列) オブジェクトはHashMapなので、簡単な例としては、プロパティを配列の要素アクセスのように呼び出せる。 ```javascript: > "hoge".length 4 > "hoge"['length'] 4 ``` > JavaScript オブジェクトのプロパティは、ブラケット表記法でもアクセスや設定ができます。オブジェクトは連想配列と呼ばれることがあります。 > [オブジェクトとそのプロパティ - Working with Objects - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) 例えば、オブジェクトの宣言方法は、オブジェクト初期化子などと言われているが、結局よくあるHashMapの宣言と同じ。 > ```javascript: > var myHonda = {color: "red", wheels: 4, engine: {cylinders: 4, size: 2.2}}; > ``` > [オブジェクト初期化子の利用 - Working with Objects - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects#Using_object_initializers) ## メソッドと関数 オブジェクトのプロパティになっている関数がメソッド。 > メソッドはオブジェクトに関連付けられた関数です。簡単に言えば、オブジェクトのプロパティのうち関数であるものがメソッドです。 > [メソッドの定義 - Working with Objects - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_methods) ただし、メソッドはオブジェクトから参照されているだけで束縛されているわけではない。 よって、オブジェクトのプロパティとして宣言された関数(つまりメソッド)を、グローバルな環境で使えば通常の関数として振る舞う。 > this は関数に「渡される」のであって、関数に固定されている訳ではありません。言い換えると、メソッドはそれをメソッドとして持つオブジェクトに束縛されているのではなく、オブジェクトによって参照されているだけです。 > ```javascript: function Car(brand) { this.brand = brand; } Car.prototype.getBrand = function() { return this.brand; } var foo = new Car("toyota"); println(foo.getBrand()); // "toyota" var brand = "not a car"; var bar = foo.getBrand; println(bar()); // "not a car" ``` >[メソッドの束縛 - this - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/this#.E3.83.A1.E3.82.BD.E3.83.83.E3.83.89.E3.81.AE.E6.9D.9F.E7.B8.9B) ## コンストラクタと関数 どの関数でもコンストラクタとして使用でき、new演算子とともに使用することでオブジェクトを作成する。 > どの JavaScript 関数もコンストラクタとして使用できます。new 演算子をコンストラクタ関数とともに使用することで、新しいオブジェクトを作成します。 > [クラスの定義 - オブジェクトモデルの詳細 - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Details_of_the_Object_Model#Defining_a_class) なお、慣習として、コンストラクタとして使う関数は、通常の関数と区別するため、大文字で始める。 ## 継承とプロトタイプチェーン クラスの継承はプロトタイプチェーンという概念によって実現される。 > ```javascript: // Object.prototype のプロトタイプは null です。 // o ---> Object.prototype ---> null var o = {a: 1}; > // 配列は Array.prototype(indexOf、forEachなどのようなメソッドを持っている)から継承します。 // a ---> Array.prototype ---> Object.prototype ---> null var a = ["yo", "whadup", "?"]; > // 関数は Function.prototype(call、bindなどのようなメソッドを持つ)から継承します。 // f ---> Function.prototype ---> Object.prototype ---> null function f() { return 2; } ``` > [継承とプロトタイプチェーン - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain) ## this について プロトタイプベースだということで、問題になりやすいのがthisが何を指すか。 Qiita上でお二方が、thisは4種類あるよと書いてくれている。それぞれ比較するとわかりやすかった。 - [JavaScriptの「this」は「4種類」?? by Haru39](http://qiita.com/Haru39/items/9935ce476a17d6258e27) - [JavaScriptのthisの覚え方 by vvakame](http://qiita.com/vvakame/items/74005adacc0e8e2a3cab) 完全におんなじことを書いているわけではないが、だいたい同じことを書いている。せっかくなので対応表を書いておく。 | Haru39流 | vvakame流 | |:--------------:|:---------------:| | メソッド呼び出しパターン | 何かに所属している時のthis | | 関数呼び出しパターン | トップレベルのthis | | コンストラクタ呼び出しパターン | コンストラクタ内のthis | | apply,call呼び出しパターン | function#apply とか function#call とかで無理矢理変えられた時のthis | このあと、[MDNのthisの説明](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/this)も読むといいかも。 - apply, callの時(4つ目)は明示的にObject、 - メソッドの時(1つ目)は暗黙的にObjectを指して、 - それ以外(2つ目)はグローバルオブジェクトになる。 - newを使ったコンストラクタのthis(3つ目)については触れられてない (thisではなく[newの説明](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/new)に書いてある)。 |
|
| 91位 |
|
|||
|
14:04:49 |
|
|
> 12/09/05 監視対象のWebサービスのURLを間違えていたため修正しました
RailsのAdvent Calendarを待ちわびていました. 今回は,[WEB+DBの最新号](http://gihyo.jp/magazine/wdpress/archive/2012/vol70)のRails高速化記事で紹介されていたパフォーマンス監視サービスのNew Relicを使ってみた話です. [New Relic](http://newrelic.com/)は.newrelic_rpmというgemをインストールすることにより,レスポンスタイムやスロークエリなど,パフォーマンスに関するさまざまな統計情報をNew Relicのサイトでみることができます. Railsに限らずPythonやJavaなどいろいろな言語に対応しているようです. さらに,HerokuやDotCloudなどのPaaSにも対応していてやばい. ##HerokuのNew Relicプラグイン Herokuにホストしたアプリケーションを監視するためには[New Relicプラグイン](https://addons.heroku.com/newrelic)を導入する必要があります. 導入の手順はこちら(https://devcenter.heroku.com/articles/newrelic) New Relicプラグインには無料版と有料版があり,有料版のほうがデータの保存期間が長かったり,発行されたSQL文やEXPLAINの出力が見れたりするようです. 個人的には無料版で十分でした. 以下では7月に公開した[DailyCoding](http://daily-coding.herokuapp.com)というサービスを監視対象としています. ###DailyCodingの環境 - Rails 3.2.8 - Unicorn - Heroku(無料) - 100PV/日(弱小) - 1テーブルのエントリ数はせいぜい数百個(弱小) では,New Relicの主要なグラフを順番にみていきます. ##ブラウザのページロード時間の内訳  ブラウザのページロード時間 = Webアプリケーション処理時間 + ブラウザ-Webアプリケーションサーバ間のネットワーク時間 + ブラウザのDOMの構築時間 + ブラウザのページレンダリング時間 となります. 明らかに通信時間がボトルネックであることがわかります.(DOM構築時間もそこそこ時間かかってる) Herokuの東京リージョンまだー ##Webアプリケーションのレスポンスタイム内訳  Ruby(Railsによるルーティング処理とかコントローラの処理とか),DBおよびWeb External(外部サイトへのリクエスト時間)などの処理時間の合計がWebアプリケーションとしてのレスポンスタイムとなります. DBの処理時間が支配的なことがわかります. ##DBのレスポンスタイム内訳 下の図はActiveRecordのクエリごとのレスポンスタイムを示しています.(正確には縦軸はレスポンスタイムではなく,トータルのDB時間のうちの当該クエリのレスポンスタイムの割合.多分,グラフをカスタムすれば縦軸をレスポンスタイムにできる?)  ##Railsのコントローラのレスポンスタイム内訳  ##その他 - 他にもいろいろなデータ - 各グラフのうち,自分に必要なものだけを選んで一覧できる機能 - パフォーマンスだけでなくサーバエラーの監視 - 週ごとのサマリーやアラートのメールによる通知 - データを日単位もしくは週単位で比較 - 把握できていない機能が他にもいろいろありそう. ##まとめ New Relicのおかげでボトルネックがネットワーク時間であることがわかりました. このボトルネックを排除するためには,転送するファイルの数およびサイズを減らす必要がありそうです, なるべくブラウザ側でコンテンツをキャッシュさせたり,ファイルを圧縮したり... アプリケーション側でキャッシュするとかSQLクエリの発行回数を減らすとかしてもあんまり意味なかったんや. というわけでNew Relic便利! 明日の担当は[@puriketu99](http://qiita.com/users/puriketu99)さんです. よろしくおねがいします. |
|
| 92位 |
|
|||
|
01:03:44 |
|
|
参考資料がすべて英語なので、日本語で情報がまとまってる場所が欲しいと思いこの記事を作りました。「俺もっと便利な機能知ってるぜ!」「こういうことできないの?」って思った人はコメント/編集リクエストをくれると僕の幸せのステージがどんどん上がります。
## DevToolsの起動するショートカットキーは? ### 開くだけ F12, Ctrl + Shift + I (Win, Linux) Opt + Cmd + I (Mac) ### 要素の検証(Inspect Element)と同じ状態で開く Ctrl + Shift + C (Win, Linux) Shift + Cmd + C (Mac) ### Consoleタブが開いた状態で開く Ctrl + Shift + J (Win, Linux) Opt + Cmd + J (Mac) ## DevToolsって下じゃなくて右に持ってこれないの? Dock To Right  ## 名前が分かってるファイルを開きたい Ctrl + O (Win, Linux) Cmd + O (Mac)  ## DevTools上で編集したコードを保存 (あとで書く) ## DevTools上でいろいろ編集したけどどこをどう変えたかわかんなくなっちゃった Local Modifications系 (あとで書く) ## Webページ上の今いるファイル内検索 Ctrl + F (Win, Linux) Cmd + F (Mac) ## Webページ上のすべてのファイルを横断検索 Ctrl + Shift + F (Win, Linux) Cmd + Opt + F (Mac) ## ページを行き来してもNetworkタブで表示しているリクエストのリストを保持し続けたい NetworkタブではHTTPリクエストが表示されるが、ページのリロードがかかるとこの内容がクリアされてしまう。「Preserve Log upon Navigation」ボタンを押すと、もう一度押すまでの間のHTTPリクエストを、たとえリロードがかかっても、どんどん貯めることができる。 ## DOMエレメントに簡単アクセス  コンソールで一時的に何かをしたいときに、DOMエレメントにいちいち `getElementById` とかでアクセスするのは非常に面倒です(idやclassが割り振られていない場合などは特に) DevToolsでは選択している要素に `$0` でアクセスすることができます。また、選択要素を変更した場合も `$N` でNつ前に選択していた要素にアクセスできます。 ## 参考資料 - https://developers.google.com/chrome-developer-tools/ - http://anti-code.com/devtools-cheatsheet/ - http://www.youtube.com/watch?v=bqfoYaKCYUI&list=SP055Epbe6d5avZGXwE5u039VQq_oQFgrc&index=5 |
|
| 93位 |
|
|||
|
00:51:04 |
(Increments 所属) |
|
# はじめに
本稿は Juri Pakaste 氏による [Cocoa review checklist](https://bitbucket.org/juri/cocoareviewchecklist) (commit [fff5703](https://bitbucket.org/juri/cocoareviewchecklist/src/fff5703c96ed7676d44e5935ed070c62134e4d97))の翻訳です。他人の Objective-C のコードをレビューするとき注意する点、また普段のコーディングで心がけるべき点についてまとめられています。 なお、原文のタイトルは Cocoa review checklist となっていますが、内容が Cocoa に限らない範囲のトピックをカバーしているため、本稿のタイトルは「Objective-C の〜」としました。 誤訳の指摘や例の補足を歓迎します。 # コードレビューチェックリスト ## コードの見た目とコード以外の問題 ### 不要な `#import` や `@class` 宣言を消す ### `#import` をソートする `.m` ファイルの中では、対応する `.h` ファイルの `#import` を最初の行に書く。空行をはさんで、ソートされた他の `#import` を書く。 ### Xcode のグループをソートする Xcode のプロジェクトツリーをソートされた状態に保つ。 ### メソッドをうまく並べる メソッドを合理的に並べる。`#pragma mark` でメソッドをグルーピングする。 訳注: `#pragma mark MarkName` ディレクティブを記述すると、 Xcode のメソッド一覧に `MarkName` が表示される。 `#pragma mark -` でメソッド一覧に分割線を挿入できる。 ### コンパイラ警告をなくす `-Werror` (少なくともリリースビルドでは)と他の警告フラグを有効にする。 ### 静的解析の警告をなくす ### 標準のスタイルに合わせてコードをフォーマットする プロジェクト内で一貫したコーディングスタイルを定め、それに従う。 [clang-format](http://clang.llvm.org/docs/ClangFormat.html) や [Uncrustify](http://uncrustify.sourceforge.net) などのフォーマッタを使うことにしたなら、レビュー前にそれが実行されているか確かめる。 ### スペルと文法 文法の委細にこだわる必要はないが、明らかな誤字や文法の誤りは残さないようにする。 ### xcconfig を使うならプロジェクトのビルド設定を変更しない 訳注:ビルド設定は、プロジェクト内に記述するほか、 xcconfig という外部ファイルにも書くことができる。 xcconfig を使うことにしたなら、ビルド設定は一貫してそちらに記述する。 ### 画像は正しいサイズで、最適化もかける [ImageOptim](http://imageoptim.com) などの画像最適化ツールを使う。画像が最適化されているかどうかレビュアーが判別できないときは、画像を追加した人に確認する。または、問題が起きる可能性もあるが、最適化ツールを自分で走らせてみる。 ### すべての画像に等倍と2倍の解像度のバージョンを用意する 2倍のバージョンは縦横のサイズが正しく元の2倍になるようにする。 ## テスト ### 失敗するテストがないようにする ### ビジネスロジックのユニットテスト プロジェクトにユニットテストを採用しているなら、少なくとも UI や IO に関係しない新規のコードにはテストが必要。 ### 機能テスト プロジェクトに [KIF](https://github.com/joshaber/KIF) などの機能テストを採用しているなら、 UI に関係する新規のコードにはテストが必要。 ## ドキュメンテーションとライセンス ### すべてのドキュメンテーションを現状のコードに一致させる ### 現状で依存しているライブラリのライセンスを、ソフトウェアライセンス表示画面に反映する ### Changelog を更新する 変更点を Changelog ファイルに記録しているなら、バージョン管理システムのすべてのフィーチャーブランチで、そのブランチでの変更点を Changelog ファイルに残す。 ## Cocoa コーディング ### Apple の命名規約に従う 変数やメソッドの命名は Apple の [Coding Guidelines for Cocoa](https://developer.apple.com/library/mac/documentation/cocoa/conceptual/codingguidelines/CodingGuidelines.html#//apple_ref/doc/uid/10000146-SW1) に従うべきである。 ### メソッドの間接呼び出し(デリゲートや通知)は読みやすく Cocoa にはメソッドを間接的に呼び出す手段がいくつかある。読みやすさの順に並べると: 1. 直接メソッドを呼ぶ 2. デリゲート 3. `NSNotificationCenter` 4. レスポンダチェイン 5. KVO 6. Cocoa バインディング リストの上にあるものほど読みやすいので、下のものより優先して使う。ただしメソッド間の結びつきを強くしすぎないこと。また一貫性に気を配る。 ### スレッドよりもキューを使う フレームワークがシングルスレッドからのアクセスを要求しているのでなければ、GCD や `NSOperationQueue` を使う。 ### 通知を送受信するスレッドにポリシーを設ける `NSNotificationCenter` はスレッド1つにつき1インスタンス作る。 `-defaultCenter` はメインスレッドからのみ使う。正しいスレッドで通知することを送信側の責任とする。 ### オブザーバを正しく登録・登録解除する 通知や KVO のオブザーバは必要に応じて登録し、不要になったら登録を解除する。とくに `-dealloc` の中で解除を忘れない。 ### イベントハンドラとロジックを分離する 通知のハンドラメソッドやアクションメソッドに複雑なロジックを書かない。 ### 絶対にメインスレッドをブロックしない ディスク IO やネットワーク IO 、複雑な計算処理がメインスレッドをブロックしないようにする。 ### NSStringFromEnum / NSStringFromStruct を使う (未訳、[これ](http://www.juripakaste.fi/blog/nsstringify.html)のことか) ### `-description` メソッドを書く すべてのクラスは、デバッグに役立つ情報を `-description` メソッドで返すようにする。 ### オブジェクトの生成は最小限にし、不要になったらすぐ解放する ループの中で一時オブジェクトを生成しているなら、それが本当に必要か考慮する。 `@autoreleasepool` に追加できないか検討する。 ### `int` より `enum` 、 `enum` より `NS_ENUM` 関連する整数値を列挙するなら、 `int` や `enum` より `NS_ENUM` マクロを使う。 訳注:例: ```objc typedef NS_ENUM(NSInteger, MyEnum) { MyEnumA, MyEnumB, MyEnumOther, }; - (void)foo { MyEnum myEnum = MyEnumA; } ``` 訳注: `NS_ENUM` で定義された列挙型の変数を `switch` 文で分岐すると、 `case` 節がすべての値を網羅しているかコンパイラがチェックしてくれる。 訳注:ビットマスクの定義には `int` より `NS_OPTIONS` を使うとよい: ```objc typedef NS_OPTIONS(NSInteger, MyFlag) { MyFlagNone = 0, MyFlagA = 1 << 0, MyFlagB = 1 << 1, }; ``` ### Cocoa の命名規約に従う (未訳) ### `nonnull` 属性 `NULL` でないポインタを受け取る関数には `__attribute__((nonnull))` 属性をつける。 訳注:例: ```objc void foo(void *p) __attribute__((nonnull)) { } ``` ### `objc_requires_super` 属性 オーバーライドされたとき子クラスのメソッドから呼び出されなければならないメソッドには `__attribute__((objc_requires_super))` 属性をつける。訳注:最近の SDK では `NS_REQUIRES_SUPER` も使える。 訳注:例: ```objc /*** 親クラス内 ***/ - (void)foo:(id)arg __attribute__((objc_requires_super)) { … } // または - (void)foo:(id)arg NS_REQUIRES_SUPER { … } /*** 子クラス内 ***/ - (void)foo:(id)arg { [super foo]; // これを呼ばないと警告が出る } ``` ### `pure` と `const` グローバルな状態を変更しない関数には `__attribute__((pure))` 属性をつける。グローバルな状態を参照することもない関数には `__attribute__((const))` 属性をつける。 訳注: `__pure` と `__const` も使える。 ### 国際化 ユーザーに向けて表示される文字列は `NSLocalizedString` などの仕組みで国際化する。対応する文字列を strings ファイルに追加する。 ### シングルトンには dispatch_once() シングルトンは本当に必要なときだけ使う。その場合、 `dispatch_once()` で初期化を1回だけ実行する。 ### frame と bounds frame プロパティと bounds プロパティの違いを意識し、使い分ける。訳注:[frameとboundsの違い](http://adotout.sakura.ne.jp/?p=525)などを参照のこと。 ## コード全体の品質 ### クラスの役割はシンプルに、同じコードは一箇所に クラスの責任を明確に定義する。分割すべきクラスはそうする。類似したコードが複数の場所に散らばっているなら、一箇所にまとめることを考える。 ### よい名前をつける 命名は合理的に。 ### どこにでもアサーションを使う 通知のハンドラメソッドは、期待するスレッドで実行されているかアサーションする。 `nil` にすべきでないものは、 `nil` でないことをアサーションする。他の値についても同様。 ### 共有された状態は最小限に、交換可能に 共有された状態は必要なもののみにする。ミュータブルであるべきか検討する。シングルトンよりも依存性注入を使う。 ### まず関数、次にクラスメソッド、最後にインスタンスメソッド ロジックはなるべく関数で実装する。そのほうが読みやすい。インスタンス変数にアクセスするほどではないが、クラス名を名前空間として使い一箇所にまとめたいロジックは、クラスメソッドで実装する。 ### 関数で複雑なオブジェクトを操作しない 巨大なビジネスオブジェクトを関数の引数にしない。 ### できれば簡潔に、どんなときでも理解しやすく 簡潔さは重要だが、理解しやすさはそれに増して大事。どこに線を引くか決めて、それを守る。 ### 短いが読みやすいメソッドと関数 メソッドと関数は短いほうがよいが、読みやすさと率直さを犠牲にしてはならない。 ### 必要ならコメントを書く パブリックなクラス、メソッド、関数には、それが何をするか明らかに分かるのでなければ、解説をつける。込み入った内部ロジックにもコメントを書く。 ### 不要なデバッグ出力は消す 不要になった `NSLog` は残さない。必要なデバッグ出力は、それが意味するところを分かるようにする。 ### よいエラーロギング エラーログには、どこでエラーが起き、何が問題だったのかを含めること。エラーログは簡潔かつ正確で役に立つようにする。 |
|
| 94位 |
|
|||
|
10:28:12 |
(Increments inc. 所属) |
|
続編を書きました - [Boxen使ってて許されるのは2013年だけだった](http://qiita.com/yuku_t/items/50b2b376604c2fefd8cd)
すごいすごいと話題な割に誰も使っていないと話題の[Boxen](http://boxen.github.com/)を使ってみた。 ## 3行で分かる結論 Boxenは... - Macのセットアップを自動化してくれる - 個人用途でも十分便利だが真価を発揮するのは大人数で使うとき - Puppet知らなくても案外使える ### この記事で分かるもの - Boxenの個人用途での使い方 - Boxenのチーム用途での使い方 - Puppetのmanifestの簡単な書き方 ### この記事を読んでも分からないもの - Puppetの詳しい使い方 ## Boxenを使うと何ができるのか BoxenはGitHub社が開発しているシステムで、Macのセットアップを簡単にできるようにしてくれる。新しくMacを購入したら開発環境や各種アプリケーションをインストールすることから始めるが、これをコマンド一発で全てやってくれるようになる。 ## Boxenにまつわる各種の誤解 ### Puppetを知らないと使えない PuppetとはBoxenが内部で使っているセットアップツール。似たものにChefなどがある。 BoxenではGitHub社が公開している各種resourceを組み合わせるだけでだいたいのことができる。個人用途ならこれで十分。 細かい制御をするにはPuppetについて学習する必要があるが、GitHub社が公開しているresourceのmanifestを見れば雰囲気でなんとかなる。実際私は今回Puppetを初めて使ったがなんとかなった。 (「nginxをインストールする」という設定があった場合、nginxはresourceで、resourceをインストールする手順が書いてあるのがmanifest。ここらへんはPuppet用語だから間違っているかも) ### 個人で使うには向かない Boxenはチームで使った方が便利だが、個人用途としても十分に使える。チーム用のBoxenリポジトリに各個人の設定も書ける。 ただ個人だとそう何度もマシンを一からセットアップする機会がある訳ではないので、やはり大人数で設定を共有した方が真価を発揮する。ハマりどころはだいたいいつも同じだからそこら辺を回避する設定を入れておくと捗ること間違いなし。 ### GitHub社に特化しているので使えない 確かにGitHub社が使っている環境を構築するのに便利なmanifestが充実しているが、自分の環境に流用できるものも多い。またmanifestは自作することも簡単に可能。 ## 使い方 1. 事前準備 2. boxen/our-boxenのコピーを作成 3. 設定を書く 1. Puppetfile 2. 個人設定 3. プロジェクト設定 4. チーム設定 4. Boxenを実行 ### 事前準備 - Xcodeをインストール(CLI、GUIどちらでもOK) - GitHubアカウントを持っていないなら作る 最近のMacにはgitがプリインストールされている。何かしらの理由でgitが無い場合はそれもインストールする。 あと以下を実行する。 ```sh:Terminal sudo mkdir -p /opt/boxen sudo chown $USER:admin /opt/boxen ``` ### boxen/our-boxenのコピーを作成 [boxen/our-boxen](https://github.com/boxen/our-boxen)はBoxenを使うためのひな形プロジェクト。まずはこれのコピーを作って、そこに設定を書いていく。 ```sh:Terminal mkdir -p ~/src/my-boxen #名前は好きにする cd ~/src/my-boxen git init git remote add upstream https://github.com/boxen/our-boxen git pull upstream master ``` これで~/src/my-boxenディレクトリにour-boxenのコピーが作られた。 ### 設定を書く Boxenの設定する手順は大雑把にいって2段階踏む 1. GitHub(やその他公開リポジトリ)上で公開されているresourceを指定 2. 設定ファイルで1で指定したものをincludeする #### Puppetfile 外部のresourceを使う場合はここで指定する。現在主に仕えるのは[boxen](https://github.com/boxen)にある _puppet-*_ という名前のリポジトリに入っているもの。設定自体は~/src/my-boxenにあるPuppetfileに書く。 Puppetfileには初期状態で何個か設定がある。こいつらはBoxen自体が使ったりするので消さないこと。 _puppet-*_ の中で今回は以下のものを追記した。 ```ruby:Puppetfile # Puppetfileの末尾に追記 # これはGitHub上にあるboxen/puppet-dropboxリポジトリの1.0.0 # を使うという意味。バージョン名はGitHub上で確認する必要がある。 github "dropbox", "1.0.0" github "mysql", "1.0.0" github "iterm2", "1.0.0" github "chrome", "1.0.0" github "skype", "1.0.0" github "redis", "1.0.0" github "icu4c", "1.0.0" github "imagemagick", "1.0.0" github "xquartz", "1.0.0" github "libtool", "1.0.0" github "osx", "1.0.0" ``` 例えばdropbox、skype、iterm2、chromeというのがあることからも分かるように、Boxenでアプリケーションのインストールもできる。 ここで指定したresourceをインストールするには~/src/my-boxen/manifests/site.pp内で `include` する。しかしsite.ppは全てのマシンに適応される設定を書く場所なので、基本的にはここには書かず、後述する個人設定やプロジェクト設定に書く。 もしhomebrew上に存在するがそれをインストールする _puppet-*_ resourceが提供されていない、という場合、自前でresourceを用意してもいいが、 **homebrewでインストールできる場合に限って** 簡単に書く方法をBoxenが用意しているのでそれを使ってもいい。この方法については「プロジェクト設定」で触れる。 デフォルトではNodejsとRubyがそれぞれnvmとrbenvを使って古いバージョンもインストールされるようになっているが、最新のものだけで十分なのでコメントアウトした。 `include` コマンドに渡されている `nodejs` や `ruby` は確かにPuppetfileの中に現れている。 ```puppet:manifests/site.pp # site.ppの下の方にある node default { # すべてのマシンで実行される設定 # node version #include nodejs::0-4 #include nodejs::0-6 include nodejs::0-8 # default ruby versions #include ruby::1-8-7 #include ruby::1-9-2 include ruby::1-9-3 } ``` #### 個人設定 事前準備に「GitHubアカウントを持っていないなら作る」があったことを思い出してほしい。Boxenではmodules/people/manifestsディレクトリ内に \<GitHubアカウント名\>.pp というmanifestがあったらそれを自動的に実行するようになっている。個人用途で使う場合は、ここにもりもり書く感じになる。 例えば私([taka84u9](https://github.com/taka84u9/))の場合はmodules/people/manifests/taka84u9.ppに以下のように書いた。 ```puppet:modules/people/manifests/taka84u9.pp class people::taka84u9 { # 自分の環境で欲しいresourceをincludeする include dropbox include skype include iterm2::stable #::devもある include chrome # homebrewでインストール package { [ 'tmux', 'reattach-to-user-namespace', 'tig', ]: } $home = "/Users/${::luser}" $src = "${home}/src" $dotfiles = "${src}/dotfiles" # ~/src/dotfilesにGitHub上のtaka84u9/dotfilesリポジトリを # git-cloneする。そのとき~/srcディレクトリがなければいけない。 repository { $dotfiles: source => "taka84u9/dotfiles", require => File[$src] } # git-cloneしたらインストールする exec { "sh ${dotfiles}/install.sh": cwd => $dotfiles, creates => "${home}/.zshrc", require => Repository[$dotfiles], } } ``` だいたい雰囲気で何をしているかは分かると思う。 `File` というのはfileタイプのresourceを指定するのに使う。詳しい説明はしないが、resourceの定義には全て小文字、指定するには先頭大文字になる。 `$src` のfile定義が無い、と思うかも知れないが~/srcはBoxenが使うために事前に定義しているので今回は場合は必要ない(特殊事例)。普通は以下のように書かねばならない(~/codeディレクトリの場合)。 ```puppet:modules/people/manifests/taka84u9.pp class people::taka84u9 { $code = "/Users/${::luser}/code" file { $code: ensure => derectory #これはディレクトリです } repository { "${code}/dotfiles": #こういう書き方もできる source => "taka84u9/dotfiles", require => File[$code] } } ``` 詳しくはPuppetのドキュメントを読むべし。 ##### package を使いこなす Macアプリをインストールしたい場合は次のように書く。 `source` でダウンロードURLを指定して、 `provider` は .zip なら`compressed_app` .dmg なら `pkgdmg` にする。これを書いておくだけで[Kobito](http://kobito.qiita.com/)と[XtraFinder](http://www.trankynam.com/xtrafinder/)がインストールされる。 ```puppet package { 'Kobito': source => "http://kobito.qiita.com/download/Kobito_v1.2.0.zip", provider => compressed_app; 'XtraFinder': source => "http://www.trankynam.com/xtrafinder/downloads/XtraFinder.dmg", provider => pkgdmg; } ``` Homebrew経由でインストールするには同じく `package` コマンドを使うが、インストール時のオプションを指定するには `install_options` を指定する。ここではzshを `--disable-etcdir` オプションでインストールしている。(`--disable-etcdir` については `brew info zsh` 参照) ついでに説明すると file_line はファイルに行を追加する。新しくインストールしたzshをログインシェルとして指定できるように/etc/shellsに追記する。依存関係としてはzshのインストールが済んだあとにしている。 osx_chsh は `include osx` をすることで使えるようになる便利コマンドで自分のログインシェルを変更する。file_lineがbeforeで指定しているので、osx_chshが実行される時点では既にログインシェルとして新しいzshは登録可能な状態にある。 [boxen/puppet-osx](https://github.com/boxen/puppet-osx) にはこれ以外にもosx_login_itemなど便利なのが定義されているので使いこなせると一気に幸せになれそう。 ```puppet include osx package { 'zsh': install_options => [ '--disable-etcdir' ] } file_line { 'add zsh to /etc/shells': path => '/etc/shells', line => "${boxen::config::homebrewdir}/bin/zsh", require => Package['zsh'], before => Osx_chsh[$::luser]; } osx_chsh { $::luser: shell => "${boxen::config::homebrewdir}/bin/zsh"; } ``` #### プロジェクト設定 個人用途だったらここから先の内容は不要。 Boxenの本来の目的は新しく会社に来た人や、マシンを新しくした人がさっさと開発に取り掛かれるようにすることだ。会社なら複数のプロジェクトがあり、それぞれにセットアップ方法が異なるのが普通だろう。Boxenではそれをmodules/projects/manifestsディレクトリに \<プロジェクト名\>.pp で定義する。 仮にawesomeprojectというプロジェクトがあるとする。このプロジェクトの設定ファイルはmodules/projects/manifests/awesomeproject.ppになる。 このプロジェクトではRMagickというgemを使っているのでimagemagickとlibtoolを `include` した。それに加えてRMagickをビルドするにはxzというのも必要なのだが _puppet-*_ を探してみてもそれらしきものが存在しない。だがxzはhomebrewを使ってインストールできる。 実際にインストールしてみて発覚したのだが、imagemagickをインストールして作られるdylibファイルは名前がおかしくてエラーになる。 [この記事](http://qiita.com/items/b6c7b56e060849c7509f) を参考にシンボリックリンクを貼って回避することにした。またhomebrewによってlittle-cmsというパッケージもインストールされている(imagemagickかlibtoolかxzが依存していてhomebrewによって自動的にインストールされた)のだが、こちらも同様の理由からシンボリックリンクを作成している。 最後にリポジトリの用意をする。Gitリポジトリを用意するには `repository` コマンドを使うが、 `boxen::project` コマンドという便利なものも用意されている。詳しい使い方は [ここを読む](https://github.com/boxen/puppet-boxen/blob/master/manifests/project.pp#L1-L46) ```puppet:modules/projects/manifests/awesomeproject.pp class projects::awesomeproject { # awesomeprojectで必要なものをincludeする include imagemagick include libtool package { 'xz': } # homebrewでインストール $lib = "/opt/boxen/homebrew/lib" file { "${lib}/libMagickCore.dylib": ensure => link, # シンボリックリンク target => "./libMagickCore-Q16.7.dylib"; "${lib}/liblcms2.2.dylib": ensure => link, target => "./liblcms.dylb" } boxen::project { 'awesomeproject': ruby => '1.9.3', mysql => true, redis => true, # GitHub上のtaka84u9/awesomeprojectをgit-cloneする # クローン先はデフォルトで~/src/awesomeproject source => 'taka84u9/awesomeproject' } } ``` プロジェクト設定はこのままでは適応されない。これをインストールするには個人設定でプロジェクトを `include` する。 ```puppet:modules/people/manifests/taka84u9.pp class people::taka84u9 { # owsomeprojectがインストールされる include projects::owsomeproject # allプロジェクトは全てのプロジェクトという意味 # modules/projects/manifests/all.pp参照 include projects::all } ``` #### チーム設定 個人のプロジェクトが所属しているチームに応じて決まる、という場合はチーム設定を使うと便利。 modulesディレクトリにはpeopleとprojectsというディレクトリがあるが好きに追加できる。 例えばmiraclecompanyという会社にamazingteamというチームがいるとする。そしてこのチームはawesomeprojectとsuperprojectプロジェクトを担当しているならmodules/miraclecompany/manifests/amazingteam.ppは次のようになる。 ```puppet:modules/miraclecompany/manifests/amazingteam.pp class miraclecompany::amazingteam { include projects::awesomeproject include projects::superproject } ``` そしてこのamazingteamに所属するgeniushuckerの個人設定でチームを `include` する。これでGitHubアカウントがgeniushuckerの人がBoxenを実行するとamazingteam用の環境がサクッと作られる。 ```puppet:modules/people/geniushucker.pp class people::geniushucker { include miraclecompany::amazingteam } ``` すべての人に共通する内容を記述するには、例えばmiraclecompany::environmentsを作ってmanifests/site.ppで `include` すればいい。(site.ppは全員で実行されることを思い出してほしい) ### Boxenを実行 Boxenはデフォルトでディスクの暗号化を要求する。企業で使っているのであれば基本的に行った方がいいだろうと思う。ただMacはデフォルトで暗号化を行わない設定になっているので、システム設定を開いて暗号化を許可しなければならない。英語インタフェースなら"System Preferences"→"Security & Privacy"→"FileVault"にある。 以上を行ってから下記実行。 ``` cd ~/src/my-boxen script/boxen # 暗号化を行わない場合は # script/boxen --no-fde ``` 実行すると、Gitリポジトリに変更がある状態だといろいろ言ってくるが気にしなくてもOK。しばらくすると完了する。 それができたら.zshrcや.bashrcに以下を追記する。 ```sh:.zshrc [ -f /opt/boxen/env.sh ] && source /opt/boxen/env.sh [ -f /opt/boxen/nvm/nvm.sh ] && source /opt/boxen/nvm/nvm.sh ``` あと自分のリポジトリに `git-push` する。プライベートリポジトリ推奨。 リポジトリの場所はGitHubじゃなきゃいけない訳ではない。GitHubのプライベートリポジトリが有料なので[Bitbucket](https://bitbucket.org/)に無料でプライベートリポジトリを作った。会社なら、会社のプライベートリポジトリに `git-push` する。 ```sh:Terminal ssh-keygen pbcopy < ~/.ssh/id_rsa.pub # Bitbucketに公開鍵を追加 git remote add origin git@bitbucket.org:taka84u9/my-boxen.git git push origin master ``` これで完了 ### 作ったBoxenリポジトリを使う 他のマシンで実行するには最初の方で書いた *事前準備* をして、リポジトリを `git-clone` して、Boxenを実行して、.zshrcに追記するだけ。 このとき必要なら個人設定も書く。 ```sh:Terminal sudo mkdir -p /opt/boxen sudo chown $USER:admin /opt/boxen mkdir ~/src git clone git@bitbucket.org:taka84u9/my-boxen.git ~/src/my-boxen cd ~/src/my-boxen script/boxen # 暗号化を行わない場合は # script/boxen --no-fde ``` ```sh:.zshrc [ -f /opt/boxen/env.sh ] && source /opt/boxen/env.sh [ -f /opt/boxen/nvm/nvm.sh ] && source /opt/boxen/nvm/nvm.sh ``` ## 合わせて読みたい - [大人数でBoxen使うならboxen-webが断然便利 #Boxen #Mac - Qiita](http://qiita.com/items/2f7935f924b6e6c0732d) - [Boxenをアップデートする方法 - Qiita](http://qiita.com/yuku_t/items/9312fd82a0fd291b683f) - [Boxenを実行すると何が起こるのか - ✘╹◡╹✘](http://r7kamura.hatenablog.com/entry/2013/02/22/152133) |
|
| 95位 |
|
|||
|
00:24:25 |
(Consensus Base Inc. 所属) |
|
## はじめに アホでもわかるように解説してみるテスト。 あらゆる方向で説明してみる。 大雑把にまとめると「依存していた部分を、外から注入すること」です。 勘違い、間違いが沢山ありそうなので、是非ご指摘を! ## 登場人物 (用語) - 依存性の注入 (日本語) - Dependency Injection (英語) - DI (Dependency Injectionの略語) 依存関係を設定ファイル等で定義してよろしくやってくれる「DIコンテナ」については書いておりません! ## 何が問題なの? クラス内などで固定化されたものがあると * 柔軟性がない * テストしにくい ## 解決方法 「依存している部分を外から注入する」 ## DIにおける「依存性」と「注入」の意味 - 依存性 (大雑把に)とあるクラスに、固定の定数、変数、インスタンスが入っちゃっている状態 つまりそのクラスは、その定数、変数、インスタンスに依存している - 注入 そのクラスの外から定数、変数、インスタンスをあるクラスにぶちこむこと 注意) クラスだけに限らないという話もある。けど、ほとんどの場合クラスになるので、以下もクラスにする ## つまり、何が何に依存している? とあるクラスが、固定した他の(定数、変数、クラスなど)に依存している ## どうしたいの? 依存性をなくすために、動的に動作を注入しようぜ! ってこと つまり、あるクラス内の決め打ち定数、変数、インスタンスを排除して、外から注入することで、動的に動作を変えられるようにする ## 依存していると、何が嫌なの? - 外から動的に動作を変更できないので、テストしづらい - 決め打ちなので、柔軟性がなくカスタマイズしにくい ## 具体的にどう困るの? あるクラスだけテストしたいのに - 中に別のクラスが入っているとテストしにくい - テストに時間がかかるメソッドが中にあってテスト終了に時間がかかる ## で、結局どうしろっての? - 引数で、クラスや変数を外から受け取れるようにする ## 車で例えてみる 結論から言うと、 「依存性の高い車は、特定のメーカーのパーツで固定されちゃっていて、カスタマイズできない車」 ということになる ### 依存性の高い車の場合 車が以下のパーツできている - ホンダのエンジン - ブリジストンのホイール - パナソニックのバッテリー しかし、全部のパーツが半田ゴテでくっつけちゃってあるので、他のメーカーのバッテリーに変更ができない! テストの視点から例えると、バッテリーだけの消耗テストがしたい場合 - バッテリーだけ取り出して放電、充電の繰り返しテストができない - 配線も半田ゴテでくっつけちゃっているので、車のランプをつけたり、エンジンをつけて放電・充電をしなくてはいけない! つまり、全体としてしかテストできない!! ### 依存性の低い車の場合 パーツをどのようにもカスタマイズできる車 他社のバッテリーを入れる(注入する)ことができる ### コードで説明 以下が、依存性の高い、車クラス ```java class Car { private Engine en = new HondaEngine(); private Wheel wh = new BridgestoneWheel(); private Battery bt = new PanasonicBattery(); // ... } ``` 車のパーツが決め打ちである。 Carクラスをテストしたい場合や他のEngineクラスに変更したい場合、コードを書き換えないとできないことになって面倒! 依存性の低い(注入している)コードは… 誰か書いて下さい! (他人に依存) 基本的にこんな感じでコーディングすると思います。 - Engine, Wheel, Battery クラスを作る - 上記を継承(Javaなら implements)して、各メーカーのクラスを作る (PanasonicBattery()など) - Carクラスにセッターを作る (setBattery() など) - setBattery(new PanasonicBattery()) で注入 DIなコードは、以下が参考になります。 <a href="http://ganeshtiwaridotcomdotnp.blogspot.jp/2011/05/dependency-injection-with-java-spring.html">GT's Blog: Dependency Injection with Java Spring IoC, A Complete Example</a> ## インジェクションの方法 * コンストラクター・インジェクション * セッター・インジェクション ## DIのメリット 以下の記事によるメリット <a href="http://itpro.nikkeibp.co.jp/free/ITPro/OPINION/20050216/156274/?ST=develop&P=2">(2/3)記者の眼 - Java開発を変える最新の設計思想「Dependency Injection(DI)」とは:ITpro</a> ``` ・ソフトウエアの階層をきれいに分離した設計が容易になる ・コードが簡素になり、開発期間が短くなる ・テストが容易になり、「テスト・ファースト」による開発スタイルを取りやすくなる ・特定のフレームワークへの依存性が極小になるため、変化に強いソフトウエアを作りやすくなる(=フレームワークの進化や、他のフレームワークへの移行に対応しやすくなる) ``` ## デメリット - はじめに工数がかかる場合が多いと思われる クラスをたくさん作るので(大抵の場合) - プログラムの実行スピードが遅くなる可能性が高い - クラスやファイルが沢山できる 他にもデメリットがあれば、教えて下さい ## もっと例えが欲しい? 女の子に例えると ある女の子が固定の男性に依存している場合、その依存性を排除して、外部から男の子を注入できるようにしようという願望です。 メリットとしては - 特定の男性への依存性が極小になるため、変化に強い女の子が作りやすくなります。(=男性の進化や、他の男性への移行に対応しやすくなる) ## 参考URL * design patterns - What is dependency injection? - Stack Overflow http://stackoverflow.com/questions/130794/what-is-dependency-injection * GT's Blog: Understanding Dependency Injection and its Importance, A tutorial http://ganeshtiwaridotcomdotnp.blogspot.jp/2011/05/understanding-dependency-injection-and.html * [Inversion of Control コンテナと Dependency Injection パターン](http://kakutani.com/trans/fowler/injection.html) |
|
| 96位 |
|
|||
|
14:55:08 |
(mixi 所属) |
|
 私の場合、開発環境では複数の言語のコードを書いたり実行したりします。 例えば perl, ruby, nodejs, python など。 これらを利用するために、plenv, rbenv, nodebrew, pyenv などを利用していました。 しかし、遅ればせながら [anyenv](https://github.com/riywo/anyenv) というものがあると聞いたので、こっちに乗り換えました。 **envを2つ以上使ってる人はこれ使うのが良いと思います。おすすめ。 - http://blog.riywo.com/2013/06/22/155804 - https://github.com/riywo/anyenv ### インストールは簡単 [README.md](https://github.com/riywo/anyenv/blob/master/README.md) のとおりにやればOK ```bash $ git clone https://github.com/riywo/anyenv ~/.anyenv ``` ### PATHの設定 zshrc がシンプルになりました! - https://github.com/luckypool/dotfiles/compare/501474e2411dd1cdbf8cc60e9a34165f7160ad75...master ```diff # ------------------------------------------------------------------------ -# plenv +# anyenv # ------------------------------------------------------------------------ -if [ -d ${HOME}/.plenv ] ; then - export PATH=${HOME}/.plenv/bin:${HOME}/.plenv/shims:${PATH} - eval "$(plenv init -)" -fi - -# ------------------------------------------------------------------------ -# rbenv -# ------------------------------------------------------------------------ -if [ -d ${HOME}/.rbenv ] ; then - export PATH=$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH - #export PATH="$HOME/.rbenv/bin:$PATH" - eval "$(rbenv init -)" -fi - -# ------------------------------------------------------------------------ -# nodebrew -# ------------------------------------------------------------------------ -if [ -d ${HOME}/.nodebrew ] ; then - export PATH=${HOME}/.nodebrew/current/bin:${PATH} -fi - -# ------------------------------------------------------------------------ -# pyenv -# ------------------------------------------------------------------------ -# git://github.com/yyuu/pyenv.git -if [ -d ${HOME}/.pyenv ] ; then - export PATH="$HOME/.pyenv/bin:$PATH" - eval "$(pyenv init -)" +if [ -d $HOME/.anyenv ] ; then + export PATH="$HOME/.anyenv/bin:$PATH" + eval "$(anyenv init -)" fi ``` もっと早く知っていればよかった・・・。 #### tmux を使っている場合(追記) - shims へのPATHを通さないと tmux 起動した後だと system の方をみちゃうようです - そこで下記のような差分が必要そうです - https://github.com/luckypool/dotfiles/commit/08e5af8b145e183a41fec5c9b3ecbe6e923f5d01 - 追記 - find より ls の方が良いよと[コメント](http://qiita.com/luckypool/items/f1e756e9d3e9786ad9ea#comment-5df6f301c3ad08cf749b)がありました!(Thanks, @labocho さん) - https://github.com/luckypool/dotfiles/commit/c603e436cf4fea93ccee478eda994109a05eddca - https://github.com/luckypool/dotfiles/commit/89e38defd63098cca2b3d0fc28fed751a1f9d5dc ```diff if [ -d ${HOME}/.anyenv ] ; then export PATH="$HOME/.anyenv/bin:$PATH" eval "$(anyenv init -)" + for D in `ls $HOME/.anyenv/envs` + do + export PATH="$HOME/.anyenv/envs/$D/shims:$PATH" + done + fi ``` 参考 - [tmuxでanyenv(*env)で*env/shimsがsystemのPATHより前に読まれてsystemが使われてしまう対処](http://monmon.hateblo.jp/entry/2013/12/13/233242) ### anyenv install 以下はインストールした時のメモです。 ruby, perl, python, node.js に相応する **env をインストールします。 ``` $ anyenv install rbenv $ anyenv install plenv $ anyenv install pyenv $ anyenv install ndenv ``` 初期状態は下記のようにsystemのものを指していると思います。 ```bash $ anyenv versions ndenv: * system (set by /Users/luckypool/.anyenv/envs/ndenv/version) plenv: * system (set by /Users/luckypool/.anyenv/envs/plenv/version) pyenv: * system (set by /Users/luckypool/.anyenv/envs/pyenv/version) rbenv: * system (set by /Users/luckypool/.anyenv/envs/rbenv/version) ``` ということで、おなじみ **env からインストールします。 ```bash $ rbenv install 2.1.0 $ plenv install 5.16.3 $ ndenv install v0.10.25 $ pyenv install 2.7.6 $ pyenv install 3.3.3 ``` それぞれ時間かかかりますが、ちゃんとインストールされると下記のようになります。 ```bash $ anyenv versions ndenv: system * v0.10.25 (set by /Users/luckypool/.anyenv/envs/ndenv/version) plenv: * system (set by /Users/luckypool/.anyenv/envs/plenv/version) 5.16.3 pyenv: * system (set by /Users/luckypool/.anyenv/envs/pyenv/version) 2.7.6 3.3.3 rbenv: * system (set by /Users/luckypool/.anyenv/envs/rbenv/version) 2.1.0 ``` ということで、インストールしたversionを指すようにglobalを設定します。 ``` rbenv global 2.1.0 plenv global 5.16.3 ndenv global v0.10.25 pyenv global 3.3.3 ``` 変更されましたね! ```bash $ anyenv versions ndenv: system * v0.10.25 (set by /Users/luckypool/.anyenv/envs/ndenv/version) plenv: system * 5.16.3 (set by /Users/luckypool/.anyenv/envs/plenv/version) pyenv: system 2.7.6 * 3.3.3 (set by /Users/luckypool/.anyenv/envs/pyenv/version) rbenv: system * 2.1.0 (set by /Users/luckypool/.anyenv/envs/rbenv/version) ``` ### 転ばぬ先の杖 必要に応じて確認すればいいですが、忘れがちなので、下記の確認もしておきます。 rubyのコードのためにbundlerをいれときます。 ```bash $ which gem /Users/luckypool/.anyenv/envs/rbenv/shims/gem $ gem install bundler $ which bundle /Users/luckypool/.anyenv/envs/rbenv/shims/bundle ``` perlのコードのためにcpanmとcartonをいれときます。 ```bash $ plenv install-cpanm $ cpanm Carton $ which carton $ /Users/luckypool/.anyenv/envs/plenv/shims/carton ``` 一応 pip と npm もPATHが通っているか確認します。 ```bash $ which pip /Users/luckypool/.anyenv/envs/pyenv/shims/pip $ which npm /Users/luckypool/.anyenv/envs/ndenv/shims/npm ``` ---- 以上! |
|
| 97位 |
|
|||
|
22:50:55 |
(株式会社ソニックムーブ 所属) |
|
この内容は前回の記事同様に始めたばかりの[個人ブログ](http://sakahara.hatenablog.jp/entry/2014/02/12/000341)に掲載していますが、少しでもたくさんの方に見ていただきたいので、Qiitaにも掲載することにしました。 ## 概要 アプリを開発する環境やチームは人それぞれだと思います。数人のプログラマーとデザイナーのチームで開発することもあればほとんど全てを一人でやる場合など。 特に日曜プログラマーの人でアプリを公開してみたいとなるとデザイナーの知り合いにお願いするか、お金を払ってデザインの発注するをするのはちょっと厳しい場合もあると思います。 そんな時に全てではなくともある程度のデザインを自力でする力があると非常に助かります。 私も個人でアプリ出してますが、デザインがうまくできないせいでホントによく苦労しました。そんな中で参考にさせていただいたサイトや実際につかっているアイコン集などもあるので、数は少ないですが紹介してみます。 ## おすすめサイト ・[pttrns](http://pttrns.com/) <p><span itemscope itemtype="http://schema.org/Photograph"><a href="http://pttrns.com/"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/s/sakahara/20140211/20140211235130.png" alt="f:id:sakahara:20140211235130p:plain" title="f:id:sakahara:20140211235130p:plain" class="hatena-fotolife" itemprop="image"></a></span></p> 実際にリリースされているデザインのよい様々なアプリのスクリーンショットをカテゴリ毎に見れます。デザインの参考になるアプリが見たい場合はまずこちらがお勧めです。 ・[dribbble](http://dribbble.com/) <p><span itemscope itemtype="http://schema.org/Photograph"><a href="http://dribbble.com/"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/s/sakahara/20140211/20140211235138.png" alt="f:id:sakahara:20140211235138p:plain" title="f:id:sakahara:20140211235138p:plain" class="hatena-fotolife" itemprop="image"></a></span></p> アプリ以外のデザインもありますが、ここに掲載されているアプリのデザインもとてもよいものが多いです。ここに掲載されているものは実際のアプリではなくコンセプト的なデザイン画像がほとんどです。 他にも日本のサイトでよいデザインを紹介してくれているサイトです。 ・[photoshopvip](http://photoshopvip.net/archives/57164) <p><span itemscope itemtype="http://schema.org/Photograph"><a href="http://photoshopvip.net/archives/57164"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/s/sakahara/20140211/20140211235145.png" alt="f:id:sakahara:20140211235145p:plain" title="f:id:sakahara:20140211235145p:plain" class="hatena-fotolife" itemprop="image"></a></span></p> こんなデザインがゼロからできるようになりたい!とよく思いますw 私はデザインのプロではないですし、デザインについて語れるほどの知識は持ち合わせていませんが、様々なデザインを見て実際にアプリを触ってみることで、より良い方法を知ることができます。そしてそれらを取り入れていくことが良い物を作る一番の近道だと思います。 自分でアイコン作るのも無理!という方は下記ここらのサイトからアイコンをゲットするのがよいです。有料のものが多いですが、一から描くことを考えると購入した方がかなり工数削減できます。それに加えVectorなどの様々なフォーマットに対応しているのでカスタマイズもできます。 ## アイコン等 ・[glyphish](http://www.glyphish.com/) <p><span itemscope itemtype="http://schema.org/Photograph"><a href="http://www.glyphish.com/"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/s/sakahara/20140211/20140211235151.png" alt="f:id:sakahara:20140211235151p:plain" title="f:id:sakahara:20140211235151p:plain" class="hatena-fotolife" itemprop="image"></a></span></p> ・[glyphicons.com](http://glyphicons.com/) <p><span itemscope itemtype="http://schema.org/Photograph"><a href="http://glyphicons.com/"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/s/sakahara/20140211/20140211235201.png" alt="f:id:sakahara:20140211235201p:plain" title="f:id:sakahara:20140211235201p:plain" class="hatena-fotolife" itemprop="image"></a></span></p> ## 最後に アプリ作ってる方でデザインに苦労してる方に是非参考にして頂きたいと思います。 また紹介したサイト以外にもまだまだ参考になる情報はあるので是非探してみてください。 |
|
| 98位 |
|
|||
|
22:11:20 |
(codeTakt (ex- mixi) 所属) |
|
gitで色表示が有効になっていることを前提としています。まだの方は下記のコマンドで設定してください。 ```bash: git config --global color.ui auto # 出力先が端末のときに色をつけて表示する ``` ## git diffを見やすくする ### git diff --color-words で差分を小さく表示する 通常のgit diffは行単位なので、例えば変数名を一括変更した場合見づらいです。 --color-wordsを指定すると記号やスペースで区切られた単語単位でのdiffを表示できます。gitの設定は不要です。 ```bash: git diff --color-words # 差分を単語単位で色分けで表示する ```  より細かな表示のカスタマイズも可能です。man git-diffで--word-diffを検索してみてください。 ※ただし、変更が複雑な場合は、通常のgit diffのほうが見やすいこともあります。 ### .gitattributesを設置してもっと小さく表示する .gitattributesファイルを設置することで、言語文法に基づいて変数名、関数名といった単位でdiffを表示できます ファイル設置後にgit diff --color-wordsとすると、下記のようにさらに小さく表示できます。(下記はjsの例です)  設定のために、リポジトリのルートに.gitattributesを作成する必要があります。例えば下記のような内容にします。 ```.gitattributes *.c diff=cpp *.h diff=cpp *.cpp diff=cpp *.hpp diff=cpp *.m diff=objc *.java diff=java *.html diff=html *.pl diff=perl *.pm diff=perl *.t diff=perl *.php diff=php *.py diff=python *.rb diff=ruby *.js diff=java ``` ※全てのリポジトリに適用するには下記を参照ください。 ### .gitattributesや.gitignoreを全てのリポジトリに適用する 同じフォーマットのファイルをホームディレクトリなどに置いておき、~/.gitconfigに設定しておきます。 ```bash: git config --global core.attributesfile ~/.gitattributes_global git config --global core.excludesfile ~/.gitignore_global ``` ## git statusを見やすくする ### git status --short --branchで現状をコンパクトに表示する git statusはlsよりも使う(当社比)コマンドなのに、表示が長いので、コンパクトにします。  - ``--short``:今回のキモです - ``--branch``:一番上の"##"の行にbranch名を表示します MやUの表示の代表例は下記のとおりです。 | 凡例 | 説明 | |:------|:------------------------------------------------------------------| | M\_ | 変更がgit addされている (staged) | | \_M | 変更がgit addされていない (modified) | | A\_ | 新しいファイルがgit addされている | | \_D | rmされているけどgit addやgit rmはされていない | | ?? | gitに登録されていない(untracked) | | UU | mergeするbranchも手元も変更がありconflictした (unmerged) | | DU | mergeするbranchで変更されたが手元では削除されいるのでconflictした | 左がmergeされるbranchまたはadd済みで、右がmergeするbranchまたはローカル変更であることを覚えておくと便利です。 ## git logを見やすくする (※自分が使ってる一番普通のgit logに近いコマンドは`git log --stat --decorate --find-renames`でした) ### git log --graph --decorate --onelineでGUIツールっぽくcommit/merge履歴を表示する [EGit(eclipse)](http://www.eclipse.org/egit/)や[GitX](http://gitx.laullon.com/)、[tig](http://qiita.com/tags/tig)で表示されるgit log情報は、1行に要約されてmerge関係も表示されるので、 commitの一覧を確認するためにとても便利です。これをgitコマンドでも実現できます。 ```bash: git log --graph --decorate --oneline ```  - ``--graph``:commit履歴をグラフ形式で表示します。merge関係の表示に便利です。 ※指定しない場合のlogの順番はcommit時間が新しい順になります。 - ``--decorate``:commitごとにbranch名、タグ名を表示します。 - ``--oneline``:1行で見やすい表示にします。詳細はman git-logのCommit Formattingの節を参照のこと。 ### git log -p --statで差分や変更されたファイルも一緒に見る ```bash: git log -p # 差分を表示 git log --stat # ファイルごとの変更行数を表示 ``` ## ファイル名変更を追跡して、log -pやdiffをコンパクト化する ファイル名変更差分は、通常だと全delete+全addとして表示されますが、git diffやgit log -pに-Mもしくは--find-renamesを追加すると、rename+内容の差分で表示してくれるのでコンパクトになります。 便利なことにこれをデフォルト化するオプションがあります。 ```bash: git config --global diff.renames true ``` git log --stat --find-renamesは下記のように表示されます。 ``` path/to/{file.pl => renamed_file.pl} | 21 ++++++++++++++++----- ``` git diff --find-renamesやgit log -p --find-renamesは下記のように表示されます。 ``` diff --git path/to/file.pl path/to/renamed_file.pl similarity index 82% rename from path/to/file.pl rename to path/to/renamed_file.pl index 357d760..2e18a5c 100644 --- path/to/file.pl +++ path/to/renamed_file.pl (ファイルの内容の差分) ``` 参照: http://qiita.com/kenbeese/items/31383bb77848b98161bb ## ファイル名変更を追跡して、log \[filename]を1回で済ませる 特定ファイルの履歴を見たいとき、 `git log [filename]` を使うことができますが、履歴の途中でファイル名が変更された時には履歴が途切れてしまいます。 変更前のファイル名でもう1回git logするのが定番かと思いますが、 `git log --follow [filename]` とすることで、ファイル名変更を自動で追跡することができます。 ## git diffとgit logの範囲指定を簡単にする ### git log A..Bで特定範囲のcommit履歴を表示する 例えば、下記のような使い方ができます。 ```bash: git log 1.0.0.. # version 1.0.0から現在のbranchへのcommit履歴を表示 git log master..feature # feature branchに積まれたcommit履歴を表示(masterが進んでいてもOK) ``` ### git diff A...Bでbranch全体の差分を表示する branchに積まれた差分全体のdiffを表示するために、git logでcommit番号を確認する必要がなくなります。 ```bash: git diff master...feature # feature branchに積まれた差分を表示(masterが進んでいてもOK) ``` どのファイルが変更されたのかを見たい場合は、``--name-only``をつけます。 補足: - ``git diff A..B``はAとB間で普通のdiffを表示します。 - ``git diff A``はgit diff A..HEADと等価です。 - 枝分かれしたcommit番号自体を知りたいときは、git merge-base A Bを使います。 ``git diff A...B``は``git diff `git merge-base A B`..B``と等価です。 ## 差分をテストしやすくする ### git diff --name-onlyで変更されたファイル一覧を取得する 変更されたファイルだけテストを実行したい場合、例えば下記のように実行することができます。 ```bash: prove `git diff --name-only | egrep '\.js$|\.pm$'` # perlのテストを実行 jshint `git diff --name-only | egrep '\.js$'` # jshintを実行 ``` もちろん、git addしたファイルを表示する--cachedや、git diff \[branch名\]のような指定も可能です。 ```bash: your-test-tool `git diff --name-only --cached` # commit予定の差分をテスト your-test-tool `git diff --name-only origin/master...` # origin/masterにmerge予定の差分をテスト ``` ## 上記の全部を便利にする ### .gitconfigにaliasを作成する 例えば、commit/merge履歴をグラフで表示するコマンドをgit graphにしたい場合、下記のコマンドでOKです。 ```bash: git config --global alias.graph 'log --graph --decorate --oneline' ``` 削除は下記のとおりです。 ```bash: git config --global --unset alias.graph ``` ※よく使うものは.bashrcや.zshrcのaliasにしたほうが便利です。 |
|
| 99位 |
|
|||
|
11:54:17 |
(Freelancer 所属) |
|
『[iOSアプリ開発に役立つTips](http://www.facebook.com/iOSDevTips)』という Facebook ページをやっておりまして、そこで**評判が良かった投稿**を 20 個ほど紹介します。(※ Facebook ページの insights にある「クチコミ度」の高いもの)
##アプリ実行中にコードを修正してそのまま実行中のアプリに反映させる アプリ実行中にコードを修正してそのまま実行中のアプリに反映させることができるXcodeプラグイン。 実機でもシミュレータでも使用可能とのこと。実機での動作検証中に修正して再インストールは時間かかるので、超ありがたいかも。 [Injection for Xcode](http://injectionforxcode.com/) [投稿:2013/2/4](http://www.facebook.com/iOSDevTips/posts/600082166672616) ##「PCのwebブラウザからiPhoneのカメラロールにアクセスできるようにするアプリ」のしくみ PCのwebブラウザからカメラロールにアクセスできるようにするアプリ。 アプリ側でURLを発行している画面キャプチャがあるので、**アプリにHTTPサーバ機能をもたせて**(古いけどCocoaHttpServerとか)、**アプリ側でカメラロールにアクセスしてその情報をweb経由で見せる**、というしくみでしょうか。  [新作!iPhoneのカメラロールをブラウザからアクセス出来るようにするアプリ「AirLib」を公開します | 関西/大阪のiPhone・iPadアプリ開発 feedtailor Inc. 社長ブログ](http://feedtailor.jp/wp/?p=11580) [投稿:2013/4/15](http://www.facebook.com/538949829459616/posts/572670282754237) ##イラレで書いたパスデータを、UIBezierPathのコードに変換してくれるイラレ拡張 イラレで書いたパスデータを、UIBezierPathのコードに変換してくれるイラレの拡張。 UIBezierPathでコードだけでいい感じの図形とか画像を描くのは無理があるので、これは便利かも。 [Drawscript - convert Illustrator shapes into code](http://drawscri.pt/) [投稿:2013/4/1](http://www.facebook.com/538949829459616/posts/122493027943932) ##「アプリを操作中の写真」を自動生成してくれるサービス スクリーンショットの画像をアップすると、**ハメコミ合成で「アプリを操作中の写真」を自動生成**してくれるサイト。 ちょっと手元のスクリーンショットでやってみましたがなかなかいい感じの仕上がりでした。  [PlaceIt by Breezi - Generate Product Screenshots in Realistic Environments](http://placeit.breezi.com/productshots/83) [投稿:2013/4/10](http://www.facebook.com/538949829459616/posts/367630723354164) ##コマンドラインからJSONをごにょごにょする 特にiOSアプリ開発に限った話ではないのですが、jqという、「コマンドラインからJSONをごにょごにょできる」コマンドがかなり便利そうです。 たとえば、ルート直下の要素だけを取り出したい場合は、 ```` $ jq ".[0]" hoge.json ```` "entities"要素内の"hashtags"要素内の"text"という要素を取り出したい場合は ```` $ jq ".[].entities.hashtags[].text" hoge.json ```` みたいな感じで簡単にとれて、はじめて使うAPIのレスポンスの構造を調べるときとかに活躍しそうです。 [jqコマンドが実は高性能すぎてビビッた話](http://beatsync.net/main/log20130428.html) [投稿:2013/4/26](http://www.facebook.com/538949829459616/posts/577161045638494) ##NSNumberFormatterで英語の勉強 NSNumberFormatterのいろいろなサンプル。3桁ごとにカンマ区切りにする「以外」のサンプルってあまり見つからないので参考になりました。 とくに、NSNumberFormatterSpellOutStyleがおもしろくて、たとえば、 ```` [formatter setNumberStyle:NSNumberFormatterSpellOutStyle]; NSString *formattedOutput = [formatter stringFromNumber:@129]; ```` こうやると、 > one hundred twenty-nine って出力してくれます。 これで英語の勉強アプリつくれそうです。 [Formatting Numbers – NSNumberFormatter Examples](http://iosdevelopertips.com/cocoa/formatting-numbers-nsnumberformatter-examples.html) ##iPhoneの設定から通信速度を制限する 久しぶりに設定の「デベロッパ」のとこ見てみたら、この通信速度以外にも、iAdのテスト用設定とかも追加されてました。  [iPhone5実機(iOS6.0.2)で通信速度を制限する #iOS #iPhone - Qiita](http://qiita.com/items/efd3a033ac42afd93714) [投稿:2013/2/14](http://www.facebook.com/iOSDevTips/posts/420807358006603) ##『Core Animation Programming Guide』大幅改定 『Core Animation Programming Guide』が大幅改定されたもよう。前回の更新から実に2年4ヶ月ぶり。『Animation Types and Timing Programming Guide』の内容もこっちに組み込んだとのこと。 [Core Animation Programming Guide](http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CoreAnimation_guide/) [投稿:2013/2/19](http://www.facebook.com/538949829459616/posts/299365523522567) ##CAEmitterLayer, CAEmitterCellを使ったパーティクルエフェクトの実装方法 iOS5からCore Animationに追加された、CAEmitterLayer, CAEmitterCellを使ったパーティクルエフェクトの実装方法について書きました。 [UIKit上でパーティクルエフェクトを表示する - Over&Out その後](http://d.hatena.ne.jp/shu223/20130315/1363298992) [投稿:2013/3/13](http://www.facebook.com/538949829459616/posts/217483281727841) ##コマンドラインから.ipaをつくる コマンドラインから.ipaをつくると"make adhoc"だけでできて楽ですよ、という話。 最初だけMakefileをつくるひと手間が必要ですが、curlコマンドも書いておけばビルド完了後に自動でtestflightにアップもできる、というのは便利そうと思いました。 [.ipaをサッとつくる - blog.ishkawa.org](http://blog.ishkawa.org/blog/2013/02/27/make-ipa/) [投稿:2013/3/11](http://www.facebook.com/538949829459616/posts/588602487834938) ##バッテリーの超詳細情報を取得する これを実機で実行すると、バッテリーの相当細かい情報まで取得できます。電圧までとれる。 ここまでとれるのか〜、とソースの中身みてみると、もろにプライベートAPI使ってました。。OSDBatteryっていうクラスがあるようです。 [BatteryInfo](https://github.com/lyonanderson/BatteryInfo/) [投稿:2013/4/23](http://www.facebook.com/538949829459616/posts/575521922469073) ##自然言語のテキストを属性で区分 NSLinguisticTaggerについて書きました。 自然言語のテキストを品詞(名詞、動詞、代名詞)や「個人名」「地名」といった属性で区分することができます。あと、日本語の形態素解析も可能です。  [自然言語のテキストを属性で区分する - Over&Out その後](http://d.hatena.ne.jp/shu223/20130318/1363566717) [投稿:2013/3/16](http://www.facebook.com/538949829459616/posts/551597251540891) ##Auto Layoutの詳説スライド 70ページにわたるAuto Layoutの解説スライド(日本語) Auto Layoutの基礎知識から、IBでの設定方法、ソースでの設定方法などなど。  [iOS6 Auto Layout](http://www.slideshare.net/classmethod/i-os-auto-layout) [投稿](http://www.facebook.com/iOSDevTips/posts/307662352688900) ##画像を使用せずアイコンを描画 FacebookとかTwitterアイコンのUIButtonを描画してくれるOSS。 UIBezierPathでパス描画してUIImageを生成してるので画像ファイル不使用。与えたサイズに対して相対位置で描画するように実装してあるので大きさも自在。 [GRButtons](https://github.com/goncz9/GRButtons) [投稿](http://www.facebook.com/538949829459616/posts/607527675930034) ##MacのキーボードからiPhoneに文字入力 MacのキーボードからiPhoneとかiPadの文字入力できるようになります。今設定してみたばかりだけど超便利な予感。  [MacをWinodwsのキーボードにできるアプリ、1Keyboardが理想的すぎて感動した : しょぼいエンジニアが教えたいこと](http://blog.livedoor.jp/lashow/archives/4267145.html) [投稿](http://www.facebook.com/iOSDevTips/posts/516824555023406) ##ファイルテンプレートの詳細な解説ページ Xcode4の、ファイルテンプレートまわりの詳細な解説ページ。 TemplateInfo.plistの各要素の説明とか、"___FILEBASENAMEASIDENTIFIER___"とかのテンプレート関連マクロが何に展開されるか等、とにかく徹底解説な感じです。 このあたりは**Appleの正式なドキュメントがない**ので、レア情報かと。  [Creating Custom Xcode 4 File Templates](http://rypearts.com/proof/bob/r1/2012/03/04/creating-custom-xcode-4-file-templates/) ##画面の解像度種別を返してくれるカテゴリ iPhone5の画面サイズ対応についての質問と回答。回答2つめあたりの、UIDevice+Resolutionsっていう**enumで定義した画面の解像度種別を返してくれるカテゴリ**はつくっとくと便利そう。  [iOS 6 apps - how to deal with iPhone 5 screen size? - Stack Overflow](http://stackoverflow.com/questions/12396545/ios-6-apps-how-to-deal-with-iphone-5-screen-size) [投稿](http://www.facebook.com/iOSDevTips/posts/551523098206360) ##AppStoreのレビューをスクレイピングしてDBに保存 AppStoreのレビューをスクレイピングしてDBに保存したり表示したりするツール。 保存したレビューの一覧表示だけじゃなく、★の数を集計してグラフ表示したり、頻出キーワードを抽出したりもできるとのこと。 [AppReviewViewer](https://github.com/punchdrunker/AppReviewViewer) [投稿](http://www.facebook.com/538949829459616/posts/309018215888340) ##iOSアプリ開発の細かい仕事で小遣い稼ぎ 仕事をアウトソースできるwebサービス。Skill RequiredをiPhoneに絞ると結構iOSアプリ開発まわりの仕事もでてきます。 アプリまるっと作ってくれ系は安すぎるものが目立ちますが、リンク先はGPUImageの調整をしてほしいというもので、こういう細かい仕事は小遣い稼ぎにはいいかもしれません。(この案件は既にclose)  [GPUImage adjustment | iPad | iPhone](http://www.freelancer.com/projects/iPhone-iPad/GPUImage-adjustment.html) [投稿]((http://www.facebook.com/iOSDevTips/posts/525518434137438) ##ビューにスポットライトをあてる スポットライトのように、中心をピークに、放射状に徐々に暗くするOSS。`addSpotlightInView:atPoint:` っていう感じで**スポットライトをあてるビューと位置を指定するだけ**なので直観的。 ソースを見たところ、内部実装としては、CoreGraphicsのCGGradientRefを使用して実装している。ちゃんと**add/removeのときにふわっとするアニメーション**も実装してある。  [MLPSpotlight for iOS - Cocoa Controls](http://www.cocoacontrols.com/controls/mlpspotlight) ##おわりに 今回20個ほどピックアップしましたが、日々ちまちまと投稿しているので、Facebookページの方にはこの10倍ぐらいはTipsがたまってると思われます。 **「いいね!」しておくとTipsがタイムラインに流れる**ようになるので、よろしければどうぞ! [iOSアプリ開発に役立つTips](http://www.facebook.com/iOSDevTips) |
|
| 100位 |
|
|||
|
01:56:27 |
|
|
**TestFlightへのアップロード部分の内容はAppleに買収されiTunes Connectに統合される前のTestFlightについての情報です、testflightapp.comは2015/2/26に終了です** コードを書き終わったがもう会社に行かなければならない、テストのために手持ちのデバイスにインストールしておきたい・・・。趣味でつくっているアプリこそ自動化して開発の時間を捻出すべきなのでは?そんなことを思っていたらMarvericksが無料でリリースされたので押入れで眠っていたMacBook AirにMarvericksとJenkinsを入れてリリース作業を自動化してみました。 ## 前提として * Jenkinsのセットアップは割愛しています。かわりに別の記事をあげているので参考にしてください。 * [OS XにJenkinsをHomebrewでセットアップする](http://qiita.com/makoto_kw/items/cbe93d4ebbc35f3b43fd) * Xcode5, Jenkins 1.542, CocoaPodsを使ったプロジェクトを使います * Xcodeのプロジェクト名、ワークスペース名を **MyProject** としているので読み替えて使ってください * 今回はビルドと配布に焦点をあてています、静的解析やテストに関する話はありません * TestFlightへの自動アップロード、AppStoreに提出するipaファイルの自動ビルドまでを行います ## ビルド環境のセットアップ 新しいマシンにセットアップする場合は以下を忘れないようにしておきます。 * Xcode * Xcode Command Line Tools * iPhone Developer Program証明書 * CocoaPods (使っている場合) KeyChainへのアクセスなど初回に対話が必要な設定があるのでいきなりJenkinsでビルドするのではなく、一度プロジェクトを取得して、Xcodeでビルドができるか確認しておきましょう。 ## 自動ビルドに必要なファイルのコミット ### Provisioning Profile Provisioning Profileを取得、更新しなくてよいようにリポジトリにコミットしておきます。Dropboxから参照するのもよいでしょう。ファイルは任意の場所にあってもビルド前に行うシェルスクリプトでプロジェクトディレクトリにコピーします。 ### スキーマ設定ファイル xcodebuildでワークスペースファイルを使う際にスキーマファイルが必要になります。このファイルが **Xcodeでワークスペースファイルを開いた際に生成される** ものであるため、CocoaPodsで自動生成したワークスペースをxcodebuildでビルドしようとするとスキーマファイルが見つからずにビルドが失敗します。 ``` MyProject.xcodeproj/xcuserdata/<UserName>.xcuserdatad/xcschemes/MyProject.xcscheme MyProject.xcodeproj/xcuserdata/<UserName>.xcuserdatad/xcschemes/xcschememanagement.plist ``` この問題を回避するために事前にスキーマファイルをJenkinsを実行するユーザのファイルとしてコミットしておきます。 参考: [JenkinsでiOSのビルドを行う時にハマるポイントとその解決法・1](http://qiita.com/mokemokechicken/items/5ea39c87512a515071f5) ## 使うJenkinsプラグイン 以下のものを使っています。 * [Git Plugin](http://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin) * [Git Client Plugin](http://wiki.jenkins-ci.org/display/JENKINS/Git+Client+Plugin) * [Xcode Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Xcode+Plugin) * [Testflight Plugin](http://wiki.jenkins-ci.org/display/JENKINS/Testflight+Plugin) * [Run Condition Plugin](http://wiki.jenkins-ci.org/display/JENKINS/Run+Condition+Plugin) * [Conditional BuildStep Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Conditional+BuildStep+Plugin) * [Flexible Publish Plugin](http://wiki.jenkins-ci.org/display/JENKINS/Flexible+Publish+Plugin) * [Next Build Number Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Next+Build+Number+Plugin) [Jenkins CLI](https://wiki.jenkins-ci.org/display/JA/Jenkins+CLI)を使うなら以下のようにしてインストールできます。 ``` wget http://localhost:8080/jnlpJars/jenkins-cli.jar java -jar jenkins-cli.jar -s http://localhost:8080 install-plugin \ git git-client next-build-number run-condition conditional-buildstep flexible-publish \ xcode-plugin testflight java -jar jenkins-cli.jar -s http://localhost:8080 safe-restart ``` rubyやbundlerで細かく制御するためCocoaPodsプラグインは使っておらずシェルで以下を実行します。(余談を参照) ``` pod install ``` ## ビルドの種類 Jenkinsからは1つのジョブで以下の3種類のビルドを行います。 1. Dailyビルド 1. TestFlight配布用ビルドとアップロード (テスト用) 1. AppStore提出用ビルド 1が静的チェックやテストが目的のジョブです。SCMをポーリングさせればコミットしていない日はジョブを実行しないことも可能です。 2と3は配布のためのジョブです。自分のタイミングで行うためにパラメータ付きビルドで手動で行うジョブにします。この2つはTestFlight SDKを組み込むかどうかと、Provisioning Profileを変えているため別々のビルドで成果物を作成します。 ## 使うパラメータ パラメータ付きビルドで使うパラメータは以下のとおりです。ジョブを作成したら登録してください。デフォルト値がオフなので毎日の自動ビルドでは配布に関する処理は何も行いません。 |パラメータ名|型|デフォルト値|説明| |:--|:--|:--|:--| |TESTFLIGHT_UPLOAD|真偽値|false|TestFlightへアップロードするかどうか| |BUILD_FOR_APPSTORE|真偽値|false|AppStore配布用のビルドを行うかどうか| パラメータ付きビルドで使ったパラメータの値はJenkinsのジョブの実行時に **環境変数** に設定されます。シェルなどから環境変数を参照すれば処理の分岐が可能になります。 ## ビルドスクリプトの抜粋 ### Jenkins Xcode Plugin の前処理 **ビルド手順の追加** から **シェルの実行** を追加し、以下のようなシェルの処理を Jenkins Xcode Plugin のビルドの **前** に行っています。ここで行うのは * Provisioning Profileの切り替え * TestFlight SDK の有効・無効 * CocoaPodsのインストール です。 TestFlightでAdHocに配布する場合と、AppleStoreに配布する場合とでProvisioning Profileを分けているので配布方法によってファイルを切り替えています。Jenkins Xcode Plugin には ``MyProject.mobileprovision`` といった適当なファイル名を設定しておき、その名前に元ファイルからコピーしています。 ```bash cp MyProject_Ad_Hoc_Distribution.mobileprovision MyProject.mobileprovision if $BUILD_FOR_APPSTORE = true ; then rake configure_appstore cp MyProject_AppStore_Distribution.mobileprovision MyProject.mobileprovision fi pod install ``` ``rake configure_appstore`` の処理ではTestFlight SDKを使わないようにソースファイルを変更しています。 Rakefileは以下のような内容になっています。 ```ruby:Rakefile # encoding: utf-8 require 'rake' task :configure_appstore do build_content = File.read('MyProject/MyProjectBuild.h').gsub(/(USE_TESTFLIGHT_SDK) 1/, '\1 0') open('MyProject/MyProjectBuild.h', 'w') do |f| f.<< build_content end podfile_content = File.read('Podfile').gsub(/^pod 'TestFlightSDK'/, '#\0') open('Podfile', 'w') do |f| f.<< podfile_content end end ``` TestFlight SDKをCocoaPodsで利用している前提です。あるヘッダファイルとPodfileをそれぞれ以下のように置換しています。 ``#define USE_TESTFLIGHT_SDK 1`` -> ``#define USE_TESTFLIGHT_SDK 0`` ``pod 'TestFlightSDK'`` -> ``#pod 'TestFlightSDK'`` リポジトリにコミットしてあるソースコードはTestFlight SDKを組み込んだ状態になっているので **configure_appstore** タスクを実行した場合に TestFlight SDKを使うコードをコンパイルしないように変更します。 **USE_TESTFLIGHT_SDK** で処理を分岐する話は [iOSでTestFlight SDKを使うときのメモ](http://qiita.com/makoto_kw/items/75e684435e650a4442a4) を参考にしてください。 ### Jenkins Xcode Plugin の設定 Xcodeビルドの設定に入ります。 **ビルド手順の追加** から **Xcode** を追加します。 #### ビルドの設定 **General build settings** を設定します。配布が目的なのでビルドの構成である **Configration** に ``Release`` を設定し、成果物として使う ipaファイルの名前は ``MyProject-${VERSION}-${BUILD_DATE}`` としておきます。  次に **Advanced Xcode build options** を設定します。Jenkins Xcode Pluginがわかりにくいのがこの部分です。Xcodeの場合、普通にプロジェクトを作ると、指定したプロジェクト名でプロジェクトディレクトリ、プロジェクトファイル、ワークスペースファイル、ターゲットがつくられるので何をどこで設定しているのがわからなくなります。 参考までに設定しているプロジェクトの階層を以下に示します。ここではソースコードと他のファイルを管理するために、リポジトリの1階層下にプロジェクトのファイルがあります(#1)、リポジトリ直下にファイルがない場合は **Xcode Project Directory** にプロジェクトのあるパスを設定します。 CocoaPodsが作成したWorkspaceファイル (#2) を使うので **Xcode Workspace File** にファイル名を指定しています。 **SYMROOT**に ``${WORKSPACE}/build`` (#3) を設定しているのでここにipaファイルが出力されます。 ``` . ├── MyProject # 1 │ ├── MyProject │ ├── MyProject.xcodeproj │ ├── MyProject.xcworkspace # 2 │ ├── Podfile │ ├── Podfile.lock │ └── Pods ├── build #3 │ └── Release-iphoneos │ └── MyProject-NN-YYYY.mm.dd.ipa └── README.md ```  #### 署名 署名は **Code signing & OS X keychain options** 項目で行います。 **Embedded Profile** はビルド前の処理でコピーしておいたファイルを設定します。 homebrewでインストールした場合ユーザ権限でJenkinsを動かせるので **Unlock Keychain** を有効にして **Keychain path** に ``${HOME}/Library/Keychains/login.keychain`` と **Keychain password** にパスワードを入力しておけばユーザの証明書を参照できます。  #### ビルド番号の設定 Jenkins Xcode Pluginでは **Versioning** 項目にチェックをいれるとバージョン番号を自動で設定することができます。  **Technical version** に ``${BUILD_NUMBER}`` を設定することでJenkinsのビルド番号を **CFBundleVersion** に設定できます。 Marketing version(CFBundleShortVersionString)はリポジトリで管理してCFBundleVersionはJenkinsに任せるのが良いと思います。 Jenkinsのビルド番号は **1** から始まりますが、[Next Build Number Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Next+Build+Number+Plugin) を使えば番号を飛ばすことができます。過去のビルドを削除するなどしてより大きな数値のビルドが存在しないようにすれば番号を戻すことも可能です。 ### Jenkins Xcode Plugin の後処理 **ビルド手順の追加** から **シェルの実行** をもう一つ追加し、以下のようなシェルの処理を Jenkins Xcode Plugin のビルドの **後** に行っています。 ``` BUILD_DATE=`date '+%Y.%m.%d'` cd build/Release-iphoneos if $BUILD_FOR_APPSTORE = true ; then mv MyProject-${BUILD_NUMBER}-${BUILD_DATE}.ipa MyProject-${BUILD_NUMBER}-${BUILD_DATE}_for_appstore.ipa fi ``` ``BUILD_FOR_APPSTORE`` が有効のときにファイル名のsuffixに ``_for_appstore`` をつけています。これは間違えてTestFlightを組み込んだビルドをアップロードしてしまわないように人間がファイル名で判別しやすいようにするためです。 ## TestFlightへのアップロード設定 ここからはビルド後の処理の設定です。 TestFlightへのアップロードには **Testflight Plugin** を使います。Testflight Plugin をインストールするとビルド後の処理でTestFlightへのアップロード作業を追加できます。 アップロードにはTokenが必要になるので取得しておきます。 API Token: https://testflightapp.com/account/#api TeamToken: https://testflightapp.com/dashboard/team/edit/ Tokenの設定はジョブではなく、 **Jenkinsの管理 > システムの設定 > Test Flight** で行います。チームごとに複数のTokenが設定できます。  条件付きでアップロード処理を実行させるため、 **ビルド後の処理の追加** で **Flexible publish** を使い Token を ``${ENV,var="TESTFLIGHT_UPLOAD"}`` にし、 **Action** で **Upload to TestFlight** を選択します。 これにより手動によるパラメータ付きビルドで ``TESTFLIGHT_UPLOAD`` をチェックした場合にのみ TestFlight へのUploadが行えます。  TestFlight Pluginでは高度な設定から配布時の設定が行えます。まずは **Distribution Lists** を使って自分宛に絞って送るようにしています。 Gitのコメントなどを Build Notes に設定することもできますが、コミットのコメントと知人などのテスターへ適切な説明を両立させるのはなかなか困難です。TestFlightではビルドの説明や配布するメンバーを後からでも設定できるのでまずは自分で試して問題ないことを確認してから、ビルドの説明を入力して、配布するメンバーを追加するという運用が良いと思います。  ## 成果物の設定 **ビルド後の処理の追加** で **成果物を保存** を追加し、 ``build/**/*.ipa,build/**/*dSYM.zip`` を保存するようにしておきます。  これでビルドごとのipaファイルがJenkinsからダウンロードできるようになります。 ## ジョブの実行 ジョブの設定ができたらSCMをポーリングするなどして毎日自動のビルドを走らせます。静的チェックやユニットテストはここで行いましょう。 配布するときはJenkinsにログインして手動でビルドを実行します。手動でジョブを実行しようとするとパラメータの入力を求められるので、TestFlightにアプロードするときは ``TESTFLIGHT_UPLOAD`` にチェックを入れ、AppStoreにリリースするときは ``BUILD_FOR_APPSTORE`` にチェックを入れてビルドを実行します。TestFlight SDKを組みこんだものをAppStoreに提出したくないのでこの2つのパラメータは排他的に使います。  ### TestFlightへのアップロード ジョブの設定が正しく動いていればTestFlightにアプロードは自動で行えます。実装が終わったらビルドボタンを押すだけでiOS端末でテストができるようになります。 ### AppStoreへの提出 パラメータ付きビルドで ``BUILD_FOR_APPSTORE `` を有効にしてビルドを行うと、ipa ファイルがビルドの成果物として作成されるのでこのファイルをAppStoreに提出します。 ipaファイルを指定してアップロードする場合は Xcode (Organizer) ではなく **Application Loader** を使います。Xcode のメニューの **Xcode > Open Developer Tool > Application Loader** から起動できます。 Organizerを使う場合と同じく、iTunesConnectのアプリのVersionで **Ready to Upload Binary** を押した状態になっていればアップロードが行えます。 ## 余談 * 本当は OS X Server + Bot に興味があったのだけど、CocoaPods周りが面倒そうだったので挫けました。でも諦めてはいません * どうもiOSプロジェクトでAnt, MavenやGradleを使うことに違和感を覚え、Macにはrubyが入っているし、CocoaPodsもrubyが必要なのでシェルやrakeで済ませることにしました * TestFlightでビルドを配布する上でTestFlightSDKは必須ではありません。今回はTestFlightSDKを使うビルド、使わないビルドをそれぞれ作っていますが、TestFlightSDKを使わないで同じビルドで署名だけをつけかえるだけという方法も有効です。 * 参考: iOSアプリの署名を付け替える http://qiita.com/beakmark/items/33c0b73603e491f08a33 * Jenkins上で署名できるようになったので署名に関する設定をプロジェクトファイルから削除しました(リポジトリにコミットしているのがなんか嫌だった) * いつもOrganizerを使ってAppStoreに提出していたので ipa ファイルを指定して提出する方法の調べ方がわからなかったのですが、Xcodeを使わないフレームワーク (AIR for iOSとか) 周辺のドキュメントを調べるとわかりました * 今回の内容とは関係ないですが Ruby の実行に rbenv を使っていて CocoaPods のバージョンをプロジェクトによって固定するために bundler でプロジェクトローカルにインストールしています。実際には ``pod install`` のコマンド部分は以下のような処理を実行しています。 ``` export PATH=$HOME/bin:$HOME/.rbenv/bin:/usr/local/bin:$PATH eval "$(rbenv init -)" rbenv shell 1.9.3-p392 bundle install --path=vendor/bundle bundle exec pod install ``` * Jenkinsを長時間起動していると状態がおかしくなる問題に遭遇していて定期的に再起動させています。 https://issues.jenkins-ci.org/browse/JENKINS-17526 ```xml:~/Library/LaunchAgents/homebrew.mxcl.jenkins.restart.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>homebrew.mxcl.jenkins.restart</string> <key>ProgramArguments</key> <array> <string>/usr/bin/java</string> <string>-jar</string> <string>/Users/Shared/jenkins-cli.jar</string> <string>-s</string> <string>http://localhost:8080</string> <string>safe-restart</string> </array> <key>StartCalendarInterval</key> <dict> <key>Hour</key> <integer>15</integer> <key>Minute</key> <integer>00</integer> </dict> </dict> </plist> ``` ``` wget http://localhost:8080/jnlpJars/jenkins-cli.jar /Users/Shared/jenkins-cli.jar launchctl load ~/Library/LaunchAgents/homebrew.mxcl.jenkins.restart.plist ``` |
|
| 101位 |
|
|||
|
06:53:08 |
(株式会社Abby 所属) |
|
# 新言語 Hack とは
こんにちは、新言語 Hack が Facebook より OSS としてリリースされましたね。 詳細は以下の通り。 https://code.facebook.com/posts/264544830379293/hack-a-new-programming-language-for-hhvm/ 新言語 Hack は **HHVM 向けの開発言語で静的型付き言語の性質を取り入れ PHP での素早い開発を実施し、PHPとの互換性があります** http://hacklang.org/ http://hhvm.com/ HHVM (HipHop Virtual Machine for PHP) とは、Facebook が OSS で開発している PHP 用の JIT コンパイラです。 HHVM は PHP のコードをダイナミックにバイナリコードへと変換することで高速実行の実現を目指しています。 これは、PHPer にとって最強 Hacker になれる大チャンスです! HipHop 大好きエンジニアには持ってこいではないでしょうか。 ------ 2017年5月追記 本記事は2014年のものです。2017年3月時点で `Hack` 本を書きましたので、最新情報はそちらも参考にしてください。 [プログラミングHHVM Hack 執筆しました](http://yone098.hatenablog.com/entry/2017/05/22/160926) <img src="http://b.hatena.ne.jp/entry/image/http://yone098.hatenablog.com/entry/2017/05/22/160926" title="はてなブックマーク - プログラミングHHVM Hack 執筆しました" alt=""> ------ = *追記* HHVM Hackをすぐに試せるサーバをAWS AMIを公開しましたので、こちらを参考にすぐに動かして試して下さい http://qiita.com/yone098@github/items/c996f5fd8a25af9b1ce3 = エンジニアの皆さんなら、コードを見た方が早いですね。 Hack のコードは以下のような感じになります。 今まで `<?php` だったものを `<?hh` とするだけでgenerics、Nullable Type、Collection, Lambda, Async, Await, Tuple, Override Attribute, Type aliasing などが利用出来るようになります。 つまり、互換性があるため既存のPHPのシステムを全てHHVMに乗り換える事も出来ます。(PHPファイルの先頭を一括置換するだけ) ```php:mopemope.hh <?hh // generics class Mopemope<T> { public function __construct(private T $data) {} public function get(): T { return $this->data; } public function set(?T $x): void { $this->data = $x; } } function generics_test(): Mopemope<string> { $x = new Mopemope("松原!"); return $x; } function apply_int<T>((function(int): T) $callback, int $value): T { return $callback($value); } $func = function(int $num) : string { return "mopemope is " . $num . " years old."; }; var_dump(apply_int($func, 28)); // newtype を宣言 newtype user_name = string; function get_user_name(): user_name { return "yone098"; } // 引数は user_name 型しか受け付けない function get_user_id(user_name $name) : int { // user_name で DB から検索処理など $user_id = find_by_user_name($name); return $user_id; } // 以下は型が違うためエラー // get_user_id(12345); echo(get_user_id("mopemope") . "\n"); ``` `$ hhvm mopemope.hh` で実行します。 なんだか PHP4 を触っていた人には別言語に見えますね。 # Hack を実際に動かす 軽くコードだけみてだいたいは分かるとは思いますが、せっかくなので実際にインストールして動かしてみましょう。 ## install 私は macosx にインストールを試みましたが、以下にあるように正式にサポートされてないのと、issueと同様のエラーが出てインストール出来なかったので ubuntu 13 で試しました。 `Mac OS X and FreeBSD are not officially supported or tested but for those who want to experiment or help out:` インストールはこちら https://github.com/facebook/hhvm/wiki#installing-pre-built-packages-for-hhvm ### ubuntu 13 install 私は ubuntu 13 にインストールを行いました。 === *注意 2014/4/2 追記* この記事はHHVM2.4であり最新版HHVM3.0.1は`sudo apt-get install hhvm`だけでインストール出来ます。 HHVM Hack(HHVM3.0.1)をインストール済み環境のAWS AMIを用意しましたので、そちらで手軽に試して下さい。 http://qiita.com/yone098@github/items/c996f5fd8a25af9b1ce3 またHHVM3系からは、hhvmビルドインのwebserverは使用出来なくなりましたので、webserver fastcgt hhvmの構成で使用します。 設定ファイルも、hdfではなく`php.ini`と`server.ini`に変わっていますのでご注意ください。 === ``` $ sudo apt-get install autoconf automake binutils-dev build-essential cmake g++ git \ libboost-dev libboost-filesystem-dev libboost-program-options-dev libboost-regex-dev \ libboost-system-dev libboost-thread-dev libbz2-dev libc-client-dev \ libc-client2007e-dev libcap-dev libcurl4-openssl-dev libdwarf-dev libelf-dev \ libexpat-dev libgd2-xpm-dev libgoogle-glog-dev libgoogle-perftools-dev libicu-dev \ libjemalloc-dev libmcrypt-dev libmemcached-dev libmysqlclient-dev libncurses-dev \ libonig-dev libpcre3-dev libreadline-dev libtbb-dev libtool libxml2-dev zlib1g-dev libevent-dev \ libmagickwand-dev libxslt1-dev ocaml-native-compilers $ mkdir dev $ cd dev $ export CMAKE_PREFIX_PATH=`pwd` $ git clone git://github.com/facebook/hhvm.git $ cd hhvm $ git submodule init $ cd ../hhvm $ git submodule update --init $ cmake . $ make ``` ビルドには少々時間がかかりますが HipHop でも聞きながら気長に待って下さい。 ビルドが成功したら、`hphp/hhvm/hhvm`フォルダにPATHを通して下さい。 正しくインストールされたかは hhvm コマンドを実行して確認出来ます。 ```yone098$ hhvm --version HipHop VM 3.0.0-dev (rel) Compiler: heads/master-0-g5b00837d9b2ecc28e6225336296be88449637b2e Repo schema: b602fe3a78ec9eec7b65ec874110b9323ceabf88 ``` ## 実行方法 実行方法は2種類あります。 hhvm コマンドで hack ファイルを指定するものと、ビルドインされている webserver を起動させる方法です。 `hhvm your_hhvm_file` `sudo hhvm -m server -c config.hdf` これであなたも HipHop な Hacker の仲間入りです。 # Hack の特長 それではサンプルコードを見ながら Hack の特長を見てみましょう。 ## Type Annotations Hack の静的型付け。関数の引数や戻り値に明示的に型を指定出来ます。 ```php:garsue.hh <?hh // 引数と戻り値は bool function garsue(bool $x): bool { return !$x; } $ret = garsue(true); var_dump($ret); // 型が異なるためにエラー $str = "hoge"; garsue($str); ``` 引数に文字列を指定して実行するとエラーが発生します。 `yone098$ hhvm garsue.hh bool(false) HipHop Fatal error: Argument 1 passed to garsue() must be an instance of bool, string given in /home/yone098/1.hh on line 5` ## Nullable null を許容するかどうかを ? で指定出来ます。 少し実用的なコードのサンプルです。 ```php:garsue.hh /////////////////////////////////////////////////////// // ## Nullable Types // ? 付きでNullable interface mohikan { public function say(): string; } class mopemope implements mohikan { public function say(): string { return "コード書け!"; } } // $mohikan に null が渡ってくる場合があるのでチェック function say_args_null_ok(?mohikan $mohikan): string { if ($mohikan !== null) { return $mohikan->say(); } return "mohikan が null です。"; } // 引数 $mohikan が null なら実行されない function say_args_null_ng(mohikan $mohikan): string { return $mohikan->say(); } function test_nullable_type(): void { // null を指定 say_args_null_ok(null); // mopemope インスタンスを指定 $mopemope = new mopemope(); say_args_null_ng($mopemope); // null 指定でエラー say_args_null_ng(null); } test_nullable_type(); ``` 実行すると、say_args_null_ng 関数の引数に null を指定しているところでエラーが発生します。 `yone098$ hhvm garsue.hh HipHop Fatal error: Argument 1 passed to say_args_null_ng() must be an instance of mohikan, null given in /home/yone098/garsue.hh on line 39` ## Collections Collection として Vector, Set, Map, Pair が用意されています。 Collection には generics で明示的に型が指定出来ます。 filter 関数も用意されていますので実際にコードを動かして試してみて下さい。 関数の引数に指定する際に `Map<string, int>` や `Map<string, MyClass<string>>` のような指定で可能です。 例) `function mope(Map<string, Map<string, Vector<int>>> args): void { };` ```php:garsue.hh /////////////////////////////////////////////////////// // Collections function test_collections(): void { // Vector echo "Collections Vection<T>\n"; $vector = Vector<int> { 100, 200, 300, 400 }; $vector[] = 700; // 追加 $vector[0] = 111; // 上書き echo "vector->get(1):" . $vector->get(1) . "\n"; var_dump($vector); $vector->removeKey(2); foreach ($vector as $v) { echo $v . "\n"; } // filter $v_filter = $vector->filter(function($v) { return 150 <= $v; }); foreach($v_filter as $key => $val) { echo "key:" . $key . "=>" . $val . "\n"; } // Map echo "Collections Map<K, V>\n"; $map = Map<string, long> {"a" => 100, "b" => 200, "c" => 300}; $map["d"] = 400; echo "map[0] = " . $map["a"] . "\n"; // get echo "map->get(\"b\") = ". $map->get("b") . "\n"; // contains var_dump($map->contains("a")); // map filterWithKey $map_filter = $map->filterWithKey(function($k, $v) { return ($k === "b" || $k === "c"); }); var_dump($map_filter); } test_collections(); ``` ### 実行結果 ``` Collections Vection<T> vector->get(1):200 object(HH\Vector)#1 (5) { [0]=> int(111) [1]=> int(200) [2]=> int(300) [3]=> int(400) [4]=> int(700) } 111 200 400 700 key:0=>200 key:1=>400 key:2=>700 Collections Map<K, V> map[0] = 100 map->get("b") = 200 bool(true) object(HH\Map)#6 (2) { ["b"]=> int(200) ["c"]=> int(300) } ``` ## Override PHP における override は継承元クラスと同名のメソッドを実装することにより実現しますが typo に気付きづらい部分があると思います。 hacklang の `<<Override>>` にはそれを解決するものだと思います。 `say_hello` メソッドを `sya_hello` と override しても親のメソッドがコールされて継承したクラスのメソッドが呼ばれない事に気がつきにくいと思います。 hacklang の場合は、`<<Override>>` してメソッドをコールした際にメソッドが名が異なっている場合にはエラーが発生します。 ```php:garsue.hh /////////////////////////////////////////////////////// // Override class Yone { public function yone_098(): string { return "yone_098"; } } class Yone098 { // override なのに親メソッドと異なるメソッド名 <<Override>> public function yone_123(): string { return "yone_123"; } } function test_override(): void { echo "call test_override()\n"; $yone = new Yone098(); echo "yone->yone_098() : " . $yone->yone_098() . "\n"; } test_override(); ``` ### 実行結果 ``` call test_override() Fatal error: Call to undefined method Yone098::yone_098 from anonymous context in /home/yone098/hhvm_work/garsue.hh on line 115 ``` ## Type Aliasing type aliasing ではC言語でのtypedefのような機能があります。 ```php:garsue.hh /////////////////////////////////////////////////////// // Type Aliasiling type mopeInt = int; function sum(mopeInt $v): int {} newtype yone_name = string; // 引数は yone_name 型しか受け付けない function foo(yone_name $v): void {} newtype mopePoint = (long, long); function test_point(mopePoint $v): void {} ``` ## Async async の signature example。 ```php:async_sample.hh <?hh async function genFoo(): Awaitable<Foo> { return new Foo(); } async function cached_result<T>(T $x): Awaitable<T> { return $x; } async function gen_void(): Awaitable<void> { return; } async function gen_add(Awaitable<int> $genA, Awaitable<int> $genB): Awaitable<int> { list($a, $b) = await genva($genA, $genB); return $a + $b; } class Preparable<T> implements Awaitable<T> { ... } class MyPreparable extends Preparable<MyPreparable> { ... } ``` # まとめ この他にも、Traits, Lambda, Tuples, Typing XHP などの機能があり今PHPで開発している現場にいれてもいいのではないかと思います。 これから簡単なウェブアプリケーションを作ってパフォーマンスを計測してみようと思います。 皆さんも hacklang で素敵な PHPライフをお過ごしください! そして最強の PHPer になりましょう! |
|
| 102位 |
|
|||
|
11:54:52 |
|
|
こんにちは、@cutmailです.
365日Xcode触ってますか? # はじめに Xcodeはそのまま使ってもいいですが、いくつかの便利なプラグインを入れてみるとより開発がしやすくなるかもしれません。 プラグインは入れ過ぎるとXcodeが重くなったりするかもしれませんので、必要なもののみインストールしたほうがよいかと思われます。 ## [XVim](https://github.com/JugglerShu/XVim) Vimmerのための、XcodeでVimのキーバインドが使えるようになるプラグイン https://github.com/JugglerShu/XVim ## [XTodo](https://github.com/trawor/XToDo) `TODO`や、`FIXME`などのコード内の注釈を抜き出して表示してくれるプラグイン  https://github.com/trawor/XToDo ## [XAlign](https://github.com/qfish/XAlign) コードを自動的に整列するプラグイン  https://github.com/qfish/XAlign ## [ShowInGitHub](https://github.com/larsxschneider/ShowInGitHub) 選択した行をGitHubで表示してくれるプラグイン  https://github.com/larsxschneider/ShowInGitHub ## [KFCocoaPodsPlugin](https://github.com/ricobeck/KFCocoaPodsPlugin) Xcode上でCocoaPodsライブラリの管理ができるプラグイン。 補完などもしてくれる。  https://github.com/ricobeck/KFCocoaPodsPlugin ## [SCXcodeMiniMap](https://github.com/stefanceriu/SCXcodeMiniMap) XcodeでSublime Text 2のようなMiniMapを表示するプラグイン  https://github.com/stefanceriu/SCXcodeMiniMap ## [Dash-Plugin-for-Xcode](https://github.com/omz/Dash-Plugin-for-Xcode) XcodeからDashで検索できるプラグイン https://github.com/omz/Dash-Plugin-for-Xcode ## [Lin-Xcode5](https://github.com/questbeat/Lin-Xcode5) NSLocalizedStringを補完したり、簡単に編集できるプラグイン  https://github.com/questbeat/Lin-Xcode5 ## [KSImageNamed-Xcode](https://github.com/ksuther/KSImageNamed-Xcode) UIImageのファイル名を補完してくれるプラグイン  https://github.com/ksuther/KSImageNamed-Xcode ## [HOStringSense-for-Xcode](https://github.com/holtwick/HOStringSense-for-Xcode) 文字列を簡単に編集できるようになるプラグイン。 `\n`はちゃんと改行として認識される。  https://github.com/holtwick/HOStringSense-for-Xcode ## [AutoresizeMask-for-Xcode](https://github.com/garnett/AutoresizeMask-for-Xcode) ViewがどのようにAutoresizeされるか視覚的に表示してくれるプラグイン  https://github.com/garnett/AutoresizeMask-for-Xcode ## [Alcatraz](http://alcatraz.io/) Xcode上でプラグインやColor Schemeやテンプレートなどを管理できるパッケージマネージャ  http://alcatraz.io/ ## [XcodeColors](https://github.com/robbiehanson/XcodeColors) コンソールに出力する際に色を付けることができるプラグイン  https://github.com/robbiehanson/XcodeColors |
|
| 103位 |
|
|||
|
21:39:45 |
(私立リリアン女学園高等部 所属)
|
|
『カリー化』(Currying) という概念をご存知でしょうか。"Curry" は食べ物のカレー(Curry)と同じスペルですが、ここでいう Curry はそれとは別のもので、多くのプログラミング言語に応用できるかもしれない、とても便利かもしれない概念です。
## では教えてくれ。”カリー化”とはなんのことだ? 『ふたつの引数のうち、大きい方の数を返す』という機能を持った関数 `max` を実装したいとしましょう。そのようなとき、大抵は次のように定義すると思います。 ```js function max(x, y){ return x > y ? x : y; } ``` しかしながら、次のように定義した別の関数 `_max` でも同じような機能を実現することができます。 ```js function _max(x){ return function(y){ return x > y ? x : y; } } ``` この関数 `_max` も『大きい方の数を返す』という機能を持っていますが、`_max` は `max` とは呼び出し方が少し異なります。たとえば、`_max` で 1 と 2 のうち大きい方を求めるには、 `_max(1)(2)` のように書きます。 `_max` が通常の `max` と異なるところは、関数を呼び出すときに引数をひとつづつ渡すようになっているところです。引数をひとつ渡すと、『引数をもう一つわたすと、最初に渡した引数と今渡された引数のうち大きいほうを返す関数』を返すようになっているのです。括弧が増えるぶんソースコードはいくらか長くなりますが、呼び出し方が異なるだけで目的とする機能が実現できていることには違いはありません。このように、『2引数以上の関数を、1引数の関数の定義だけで同じ機能を持つように定義を書き換えること』を **カリー化** といいます。もちろん2引数に限らず、引数の数がそれ以上であってもカリー化することは可能です。 ## カレーがシャッキリポンとコード上で踊るわ! さて、わざわざこんな妙な関数の定義をして、いったい何が嬉しいんでしょうか?プログラミングにおけるカリー化のメリットのひとつとして、 **カリー化された関数に引数を渡すだけで、べつの機能を持つ関数を簡単に作り出せる** 、というものがあります。 たとえば、配列 brightness に画像の各ピクセルの明るさが格納されていて、いくつかの画像処理の結果、要素に負の数の要素が現れるようになったとしましょう。明るさは 0.0~1.0 の範囲で、明るさが 0 より小さくなってはまずいので 0より小さい値は 0 に変えたいとします。配列のすべての要素に関数を適用する関数 [Array.prototype.map](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map) を利用して実装することにすると、`map` に『0より小さい値は 0 に変える』というような関数を渡せばいいでしょう。 でもそのような関数はないので、無名関数を使って次のように定義することになると思います。 ```js var brightness = [0.2, -0.3, 1.0, -0.5]; brightness = brightness.map(function(x){ return max(0, x) }); ``` 上のコードの `function(x){ return max(0, x) }` という式は、『引数をもう一つわたすと、0 と今渡された引数のうち大きいほうを返す関数』という関数を表しています。そういえば、先ほど定義した `_max` は、引数を渡すと『引数をもう一つわたすと、最初に渡した引数と今渡された引数のうち大きいほうを返す関数』を作ってくれる関数でした。それなら、`_max(0)` は『引数をもう一つわたすと、0 と今渡された引数のうち大きいほうを返す関数』を返してくれるはずです。つまり、無名関数を使わずとも `_max` を使うと次のように書きなおせることがわかります。 ```js var brightness = [0.2, -0.3, 1.0, -0.5]; brightness = brightness.map(_max(0)); ``` [JSFiddle で見る](http://jsfiddle.net/TvqcF/) function 式もなくなって、とてもシンプルになりました!このように、関数リテラルで `function(x){ return max(0, x) }` のような面倒な記述をしなくても、カリー化された関数なら引数を渡すだけで別の関数を簡単に作り出せるのです。 ## なるべく手をかけず簡単に。これが美味しいカレーを作るこつです さて、先程は `max` を手作業でカリー化された形式 `_max` を定義しなおしたわけですが、いくらカリー化された関数にメリットがあるとしても、関数を定義するたびに先ほどのように function 式を入れ子にして書くのはなかなか面倒です。なんとか関数を簡単にカリー化できないものかと、カリー化を行う関数 `curry` を次のように定義してみました。 ```js function curry(f){ return function _curry(xs){ return xs.length < f.length ? function(x){ return _curry(xs.concat([x])); } : f.apply(undefined, xs); }([]); } ``` この関数 `curry` を使えば、さきほどの `max` のような関数や、一部の標準ライブラリ関数などを簡単にカリー化することができます。たとえば、次のように標準ライブラリの `Math.max` をカリー化することで `_max` を定義できます。 ```js var _max = curry(Math.max); ``` したがって、先ほどの画像処理のプログラムも、`curry` を使えば次のように書くことができます。 ```js var brightness = [0.2, -0.3, 1.0, -0.5]; brightness = brightness.map(curry(Math.max)(0)); ``` [JSFiddle で見る](http://jsfiddle.net/HC4Lc/) ## このカレーはできそこないだ。食べられないよ さて、カリー化されていない関数でも `curry` に通すだけで簡単にカリー化できるようにはなりましたが……果たしてコレ、役に立つんでしょうか?既存の関数はもちろんカリー化されていないので、map や filter のような関数にはいちいち curry を呼び出してから渡す必要があります。記述は多少短くなったものの、`curry` を呼ぶ手間に関してはどうしようもありません。また、場合によっては `curry` の呼び出しやカリー化された関数の呼び出しのオーバーヘッドも無視できません。カリー化のメリットが最も発揮されるのは、 1. 通常の方法で定義された関数は、すべてカリー化されている 2. 関数適用の構文がシンプル 3. 高階関数を多用する API が用意されている という条件が揃った時だと思います。言語そのものにカリー化の機能があればとても便利ですが、そうでない言語において `curry` のような関数を持ち込んでも、結局のところ `curry` の呼び出し自体を取り除くことはできませんし、あまり標準的なコーディングでない方法を持ち込むことで他の人にコードが読みにくいと言われるかもしれません。多少コードが冗長になったとしても、カリー化しないで標準的な function 式を使うコーディングを使ったほうがいいかもしれません。 じゃあカリー化がちゃんと活躍している言語にはどんなものがあるかというと、Haskell や OCaml が挙げられます。Haskell では先程の画像処理のプログラムを次のように書くことができます。 ```hs brightness = [0.2, -0.3, 1.0, -0.5] brightness' = map (max 0) brightness ``` このとき、 1. `max` は Haskell 組み込みの関数で、最初からカリー化された形式で定義されています。関数を普通に定義すると、それは自動的にカリー化された関数になります。 2. 関数適用の構文は、`max 0 1` のように関数の後ろに空白で区切って引数を書くだけなので、普通に呼び出しても括弧だらけになったりしません 3. リストの各要素に関数を適用して別のリストに写す `map` を始め、このような高階関数(関数を引数にとったり関数を返す関数)がごく当たり前の手法として用いられています。for 文などは最初から存在しません。 これを考えると、JavaScript にカリー化を持ち込んで味わえる旨味というのは、Haskell で味わえる旨味には及ばないと言わざるを得ないのです。残念……。 ## このカレーを作ったのは誰だあっ!! カリー化(Currying) の "Curry" は食べ物のカレーとスペルがまったく同じですが、カリー化は食べ物とは関係なくて、この辺りの分野に大きな業績のある論理学者・数学者のハスケル・カリー(Haskell Curry)さんの名前に由来します(そうです、Haskell もこのひとの名前に由来しています)。でもWikipedia によれば、最初にカリー化の概念を示したのは Moses Schönfinkel さんとゴットロープ・フレーゲさんだそうな。自分は Schönfinkel さんは失礼ながらご存じあげないのですが、フレーゲさんは論理学者としてとても有名な方ですよね。カリー化はプログラミング特有の概念というわけではなく、もともと数学で関数の振る舞いを調べるときに便利なように考案されたものみたいです。 ## やれやれ。こんな部分適用をカリー化と言っているようじゃ、ほんとに理解しているのか怪しいもんだ 複数の引数をもつ関数に、一部だけ引数を渡して呼び出すことを関数の **部分適用** といいますが、カリー化は関数の部分適用とよく混同されるようです。一部の言語において実際には部分適用を行う関数に何故か `curry` という名前を付けてしまったものが存在し、それがさらに混乱を招いているということもあるようです。確かにカリー化と部分適用は非常に近しい関係にありますが、あくまで両者は似て非なるものです。せっかくですので、カリー化と部分適用の違いについて正確に理解しておきましょう。 自然言語でいろいろと説明を尽くすのも大事なことですが、正確な理解のためには形式的な定義に勝るものはありません。カリー化を形式的にいうと、次のようになります。簡単のためここでは2引数の関数についてのみ述べますが、3引数以上の関数でも同じように議論できます。( **数式を読むのが面倒臭いひとは、結論だけ読んでください** )  また、  関数 `f` を `curry` に適用するだけでカリー化された関数 `g` に変えてくれるわけですね。 それに対し、部分適用はというと、  この操作 `partial(f, x)` を関数の **部分適用** といいます。関数 f に x を部分適用した式 `partial(f, x)` は、残りの引数を渡すと普通に `f` にすべての引数を渡したのと同じように計算してくれます。あくまで `f` は2引数の関数ですが、そのうち最初の 1 つの引数だけを適用しているようになっているので『部分』適用と呼ぶわけです。 最後に、カリー化関数 `curry` と部分適用関数 `partial` の関係を形式的に表してみましょう。  『f に x を部分適用する』というのは、『 f をカリー化してから x を適用する』のとおなじだ、というわけです。したがって、『f に x を部分適用する』というのは、『 f をカリー化する』のとは異なります。これで部分適用 `partial(f, x)` とカリー化 `curry(f)` の違いがおわかり頂けたのではないでしょうか。 ## 参考 * [JavaScript でカリー化 - Qiita](http://qiita.com/items/e099910c11c4be5cd651) * [JavaScriptでカリー化 - 檜山正幸のキマイラ飼育記](http://d.hatena.ne.jp/m-hiyama/20051213/1134446855) * [JavaScriptでカリー化、再び - Days on the Moon](http://nanto.asablo.jp/blog/2008/02/14/2626240) * [カリー化 != 部分適用](http://d.hatena.ne.jp/kmizushima/20091216/1260969166) * [美味しんぼ-名言集-](http://www.page.sannet.ne.jp/d-rigel/oisinbomatome2.htm) |
|
| 104位 |
|
|||
|
21:27:24 |
(Consensus Base Inc. 所属) |
|
技術選定のためや、俺が問題解決するぜっ!的な人向け。 順次解決されると思うので、順次更新します。 以下に上げたものも、解決策があるものが多いです。 ## はじめてのNode.js (2013年3月26日初版) * どこか1か所CPUリソースを多く消費するような重い処理が入ると、全体のパフォーマンスが低下する * マルチコア/マルチCPU環境を十分に生かすことができない * コールバックを多用するためにコードが複雑になる ## merittyの記事 (2012年12年23日) [Node.jsのメリットとデメリット | meritty [メリッティ]](http://www.meritty.com/titles/detail/52 "Node.jsのメリットとデメリット | meritty [メリッティ]") > * JavaScriptの限界、オブジェクト指向が不完全 > * マルチコアサーバで性能を十分に発揮できない > * 文法エラーが、サーバーの停止を引き起こす > * あるリクエストに問題があると、他のリクエストをブロックする ## ZEALOT社員の方 (2012年10月29日) 引用: [Node.jsについて個人的なまとめ | Z BLOG | ZEALOT社員ブログ](http://blogs.zealot.co.jp/archives/30 "Node.jsについて個人的なまとめ | Z BLOG | ZEALOT社員ブログ") > * まだ商用で使うには不安定な部分がある。 > * 開発者が少ない。 : JavaScript開発者がNode.jsの開発者ではない。 > * メジャーでもない。 > * 非同期処理なのでエラーハンドリングが難しい。 > * 基本的にシングルプロセスなのでどこかでシステムエラーが発生するとサーバダウン。 > * Hot deployができない。 ## GREE CTO藤本さん (2012年8月) 引用: [GREEが悩むNode.jsの問題を考えるヒント - ぼちぼち日記](http://d.hatena.ne.jp/jovi0608/20120809/1344474864 "GREEが悩むNode.jsの問題を考えるヒント - ぼちぼち日記") > 1. ひたすらすごい勢いでバージョンアップしているので安定しない。コストを払ってついていく覚悟を持って取り組んでいる。 > 2. メモリリークがあるので、サーバを起動しっぱなしにするとメモリが食いつぶされる。 > 3. コードをデプロイしても再起動しないと読み込まれない。 解決策は、上記リンク先に書いてあります。 ## IIJ > node-v0.6からはクラスターモジュールが導入され、Node.jsがマルチプロセスで動作するようになりましたが、マルチプロセスを意識してプログラミングの設計をする必要があります。 > 玉石混淆のユーザモジュール [注目のサーバサイドJavaScript実行環境「Node.js」 | 最新の技術動向 | IIJ](http://www.iij.ad.jp/company/development/tech/activities/nodejs/ "注目のサーバサイドJavaScript実行環境「Node.js」 | 最新の技術動向 | IIJ") ## デブサミ (2012年2月) [ソーシャルアプリのインフラはNode.jsが主役になるか~デブサミまとめレポート(ソーシャル&インフラ編) - @IT](http://www.atmarkit.co.jp/fsmart/articles/devsummit2012_01/01.html "ソーシャルアプリのインフラはNode.jsが主役になるか~デブサミまとめレポート(ソーシャル&インフラ編) - @IT") > 例えばNode.jsはI/Oや大量データ処理には強いものの、シングルスレッドでCPUパワーを使うようなタスクには弱い。そこで、結果はできるだけキャッシュするなどしてCPUリソースを節約する工夫が必要になる。HTMLのテンプレートやレンダリングなどはCPUリソースを消費するので、クライアントに処理を委譲してしまう対策も有効だという。 > データアクセスに関しては、Node.jsの処理は非常に高速だが、その分リクエスト回数が増えてDBに負荷を掛けることになる。そこで、制約をアプリケーションレベルで保持することによってDBの処理を低減しているという。 > 逆にNode.jsは、数値解析などプロセッサ処理が中心的となるような処理に対しては不適切 > 定期的に発生するV8エンジンのGC(ガベージコレクション ## ブログ記事 (2011年2月20日) [node.jsは普及しそうに無い: ニュースの社会科学的な裏側](http://www.anlyznews.com/2011/02/nodejs.html "node.jsは普及しそうに無い: ニュースの社会科学的な裏側") >1. イベントループ・モデルで処理を直列化しているので、あるリクエストの処理に時間がかかる場合、他の全てのリクエストをブロックする可能性がある。 >2. イベントループ・モデルで平行処理を行わないため、複数コアを持つCPU/MPU、複数のCPUを使うSMPでパフォーマンスの上昇が、JavaScriptのコード部分は期待できない。 > 3. JavaScriptのプログラミング言語としての限界が影響する。つまり、オブジェクト指向が不完全であること、動的型付けでJavaやC#に対して速度面に限界があること等が、大規模で複雑なアプリケーションには影響を及ぼす可能性がある。 > 4.あるページを表示するためのサーバー側のJavaScriptの文法エラーが、サーバーの停止を引き起こす。簡単なプログラム・ミスが、アプリケーションの非クリティカルな部分に発生したとしても、システム全体がダウンする結果となる。 > 5. 非同期化によるパフォーマンス向上がイベントループ・モデルに依存するため、イベントを受けるコールバック関数が多くなり(単純なファイル操作でも3回コールバックが発生する)、ソースコードの見通しが悪くなる。 > 6. 利用可能ライブラリが限定的であり、開発支援ツールが無い。Apacheモジュールも種類や用途が多いし、JavaやPHPにも覚えきれないほどのフレームワーク製品群とライブラリがある。これらに依存しない用途にしか、現状では利用できない。 上記、解決策は上記引用元や以下でも指摘があります。 [Node.jsについてのよくある誤解 - 自分の感受性くらい](http://meso.hatenablog.com/entry/20110220/1298202879 "Node.jsについてのよくある誤解 - 自分の感受性くらい") ## 解決策 ### 開発時の再起動問題 以下のモジュールを利用して、ファイル変更を検知して再起動する * nodemon [remy/nodemon](https://github.com/remy/nodemon) * supervisor [isaacs/node-supervisor](https://github.com/isaacs/node-supervisor) * node-dev [fgnass/node-dev](https://github.com/fgnass/node-dev "fgnass/node-dev") * hotnode [saschagehlich/hotnode](https://github.com/saschagehlich/hotnode "saschagehlich/hotnode") * always [edwardhotchkiss/always](https://github.com/edwardhotchkiss/always "edwardhotchkiss/always") * Qilin (麒麟? 作者は日本人の方) [atsuya/qilin](https://github.com/atsuya/qilin "atsuya/qilin") ### 本番サーバでのホットデプロイ問題 * forever, up, pm2 ### コールバック地獄問題 * future, promise, delay 並列処理のデザインパターンを使う [future - Wikipedia](http://ja.wikipedia.org/wiki/Future "future - Wikipedia") * 定番の async.js を使う * yield を使う (Node.js version 0.12以降) ### 重い処理で処理が待たされる * child_process.fork(filepath) [node.jsでCPUを使う重い処理のlibrary (child process使い) を1 fileで作る - c4se記:さっちゃんですよ☆](http://c4se.hatenablog.com/entry/2013/10/25/131245 "node.jsでCPUを使う重い処理のlibrary (child process使い) を1 fileで作る - c4se記:さっちゃんですよ☆") * fugue, Spark2, cluster * process.nextTick() (v.0.10.xまで?ループで一回処理ならこちら) * ループでn回の処理が必要なCPUヘビーな処理の場合、 setImmediate() ### マルチスレッド [Node.js でマルチスレッド対応のネイティブモジュールを作成する - 凹みTips](http://d.hatena.ne.jp/hecomi/20121021/1350819390 "Node.js でマルチスレッド対応のネイティブモジュールを作成する - 凹みTips") ### 文法エラーで止まっちゃう問題 * 事前対策 * 例外をキャッチする * Event: uncaughtException のリスナを登録 * JSLint, JSHint等をコミットフックやデプロイ時に仕掛ける * ユニットテストを行う * 事後対策 * 監視して再起動する ### エラーハンドリング問題 調査が甘いので、偉い人教えて下さい。 * 非同期コードの例外をキャッチする独自モジュール(node-gree) ### メモリーリーク問題 * node-v0.10.22 のファイルハンドラのメモリーリーク修正で解決? (コメントより。 thx shigeki@github) ## 時が解決する問題 * 頻繁なバージョンアップ * 安定性問題 * Node.js 技術者が少ない * ドキュメントが少ない問題 ## 偉い人が解決してくれるだろう問題 * JavaScript の言語仕様の問題 * オブジェクト指向 ## 解決されていない問題 * 玉石混淆のユーザモジュール * npm 上の評価システムの実装 |
|
| 105位 |
|
|||
|
03:14:35 |
(ShouldBee 所属) |
|
サムネイルを表示するときに、サーバサイドプログラムで画像の縦横を計算してサムネイル画像を作ることがあります。しかし、場合によってはCSS3でサムネイル画像を描画したほうが実装コストが低い場合があります。ここでは、CSS3を使ってサムネイル画像をキレイに描画する方法を紹介します。
# 1. 内接・外接とは? 画像の変形方法はいろいろありますが、ここでは内接リサイズ・外接リサイズについてのみ紹介します。内接リサイズ・外接リサイズとは、画像の縦横比を維持したまま、枠のサイズにフィットするように画像のサイズを変更する描画方法です。印刷出版系の用語だそうです。 | 内接リサイズ | 外接リサイズ | |:------------:|:------------:| | 画像の長辺を枠に合わせ画像全体が枠内に収まるようにリサイズする。 | 画像の短辺を枠に合わせてリサイズする。枠からはみ出す部分はカットされる。 | | ![inside] | ![outside] | 以下は、横長と縦長のサンプル画像です。これらをCSSでリサイズしてみます。 | 横長.jpeg | 縦長.jpeg | |:----:|:----:| |![horizontal long] | ![vertically long]| # 2. `background-size`を使った実装 CSS3で追加された `background-size` プロパティを使います。このプロパティは、IE9+, Firefox 4+, Opera, Chrome, Safari 5+で動作します。 _background-sizeのブラウザ対応状況 [Can I use... Support tables for HTML5, CSS3, etc](http://caniuse.com/#feat=background-img-opts)より_ ![can-i-use-background-size] ## 2.1 内接リサイズ ```css .contain { display: inline-block; background-color: #ccc; background-position: center center; background-repeat: no-repeat; margin: 5px; width: 200px; height: 200px; border: 1px solid #ccc; background-size: contain; } ``` ```html <span class="contain" style="background-image: url('./横長.jpeg')"></span> <span class="contain" style="background-image: url('./縦長.jpeg')"></span> ``` ### 2.1.1 描画結果 ![contain] ## 2.2 外接リサイズ ```css .cover { display: inline-block; background-color: #ccc; background-position: center center; background-repeat: no-repeat; margin: 5px; width: 200px; height: 200px; border: 1px solid #ccc; background-size: cover; } ``` ```html <span class="cover" style="background-image: url('./横長.jpeg')"></span> <span class="cover" style="background-image: url('./縦長.jpeg')"></span> ``` ### 2.2.1 描画結果 ![cover] # 3. `object-fit`を使った実装 `background-size`は背景画像を使った一種のハックでしたが、CSS3では`object-fit`という画像のサムネイル表示にぴったりなプロパティが追加されています。しかし、IEやEdgeが対応待ちになっているので、IE/Edgeをサポートするサイトで今すぐ使うのは難しいかもしれません。 _object-fitのブラウザ対応状況 [Can I use... Support tables for HTML5, CSS3, etc](http://caniuse.com/#feat=object-fit)より_ ![can-i-use-object-fill] とはいえ、IE/Edgeが対応すれば、`background-size`よりもシンプルなCSSになるので、`object-fit`の実装方法も紹介したいと思います。 ## 3.1 内接リサイズ ```css .contain { object-fit: contain; width: 200px; height: 200px; background-color: #ccc; border: 1px solid #ccc; } ``` ```html <img src="横長.jpeg" class="contain"> <img src="縦長.jpeg" class="contain"> ``` <a href="http://codepen.io/suin/pen/xZjrar" class="btn btn-info">このCSSをCodePenで試す</a> ### 3.1.1 描画結果 ![contain] ## 3.2 外接リサイズ ```css .cover { object-fit: cover; width: 200px; height: 200px; background-color: #ccc; border: 1px solid #ccc; } ``` ```html <img src="横長.jpeg" class="cover"> <img src="縦長.jpeg" class="cover"> ``` <a href="http://codepen.io/suin/pen/xZjrar" class="btn btn-info">このCSSをCodePenで試す</a> ### 3.2.1 描画結果 ![cover] ## 4. まとめ CSS3の `background-size` プロパティや `object-fit` プロパティを使うとプログラムレスで画像の内接リサイズ・外接リサイズできるということを説明しました。サーバサイドで画像をリサイズしないので、画像の転送量は元画像と同じになってしまいますが、プロダクトのモックアップや初期リリース段階など、ベロシティが要求される場面で使ってみてはいかがでしょうか。 [inside]: https://qiita-image-store.s3.amazonaws.com/0/889/b9d32f5b-6206-4144-44cd-b216998c78b2.png [outside]: https://qiita-image-store.s3.amazonaws.com/0/889/356c7f47-2e79-c511-d09c-8ee5ba49905f.png [horizontal long]:https://qiita-image-store.s3.amazonaws.com/0/889/9f6b538b-8c23-9bce-8d3e-79ecc187a1d9.jpeg [vertically long]: https://qiita-image-store.s3.amazonaws.com/0/889/d15e9d89-73bf-5841-9b1a-e14783fdfea8.jpeg [contain]: https://qiita-image-store.s3.amazonaws.com/0/889/c178e213-2a4b-0fde-86cd-41cedaebde4d.png [cover]: https://qiita-image-store.s3.amazonaws.com/0/889/1f13a622-b417-a30e-57f0-10d2be6bd32d.png [can-i-use-background-size]: https://qiita-image-store.s3.amazonaws.com/0/889/f524c583-6117-ae50-7e00-322fb0d6723a.png [can-i-use-object-fill]: https://qiita-image-store.s3.amazonaws.com/0/889/d80b717e-85fc-9e3c-d0f8-7c7cc2d580d6.png |
|
| 106位 |
|
|||
|
23:20:45 |
(Unicast Inc. 所属) |
|
概要
------- Git 初期設定の鉄板です。 何回やっても忘れるのでメモ。 気がついたら追記していきます。 ユーザー情報を設定する --------------------- 最近の Git はこれを設定しないとエラーを吐くようになりました。いい機会です、是非設定しましょう。 ``` git config --global user.name "First-name Family-name" git config --global user.email "username@example.com" ``` エディタを Vim に設定する ----------------------- コミットログを書くとき、 Ubuntu のデフォルトだと nano に設定されてしまうのでこれをvimに修正する。 ``` git config --global core.editor 'vim -c "set fenc=utf-8"' ``` ### そもそも nano なんて使わねーよという時 ブコメや、 @fumiyas さんからも指摘がありましたが、 nano 自体を全体的にそもそも使いたくないときは - nano は削除する ```bash sudo dpkg -P nano ``` - ほかのエディタをデフォルトにする ```bash export GIT_EDITOR= ``` or ```bash export EDITOR= ``` or 私のやった方法は以下です。 ```shell-session $ sudo update-alternatives --config editor alternative editor (/usr/bin/editor を提供) には 4 個の選択肢があります。 選択肢 パス 優先度 状態 ------------------------------------------------------------ * 0 /bin/nano 40 自動モード 1 /bin/ed -100 手動モード 2 /bin/nano 40 手動モード 3 /usr/bin/vim.basic 30 手動モード 4 /usr/bin/vim.tiny 10 手動モード 現在の選択 [*] を保持するには <Enter>、さもなければ選択肢の番号のキーを押してください: 3 update-alternatives: /usr/bin/editor (editor) を提供するためにマニュアルモードで /usr/bin/vim.basic を使います ``` その他 ------- ### git diff に色付けしたい ```bash git config --global color.diff auto git config --global color.status auto git config --global color.branch auto ``` ### push 方式を指定 **Git 1.8系** 以降で使える。というか警告が表示される。 新しい Git だと警告が表示されるので push 方式を明示する。 ``` git config --global push.default simple ``` **Git 1.8系以前** だとこれを設定すると `Malform` だって言われて以下の様なエラーになっちゃう。ひい。 ``` error: Malformed value for push.default: simple error: Must be one of nothing, matching, tracking or current. fatal: bad config file line 8 in /home/wnoguchi/.gitconfig ``` 以下のようにして削除する。 ``` git config --global --unset push.default ``` 詳しくは以下を参照。 - [git push時に表示されるwarning: `push.default is unset...`の意味と解決方法 - Qiita キータ](http://qiita.com/yaotti/items/a8e9f5de8dcca81d3214) ### Macユーザーに贈るUTF-8問題解決設定 Git 1.7.12以降で使える。 Mac ユーザーの中では有名ですが、濁点つきのディレクトリ・ファイルが分けて表示されてしまう UTF8-MAC 問題の解決方法。 忘れがちなので注意。 * 今後クローンするリポジトリに対して有効になる ``` git config --global core.precomposeunicode true ``` * 既存リポジトリの `.git/config` に対してすること ``` git config --local core.precomposeunicode true ``` ### git status とかで表示される日本語ファイル名がエスケープされてうざい ``` git config --global core.quotepath false ``` まとめ ------- 以下のスクリプトを流し込むとそれっぽく初期設定してくれる。 ```bash:git-bootstrap.sh #!/bin/bash git config --global user.name "First-name Family-name" git config --global user.email "username@example.com" git config --global core.editor 'vim -c "set fenc=utf-8"' git config --global color.diff auto git config --global color.status auto git config --global color.branch auto #git config --global push.default simple git config --global core.precomposeunicode true git config --global core.quotepath false ``` 現在の設定を確認する -------------------- ``` % git config --list user.name=Wataru Noguchi user.email=username@example.com core.quotepath=false core.excludesfile=/Users/noguchiwataru/.gitignore_global core.precomposeunicode=true difftool.sourcetree.cmd=opendiff "$LOCAL" "$REMOTE" difftool.sourcetree.path= mergetool.sourcetree.cmd=/Applications/SourceTree.app/Contents/Resources/opendiff-w.sh "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED" mergetool.sourcetree.trustexitcode=true push.default=simple ``` 参考サイト ----------- 1. [git 1.7.12でUTF8-MAC問題が解決 | Butaman-kun Project](http://btmn.jp/2012/08/22/git-1-7-12-utf8-mac/) 1. [git push時に表示されるwarning: `push.default is unset...`の意味と解決方法 - Qiita キータ](http://qiita.com/yaotti/items/a8e9f5de8dcca81d3214) 1. [引数なしのgit pushは危険なので気をつけましょう - DQNEO起業日記](http://dqn.sakusakutto.jp/2012/10/git_push.html) 1. [gitのコミットログ編集用エディタをvimにする - Sticker@Something](http://d.hatena.ne.jp/hikm/20110323/1300887533) 1. [git git diff で色付け - mat_akiの日記](http://d.hatena.ne.jp/mat_aki/20080803/1217783102) 1. [git config --global で追加した設置値を削除したい場合 - knt45の日記](http://knt45.hatenablog.com/entry/2013/08/26/152830) |
|
| 107位 |
|
|||
|
14:09:36 |
|
|
よく忘れるvimのコマンドをまとめました。
私向けの備忘録となってます。 間違いやあらも多いのでお気をつけ下さい。 # 基本 | コマンド | 説明 | |:-----------|:------------| |x |1文字削除 | |dd(連打) |1行切り取り | |dd(x行目で1回目のdを押し、y行目で2回目のdを押す)|x行目〜y行目までを切り取り| |5dd (xdd)|5(x)行削除| |yy(x行目で1回目のを押し、y行目で2回目のyを押す)|x行目からy行目までをコピー| |p|貼り付け| |u|undo| |CTRL + r|redo| |.|直前のコマンドを実行| |CTRL+n|補完| |CTRL+p|前候補を検索| |CTRL+n|次候補を検索| # カーソルの移動 | コマンド | 説明 | |:-----------|:------------| |h,j,k,l|←,↓,↑,→| |w|1単語分前へ移動| |b|1単語分後ろへ移動| |gg|ファイル先頭に戻る| |Shift + g|ファイル末尾に進む| |0|行頭へ| |$|行末へ| |^|行の最初の空白でない文字へ| |5l|5文字分右へ| |CTRL+f|ページアップ| |CTRL+b|ページダウン| |Shift+m|カーソルを画面の中心へ| |f|fを押して文字を打ち込むと、カーソル行にあるその文字のところに進む。文字入力後、; を押すと戻る| |%|(, ), {, }, [, ] カッコ移動などの対応する場所へ移動| |mk|現在のカーソル位置をマーク| |'k|マークした位置へ| |d'k|現在位置からマークまでを削除| # 見た目 ## インデント | コマンド | 説明 | |:-----------|:------------| |=|選択行のインデント修正| |gg, shift+v, shift+g, =|ファイル全体のインデント修正| |ctrl-t, ctrl-d|挿入モードでインデントを追加/削除| |>>|インデント| |<<|インデントを削除| |:set shiftwidth=4|インデント幅をスペース4つ分にする| |:set autoindent|オートインデントをON| |:set smartindent|スマートなオートインデントをON| ## フォーマット | コマンド | 説明 | |:-----------|:------------| |:%!fmt|すべての行をフォーマット| |!}fmt|現在位置にあるすべての行をフォーマット| |5!!fmt|次の5行をフォーマット| ## その他設定、Syntax highlightingなど | コマンド | 説明 | |:-----------|:------------| |:syntax on|色付けする| |:syntax off|色付け止める| |:set syntax=ruby (rubyの部分は言語名)|指定した言語のモードで色付け| |:set number|行番号の表示| |:set nonumber|行番号の非表示| [:set syntax={}に関してはコチラ](http://vim-jp.org/vimdoc-ja/syntax.html) # 検索・置換・正規表現 ## 検索 | コマンド | 説明 | |:-----------|:------------| |/lover|文字列hogeを検索(上から下へ)| |?lover|文字列hogeを検索(下から上へ)| |検索候補表示中に n |カーソルより下側にある検索候補に移動| |検索候補表示中に shift+n |カーソルより上側にある検索候補に移動| |*|今単語がある位置の単語を検索(カーソルより下側)| |#|今単語がある位置の単語を検索(カーソルより上側)| |/lo[vb]er|文字列の"lover"か"lober"を検索する| |/\<love|文字列"love"から始まる単語を検索| |/love\>|文字列"love"で終わる単語を検索| |/\<love\>|文字列"love"を検索| |/^love|文字列"love"から始まる行を検索| |/love$|文字列"love"で終わる行を検索| |/^love$|文字列"love"だけの行を検索| |/\<….\>|4文字の単語を検索| |/love\|kill|"love"と"kill"を検索| |/\<\d\d\d\d\>|4桁の数字を検索| |/^n{3}|空の3行を検索| |:bufdo /love/|現在開いているファイル全部の中から文字列"love"を検索| [vimでの正規表現に関してはコチラ](http://archiva.jp/web/tool/vim_regexps.html) ## 置換 | コマンド | 説明 | |:-----------|:------------| |:s/before/after|カーソルのある行にある最初の文字列"before"を"after"に置換(1つずつ)| |:s/before/after/g|カーソルのある行にある文字列"before"をすべて"after"に一括置換| |:s/before/after/gc|カーソルのある行にある文字列"before"をすべて"after"に置換。確認を表示させる| |:%s/before/after/g|現在使っているファイルにある文字列"before"を"after"に一括置換| |:%s/before/after/g|現在使っているファイルにある文字列"before"を"after"に置換。確認を表示させる| |:%s;/var/home;/usr/home;gc|パス名などを置換するときは、"/"でなく、";"を区切り文字に使用| |:63,79s /lover/killer/g|特定範囲のみ置換: 63〜79行目までの範囲にある"lover"を"killer"に一括置換| |:%s/^/Goodbye/g|ファイル全体のすべての行の先頭に"Goodbye"を追加| |:%s/$/\./g|ファイル全体のすべての行の末尾に"."を追加| |:3,$s/lover/killer/g|特定範囲のみ置換: 3行目から最終行の範囲にある"lover"を"killer"に一括置換| |:%s/ /^M/g;|ファイル全体の中からスペース(空文字)を改行(CR)に置換(改行は CTRL+vを押した後にEnterを押すと入力される)| |:%s/GoodByeMyLove/ILoveYou/gi|ファイル全体の中から"GoodByeMyLove"を"ILoveYou"に置換する。"GoodByeMyLove"は大文字小文字関係なく検索する| |:%s/ *$/g|ファイル全体の行末の空文字(空白、スペース)を削除| |:%s#<[^>]+>##g|タグに囲まれたテキストは残しつつ、HTMLタグを全て削除| |:g/love/d|"love"を含んでる行を削除| |:v/goodbye|"goodbye"を含んでいない行だけを削除| |:%s/^(.*)n1$/1/|同じ行が2回続いたら片方削除| |CTRL+a|カーソル上の数字をインクリメント| |CTRL+x|カーソル上の数字をデクリメント| |argadd **/*.c|カレントディレクトリ以下にある.cファイルを選択| |:argdo %s/lover/friend/gc \| update|選択されたファイルの中の文字列"lover"を"friend"に置換| |:tabdo %s/lover/friend/g|すべてのタブに対して、"lover"を"friend"に置換| # ファイル操作 | コマンド | 説明 | |:-----------|:------------| |:e filename|filenameを現在のウィンドウで開く| |:split filename|filenameを横分割したウィンドウで開く| |:vsplit filename|filenameを縦分割して開く| |:sview filename|filenameを横分割で読み取り専用で開く| 画面分割については下に。 # 画面 ## 画面分割 | コマンド | 説明 | |:-----------|:------------| |:sp|上下分割| |:vs|左右分割| |CTRL+w CTRL+w(CTRL+wを2回押す)|次の画面へ| |CTRL+w ↑(矢印)|カーソルを上のウィンドウへ| |CTRL+w _|選択中のウィンドウを最大化| |CTRL+w =|分割したすべてのウィンドウを同じ大きさに| |10 CTRL+w +|選択中のウィンドウを10行大きくする| |:close|画面を閉じる| |:hide|現在のウィンドウを閉じる| |:only|現在のウィンドウ以外を閉じる| |:b 2|バッファの2番目を開く| ## タブ | コマンド | 説明 | |:-----------|:------------| |:tabnew|新しいタブ作成| |:tabe filename|filenameをタブで開く| |:tabclose|タブを閉じる| |gt|次のタブへ| |g shift+t|前のタブへ| |{i}gt|i番目のタブへ| |:tabdo %s/before/after/g|すべてのタブに対して、一括置換| |:tabs|開いているタブをリスト表示| |:tabo|今使っているタブ以外を閉じる| |:tabfirst|最初のタブを表示| |:tablast|最後のタブを表示| |:tabm n|現在のタブをn番目へ| |:tab ball|バッファをすべてタブで開く| ```vim vim -p file1 file2 open files as tabs ``` file1 file2をタブで開く # ヴィジュアルモード | コマンド | 説明 | |:-----------|:------------| |v|ヴィジュアルモードに入って一文字選択| |shift+v|ヴィジュアルモードに入って一行選択| |CTRL+v|矩形(くけい)選択 (複数行選択)| |v, gg, shift+g|全選択| |CTRL + v, I(shift + i), input words or delete, esc|矩形選択後、選択範囲の編集| # その他便利なコマンド ## UNIXとのインタラクション | コマンド | 説明 | |:-----------|:------------| |:!pwd|pwd” を実行し、Vimに戻る| |!!pwd|“pwd” を実行し、実行結果をファイルに挿入| |:sh, CTRL + z|一時的にUnixへ戻る| |$ exit, fg |Vimに戻る| ## 設定 | コマンド | 説明 | |:-----------|:------------| |:set tabstop=x|タブの設定| |:set ignorecase|大文字小文字の区別をなくす| |:set noignorecase|大文字小文字を区別する| |:ab hoge abcdefghijklmn|abcdefghijklmnのショートカットにhgoe定義| |Ctrl+n Ctrl+p(挿入モードで)|単語を補完| |Ctrl+x Ctrl+l(挿入モードで)|行を補完| |:set dictionary=hoge|“hoge” を “dictionary” に定義| ## cit | コマンド | 説明 | |:-----------|:------------| |c or d or i|変更 or 削除 or 挿入| |i or a|中身 or 全体| |t or " or ) or ]|binding character(<>. (), tag, [])| |cit|タグの中身を削除して入力モードに入る| |ca"|""を含んだ全体を削除して入力モードに入る| ********** OSによっては、~/.vimrc ファイルに:filetype indent on という記述がないと、インデントがきかない場合があるらしい。 .vimrc ファイルがない場合は、ホームディレクトリ以下に .vimrc というファイルを作成して、編集してみるのもよいかと。 ********** 【参考】 ・http://loumo.jp/wp/archive/20080701175525/ ・http://archiva.jp/web/tool/vim_regexps.html |
|
| 108位 |
|
|||
|
10:28:03 |
(Abby 所属) |
|
# Docker でデータのポータビリティをあげ永続化しよう
こんにちは、Docker 0.91 が出ましたね。 CoreOS でのデータの置き場所をどうすべきか考えていた時に、CoreOS-devで出ていた話です。 なので知っている人は知ってるかも知れません。 全てにおいて使えるパターンではないのですが、運用形態のひとつとして紹介します。 (もちろん、Dockerはどんどん進化しているのでこのパターンは陳腐化する可能性もあります) ## データの永続化の問題 Docker で悩ましいのはデータの永続化をどうするか?というとこでしょうか. 例:mysql のコンテナを立ち上げる ``` sudo docker run -d -v /var/host_lib/mysql:/var/lib/mysql me/mysql_5_5 ``` `-v` オプションをつけて `mysql` のデータを永続化していますね。 さてこれはこれでよいのですが、Docker の旨みを活かせていませんね。 Docker の特徴はコンテナであり、コンテナにするとポータビリティがあげられるわけです。 上記の方法だと Volume でホストにべったり実データが張り付いてしまい、データ自体のポータビリティが下がっ てしまいます。 また、Docker 内で閉じていない場合、うっかり誰かがデータを削除してしまうかも知れません。 ## Data-only Container Pattern そこで、データのみを格納するコンテナを作成し、データ部のポータビリティをあげてみましょう。 では早速、Dockerfile を書いていきます。 今回はざっくりと動作の確認をするため、シンプルなもので試しています。 データ格納コンテナのDockerfile ``` FROM busybox VOLUME /opt CMD /bin/sh ``` build. ``` sudo docker build -t me/storage . ``` run. ``` sudo docker run -i -t --name my_data me/storage ``` あるいは 以下一行でもよいでしょう。 ``` sudo docker run -i -t -v /opt --name my_data busybox /bin/sh ``` 今回は`--name`オプションで任意の名前をつけています。 `--name`はユニークである必要があるので気をつけましょう。 VOLUME で /opt を指定しています。なので/opt以下を格納するコンテナになります。 ではデータ格納コンテナを使ってみます。 別ターミナルなどから以下のコマンドで使ってみましょう。 ``` sudo docker run -t -i --volumes-from my_data ubuntu /bin/bash ``` `--volumes-from` で先ほどのコンテナを指定しています。 これで先ほどのデータ格納コンテナとつながりました。 /opt が接続されているので確認してみましょう。 ``` root@f2c68ec76ba3:/# touch /opt/test root@f2c68ec76ba3:/# ls -al /opt/ total 8 drwx------ 2 root root 4096 Mar 27 00:34 . drwxr-xr-x 33 root root 4096 Mar 27 00:34 .. -rw-r--r-- 1 root root 0 Mar 27 00:34 test root@f2c68ec76ba3:/# touch /opt/testa root@f2c68ec76ba3:/# ls -al /opt/ total 8 drwx------ 2 root root 4096 Mar 27 00:35 . drwxr-xr-x 33 root root 4096 Mar 27 00:34 .. -rw-r--r-- 1 root root 0 Mar 27 00:34 test -rw-r--r-- 1 root root 0 Mar 27 00:35 testa root@f2c68ec76ba3:/# ``` データ格納コンテナ側からも見てみます。 ``` / # ls -al /opt total 8 drwx------ 2 root root 4096 Mar 27 00:35 . drwxr-xr-x 30 root root 4096 Mar 27 00:34 .. -rw-r--r-- 1 root root 0 Mar 27 00:34 test -rw-r--r-- 1 root root 0 Mar 27 00:35 testa / # ``` できてますね。 データ格納コンテナを`Ctrl+D`などで落としてみましょう。 使用している側のコンテナに戻って以下を実行してみましょう。 ``` root@f2c68ec76ba3:/# touch /opt/hoge- root@f2c68ec76ba3:/# ls -al /opt/ total 8 drwx------ 2 root root 4096 Mar 27 00:38 . drwxr-xr-x 33 root root 4096 Mar 27 00:34 .. -rw-r--r-- 1 root root 0 Mar 27 00:38 hoge- -rw-r--r-- 1 root root 0 Mar 27 00:34 test -rw-r--r-- 1 root root 0 Mar 27 00:35 testa ``` そうです。当たり前ですがデータ格納コンテナは`run`で走らせてなくてもよいのです。 落としたコンテナを`start`で再度起こして中身を見てみましょう。 `docker ps -a`で確認して起動します。 ``` sudo docker start <CONTAINER_ID> sudo docker attach <CONTAINER_ID> / # ls -al /opt/ total 8 drwx------ 2 root root 4096 Mar 27 00:38 . drwxr-xr-x 30 root root 4096 Mar 27 01:02 .. -rw-r--r-- 1 root root 0 Mar 27 00:38 hoge- -rw-r--r-- 1 root root 0 Mar 27 00:34 test -rw-r--r-- 1 root root 0 Mar 27 00:35 testa / # ``` 問題ありませんね。 これでポータビリティを確保しつつ、データの永続化を実現出来ました。 データ格納コンテナのデータのバックアップや確認は`docker cp`を使ってホスト側にもってくることで可能です。 また`export`してアーカイブ化してしまうのも手でしょう。 アーカイブしたものを別ホストで展開すれば別ホストでも使用できます。 これを応用すればmysqlのコンテナと複数のデータ格納コンテナを使用してmysql環境をバンバン立ち上げること ができます。 またnameを変えるだけでデータを切り替えることができるのでテストなどでも使えるかも知れません。 必要に応じて`commit`してregistryにpushして配布するなども場合によってはいいかも知れませんね。 先日書いた以下の記事の開発するプロジェクトデータ部をコンテナに追い出すのもよいでしょう。 [Docker で開発環境も使い捨てにしよう! ](http://qiita.com/mopemope/items/495ab1f74bcbef0f88bb) データ格納コンテナは走らせてなくてもよいのですが、うっかり削除する可能性もあるので走らせておくとよい でしょう。 |
|
| 109位 |
|
|||
|
12:37:46 |
|
|
iOS7ではスタースバーは透明、ナビゲーションバー、タブバー、ツールバー、検索バー、スコープバーは半透明。一般的なルールとしてコンテンツの上にこれらのバーを被せることを想定しスタースバーの下に何もバーがないならコンテンツはフルスクリーンコンテンツにすべき。とある。 ## iOS7 フレームワーク判定 * [iOS 7 UI Transition Guide](https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/TransitionGuide/index.html)にはこんなやり方が書いてある。場合によってはバージョンよりもNSFoundationVersionNumberを使うほうが意図が明確になる。 ```objective-c if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { // Load resources for iOS 6.1 or earlier } else { // Load resources for iOS 7 or later } ``` ## wantsFullScreenLayoutの廃止 iOS7ではフルスクリーンコンテンツを想定しているのでiOS6以下であった ``wantsFullScreenLayout`` は非推奨になる。 ## UIViewControllerBasedStatusBarAppearance iOS7で動的にスタイルを変更する場合はアプリのInfo.plistファイルで **UIViewControllerBasedStatusBarAppearance** を **YES** に設定しておく。Info.plistをソースから編集する場合は以下の二行を追加すればいい。 ```xml:Info.plist <key>UIViewControllerBasedStatusBarAppearance</key> <true/> ``` Properrty List Editorから編集する場合は **View controller-based status bar appearance** を選択し **YES** を設定する。 ## ステータスバーを消す アプリにステータスバーが要らない場合は、XcodeのTargetのGeneraのHide during application launchにチェックを入れる。これだけだと反映されないのでinfoファイルのUIViewControllerBasedStatusBarAppearanceを **NO** にしておく。 ## ステータスバーのスタイルを設定する アプリのInfo.plistファイルで **UIStatusBarStyle** によって設定できる。 ```xml:Info.plist <key>UIStatusBarStyle</key> <string>UIStatusBarStyleLightContent</string> ``` コードで変更もできるがポイントになるのはiOS7での起動中の話。iPadの起動画面の画像サイズが変わったように起動イメージがステータスバーの領域を含めるので、起動イメージからスタイルを変えたい場合はInfoファイルで制御する。 iOS7で設定可能な値は以下となる。 ```objective-c typedef enum : NSInteger { UIStatusBarStyleDefault, UIStatusBarStyleLightContent, UIStatusBarStyleBlackTranslucent, UIStatusBarStyleBlackOpaque } UIStatusBarStyle; ``` 時刻などステータスバー内のコンテンツの色を白にする場合は ``UIStatusBarStyleLightContent`` をつかうと良い。 参考: http://stackoverflow.com/questions/18924345/how-to-change-status-bar-style-during-launch-on-ios-7 ## 動的にステータスバーのスタイルを変更する iOS7で動的にスタイルを変更する場合はUIViewControllerBasedStatusBarAppearance を **YES** にしておく。 デフォルトはApplicationのstatusBarStyleで変更できる。 ```objective-c [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent; ``` ViewController単位での変更も可能で動的にスタイルを変える場合は ``(UIStatusBarStyle)preferredStatusBarStyle``を実装して変更したいUIStatusBarStyleを返すようにする、任意のタイミングで変更する場合は``setNeedsStatusBarAppearanceUpdate`` を呼び出すことで更新ができる。 ```objective-c { UIStatusBarStyle _statusBarStyle; } - (IBAction)toggleStatusBar:(id)sender { if (_statusBarStyle == UIStatusBarStyleDefault) { _statusBarStyle = UIStatusBarStyleLightContent; } else { _statusBarStyle = UIStatusBarStyleDefault; } if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) { [self setNeedsStatusBarAppearanceUpdate]; } } - (UIStatusBarStyle)preferredStatusBarStyle { return _statusBarStyle; } ``` ## 動的にステータスバーを非表示にする iOS7からはViewControllerで ```objective-c - (BOOL)prefersStatusBarHidden { return YES; } ``` を入れるとViewController単位でスターテスバーが消せる。アニメーションは``preferredStatusBarUpdateAnimation``で指定。iOS6も対応させるなら以下のようになる。 ```objective-c - (void)viewDidLoad { [super viewDidLoad]; if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade]; } } - (BOOL)prefersStatusBarHidden { return YES; } - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation { return UIStatusBarAnimationFade; } ``` iOS7の ``prefersStatusBarHidden`` による対応はそのUIViewControllerのみに限定されるが、iOS6向けのUIApplicationに対する変更はアプリケーション全体に適応されるので画面ごとに切り替える場合は元に戻す処理も必要になるところが注意点。 またiOS7ではUINavigationControllerなどがStatusBarの有無によるマージンをうまく取り扱ってくれることがあるがいくつかのパターンではStatusBarを消したあとのレイアウトも変更する必要がある。 参考: http://stackoverflow.com/questions/18375898/status-bar-appear-over-my-views-bounds-in-ios-7 ## UINavigationController, UITabBarController 経由の場合 iOS7でUINavigationControllerやUITabBarControllerをつかう場合は内部のViewControllerの ``preferredStatusBarStyle `` や``prefersStatusBarHidden`` が呼び出されない。 自身で設定するか ``childViewControllerForStatusBarStyle`` や``childViewControllerForStatusBarHidden`` をオーバーライドして処理を委譲するChildViewControllerを指定できる。 ```objective-c - (UIViewController *)childViewControllerForStatusBarStyle; - (UIViewController *)childViewControllerForStatusBarHidden; ``` ## AutoLayoutでStatsuBarに対する余白を変更する iOS7向けにステータスバーに重ならないようにViewを配置していると、iOS6ではそれが余計な余白になる。例えばiOS7でステータスバーの真下にラベルを配置すると  iOS6ではこのようにiOS7向けでStatusBarに対して設定した余白が余分になってしまう。  iOS6、あるいはiOS7のときにViewの位置を変えるという試練が待ち受けている。iOS6以上ならAutoLayoutを使った方が簡単になる。 簡単な解決の方法は **topLayoutGuide** を使う方法。topのマージンをtopLayoutGuideから0とすると、iOS7ではステータスバーがある場合はステータスバーのbottomからの距離となり、iOS6ではステータスバーは画面領域に含めないので画面のtopからの距離になる。  もう一つの方法はAutoLayoutの値を自分で調整する方法。例えばまずiOS7向けにViewのtopのマージンを20と設定する。  AutoLayoutのConstraintはIBOutletでコードから操作することができる。  これを利用してiOS6のときにtopのマージンを20->0にするという実装が可能になる。 ```objective-c { IBOutlet NSLayoutConstraint *_headerMargin; } - (void)viewDidLoad { [super viewDidLoad]; if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { _headerMargin.constant = 0; } } ``` ## iOS5でStatsuBarに対する余白を変更する iOS5をターゲットにしているとAutoLayoutが使えないのでtopLayoutGuideを利用できない。 代わりに ``iOS 6/7 Deltas`` をつかうことで解決する。これはiOS6(以下)向けに座標やサイズを調整する機能でY座標を-20にすればiOS7向けに設定したステータスバー分の余白を相殺することができる。(View as ``iOS 7.0 and Later`` で表示している場合、iOS 6.1 and EarilerでみるとDeltaが逆になる)  この例ではUILabelを直接設定しているが、他のコントロールも調整が必要になるのでレイアウト用にコンテナのUIViewを追加してそこで iOS 6/7 Deltas を設定し、それ以外はそのコンテナUIViewのsubViewとしてレイアウトするのが良いと思われる。レイアウトに使うUIVewiはY座標を-20をした場合にはHeightを+20しないと画面下部に余白ができてしまうことに注意する。 参考: [iOS 6/7 で UILabel などの見た目がずれるよ〜とお嘆きのあなたへ](http://qiita.com/ne_ko_/items/5da4b2a247e65431c7cf) ## StatusBarをUINavigationBarにかぶせる iOS7でUINavigationControllerをつかう場合、UINavigationBarの上にStatusBarがかぶさるようになっている。 UINavigationControllerを使わずにUIコントロールとして直接UINavigationBarを直接つかう場合、単にStatusBar分の余白を空ける方法だと以下のようにUINavigationBarの色が画面上部まで適応されないのでiOS7らしくならない。  これを解決するために ``UIBarPositioningDelegate``の``(UIBarPosition)positionForBar:(id<UIBarPositioning>)bar`` で **UIBarPositionTopAttached** を返す。 ```objective-c { IBOutlet UINavigationBar *_navBar; } - (void)viewDidLoad { [super viewDidLoad]; _navBar.delegate = self; if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) { _navBar.barTintColor = [UIColor yellowColor]; } } - (UIBarPosition)positionForBar:(id <UIBarPositioning>)bar { return UIBarPositionTopAttached; } ``` これによりbarTintColorがStatusBarの下にもちゃんと設定されるようになる。  これは前節のtopLayoutGuideと合わせてつかった場合。なお、UIBarPositioningDelegateやbarTintColorなどはiOS 7以降の機能となっている。 参考: https://devforums.apple.com/message/853889 ## その他 AutoLayoutやiOS 6/7 DeltasなどInterface Builderに頼らない場合は以下にあるように直接ViewのFrameを設定するなどの調整が必要になる。 * http://stackoverflow.com/questions/18294872/ios-7-status-bar-back-to-ios-6-style/ |
|
| 110位 |
|
|||
|
15:16:02 |
(フリーランス 所属)
|
|
by @mixiappwchr
前回のまとめでは、有名どころを書いたので、今回は、あまり知られてないところから探してみました。自分のメモ用に。 # 開発 ## canvas http://canvaspod.io/  アニメーションをInterface Build上で設定できるライブラリです。 部品にこまかなアニメーションつけるとき便利そうです。 ## injection for xcode http://injectionforxcode.com/  アプリを落とすことなく動的にソースコードを反映させるxcode pluginです。手元でちゃんと動かせてないですが、使えれば細かい修正の調整には非常に便利そうです。 ## cococa+cat http://cocoacats.com/  カテゴリーライブラリのみを集めたサイトです。便利なカテゴリーは手放せないので、色々探してみると面白そうです。 ## Nomad http://nomad-cli.com/  Mattt先生作のコマンドラインツール。pushを送ったりipaビルドしたり,devsiteスクレイピングしたり、いろいろ捗ります。 # 解析、ユーザーテスト、バグレポート ## Repro https://repro.io/  リモートでユーザーの行動をアプリの実際の画面や操作しているユーザーの表情、音声まで録画してアップロード、解析できるサービスです ## instabug http://instabug.com/  シェイクしてバグレポートを送れるサービスらしいです。 情報が集まりやすそうですね。 ## appsee http://www.appsee.com/  デバイスのヒートマップなどがわかる解析サービスです。 webでは存在しますがアプリではなかなかないので面白そうです。 ## プロモーション placelt https://placeit.net/  スクリーンショットを特定のシーンと合成して作ってくれるサービスです。 お手軽におしゃれな画像が作成できます。 個人的に一番刺さりました。使っていきます! 引き続きまだまだ便利そうなサービス探していきます ##### appwchr post ---- [あんなすごいエンジニアともであえるかも??Qiitaユーザーが集まるSlack Teamを作ってみたよ!](http://qiita.com/appwatcher/items/00faadbf02f691186a54) [goからiOSまで一人でアプリ開発をしてたらいつの間にかマインクラフトエンジニアになった話](http://qiita.com/appwatcher/items/6c0280cda9c4c8b3c65f) [API開発の効率化の架け橋!APIのStubサーバーを導入して,API開発に効率化、スピード化、柔軟性を手に入れよう!](http://qiita.com/appwatcher/items/49807e72d9600db7196d) [アプリエンジニアから見てAPI設計において気をつけてもらえるとうれしいこと](http://qiita.com/appwatcher/items/4a0dd09c393cc70d961a) [Goodbye... Jenkins... Jenkinsを卒業してお手軽CI! iOSもAndroidもCircle CIでアプリのCIを回そう](http://qiita.com/appwatcher/items/4cdf39804d6e46ab7af5) [まだTestFlight使ってたの?急げ!終了目前のTestFlightから,今すぐにiOSもDeployGateに移行しよう!移行パターンも紹介するよ。](http://qiita.com/appwatcher/items/632460e15fbdb81b7a71) [Swiftを使ってみて直面した闇。現時点で現場でSwiftを採用すべきかどうかの判断材料](http://qiita.com/appwatcher/items/50295e82ab0095902aaa) [iOSの開発をする上で絶対に使うべき!外せない!webサービス、開発ツール集【完全版】](http://qiita.com/appwatcher/items/07a3babcb9b6cefb307e) [注目のiBeacomなどの波に乗り遅れないために!iOSのBluetooth開発を容易にするライブラリを書きました。] (http://qiita.com/appwatcher/items/7491beffd7260b713542) [まだまだあった!iOSの開発を劇的に改善する最新のwebサービス、開発ツール集1] (http://qiita.com/appwatcher/items/f0024fe2ac34da345f04) [さらに快適なアプリ開発を!iOSの開発をもっと劇的に改善する最新のwebサービス、開発ツール集2] (http://qiita.com/appwatcher/items/c15d7311e71b4c2b77f1) [スパゲッティから脱出!iOS開発における遷移の問題をすっきり解決する便利ルーティングライブラリをご紹介] (http://qiita.com/appwatcher/items/259e8d13fff0547e90af) |
|
| 111位 |
|
|||
|
16:19:23 |
(レジュプレス株式会社 所属) |
|
# vim タブページ
みなさんvimのタブページ機能を使っていますか? この一番上のバーに出ている、ブラウザなどでよくある「タブ」のことです。↓  これはvim 7.3から導入された機能なのですが、なぜかあまり使われていません。 [Google検索 vim タブページ](https://www.google.co.jp/search?q=vim+タブページ&oq=vim+タブページ) ##### しかし、これはめちゃめちゃ便利です! ##### 今までtmuxでvimを複数個立ち上げており、ちょっと別の作業をするときは別のvimを起動していたのですが、タブページを利用することによって1つのvimで作業することができ、更にヤンクももちろんタブ間で共有されるので開発効率がかなり上がりました。 あまり使われていない原因はおそらく、デフォルトの状態だと、タブ間の移動がかなりめんどくさいからだと思います。 デフォルトはこんな感じ  右側のタブに移動するたびに `:tabnext` ラストのタブに移動するには `:tablast` など打っていられません。 また、`:tabnext3` などのコマンドで、左から3番目のタブに移動できるのですが、 デフォルトのタブライン(タブのステータスバー)は番号も書いてないので、簡単には移動出来ません。 そこで暗黒美夢王(ダークビムマスター)ことShougoさんの.vimrcを参考に 快適にタブページ機能を使えるよう.vimrcを作りました。 [参考 Shougo/shougo-s-github](https://github.com/Shougo/shougo-s-github/blob/master/vim/.vimrc) ```vim:.vimrc " Anywhere SID. function! s:SID_PREFIX() return matchstr(expand('<sfile>'), '<SNR>\d\+_\zeSID_PREFIX$') endfunction " Set tabline. function! s:my_tabline() "{{{ let s = '' for i in range(1, tabpagenr('$')) let bufnrs = tabpagebuflist(i) let bufnr = bufnrs[tabpagewinnr(i) - 1] " first window, first appears let no = i " display 0-origin tabpagenr. let mod = getbufvar(bufnr, '&modified') ? '!' : ' ' let title = fnamemodify(bufname(bufnr), ':t') let title = '[' . title . ']' let s .= '%'.i.'T' let s .= '%#' . (i == tabpagenr() ? 'TabLineSel' : 'TabLine') . '#' let s .= no . ':' . title let s .= mod let s .= '%#TabLineFill# ' endfor let s .= '%#TabLineFill#%T%=%#TabLine#' return s endfunction "}}} let &tabline = '%!'. s:SID_PREFIX() . 'my_tabline()' set showtabline=2 " 常にタブラインを表示 " The prefix key. nnoremap [Tag] <Nop> nmap t [Tag] " Tab jump for n in range(1, 9) execute 'nnoremap <silent> [Tag]'.n ':<C-u>tabnext'.n.'<CR>' endfor " t1 で1番左のタブ、t2 で1番左から2番目のタブにジャンプ map <silent> [Tag]c :tablast <bar> tabnew<CR> " tc 新しいタブを一番右に作る map <silent> [Tag]x :tabclose<CR> " tx タブを閉じる map <silent> [Tag]n :tabnext<CR> " tn 次のタブ map <silent> [Tag]p :tabprevious<CR> " tp 前のタブ ``` これを.vimrcに書き込むことにより、下のような写真の状態になり、  * `t1`, `t2`,,,`t9` で左からn番目のタブにジャンプ * `tc` で新しいタブ, `tx`でタブを閉じる でタブページを効率的に使えます。 tに既にコマンドを割り当てている場合は、[Tag]を別にキーに当てはめれば問題なく使えると思います。 P.S. [STORYS.JP](http://storys.jp)、 [coincheck](https://coincheck.jp) などのサービスを運営、開発しています。興味のある方はぜひ [和田](http://qiita.com/wadako111)まで連絡を! |
|
| 112位 |
|
|||
|
22:28:58 |
(freee 所属) |
|
GitHubやばい、何がやばいってソース公開していいならWebサーバなくてもWebページ公開できる。
JavaScriptもちゃんと動いてる。(サーバ必要系はたぶん無理) というわけで表題の件ですが、GitHubは主に2つの形式でWebサイトを公開できます。 テンプレサイト作るだけならgitコマンド叩いたり、ローカルにリポジトリ作る必要もありません。 * ユーザーアカウントに対して紐付けられているWebページ * リポジトリごとのWebページ ほとんどリンク先見てね状態ですが、毎回ググるのめんどくさいのでメモ書き。 GitHubアカウントもっているならば、3分かからないです。 --------- #ユーザーアカウントに対して紐付けられているWebページ。 `yourself-account.github.io.git`リポジトリを作成することでWebページが公開できます。 ~~こんな感じ。~~ ~~[http://budougumi0617.github.io/](http://budougumi0617.github.io/)~~ # 2017/07/03 現在は`Hugo`生成のブログになっています。  公式最強ということで、後はリンク先参照のこと。 [http://pages.github.com/](http://pages.github.com/) 上記URLには載っていないのですがテンプレサイトが作れます。 GitHubで`yourself-account.github.io.git`リポジトリの「setting」を開きます。 画面をスクロールすると、「Update your site」という項目があるので、隣の「Automatic Page Generator」というボタンをクリック あとは指示に従ってテンプレを選んだりすると完成します。 URLは`http://yourself-account.github.io/`となります。 例: [http://budougumi0617.github.io/](http://budougumi0617.github.io/) そのあとはcloneしてお好みのエディタで編集するなり、すげ替えちゃってください。 NetBeansの「新規プロジェクト」、「既存のリソースを利用するHTML5アプリケーション」で取り込み、編集、Chrome連携実行まで確認できました。 ----- #リポジトリごとのWebページ 今ある既存のリポジトリにもWebページを構成できます。 なので、ライブラリなどをGitHubで公開した際はそのままそのリポジトリでWebページも作成出来ます。 ~~おおざっぱに言うと、`gh-pages`というブランチを切って、必要ファイルをそのブランチに突っ込めば、完成です。~~ **2016/08/18 追記** **masterブランチにdocsディレクトリを作っても公開できるようになったそうです** [2016年新機能! GitHubのmasterブランチをWebページとして公開する手順](http://qiita.com/tonkotsuboy_com/items/f98667b89228b98bc096) Configuring a publishing source for GitHub Pages [https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/](https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/) 以下は適当なindex.htmlを作成してとりあえず公開してみる方法。 前提は`yourRepository/`をルートとして、`yourRepository`リポジトリをcloneしている状態。 ある程度Git叩けることが条件 ```bash:CommandLineLog cd yourRepository/ git checkout --orphan gh-pages git rm -rf . #これは別にやらなくてもいいかも echo "My GitHub Page" > index.html git add index.html git commit -a -m "First pages commit" git push origin gh-pages ``` あとは(若干タイムラグがあるらしいので、)一服してから`http://yourself-account.github.io/yourRepository`アクセスするだけ。 例:学習サイト見ながら作っただけの`localStorage`たたくメモ帳。jQueryが動くことは確認できた。 [http://budougumi0617.github.io/html5NotePad/](http://budougumi0617.github.io/html5NotePad/) --- 以上で、GitHub上でWebページを公開する方法でした。 privateリポジトリでは作成できないなどの制約はありますが、pushするだけで実際に公開できるのは嬉しいですねー! JavaScriptは動かないと思っていたので、それも嬉しい点です。 ---- #参考 GitHub Pages [http://pages.github.com/](http://pages.github.com/) GitHubでサイトを公開する方法 [http://soudai1025.blogspot.jp/2012/07/github.html](http://soudai1025.blogspot.jp/2012/07/github.html) GitHub の プロジェクトページ を 自動生成 & 公開 する 方法 [http://garafu.blogspot.jp/2013/06/gihub.html](http://garafu.blogspot.jp/2013/06/gihub.html) gh-pagesでGithubのプロジェクトをWEBページとして公開する方法 [http://peroon.hatenablog.com/entry/2013/05/18/171144](http://peroon.hatenablog.com/entry/2013/05/18/171144) html5NotePadページはドットインストールさんで学習させて頂きました。 ドットインストール:HTML5で作る「シンプルメモ帳」 (全8回) [http://dotinstall.com/lessons/memo_html5](http://dotinstall.com/lessons/memo_html5) 2016/08/18 追加 2016年新機能! GitHubのmasterブランチをWebページとして公開する手順 [http://qiita.com/tonkotsuboy_com/items/f98667b89228b98bc096](http://qiita.com/tonkotsuboy_com/items/f98667b89228b98bc096) Configuring a publishing source for GitHub Pages [https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/](https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/) |
|
| 113位 |
|
|||
|
14:34:28 |
|
|
自分の担当したWebアプリケーションを引き継ぐ際に、予備知識として説明したことのまとめ
# 注意事項 - もともと明確に定義されていない概念や、簡単に説明するため正確さを犠牲にした部分が多い - 間違っていることを前提に、疑いながら読むのがベター # アプリケーションの層構造 - アプリケーションを構成するオブジェクトには非常の多くの種類がある - アプリケーションの(より良い)構成をオブジェクト単位で考えるのは難しいので、もっと粒度の大きい単位で考えたい - アプリケーションをいくつかの層(オブジェクトの所属するグループ)に分割し、層単位でアプリケーションの構成を考える ## View層(ビュー層) - レスポンスをクライアントにとって都合のいい形(i.e. 画面)に変換する層 - View層のオブジェクトは - Controller層のオブジェクトから利用される - DomainModel層のオブジェクトを利用して、ユーザーに表示したいデータを用意する - RESTfulなアプリケーションの場合、View層が存在しないこともある ## Controller層(コントローラー層) - ユーザーからのリクエストに対応し、レスポンスを生成する層 - Controller層のオブジェクトは - View層のオブジェクトを利用して、データをユーザーにとって都合のいい形に変換する - DomainModel層のオブジェクトを利用して、アプリケーション特有の問題を解決する - DataAccess層のオブジェクトを利用して、DomainModel層のオブジェクトを取得/保存する ## Service層(サービス層) - Controller層とDomainModel層、DataAccess層を仲介する層 - Service層のオブジェクトは - Controller層のオブジェクトから利用される - DomainModel層、DataAccess層のオブジェクトを利用する ## DomainModel層(ドメインモデル層) - アプリケーション特有の問題(i.e. 料金計算)を解決する層 - DomainModel層のオブジェクトは - DomainModel層、およびInfrastructure層以外のオブジェクトを利用しない - アプリケーション特有の問題を解決することだけに集中する ## DataAccess層(データアクセス層) - ファイルや関係データベースなどに保存されたデータにアクセスする層 - DataAccess層のオブジェクトは - DomainModel層のオブジェクトをデータに変換、保存する - データを取得し、DomainModel層のオブジェクトに変換する ## Infrastructure層(インフラストラクチャ層) - アプリケーションの構成を決定、技術的な問題を解決し、その他の層の実装をサポートする層 - たとえば - フレームワーク(i.e. Zend Framework) - メーラー(i.e. Swiftmailer) - DIコンテナ(i.e. Pimple) - データアクセスオブジェクト(i.e. PDO) - Infrastructure層のオブジェクトは - ほか、すべての層のオブジェクトから利用される - アプリケーションの変更に直接影響されない - たとえば - アプリケーションに画面を追加する場合、変更されるのはフレームワークのコードではなく、それを利用するアプリケーション側のコード - PDOをDoctrineに入れ替える場合、変更されるのはPDOではなくPDOを利用しているアプリケーション側のコード # アプリケーションを構成する概念およびオブジェクト ## DI(Dependency Injection:依存性の注入) - 広義の「DI」と狭義の「DI」で指しているものが異なるため、注意が必要 - [Inversion of Control コンテナと Dependency Injection パターン](http://kakutani.com/trans/fowler/injection.html)が参考になる ### 広義の「DI」 - IoC(Inversion of Control:制御の逆転)に同じ - アプリケーションを構成するオブジェクトを簡単に交換できるようにすること ### 狭義の「DI」 - 広義のDIを実現するための手法、もしくはオブジェクト - ServiceLocatorと対比される - パラメータの型やType Hintingなどを参考に、あるオブジェクトが必要としている別のオブジェクトを自動的に与える(依存性を注入する) - リフレクションを駆使するため、ServiceLocatorに比べて処理の流れがつかみにくい - Zend Framework 2ではDIよりもServiceLocatorを利用することが推奨されている? ### ServiceLocator(サービスロケーター) - 広義のDIを実現するための手法、もしくはオブジェクト - DIと対比される - ServiceLocatorは(利用者側から見た場合)単にServiceのコレクションとして振る舞う - Serviceを利用したいオブジェクトは、ServiceLocatorを通じてServiceを取得する - ServiceLocatorに追加(定義)するServiceを変更するだけで、利用者が取得するServiceを簡単に変更できる - ServiceLocatorの実装と利用方法については[Zend\ServiceManager](http://framework.zend.com/manual/2.0/en/modules/zend.service-manager.intro.html)や[Pimple](http://pimple.sensiolabs.org/)が参考になる ## Service(サービス) - 文脈によってServiceという用語が指しているものは異なるため、注意が必要 ### DIに関する文脈の場合 - なんらかの機能を提供するオブジェクトを指す - ほとんどすべてのオブジェクトをServiceと呼ぶことができる - ただし、メーラー、DB接続(i.e. PDO)など、ほとんど状態が変更されない(アプリケーション中で使い回したい)オブジェクトを指すことが多い ### アプリケーションの層構造に関する文脈の場合 - Service層に所属するオブジェクトを指す - (この場合の)Serviceの役割は、Controller層とModel層を仲介すること - Model層のオブジェクトを利用する処理は複雑になることが多い - EntityやDataMapperなど、Model層に所属するオブジェクトの操作を肩代わりし、Controllerが肥大化しないようにする - Entityがうまく解決できない問題に対処するのもServiceの役割 - アプリケーション特有の問題と技術的な問題がうまく分離できない場合、処理をControllerに記述すると実装が複雑になりやすいため、とりあえずServiceを用意しておくと問題になりにくい - たとえば - 通知メールの送信 - CSVのインポート - あとからServiceの機能をEntityに移動することは容易 ## View(ビュー) - ユーザーへのレスポンスを、ユーザにとってわかりやすい形(i.e. 画面)に変換するオブジェクト - テンプレートを使って画面を生成するのがViewであり、テンプレートはViewそのものではない - テンプレートエンジン(i.e. Smarty)は - Infrastructure層に所属するオブジェクト - Viewの実装を支援する - Viewにテンプレートをレンダリングする以外の機能がない場合、Viewそのものとして扱われていることが多い ## Entity(エンティティ) - ドメインモデル層に所属するオブジェクト - 単にModel(モデル)と呼ばれることも多い - 混同されている - Entityの役割は、アプリケーション特有の問題(i.e. 料金計算)を解決すること - 自身の永続化(DBやファイルなどに保存すること)は、Entityの主な役割ではない - ActiveRecord、DataMapperを参照 - Entityは必ず一意な識別子(ID)を持つ - 2つのEntityが同じものかどうかは、属性(その他のプロパティ)に関わらず、IDか同じかどうかだけで判断する - 自然数(DBテーブルの人工キー)を利用することが多い ## Factory(ファクトリー) - オブジェクトを生成するオブジェクト - EntityやServiceの初期化は実装が複雑になることが多いため、Factoryとして別のオブジェクトに分離しておくと問題になりにくい ## ActiveRecord(アクティブレコード) - Entityを永続化するための機能をEntity自身が持つパターン、もしくはそのパターンにもとづくオブジェクト - Ruby on RailsやSymfonyで採用されていた - 現在はDataMapperが主流 - ActiveRecordを採用した場合、Entityは複数の役割を持つことになる - アプリケーション特有の問題を解決する役割 - 自身(計算結果など)を永続化する役割 - DataMapperに比べて、 - 使いやすい - 計算(アプリケーション特有の問題を解決)、永続化(計算結果などを保存)の流れが1オブジェクトで完結するため、利用者側のコードは簡潔になる - 複数の役割(機能)を持つため、Entityの実装が肥大化しやすい - オブジェクトのプロパティとDBテーブルのカラムが1:1で対応するなど、オブジェクトとDBテーブルの対応関係が簡単な場合はあまり問題にならない ```dummy.php $entity = Entity::load($id); $entity->setName($name); $entity->save(); ``` ## DataMapper(データマッパー) - Entityを永続化するための機能をEntity以外のオブジェクトが持つパターン、もしくはそのパターンにもとづくオブジェクト - Zend FrameworkやSymfony、Ruby on Railsで採用されている - ActiveRecordに比べて、 - 使いにくい - 計算(アプリケーション特有の問題を解決) => 永続化(計算結果などを保存)の流れに複数のオブジェクトが絡んでくるため、利用者側のコードは複雑になる - 単一の役割しかもたないため、Entityの実装が肥大化しにくい - オブジェクトとDBテーブルの対応関係が複雑になっても問題になりにくい - 特に思想がない場合はActiveRecordよりもDataMapperを採用しておくほうが無難 ```dummy.php $dataMapper = new DataMapper(); $entity = $dataMapper->load($id); $entity->setName($name); $dataMapper->save($entity); ``` ## Repository(リポジトリ) - Entityを効率的に永続化するためのオブジェクト - DataMapperは単一のEntityを永続化するためのオブジェクト - Repositoryは複数のEntityを永続化するためのオブジェクト - Doctrine ORMのEntityManagerはRepositoryとしての役割も持つ ```dummy.php $repository = new Repository(); $first = new Entity(); $second = new Entity(); $repository->add($first); $repository->remove($first); $repository->add($second); $repository->flush(); ``` ## TableDataGateway(テーブルデータゲートウェイ) - DBテーブルの操作を簡単にするためのオブジェクト - DataAccess層に所属する - DBテーブルと1:1で対応する - JOINなど複数のDBテーブルにまたがる操作は複雑になる - DBテーブルに対するクエリをカプセル化する - TableDataGatewayを利用する側は、メソッドを通してDBテーブルからデータを取得/DBテーブルにデータを保存できるようになる - [Zend_Db_Table](http://framework.zend.com/manual/1.12/ja/zend.db.table.html)のドキュメントが参考になる ```dummy.php $table = new TableDataGateway('table_name'); $table->find('primary_key'); $table->findAll(); $table->insert(array('column_name' => 'column_value')); $table->update(array('column_name' => 'column_value'), 'primary_key'); $table->delete('primary_key'); ``` ## RowDataGateway(行データゲートウェイ) - DBテーブル行の操作を簡単にするためのオブジェクト - DataAccess層に所属する - DBテーブル行と1:1で対応する - DBテーブル行に対するクエリをカプセル化する - RowDataGatewayを利用する側は、メソッドを通してDBテーブル行からデータを取得/DBテーブル行にデータを保存できるようになる - RowDateGatewayの実装および利用方法については[Zend_Db_Table_Row](http://framework.zend.com/manual/1.12/ja/zend.db.table.row.html)が参考になる ```dummy.php $table = new TableDataGateway('table_name'); $row = $table->find('primary_key'); $row ->set('column_name', 'column_value') ->save(); ``` ## ValueObject(値オブジェクト) - 名前の通り、値を表すオブジェクト - Entityと同じく、アプリケーション特有の問題を解決する役割を持つ **こともある** - Infrastructure層もしくはDomainModel層に所属する(?) - ValueObjectがEntityと同じくアプリケーション特有の問題を解決する役割を持っている場合、そのValueObjectはEntityと同じくDomainModel層に所属する - ValueObjectが汎用的な(アプリケーションに特有でない)機能しか持っていない場合(i.e. DateTime)、そのValueObjectはInfrastructure層に所属する - Entityとは異なり、ValueObjectはIDを持たない - 2つのValueObjectが同じものかどうかは、すべての属性(プロパティ)が同じかどうかなどで判断する - 色や日付など、ある値を表す方法が複数存在する場合、ValueObjectを用意しておくと実装が簡潔になる |値 |表記例 | |--------|------------------------------------------| |Color |#000000, rgb(0, 0, 0), hsl(0, 0, 0) | |Date |2014/1/1, 2014-1-Jan, 2014/01/01 00:00:00 | |DataSize|1バイト, 1Byte, 0.001KB | |Price |1ドル, 90円, 1.4ユーロ | ### ValueObjectの例 ```php <?php class DataSize { protected static $cofficients = array( 'byte' => 1, 'kilobyte' => 1e3, 'megabyte' => 1e6, ); protected $bytes; public function __construct($value, $unit = 'byte') { $this->value = $value * static::$cofficients[$unit]; } public function value($unit = 'byte', $precision = 1) { return round($this->value / static::$cofficients[$unit], $precision); } } ``` ```php $size = new DataSize(1000); $size->value('byte'); // 1000 $size->value('kilobyte'); // 1 ``` |
|
| 114位 |
|
|||
|
15:35:32 |
|
|
jsのconsoleまわりのテクニックはいろいろありますが、個人的に有用だと思ったものにしぼって紹介します。
chromeなら、command + option + jでconsoleを開いて下記のコードを貼りつければ実際に動きを確認できます(1分でできます!!)。 ##console.logの出力結果をcssで装飾する ###出力例  ###サンプルコード ```javascript:console.log console.log("%csuccess", "background-color:#5bb75b;color:white"); console.log("%cfail", "background-color:#da4f49;color:white;font-size:30px"); ``` ##実行時間を計測して出力する ###出力例  ###サンプルコード ```javascript:console.time console.time("time test1") console.time("time test2") console.timeEnd("time test1") console.timeEnd("time test2") ``` ##consoleの出力内容をグルーピングして可読性を向上させる ###出力例  ###サンプルコード ```javascript:console.group.js console.group("group test1"); console.log("test1"); console.groupEnd("group test1"); console.group("group test2"); console.log("test2"); console.groupEnd("group test2"); ``` ##条件式によって出力するかしないかを判断する ###出力例  ###サンプルコード ```javascript:console.assert //console.assertの第一引数がfalseの場合、第二引数が表示されます //trueの場合は何も表示されません。 console.assert(true,"trueだと何も表示しない") console.assert(false,"falseだとこの内容を出力する") ``` ##あとがき consoleには他にも色々メソッドがあります。気になった方は、consoleに紐づくメソッドを調べてみるとよいかもしれません。 |
|
| 115位 |
|
|||
|
14:41:09 |
(co-meeting, Inc. 所属) |
|
通常ブランチを作ってからブランチを切り替えて実装を始めますが、たまにはうっかりブランチを作るのを忘れてしまうことありますよね。
そんなときの対処法のメモです。要は新しく作った別のブランチにコミットを移動する方法です。 間違えて3つmasterにコミットしてしまっている状態で、新しくbranch01ブランチを作ってそこに移すというシナリオで書いていきます。 ## branch01ブランチを作る ### ブランチを作るべきだった位置からブランチを作る ``` $ git branch branch01 origin/master ``` `origin/master`のところはコミット番号でも構いません。 ### branch01ブランチに切り替え ``` $ git checkout branch01 ``` ## branch01ブランチに移動したいコミットをコピー ### コミット番号を調べる ``` $ git log --oneline --decorate --graph master * m000003 (master) なんか修正3 * m000002 なんか修正2 * m000001 なんか修正1 * m000000 (origin/master, origin/HEAD) 作業開始地点 ``` m000001からm000003までの3コミットがbranch01に移したいコミットとします。 ### branch01ブランチに移したいコミットをcherry-pickコマンドでコピー ``` $ git cherry-pick m000001 $ git cherry-pick m000002 $ git cherry-pick m000003 ``` ### 確認 ``` $ git log --oneline --decorate --graph master branch01 * b000003 (HEAD, branch01) なんか修正3 * b000002 なんか修正2 * b000001 なんか修正1 | * m000003 (master) なんか修正3 | * m000002 なんか修正2 | * m000001 なんか修正1 |/ * m000000 (origin/master, origin/HEAD) 作業開始地点 ``` branch01ブランチにコピーできてる。 コミット番号は新しく振られます。 ## masterから移したコミットを削除する ### masterに切り替える ``` $ git checkout master ``` ### コミットを3つ削除する ``` $ git reset --hard HEAD~3 HEAD is now at m000000 作業開始地点 ``` `HEAD~3`は戻す数によって変えてください。 ### 確認 ``` $ git log --oneline --decorate --graph master branch01 * b000003 (branch01) なんか修正3 * b000002 なんか修正2 * b000001 なんか修正1 * m000000 (HEAD, origin/master, origin/HEAD, master) 作業開始地点 ``` きれいになりました。 ## 参考 * [gitで誤ったブランチに対して行った変更を正しいブランチへ移す(cherry-pick編) - TIM Labs](http://labs.timedia.co.jp/2010/12/git-moving-changes-with-cherry-pick.html) |
|
| 116位 |
|
|||
|
21:59:04 |
|
|
自分が比較的よく使うadbのコマンドです。 ## adb ```bash:コマンド adb ``` ダウンロードしたSDKの`platform-tools`ディレクトリにパスを通しておく。 `adb` だけで実行すると使い方を見られる。 ```bash:オプション adb -e コマンド # エミュレータにコマンドを送る adb -d コマンド # 端末にコマンドを送る adb -s xxxx コマンド # コマンドを送る端末を指定できる ``` 接続している端末が1台だけのときは指定しなくて良い。 ## adbのバージョン ```bash:コマンド adb version ``` ```bash:出力例 Android Debug Bridge version 1.0.31 ``` このコマンドはほとんど使わないけど、この記事を書いた時の記録として。 ## 接続している端末の確認 ```bash:コマンド adb devices ``` ```bash:出力例 List of devices attached HT05FPL09004 device ``` `HT05FPL09004` が端末の識別番号のようなもの。 複数端末をつないでいる時に `adb -s HT05FPL09004 shell` のように指定すると、その端末にコマンドを送れる。 ## apkのインストール・アンインストール ### インストール ```bash:コマンド # 通常のインストール adb install xxxx.apk # アップデートインストール adb install -r xxxx.apk ``` `xxxx.apk` にインストールしたいapkのファイル名を指定する。 すでに端末に同じ署名のapkがインストールされてる場合は `adb install -r xxxx.apk` のように `-r` をつけると上書きできる。データファイルは保持される。 ### アンインストール ```bash:コマンド adb uninstall com.xxxx.appname ``` `com.xxxx.appname` にアンインストールしたいアプリのパッケージ名を指定する。 完全なパッケージ名を覚えていない場合、後述の`pm list packages` が便利。 ## ログの取得 ```bash:コマンド # オプションを指定せずにログ出力 adb logcat # 出力形式を指定してログ出力 adb logcat -v time # 現在のログをダンプする adb logcat -d # ログのクリア adb logcat -c ``` 標準出力に出力されるので、だいたいは `adb logcat -v time > log.txt` のようにリダイレクトしてる。 あとはvimで `:r! adb logcat -v time -d` のようにして取り込んだりする。 ## ファイルの転送 ```bash:コマンド # PCから端末へ adb push PC上のファイル名 端末上のファイル名 adb push hoge.txt /sdcard/ # 例 # 端末からPCへ adb pull 端末上のファイル名 PC上のファイル名 adb pull /sdcard/hoge.txt . # 例 ``` 転送先のファイル名は指定しても良いけど、ファイル名を変えることはあんまりないので省略することが多い。 ## adbの起動・停止 ```bash:コマンド # adbの停止 adb kill-server # adbを起動 adb start-server ``` adbがうまく動いてないんじゃないか?と思った時にkill→startで再起動させてる。 ## shell ```bash:コマンド # shellを起動 adb shell # shelコマンドを実行 adb shell コマンド ``` shellにコマンドを実行させたいときは `adb shell コマンド` の形で使う。 __以下はshell上で実行できるコマンド__。 ### アプリの起動 ```bash:コマンド # Activityの起動(ACTION_VIEW + URL) am start -a android.intent.action.VIEW -d http://google.com # Activityの起動(クラス名を指定) am start -n com.hoge.app/.FugaActivity # サービスの起動 am startservice ... # Intentの指定方法はActivityと同じ # ブロードキャストの送信 am broadcast ... # Intentの指定方法はActivityと同じ ``` Intentの内容を `-a`、`-d`、`-n` などのオプションで指定する。デバッグ実行したい場合は `-D` をつける。その他のオプションは `am` 単体で実行すれば見ることができる。 shellを起動せずに実行するときは `adb shell am start -a android.intent.action.VIEW -d http://google.com` のように指定する。 ### 文字入力・キーイベント送信 #### 文字入力 ```bash:コマンド input text hoge ``` マルチバイト文字列は送信できない(と思う)。 IMEが起動していると、IMEの入力モードの影響を受けることがある。 #### キーイベント送信 ```bash:コマンド input keyevent 3 # HOMEキー input keyevent 4 # BACKキー input keyevent 82 # MENUキー ``` 数値でキーコードを指定する。 キーコードは [KeyEvent | Android Developers](http://developer.android.com/reference/android/view/KeyEvent.html "KeyEvent | Android Developers") を参照。 ### パッケージ名列挙 ```bash:コマンド pm list packages ``` インストールされてるアプリのパッケージ名が列挙される。 `adb uninstall` で必要なパッケージ名を一部しか覚えていないときに `adb shell pm list packages | grep hoge` のようにして使うことが多い。 (追記) grepを使わなくても、`adb shell pm list packages hoge` でフィルタリングできた。 ### cat ```bash:コマンド cat /sdcard/hoge.txt ``` 読み込み権限のあるファイルの中身を簡単に確認するときに使う。 vimで `:r! adb shell cat /sdcard/hoge.txt` のように使うことが多い。 |
|
| 117位 |
|
|||
|
16:12:09 |
|
|
##特技は`git commit -a -m いろいろ修正`です!
なんて奴は今すぐredbullの角に頭ぶつけて詫びるべき。 しかしこまめにコミットするのは面倒臭いですよね。でもrebaseやらrevertやらcherry-pickするにもコミットログは綺麗にしたい。そんなズボラで凝り性なあなたは`git add -p`でコミットを整えるといいと思います。 ```text:members 1: 島村卯月 2: 渋谷凛 3: 本田未央 4: 前川みく 5: 高森藍子 ``` なんだかよくわからない名簿があったとします。**どういう因果か1コミットにつき1行の変更するのが決まりとしましょう。** 下のように変更してみました。 ```diff -1: 島村卯月 +1: 福山舞 2: 渋谷凛 3: 本田未央 4: 前川みく -5: 高森藍子 +5: 日野茜 ``` さてこのまま`git add`すると両方の変更が一度にステージングされてしまいます。 そこで`git add -p`すると以下のようなdiffがコンソールに表示されます。 ```diff:console diff --git a/members b/members index 15440ce..1f43716 100644 --- a/members +++ b/members @@ -1,5 +1,5 @@ -1: 島村卯月 +1: 福山舞 2: 渋谷凛 3: 本田未央 4: 前川みく -5: 高森藍子 +5: 日野茜 Stage this hunk [y,n,q,a,d,/,s,e,?]? ``` 変更のひとかたまり(ハンク)を表示し、そのハンクに対しての指示を待ち受けます。サンプルが小さいのですべて表示されますが、ハンクの間が7行以上空くと別のハンクとして扱われます。 ##変更を分割する コミットを分割するのが目的なのでここで`s`を入力します。 ```diff:console Split into 2 hunks. @@ -1,4 +1,4 @@ -1: 島村卯月 +1: 福山舞 2: 渋谷凛 3: 本田未央 4: 前川みく Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? ``` するとハンクが2つに分割され、diffの表示される範囲が狭まり一人だけの変更になりました。`y`でこの変更をステージングしましょう。 さらに次のハンクが表示されステージングするか否か選べますがひとまず`n`でスキップするか`q`で終了します。 ```git:git-status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: members # # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: members # ``` `git status`を見るとmembersステージされているにもかかわらずワークスペースに残った状態になってますね。 ここで`git diff`を見ると ```diff:git-diff @@ -2,4 +2,4 @@ 2: 渋谷凛 3: 本田未央 4: 前川みく -5: 高森藍子 +5: 日野茜 ``` 最終行の変更だけが残っています。 そしてステージングされた状態を確認するために`git diff --cached`すると ```diff:git-diff-cached @@ -1,4 +1,4 @@ -1: 島村卯月 +1: 福山舞 2: 渋谷凛 3: 本田未央 4: 前川みく ``` 先頭の変更だけがステージングされていますね。 これで1つの変更だけどコミットすることができます。 ##さらに分割する ハンク同士が1行以上空いている場合は`s`で分割できますが、連続する行の場合はそうはいきません。 ```diff:console @@ -1,5 +1,5 @@ 1: 島村卯月 -2: 渋谷凛 -3: 本田未央 +2: 双葉杏 +3: 諸星きらり 4: 前川みく 5: 高森藍子 Stage this hunk [y,n,q,a,d,/,e,?]? ``` このように変更した行が連続する場合`s`が使えません。もしこの状態を分割したい場合は`e`で直接編集する必要があります。 ```diff:edit # Manual hunk edit mode -- see bottom for a quick guide @@ -1,5 +1,5 @@ 1: 島村卯月 -2: 渋谷凛 -3: 本田未央 +2: 双葉杏 +3: 諸星きらり 4: 前川みく 5: 高森藍子 # --- # To remove '-' lines, make them ' ' lines (context). # To remove '+' lines, delete them. # Lines starting with # will be removed. # # If the patch applies cleanly, the edited hunk will immediately be # marked for staging. If it does not apply cleanly, you will be given # an opportunity to edit again. If all lines of the hunk are removed, # then the edit is aborted and the hunk is left unchanged. ``` `e`を押すとエディタが立ち上がり、diffの編集画面になります。元の状態からの差分を記述することでその変更を適用することができます。 ```diff:edit/2行目だけを適用する場合 # Manual hunk edit mode -- see bottom for a quick guide @@ -1,5 +1,5 @@ 1: 島村卯月 -2: 渋谷凛 +2: 双葉杏 3: 本田未央 4: 前川みく 5: 高森藍子 # --- # To remove '-' lines, make them ' ' lines (context). # To remove '+' lines, delete them. # Lines starting with # will be removed. # # If the patch applies cleanly, the edited hunk will immediately be # marked for staging. If it does not apply cleanly, you will be given # an opportunity to edit again. If all lines of the hunk are removed, # then the edit is aborted and the hunk is left unchanged. ``` 上記のように内容を変更し保存するとステージングとして適用されます。ちなみにdiffとして正しければ何を書いてもいいのでこの画面で変更をさらに変更することもできます。フォーマットがおかしかったり差分が解決できない場合はステージングされません。 ```diff:git-diff-cached @@ -1,5 +1,5 @@ 1: 島村卯月 -2: 渋谷凛 +2: 双葉杏 3: 本田未央 4: 前川みく 5: 高森藍子 ``` これで連続した行の変更を分割してステージングすることができました。 ##これで綺麗なコミットログがつくれるね! とは言うものの変更が増えた後にいちいちハンクを分割してコミットだのやってられないので作業のスコープをちゃんとしぼってこまめにコミットしたほうがいいと思います。ちなみに僕の特技は`git commit -a -m いろいろ修正`です! ##おまけ:git add -pした時のコマンド一覧 * `y` - このハンクをステージングする * `n` - スキップする * `q` - 終了する * `a` - 以降のハンクをすべてステージングする * `d` - 以降のハンクをすべてスキップする * `g` - 指定したハンクへ移動 * `/` - 正規表現によるハンクの検索 * `j` - 未確定な前のハンクへ移動する * `J` - 前のハンクへ移動する * `k` - 未確定な次のハンクへ移動する * `K` - ハンクへ移動する * `s` - ハンクを分割する * `e` - 手動で現在のハンクを修正する * `?` - ヘルプを表示する |
|
| 118位 |
|
|||
|
00:37:24 |
|
|
AngularJSは公式で分かりやすいチュートリアルが用意されているし、日本語の記事も増えてきたし、けっこう簡単に使い始めることができるんじゃないかと思います。
でも、チュートリアルやサンプルはクライアントサイドオンリーなことが多くて、サーバーサイドも含めたWebアプリを作ろうと思うと、どういう構成にすればいいのか迷うのではないでしょうか?(僕がそうでした) 最初は試行錯誤していたのですが、書籍やネットの記事を読んだりGitHubで見つけたアプリを真似たりしているうちに、どういう構成にすればいいのかだんだん見えてきたので、解説してみたいと思います。 # SPA 最近、SPA(Single Page Applicationまたは Single Page Web Application)という言葉をよく耳にするようになりました。 SPAとは、最初のページだけ通常のWebアプリと同じようにサーバーからHTMLを取得して表示し、それ以降は、AJAXを利用してデータやテンプレートを取得しつつ、クライアントサイドで画面を書き換えたりページ遷移を行うような構成のWebアプリケーションのことです。 AngularJSを始めとするクライアントMVCのフレームワークを使ってアプリケーションを作る場合、SPAの構成にするのが一般的なようです。(もちろん、AngularJSを使ってSPAじゃない構成にすることも可能です) では、なぜSPAを採用するのでしょうか? それは、よりネイティブアプリケーションに近いUIを提供するためです。 ユーザーが操作するたびにサーバーに問い合わせてHTMLを生成するよりも、クライアントサイドでできることはできるだけクライアントサイドで行ったほうが早いですし、より細かなインタラクションが実現できます。 一方、静的コンテンツが多くて動きの少ないアプリケーションの場合、SPAにするメリットは少ないです。 また、クローラー対策が面倒だったりするので、SEOを重視する場合は向いていないかもしれません。 # サーバーサイドとクライアントサイドの役割 さて、SPAにすることが決まったら、サーバーサイドとクライアントサイドの役割分担は明確になります。 SPAの構成では、今までサーバーサイドで行っていた処理の多くをクライアントサイドに移譲することになります。 よって、サーバーサイドは以下のようにシンプルになります。 * MVCパターンのViewにあたるものが不要となる * 主な役割はJSONを返すRESTfulなAPI * 基本的にステートレス。状態を持つとしても認証情報などの限られたものだけ 一方のクライアントサイドは複雑で規模も大きくなります。 * 保守性向上のためMVCパターンを適用し、プレゼンテーション層とドメイン層を分離する * ユーザーの操作に応じてインタラクティブに動くリッチクライアント * ステートフル クライアントMVCについて説明するとちょっと長くなりそうなので、詳細については次回の記事で解説したいと思います。 # ビルドツール Play FrameworkやRuby on Railsのようなサーバーサイドのフレームワークには、JavaScriptやCSSを結合・圧縮してくれたり、altJSをコンパイルしてくれたりするような仕組みがあります。 僕も最初はこの機能を使ってAngularJSアプリの開発を行なっていました。 だけど、サーバーサイドのフレームワークに付属するこれらの機能はあくまでもオマケであって、ちょっと込み入ったことをしようとすると機能不足を感じてしまいます。 そこで、餅は餅屋、クライアントサイドにはクライアントサイド用のツールを使うのがよいと判断しました。 ビルドプロセスはGruntで記述し、パッケージ管理はBower、ひな形の自動生成はYeoman、テストランナーはKarmaなどなど、便利なツールがたくさんあります。最初は覚えるのが大変ですが、使いこなせば捗ります。 最近よく使われるツールについては、下記のスライドがまとまっていて分かりやすいです。 * [JavaScriptフロントエンド開発の昨今](https://speakerdeck.com/naoya/javascripthurontoendokai-fa-falsezuo-jin) # ディレクトリ構成 Play FrameworkやRuby on Railsでは、サーバーサイドのソースコードを`app`に、クライアントサイドのソースコードを`app/assets`格納し、静的コンテンツを`public`に格納する構成となっています。 しかし、今回はGruntなどクライアントサイドのツールを使うことにしたので、`app/assets`ディレクトリは使いません。 代わりに`ui`ディレクトリ(名前は何でもよいです)を追加し、クライアント用のソースコードやGruntfileなどをこの中に配置します。 そして、`ui`ディレクトリ内でGruntを実行すると、コンパイル結果のJavaScriptやCSSを`public`ディレクトリに配置するようにします。 ディレクトリ構成例を以下に示します。(説明に不要なディレクトリは省略しています) ~~~ Project ├ app サーバーサイドのソースコード ├ public 静的コンテンツ └ ui クライアントサイドのソースコード ~~~ サーバーサイドにPlay Frameworkを使っている場合は下記のソースコードが参考になります。 * [Play Grunt / Angular Prototype](https://github.com/leon/play-grunt-angular-prototype) Ruby on Railsを使っている場合は下記の記事が分かりやすいです。 * [理想的な Rails, AngularJS 環境の構築](http://bokukoko.hatenablog.com/entry/20130825/1377415299) # ルーティング "シングルページ"と言われると、ページごとにURLが振られないから個別にブックマークもできないし、戻るボタンや進むボタンも使えなくて不便じゃないの?と思われるかもしれませんがそんなことはありません。 AngularJSでは、HTML5のpushState/popStateを利用することにより、クライアントサイドでページの切り替えを行いつつ、それに合わせてURLの変更を行うことができます。(HTML5モードを有効にした場合。参考:[AngularJS の $locationProvider.html5Mode について](http://qiita.com/shogogg/items/542bd6d18f777bcc24bc)) 僕は最初このあたりの仕組みをちゃんと理解できていなかったので、サーバーサイドとクライアントサイドのルーティングをどう使い分ければいいのか少し悩みました。 分かってしまえば全然難しくないんですけどね。 というわけで、[先ほどで紹介したPlay+AngularJSのサンプル](https://github.com/leon/play-grunt-angular-prototype)のルーティング設定を図示してみました。  初回のページ取得はサーバーに問い合わせ、それ以降のユーザー操作はクライアントサイドで捌き、裏でAJAXを使ってサーバーからデータやテンプレートを取得する構成となっています。 # まとめ アーキテクチャ設計は、システムの特性に応じて考えなければならないものなので、今回解説した内容がどんなアプリにでも適用できるというわけではありませんが、少しでも考え方の参考になればうれしいです。 # 参考情報 * [Single page apps in depth](http://singlepageappbook.com/) * [Mastering Web Application Development with AngularJS](http://www.amazon.co.jp/Mastering-Web-Application-Development-AngularJS-ebook/dp/B00EQ67J30) * [GUIアーキテクチャパターンの基礎からMVVMパターンへ](https://www.slideboom.com/presentations/591514/GUI%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%AE%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89MVVM%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%B8) * [ダブルMVCの意味するところ](http://wazanova.jp/post/64057743910/mvc-gogaruco-2013) * [エンタープライズ アプリケーションアーキテクチャパターン](http://www.amazon.co.jp/dp/4798105538) * [サービスデザインパターン](http://www.amazon.co.jp/dp/4048865366) * [Backbone.jsを利用したクライアントサイドMVCの導入についてそろそろ書いておくか](http://d.hatena.ne.jp/kazuk_i/20110407/1302130947) |
|
| 119位 |
|
|||
|
13:19:06 |
(Quipper, Ltd. 所属) |
|
すでにマージ済みのブランチをまとめて削除するには以下のようにする (master ブランチを checkout していると仮定する) ``` $ git branch --merged | grep -v '*' | xargs -I % git branch -d % Deleted branch foo (was ce1b0d5). Deleted branch bar (was 7623b2b). Deleted branch baz (was d4c396d). ``` ---- 長いのでエイリアスをはるとよい。シェルの特殊文字 `*` のエスケープが必要なことに注意。 ``` $ git config --global alias.delete-merged-branches '!git branch --merged | grep -v \* | xargs -I % git branch -d %' ``` 以下のように設定されていれば、以降は `git delete-merged-branches` で実行できる。 ``` $ git config --list | grep alias.delete-merged-branches alias.delete-merged-branches=!git branch --merged | grep -v \* | xargs -I % git branch -d % ``` ```~/.gitconfig [alias] delete-merged-branches = !git branch --merged | grep -v \\* | xargs -I % git branch -d % ``` ---- 2015/03/18 改訂 - [git-flow](http://danielkummer.github.io/git-flow-cheatsheet/index.ja_JP.html) ライクなブランチ運用ルールを採用している場合に `master` ブランチまで消してしまってびっくりする問題を修正 - 以下のコードスニペットをシェルスクリプトにしたものを [GitHub で公開](https://github.com/kyanny/git-delete-merged-branches) ``` git branch --merged | grep -vE '^\*|master$|develop$' | xargs -I % git branch -d % ``` 自分は以下のようなエイリアスを作って `git fetch` や `git pull` したとき同時に実行されるようにしている(シェルスクリプト `git-delete-merged-branches` をパスの通った場所にインストールしておくこと) ```~/.gitconfig [alias] fp = !git fetch -p && git-delete-merged-branches pp = !git pull -p && git-delete-merged-branches ``` |
|
| 120位 |
|
|||
|
23:48:58 |
(フリーランス 所属) |
|
ある幅に合わせて文字列を省略したいときがありますが、
レンダリングされる文字によって幅が違うので、文字数で制御するのは割と面倒ですね。 ですが以下のようにcssを書いておけばきっちり決めた幅であふれた部分を「...」で省略してくれます。 ```css p{ width: 100px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; -o-text-overflow: ellipsis; /* Opera9,10対応 */ } ``` ```html <p>もじもじもじもじもじもじもじもじもじもじもじ</p> ``` 面白いのは、省略されて後ろについた「...」までを選択してコピペすると省略されて隠れている部分までちゃんとコピペできます。 SEO的にも問題なさそうだし使ってみよう |
|
| 121位 |
|
|||
|
20:52:45 |
(Drivemode, inc. / DroidKaigi 所属) |
|
Honeycomb から登場した Fragment によって、すべてのコントローラロジックが Activity に集約されてしまう呪縛から解き放たれ、人類は平和を取り戻しました。Fragment はまた、Support Library のメンバとして、Gingerbread 以前の過去世に積み上げた罪業をも消し去ろうとしています。
そんな Fragment ですが、いまいち使い方が分からない、という声も時折耳にします。本稿では、Android アプリケーションの開発を始めて Activity と Fragment について学び、その両者の住み分けに悩んでいる方向けに、効果的な Fragment の作り方を紹介しようと思います。 ## Fragment がなかった暗黒の時代 Fragment がなかった時代、人類はどのようにして Android アプリケーションを作っていたのでしょうか。 ### タブを使った画面の切替 複数のタブを持ち、タブごとに異なるコンテンツを表示するようなアプリケーションを作ると仮定します。 このようなアプリケーションを作るには、以下の手順でタブの機能とコンテンツの表示を実現します。 1. タブ切り替えをハンドリングするホストとなる Activity を作る 2. タブの数だけ、コンテンツを表示する Activity を作る ざっくりとした手順では数こそ多くないものの、この方法にはいくつかの問題が潜んでいます。 1. Activity という割と大きなオブジェクトを、タブのホストとタブのコンテンツの数だけメモリに乗せないといけない 2. いくつかの専用のコンポーネントとガッツリ連携しなければ実現できないため、柔軟性に乏しい ### レイアウトの使い回し Android のレイアウトの仕組み上、xml に以下のようなコードを仕込んでおくと、レイアウトリソースの使い回しが実現できます。 `<include layout="@layout/hogehoge" android:id="@+id/included_layout">` これは非常に便利な反面、View のレイヤでしか使い回しが効かないので、それを統括するコントローラのロジックまでは使い回しが出来ません。 ### ダイアログの生成とライフサイクル ダイアログは、Activity が管理します。Activity にはライフサイクルが有り、ライフサイクルが終わるとその役目も終えなければならず、例えば、画面回転をすると、表示していたダイアログも消えてしまいました。 そのため、ダイアログの表示に関するフラグを持ち越すコードを記述し、そのフラグを見て、新しい Activity がダイアログの表示を継続するかどうか判断しなければなりませんでした。 ## Fragment の登場による文明開花 Honeycomb になり、Fragment は ActionBar と共に颯爽と現れました。 そして、タブ機能を実現するための既存のコンポーネント群は @Deprecated となり、代わりに、Fragment の使用を促されるようになりました。 ActionBar により、アプリ内のナビゲーションに関する振る舞いが統一され、それにともなって、ActionBar との組み合わせとして手軽に扱うことの出来る Fragment の使用が推奨されるようになったのです。 ### Fragment とは Android Developers の Fragment の Javadoc には、以下のようにあります。 > A Fragment is a piece of an application's user interface or behavior that can be placed in an Activity. UI を持ち、その UI の振る舞いを管理する、Activity の中に組み込むことが出来るコンポーネントが Fragment です。つまり、これによって、`<include>`タグだけでは実現できなかった、コントローラのロジックを使いまわす事ができるようになったのです。 また、ActionBar にかぎらず、ViewPager のような View コンポーネントからも Fragment が取り扱えます。以前のタブと異なり、実装に用いるインタフェースも単純化されたため、タブほどの実装コストも掛けずに同等の機能を実装することが出来るようになりました。 Dialog に関しては、専用の DialogFragment が提供され、Fragment の管理ポリシーに基いて DialogFragment も管理されるようになったため、以前のように、表示に関するフラグを持ったり、そのフラグを持ち越したりと言ったコードを自分で実装する必要がなくなりました。 ## マルチデバイス時代の到来 今や Android は、スマートフォンのようなハンドセット端末にとどまらず、タブレット端末でも使われます。そして、iOS のユニバーサルアプリケーションに見られるように、ハンドセット端末とタブレット端末で共通のアプリを配信する仕組みが整備され、1 つのアプリケーションが様々な端末へと配信できるようになりました。 ### 画面の大きさの違いに対応する ハンドセット端末とタブレット端末の違いで最も大きな違いは、画面の大きさです。タブレット端末の方が、より広い場所を使うことが出来るようになります。 この時、ハンドセット端末向けに作られたレイアウトをそのまま表示してしまうと、妙に伽藍とした画面になってしまうことでしょう。これでは、折角の大きく広い画面が無駄になってしまいます。 例えば、あるコンテンツの一覧画面と、各項目の詳細画面があるとします。 ハンドセット端末では、画面が小さいので、それぞれの画面は別の Activity として作るほうがよいでしょう。しかし、タブレット端末では、一覧表示だけ、詳細表示だけでは、かなりのスペースを無駄遣いしてしまいます。そこで、タブレット端末では、一覧表示を左端に寄せ、一覧から項目を選択すると、その左側に詳細表示を行うようにすします。そうすることで、広い画面を有効に活用できます。 このようなとき、一覧表示や詳細表示が、Fragment によってハンドリングされていたら、ハンドセット端末とタブレット端末への対応が、ぐっと楽になります。 なぜなら、ハンドセット端末向けには、一覧用と詳細表示用のそれぞれの Activity を作り、その Activity は Fragment を配置するロジックさえ持っておけば良く、また、タブレット端末向けには、1 つの Activity が、一覧用と詳細用の Fragment を配置するロジックを持っておけば済むようになるからです。 ## …こうして世界は Fragment に包まれた… この他にも、ViewPager と Fragment はとても相性良く連携します。ActionBar にも、簡単なインタフェースで実装できるタブの機能があり、ViewPager と ActionBar のタブの機能は、単純なインタフェースで連携することが出来ます。 実は、Fragment は UI を持たない実装をすることもできます。このようにすることのメリットは、Fragment にライフサイクルがあり、それが Activity のものと緊密に連動することから、Activity のためのデータを、ライフサイクルに合わせて管理するホルダーのような役割を持つことができることに有ります。 Activity のライフサイクルに合うようにクラスを設計するのは、面倒がつきまといます。それを、Fragment が肩代わりしてくれるのです。 また、役割の多い Activity ほど、より多くのデータを取り扱います。メンバ変数としてデータを保持するのではなく、データの保持を Fragment に任せることで、Activity の見通しが良くなります。 (若干の誇張表現がある気がするけどまあいいや…) |
|
| 122位 |
|
|||
|
02:13:07 |
(Simplearchitect 所属) |
|
はじめに
--- Kent Beck氏がスタートアップのイベントに登壇し、素晴らしい講演をしたビデオを友人のタイムラインから見つけました。[Startup Lessons Learnd: Kent Beck talks beyond agile programming](http://www.justin.tv/startuplessonslearned/b/262656520) アジャイルマニュフェストは10年が経過して、誰かの為にソフトウェアを作っていた時代から、スタートアップの時代に移行し、内容が一部古くなっていました。ところがこの講演でKentBeck氏は、それに対する素晴らしい回答をしてくれています。この内容が2010に行われているとは驚きです。 今回、このビデオを未熟なりにディクテーションして、適当ですが、日本語訳を作ってみました。本人に承認を取るつもりですが、ダメなら削除するつもりです。 スタートアップ時代のプログラマの考え方のヒントが沢山つまっているのではないでしょうか?つい、熱くなってしまいました。 また、ディクテーションも日本語訳もこれでいいかわからない部分もあるので、「違うだろ!」と思ったら是非Pull Requestを送っていただければと思います。皆さんのお役に立てれば幸いです! Kent Beck : Beyond agile programming --- ### 1. ごあいさつ 皆さん、こんな朝早くからおいでいただきありがとうございます。何か凄い事を達成したことをお話できるわけではありませんが、皆さんが来てくれて本当に嬉しいです。 私は今日予定されている講演の中で、最もイマイチなアントレプレナーとして講演する名誉を授かりありがとうございます。私は、11のスタートアップに関わってきました。しかも、その中の1社すらも凄いお金を稼いだわけではありません。 大きな規模で成功したのは、一つだけ。JUnitです。 私はお話する内容(リーンスタートアップの分野)に豊富な知識があるわけではありませんので、オープニングスピーチをしてしまっていいのだろうかとは思っています。しかし、皆さんにはすぐにお話しする事を理解していただけると思いますのでお話を始めたいと思います。 しかし、私はあきらめません。リーンスタートアップのスタッフの皆さんが、新しいビジネスに挑戦するエネルギーを私にくれたのですから! 特に私が人生を費やして磨き上げてきた技術的な側面からお話したいと思います。 ###2. やぎのかゆいところ 私は南オレゴンの、片田舎のさらにはずれにすんでいます。そして、やぎを飼っています。2匹のあかちゃんで、すごく可愛くてふわっふわなんです。彼らと外に出ていたときに、背中の方かた、その下の方にかけて掻いてあげていました。多分虫かなにかで、気持ち悪かったのでしょう。そして、お尻のあたりを掻いてあげたら突然、やぎが気持ちよさそうに啼きました「あー、気持ちいい」って多分。 面白い。私がいろいろなところを掻いてあげても、何もおこらなかったのに、あるポイント、つまりやぎがかゆいポイントを掻いてあげると、今までとずっと同じ事をしていても、やぎが気持ちよくなるのです。この事は、私は想像できませんでした。多くの他の場所でやっていた、効果薄だった同じ事が、突然、すっごくやぎにとってもむちゃくちゃ効果的な事になったのです。スタートアップもこれに似ていると思います。 あなたが何かをやり続けていても、レスポンスが無い。やっても、やっても、レスポンスが無い。8個目ぐらいのアイデアで、突然何かが起こる。これは、あなたがエッジだからわけではありません。違う場所を掻いていたからであって、あるポイントに当たると、突然何かが起こって、(顧客の)リアクションがあなたに教えてくれます。ここが、特別なポイントだと。私はこういった幸運をキャリアの中で何回か経験しています。ソフトウェアパターンとか、テスト駆動開発とか、100ぐらいのスケールしたアイデアがありますが、これは、「かゆいところ」にあたったのです。 ### 3. アントレプレナーがするべきこと アントレプレナーとして、私が探しているのはこの「かゆいところ」です。どのように沢山の時間やりつづけるかではありません。チェーンソーの通路を通るようなものではないのです。かゆくないところを何回も掻くようなことではありません。 私がやるべき事は、かゆいところを探す事です。それは、いろいろな要素が絡み合っています。タイミングであったり、幸運であったり、掻く事で自体であったり。 あなたが掻く事をしなければ、かゆいところを見つけることはできません。 ### 4. リーンスタートアップ リーンスタートアップは、このことの素晴らしい例です。何かのタイミングだったり、人々や、マーケットがOKをくれるメッセージだったり。世界中で、エリックがやっているのと同じような事を多くの人が実践しています。 1年前、かゆいポイントにはヒットしませんでした。が、リーンスタートアップに関する事がありました。過去15年の私のビジネスモデルとの組み合わせです。 ### 5. 資本効率のお話 自分がやろうと思っている事を、バリバリのモチベーションで始めます。素晴らしい資本効率はソフトウェアの中にだけある訳ではありません。ここにいる皆さんは、多分ソフトウェア畑の人がおおいですよね。多分ハードウェア系の人もいるでしょう。 資本効率が一番重要な事になったとしたら、あなたはきっと資本効率をよくするために、新しいビジネスモデルが必要になることでしょう。そして、資本効率がよくなるようなマーケットも必要になるでしょう。 そういうものが確立できて、次に来るのが、コミュニティのプラクティスでしょう、そして人々、リーンスタートアップの一部を担う、それぞれの個人だったり、名前だったり、メッセージだったり。デリバリの方法だったり、一緒にするタイミングだったり。 私にとって、リーンスタートアップは、これらの「レスポンス」を送ってくれます。世界中のかゆいところに当たった時のレスポンスを返してくれます。それが起こったら素晴らしいですね。 ### 6. Basic Loop だから、我々はわかったのです。基本的なループがあることに気づいたのです。 Build(ビルド), measure(測定), learn(学び)です。私はビルドをします。私はビルドを作るときに、本当に価値のある事をする事を選択できたら、とてもいい気分になります。測定したり、学んだり、数学的な事をやってみたり、ビルドをつくったり。 なぜなら、私はビルダーだからです。それが心地よいのです。 このループを、リーンシンキングの観点から見た場合、このループは本当は逆向きの方向進むのです(バックワード)。 ビルドから始める事。これは、私がやったことで、失敗続きだった方向なのです。ですので、皆さんは真似しなくてもいいと思います。もし、試してみたかったらやってみたらいいですが。しかし、このループは逆向きの方向が望ましいのです。 ### 7. マルチテーブル みなさんに2つのお話をしましょう。今スタートアップをやっているのです。 マルチテーブルが出来ることって良くないですか? あなたがオンラインのポーカーをやっているとします。最初は1対1でプレイができるようにします。しかし、それは相当退屈でしょう。だってリアルで出来る事ですから。 20ゲームのポーカーを一度にやること。コレはマルチテーブルといいます。 ### 8. マルチテーブル作戦 スタートアップとしては、資本効率を上げるためには、20人の人は1つのアイデアのスタートアップに要らないです。10人, 5人, 2人, 1人とどまる事をしりません。 それだけの人がいたら、あなたは、同時に2つのスタートアップができます。同時に2つのスタートアップが一度にできます。マルチテーブルみたいなものです。私は2つのことを試せるし、全くもって間違った事をやってることに気づいても致命的ではありません。 ### 9. TDDの例 なぜ、テスト駆動開発というクレイジーなアイデアを思いついたのでしょう。実際のコードを書く前に、テストコードを書きます。もちろん実体がないのですから、テストは最初はいつも失敗します。ところがいったんテストが動くようにコードを書けば、それはクールな事に変わるのです。だから、私はスクリーンキャストをしようと思っています。 TDDに関しては、本を書いて売っても儲かりません。他のメディア展開が必要です。自分でテスト駆動開発をやっているところを録画して、いい情報と、楽しめるナレーションをかぶせてみる。私にとっては一度にやるのは大変ですが、私が録音して公開すれば、人々が考えている事がわかるでしょう。 これが、ループを逆向きにまわす事でしょうか。ビルドから始まっています。 私は、いくつかのエピソードをVimeoに上げます。最初の10分を。それから、編集します。それから、人々が100回ぐらい見てくれたらOKでしょう、そうなったらマーケットに出します。 ### 10. 学習につまづく しかし、私は「学習」フェーズでつまづいてしまいました。だから、先に「ビルド」して、「計測」しました。しかし、何が「学習」を押し進められたのでしょうか? たぶん、何人かは興味をもってくれたとは思います。 私は逆向きのループに関して学びます。私はものづくりからはじめました。飛び越えて、モノ作りをすることから。 そして、今私は完成させるために、出版社さんとかスタッフの方を探しています。 しかし、私はこの流れでは、何か重要なバリューを失った気がするのです。 ### 11. 次のプロジェクト 対照的な次にプロジェクトを紹介させてください。次は、ニーズから始めました。私が出来るならば見つけ出したいだろうニーズです。(注:多分仮説の事かと思います) 49歳の脳みそは、昔のようにはいきません。私の脳力は、昔のようにはいきません。 ゆっくりしたらダメですかね?私が失った脳力を復活させられないだろうか? そこで、私は小さなゲームを作り始めました。私はこれをわざと秘密にしておきました。ものすごくいいアイデアを持っていたからではありません。 フィードバックを受ける準備ができていなかったからです。そこで、私はアナウンスできる状態になるまで、アナウンスをしないようにしようと思いました。その代わり私は「質問」から始めたのです。 私が速く、クリアに考えられるようになる手助けができないだろうか? 「学習」の次は、「測定」です。私は「ミス」と「成功」を測定します。そして次にコードを書く事です。 仮説があっているかを確認するために、測定して、そのために、コードを書くのです。 ### 12. バックワードループ 私は考え方を改善することができました。私は今回は完全に前回と違う気分でした。なぜなら私は逆向きのループをまわせたからです。 このループは、「学習ー測定ービルド」と呼ぶべきだと思います。「ビルドー測定ー学習」ではありません。私は「ニーズ」から始めたいのです。 次の質問は、このツールを自分以外の他の人が考えるときに使えるかです。この質問も正しいのであれば、次の質問は、このツールを誰か買ってくれるか?です。これが今私がやっていることです。質問から始めること。学習から始める事。次に測定がそれを助けて、最後にビルドです。 ### 13. pushモデル 「学習ー測定ービルド」のサイクルはプルの原則です。リーンの原則です。他に、プッシュ型というのもあります。 プッシュ型は大抵の人々が仕事のスケジュールを立てるやりかたです。各プログラマをできるだけ効率的に働かせて、ものをつくります。その後、誰かが買ってくれるかを確認します。これがプッシュモデルです。プロダクトをつくって、それを誰かが買ってくれるかを確認する。 プルモデルの考え方によれば、プッシュ型は、ミクロの効率を上げますが、マクロの効率を犠牲にします。プログラマは効率的にものを作りますが、できたものは誰も欲しくないといように。 これらの、失敗は、我々は、完璧で、洗練されていて、ぴっかぴかに磨きをかけたプロダクトを作っている事に関係するのです。ただし、誰も実際には買ってくれない、、、 ### 14. pullモデル プッシュ型の考えの、ソフトウェアエンジニアリングとしてよい仕事をしようとする事は、沢山のバリューを生む事にはなりません。 今日お話ししたい、最初の原則は、プルの原則です。「学習ー測定ービルド」はスタートアップでしっかりしたものを作る原則だし、2つめの原則は、フローの原則です。 ### 15. アジャイルマニフェスト フローの原則はアジャイルマニフェストの後で説明します。アジャイルマニフェストが作られたとき、私は病気で死にかけていていましたので、ミーティングのことははっきり覚えていません。 ジムハイスミスと、マーチンファウラーは、考えをまとめるにあたって、本当に貢献してくれましたし、賞賛されるべきです。 10年前、アジャイルマニュフェストは、ソフトゥエアの世界を一歩先に進めました。 その部屋にいた人が同意できることは、あまり多くはありませんでした。我々が同意出来た事は、人々に魅力的であること。そこで、我々はAgileという言葉を取り上げました。 なぜなら、みんながアジャイルになりたがっていたからです。もし、あなたのアイデアがみんなに受け入れられてもらえるように名前をつけるとき、みんなが、すでに知っていて、その言葉が、忠告してくれたり、どういう事が起こるのかを語ってくれるようにするのがいいでしょう。 ###16. プロセスやツールより、個人と相互作用 昔、いいソフトウェアを作る為には、我々はよいプロセスやツールを持つべきだという考えがありました。 もし、あなたが正しいプロセスを持ったならば、誰が実行するかは重要ではありません。だから、あなたはモンキーを雇って、ソフトウェアを書かせたら、まったく同じものが出来るあがることになります。 だから、プロセスやツールでは、十分ではありません。そこで、こういう事でソフトウェア開発の世界を一歩先に進めました。 人そのものや、人々が相互にコミュニケーションを取るのが重要だよ。プロセスとかよりもね。 これが、10年前の一歩でした。 ###17. チームのビジョンのチームの決め毎 これは、私がスタートアップのプログラマを始めたので、これでは十分ではなくなってしまいました。それを越える必要がでてきたのです。 今の私は、私がどうやっていい仕事をするか?ということを考えるのではなく、チームが如何にいい仕事をするか?という事を考える必要があるのです。 この事は、時には実践的ですが、時にはあまり面白くない事も起こることを意味します。 もし、チームが沢山の事を達成できるためには、私がベストと自分で思える事をする事を少なくしないと行けないケースがあります。 自分が、問題を解決するクールなテクニックをしっているとします。そして、チームの他のだれも、そのドメインのPhDを持っていません。もちろん、その問題を解く為の技術的なベストな方法もしりません。 もし、自分がその事をしる唯一の人だとすると、自分ならそれをやれるけど、自分はチームの一員だといって、一歩引く必要があります。 私は一歩ひいて、チーム全体としてもっと達成できるように手伝います。 そして、そのような状況だと、私はテストを動かすことも、タスクを引き受けることもNOといわないといけないかもしれません。そのかわり、チームにルールをフィットさせることをしないと行けないでしょう。 しかし、こういったことは、チーム全体のパフォーマンスを上げる傾向にあります。チームのビジョンと、チームのルールを守ってもらうことは、個人のパフォーマンスを最適化することの先をいっています。 ### 18. 包括的なドキュメントより、動くソフトウェア アジャイルマニフェストには4つの価値があります。 次は、全てがドキュメント化されていれば、上手く行くという迷信です。 そういったドキュメントは、いつもソフトウェアにくらべて古くなっていました。だから、それはもっともよくある勘違いです。 だから、ドキュメントではなく、動くソフトウェアに重点を置いたのは、大きな一歩だったのです。 もし、あなたが凍結されたシステムの完璧なるドキュメントをもっていたとすると、そのシステムは、すでに誰のものでもない問題を解決することになります。 だから、今日の問題を解決するソフトウエアをもつことが重要です。だからこの事はとても大きな一歩でした。 ### 19. 学習を検証すること スタートアップの環境では、10回に9回は、どうやってソフトウェアを書いていいのかわかりません。 フライトキャスターがいて、彼らの仕事は凄いテクノロジーの責任をもっているとします。私はそういう環境で働くのは好きです。なぜなら、私は、他の2つのサイクルの事を無視することができますから。これは素晴らしい。しかし、、、 スタートアップでは、動くソフトウェアで進捗を測定することはできません。 スタートアップは、ほとんど不可能なことのリストで始まるのです。 これは、「検証しないと致命的になる仮定」と呼ばれます。つまり、その仮定が覆ると、そのビジネス自体が成り立たなくなるのです。例えば誰かがお金をはらってくれるかとか、顧客を獲得できるのかとか。 ほとんど不可能な事に首を突っ込むゲームみたいなものです。 だれか、このWebゲームにお金を払ってくれますか?もし、それがほとんど不可能だったとして。そら、誰も払ってくれないでしょう。 これは、いつだってクレイジーなアイデアです。 しかし、ほとんど不可能リストをもって、スタートアップを始める事はとても価値のあることなのです。 あなたが、その不可能を可能に変えることに成功したらどうなるでしょう? 動作するソフトウェアは、その解の一部にはなりえますが、最高の回答ではありません。 だから、アジャイル開発をこえて、組織として、学習する機会をつくり出すことが重要なのです。単純にコードを書く事ではなくてです。 ### 20. 契約交渉よりも、顧客との対話 10年前の、その他の伝説としては、正しい契約をすれば、全てが上手く行くというお話です。反対の事は正しいと思います。あなたがチームを訪問することができたなら。 あなたが、ソフトウェアをみて、サプライやとカスタマーの間の契約がゲームオーバーと言えば、あなたはなにもできません。 この点では、契約はあまりよくありません。 顧客との対話が、最初に細かい事を決めて契約をすることよりも重要という話しをした事は大きな一歩です。 ### 21. 顧客発見 今、あなたはスタートアップです。あなたには顧客がいません。 私はコラボレートしたいですが、いないのです。コラボレートする顧客がいないのであれば、スティーブブランクが行っているとおり、ビルの外にでて、顧客を探してくるしかありません。顧客がいるなら、顧客との対話は重要です。でも、いなかったら見つけましょう。 ですので、スタートアップの場合は、それを越えるひつようがあります。 ### 22. 計画に従うよりも、変化を抱擁する 10-15年前の伝説として、ソフトウェア開発を上手く行かせる方法は計画をたてることだという話しがありました。 働くために、計画を立てて、正しく計画を実行する。これは何回もプロジェクトマネージャから聞いた事です。 多分計画を一生懸命立てても、いろいろ変わってしまうことに気づくでしょう。だから、変化を抱擁することが重要といったことは、とても大きな一歩です。 実際の世界は、計画よりずっと柔軟なものです。 変化に対応する事のメタファーは、変化に対応してプロジェクトを実行することなのです。いい感じですね。 ### 23. 変化を起こす あなたは今やスタートアップの一員です。変更するものがまだありません。 なぜなら、何も動いていないからです。最初に慣性の法則で習った事を確立させることが必要です。 これは、変化を起こすことが必要です。ただ、対応するのではありません。 あなたが(世界に)変化を起こさなければ、スタートアップとしては、何もしていないのと同じ事です。 軍隊は変化を起こす事について、偉大なコンセプトを持っています。 あるチームは、自ら事を起こします。一方他のチームは、それに反応します。 ある、歴史的に有名な軍隊ののリーダーがいます。彼は、何かを始めることについてのいい教訓です。 あなたが300人の部下しかないとして、あなたは、他のチームがせめて来るのを待ちません。なぜなら、あなたが事を起こすと、圧倒的に優位に立てるからです。もちろん、あなたは危険にさらされます。 一方、他のリーダーは、あなたが何をしでかすかばかりを心配しています。そして、彼自身が何をするかには頭がまわりません。 ###24. the Civil War このことは、Civil Warでも起こりました。グラント将軍は、西の軍隊にいきました。レポーターは彼にたずねました。彼がRoberty Leeが次に何をすると思っているかと。彼は答えました。「私はLeeが何を次にするかには興味がない。私が次に何をして彼を心配させるかのことを考えているんだ」 このことが、Civil Warの展開を変える事になった。 ###25. アジャイルマニフェストを越えて だから、私にとっては、アジャイル開発を越えて、スタートアップは、チームビジョンをもって、規律をもって、全体を最適化しないといけない。 プロセスやツールは、人のアウトプットを最大化しない。ソフトウェアを作る事よりも、学習することにフォーカスをあてよう。 そして、ニーズからはじめて逆のループをまわそう。顧客を見つけるのだ。 そして、変化を単に待つんじゃなくて、自ら変化を起こすのだ。 ### 26. プルの原則 最初の原則はプルの原則でした。私は2番目の原則について話すといっていましたね。 あなたが欲しいことに対する学習からはじめて、逆のループをまわして、あなたの欲しいものを作るのだ。 ### 27. フローの原則 2番目の原則は、フローの原則です。もし、あなたが2つのデリバリを半々の機能でリリースできるなら、1つのデリバリをするよりも、ずっと価値がある事です。 私は別の日にノルウェーの男について話していました。年上で大きなコンサルティングファームにいました。彼は私にスタータアップの戦略を教えてくれたのです。 開発者が、本当に磨き上げたプロダクトをつくりました。素晴らしすぎて誰も文句をいうことが出来ないぐらいです。あなたはそれに1年を費やしました。本当にいいものをつくりあげてリリースしました。そして、フィードバックを得ました。誰もこれを買いたくなかったのです。こういうことは起こるのです。 だから、半分のプロダクトをつくって、仮説と検証をしっかりやりましょう。しっかり学んだ後に、磨き上げましょう。 3ヶ月。これがフローの原則が有効に働く期間です。 全体のサイクルを如何に短く出来るか?これが我々が考えるべき事です。 ### 28. ペイメントゲートウェイ ゲームの話しを考えましょう。私の妻は私がプログラムコードを書いてお金にならない状況にうんざりしています。だから、このゲームが売れるのかというのを早く検証したいのです。 儲かるかな?このゲームは2つのパートからできています。あなたは最初のパートをプレイします。 そして、あなたが、最初のパートに熱中したなら、「購入」ボタンがでてきて、購入すると、次のパートに進めるという感じです。だから、私はエンジニアと話しをして、これをやるためには、ペイメントゲートウェイをつくらなあかんなと。 私は考えました。これは、スタートアップエンジニアリングのエンジニアリングゲームであると。だから、これを分割できないだろうかと。分けられないように見えるタスクを小さなピースにできないだろうか? これは、クールなパズルです。あなたは「学習ー測定ービルド」を通じて解決するのです。全てのエンジニアの脳みそをフル回転させて、自分がオッケーと思えるアイデアを思いつきました。だからお話します。 私が考えた事は、ペイメントゲートウェイなしで、どうやって実装できるだろいうか?ということでした。最初のパートが終わったあとに、購入ボタンがあらわれて、ボタンを押すと、次のパートが始まります。ただし、我々にメッセージが飛ぶようにするのです。 だから、我々は、みんなが「購入ボタン」を押してくれるかどうかを知る事ができるのです。しかし、みんなが「購入ボタン」を押すと、後半のパートを無料で遊べてしまうのです。 最高でしょう。顧客ができないのです。私は顧客がいないので、ペイメントゲートウェイがあるかどうかは、たいした問題ではないのですから。 もし、私は100万の顧客を獲得したら、そして、私がペイメントゲートウェイを実装していなかったら、お腹いっぱいになってしまうでしょう。 しかし、私は「購入ボタン」を押してくれるかどうかだけが知りたいのです。 ### 29. 全体のループを考える スタートアップの開発者として、全体のループを考える必要があります。 最も大きな違いは、どのように私が最高のソフトウェア開発をするかを考えるのではなく、「学習ー測定ービルド」のループを如何に早くまわすかということを考えるのです。 できるだけ、早くループをまわして、出来る限り最高のバリューを各サイクルで得ようとします。 私はそれらを達成するために、エンジニアリングを実施します。例えばモックアップとか。ソフトウェアすら必要ありません。インデックスカードとか、シャーピーとか。まぁ、私は嫌いですけど。 今18時間を節約したって? 私はいいエンジニアか?って。そのとおり。 私は他のエンジニアの友達を感心させたい。しかし、彼らはスタートアップの問題をかかている。 ###30. 良いスタートアップのエンジニアリングとは 時々は、ハッカーになったっていい。 しかし、基本的にはあまりよくない。 我々はコピペして、2行変えるだけだぜ! みんな、こんなゲームをプレイしたい?例えば我々が、注意深くリファクタリング、リファクタリング、リファクタリング、、、、三昧な それは、エンジニアとしては気分がいいかもしれない。しかし、それはスタートアップのエンジニアリングとしては、あまりよくない。それらは、もっとも安い方法じゃないからだ。 ループをまわすことは、スタートアップでいつもハックを繰り返すことじゃないんだ。 ### 31. モードを変える もし、ゲームがヒットしたらどうなるだろう、スケールしてしまったら。あなたは、そのサイクルから、モードを変える必要がある。あなたがやるべき事は、顧客を魅了できるかどうか確かめることでは無くなっているからだ。 今や、あなたが学習するべき事は、どうやったら顧客を倍にできるかだ。 テスト駆動開発や、自動化、リファクタリング、レスポンシブデザイン、そして、スタッフの仕事を妨げる事無く、平行で変更できるようにするようになることだ。 そして、今のお金の流れから、どうやったらより多くのお金を獲得できるかを学習するのだ。 そのとき、エンジニアリングはシフトする。完璧にスループット指向になる。どうやったらエンジニアリングチームのコストを減らす事ができるだろうか? どうしたら、運用コストを下げる事ができるだろうか? 他に何かできることは? スタートアップの良いエンジニアリングができるエンジニアになることです。 これらのフェーズで、これらモードチェンジ、つまり考え方の移行は本当に難しい事です。私もこの移行でトラブルになりました。 多くの他の人が、多くのエンジニアリングチームが機能を出来るだけ早く作るようにしていることを見てきました。 ### 32. 先週より2倍早く機能を実装できたぜ そして、彼らは気分よくなります。先週より、2倍早く機能を実装できた。俺たちクールだぜ! もし、あなたが顧客を獲得してスケールしたとき、最高のエンジニアリング戦略は、機能を削減することです。 これらの、考え方の移行は本当に難しい。なぜならこれらは、技術の問題ではなくて、文化の問題だからです。 ### 33. 結論 リーンスタートアップでの、モノ作りのゴールは、ループをまわす時間を最小化し、そこから得られるバリューを最大化させることです。 原文 --- ディクテーションしたものなので、不正確な内容が含まれていることをご了承ください。 ### 1. Introduction Thank you very much thank you all for your coming early in the morning and for those of you perform early in the morning.It’s not such an accomplishment for you to be here but I’m glad you’re here too. I have the the privilege and honour of being the worst entrepreneur on the schedule today. I (las can?) have been involved in 11 startups but none of which have made significant money. Only one of which has been successful on large-scale that’s JUnit. I wasn’t quite sure about opening my speech by disclaiming any ability in the area that we are talking about. But I figured you’d figure it out soon enough anyway so I should just open up. But the thing is I haven’t given up because I can see especially the lean startup stuff is brought me new energy for continuing trying’ out the businesses. Around the technical capabilities that I spent most of my life trying to refine. ### 2. Goats story I live in the southern Oregon, we are out on the boonies. And we have goats , no, really goats.We just had two babies. They are so cute it’s like a little furry 80-80. They are wonderful. But the other day I was out with the goats and I was starting to scratch the goats cause the bug you otherwise and the goat was okay whatever I went down the back in little further down the back. And then right near their hip, this goes to hip all of sudden, goats just went. Oh. That’s feels great.Ooh. it’s interesting so I can scratch all over these skills, nothing, And I hit this one spot and all of this itchy spot you know, hit an itchy spot and all of a sudden the same activity that I’ve been doing all along turns into something wonderful for this goat. I mean I can’t really imagine what it’s like I don’t want to really imagine what it’s like but I can see you that the same activity and lots of different places small effect and all of a sudden that same little activity has a completely outsized effects on the goat’s experience of the an... those scratchin’ and that’s so little like startups for me. You keep doing stuff no response and you do stuff no response you do stuff no response and then you do the eighth idea you try out takes off is not like you’re edging. It’s not your scratching any different you just hit an itchy spot and when you hit an itchy spot, all of sudden the reaction tells you this is something special and I’ve had the good fortune to have that happen several times in my career, Where software patterns or test driven development. Those I mean I had hundred ideas that scale but those at that time really hit an itchy spot. ### 3. Entrepreneur And as an entrepreneur that’s what I’m looking for that itchy spot not how can I somehow sustain hundred hour weeks I mean It’s not like a getting the chainsaw aisle to scratch the goaded doesn’t work not not twice. But how can I take the things that I do and find a spot that that really some there’s it it’s a combination of factors right there su..There’s timing there’s luck and there’s scratching. If you’re not scratching, you’re not getting to hit the itchy spot guarantee you that. ### 4. Lean Startup And lean startups is a great example of this for me too. Because something about the timing the message that people the market were all just right. So in the world. There are probably thousands of people, Doing the same kinds of things that Eric was doing. A year ago and it just didn’t hit an itchy spot but something about lean startups and the combination of pay my and my business model for the last 15 years. ### 5. Capital efficiency Start working what am I going to do there’s a bunch of motivation The incredible capital efficiencies not just in software I mean I think most people here probably in software but but hardware businesses, too. Their capital efficiencies go up to a factor of 10. So you got the need for a new business model you have capital efficiency You have markets that are much more efficient. And all of that came to and then a community of practice people there are isolated people who are doing parts of lean startups And the name the message The way of the deliver, the timing all of the came together so for me. Lean startups sends this response and the the response around the globe is really that’s one of those itchy spot.And it’s great that it’s happening. ### 6. Basic loop So we see. Here’s the here’s the basic loop Build, measure and learn, I’m a build guy. That’s that’s what I do that’s what I’m comfortable doing. If I have a choice between doing something really valuable In measure or learn or doing something really trivial and build I’ll build every every time Because I’m a builder it’s what I’m comfortable to do it. When I looked at this from Lean thinking kind of perspective one thing I realises it this loop is actually backward. It is the way that I’ve done it and I told you I have a long record of failure doing it that direction. so if you don’t want to, you don’t need to try it. You can if you want but this loop is backwards. ### 7. multitable I’ll tell you two stories I had two. Startups going right now. Is not cool that we can multitable now? You are in the poker when people play online poker. They started playing two games at once and this was considered rule you can play two games well, turns out pokers mostly boring so you can actually play. 20 games of poker once that’s called multitable. ### 8. multitable strategy Startups, As the capital efficiency increases right you don’t need 20 people to do is start up and you don’t need 10 people to start up then you can do start of 5 And with 2 ,with 1 is not stopping there. You can do two startups, you can do five startups the once. I mean at some point it’s got it end but It’s cool that that’s that that kind of multi tabling as possible so so I have two things going one of them I’m doing completely wrong And now I’m hoping to get some ideas here today so I thought... ### 9. TDD example Why have this test driven development idea you know which is this crazy idea that you write the test before you write the code, but test always fails, so why do you write it down well, turns out to be really cool work really well so that I’ll do some screen casts. About TDD you know you can’t make money selling books anymore (Sycanth) so I got to find some other media strategy So maybe I’ll do some screen cast. so I’ll record myself doing test driven development And then you know do a little narration trying to be both informative and entertaining which is actually too much for me to do once but they have it so I’ll record these and then I’ll put them out there and see what people think. That’s running the loop backwards. That starts with the build. So I recorded a couple episodes of putting up on Vimeo the first 10 minutes and edited And then I saw what like people yeah people view Vimeo hundred times okay so there’s some market out there ### 10. Stuck on learning But I’m kind of stuck on the learning phase. so I built, I measured but what is the how strengthen the learn. I guess some people are interested. Where do I go from here. I learn the loop backwards on that project. I started it with making something because I just jumped to make in things. And and now I’m kind of stuck I’ll push it through to completion of you don’t get them out there and find a publisher and out all that stuff. But I get the feeling I lost a lot of value in that sequence. ###11. The next project Let me contrast that with my my next project which started with the need I wanted to find out if I could. To not my 49-year-old brain so I know my memories not as good as it used to be, My cognitive skills aren’t as good as they used but. Can I slow down? Can I recover some of what I lost? So I built a little game and I’ll be cagey about this not because I’m trying of Build some you know build up interest I can see all of you are really excited about this idea. But but because I’m not ready for more feedback so I’ll announce it when the time comes to announce it but I started with a question. Can I help myself think faster and more clearly? Well okay so now what’s the measurement of that I think a task. I thymic. And I measure the mistakes okay so now what software what I need to write. In order to measure that in order to know whether or not. ###12. the loop backwards I can improve my thinking. That one feels completely different because I ran the loop backwards So the loop I think really should be called learn measure build. I don’t I don’t say build measure learn I say learn measure build because I want to start with a need. now the next question is can somebody else use the same tools also help their thinking. Turned out the answer to that is yes then the next question is Can we get somebody pay for this. and that’s what I’m working on now but It starts with a question learn then when the measurements are needed to support that And then build. ### 13. push this learn measure build. Is the principle of pull. in the lean manufacturing you got push. This is the way most people schedule work. You know, let me ... How can I get my programmers working as Good as possible, or as most efficiently as possible? Then then we’ll see if anybody will buy it. that’s the push model. Build this product and see if anybody will buy it. the pull model says... That gains micro efficiencies like the programmers to work really fast in that environment, but it sacrifices a macro efficiencies by building things and people don’t actually want. The number of those fails starts that I was talking about we are building products complicated sophisticated carefully polished products. But nobody ever actually bought so ### 14. pull In that environment looking at it from push perspective, Trying to do a better job of software engineering is not actually going to creating more value. So the first principle little talk about Today is is a principle of pull. Learn measure build is a way of making a concrete for for start ups the second one is the principal of flow. ### 15. Agile manifesto That after I finished taking a shot at the Agile manifesto that I was at the meeting where the agile manifesto was written, I was deathly ill and on some ungodly antibiotics so I don’t actually remember much of the meeting. Jim Highsmith and Martin Fowler really deserve the credit for pulling all his pieces together. 10 years ago the agile manifesto was a step forward. It was a least common denominator of the people in the room that is that with those were the things we all could agree on And we wanted to make it attractive to people so we picked the word agile. Because everybody wants to be agile well it turns out if you name your idea something that everybody, wants Everybody will say they already are that so Just word of warning or where you are is gonna happen. oh yeah. we’ll in. Here’s our here’s our 60 page lean products Beck. Bro here’s a lean stealth stealth wants instant yet ha ha Lean private beta. Is it’s a get one. And I make a great button. I mean such an in joke though but then you know you had some ache. Anyway oh and end that the reason it’s back at A is because my parents had a good sense to have a name beginning the alphabet. ###16. Individuals and interactions over processes and tools Agile manifesto anyway so processes and tools there was a day when When people thought in the non-focusing I’m just on the build process when people thought That what you needed to do to build software better was to have better processes and tools if you have the right tools. And if you have the right processes it wouldn’t matter who was executed. You could have any monkey write the software and it would come out exactly the same. Turns out that’s true. So process and tools are not enough what You need at that point it was a big step forward to say hey, The people matter and how they interact with each other matters More than following some set process. So that was a step forward 10 years ago ###17. Team vision and disipline If I look at software development as I practice it in startups though That’s not enough I need to go beyond that So it would be as an individual in a team building a startup I need to think not about how good a job I can do but how good a job we’re doing. That means sometimes very I mean there’s some practical and sometimes unattractive implications of that sometimes. If I should do less than What I believe to be the very best in order for the team to achieve more. So I might know about some really cool technique for solving a problem. But nobody else in the team has a PhD in that particular little area, So even though that might be the technically best way of solving the problem. I need to back away from that and say hey I’m on it yes but I’m on a team, If I’m the only person who understands this part of the system. Then I need to back away from everything I know so the team can achieve more. I might be in a situation where I can check this scene and I don’t have to run the test. And I need to say no the way we do things here is And have the discipline to fit in to. what the whole team is doing the individuals interacting have a tendency to optimise their own performance. But team vision and discipline goes beyond that to talk about How are we going to make the most progress we can together. ###18. Working software over comprehensive documentation So there’s four bolts and then manifesto and go through (Brigitte) so the second one is about comprehensive documentation there was a day when The story was if you had everything documented if you had everything written down Then you’ll be just fine right. You would’nt have to go like Talk to people you could just open up the book and read what the software did except Always the documentation was out of date. it was the best misleading. And and that was about it and ten years ago he looked at that said You know, documentation is not the point working software is a big step forward. So if you have a perfect documentation for frozen system solves the problem nobody has anymore That’s just not as good as having some software That solves today’s problems and that was a big step forward. But the problem is it only goes so far ### 19. Validated learning In a startup environment, it’s not that you don’t know how to write the software nine times out of 10. I mean my hats off to somebody like a flight Caster who Clearly has some heavy duty technology behind what they do And I would frankly love to work in that environment because then I could pretend like I could ignore the Other two parts the cycle when that’ll be cool. but You can’t measure progress by working software in a start up you start out a startup with a list of almost impossibles Used to call them the potentially fatal assumptions but that’s two negative you know you Potentially fatal assumption somebody will pay money for this you know that we can acquire customers. That this will be an engaging game whatever those are all protected(pretend) almost it’s almost impossible. Did somebody will pay money for a game on the web right really Well if it’s only almost impossible when you have something. If it’s clearly true then you don’t have anything. Hey it’s always it’s always the crazy ideas that worked out That is the most valuable to you start out every start up with his list of almost impossible. And to succeed you need to find out Which of those really is impossible and which of them turns out With some work to be possible. Working software can be part of answering those questions but it isn’t necessarily the best way to answer those questions. So beyond agile software development is about creating the opportunity for learning as an organisation. Not just about writing code. ### 20. Customer collaboration over contract negotiation Another myth from 10 years ago that if you just have the right contract everything would be clearing goes smoothly And the counterfactual is true you could go visiting team. You look at their soft that that the contracted between the supplier and customer he says Game over you’re finished there’s no way that you’re going to be successful with this. Just because the contract was wrong so with that point It’s it’s a big step forward to say collaborating with customers is much better than trying to nail down all the details right at the beginning. Yes yes that is definitely much better so ###21. Customer discovery Now you’re startup. you don’t have customer I’d like to collaborate I want to collaborate you can hear an echo. but that’s it. what you do If you don’t have a customer with whom to collaborate well and Steve Blank will give you chapter and verse of this and I hope I can pick up some pointers You have to go find out who your customer is. customer collaborations great if you got a customer and if you don’t you got to go find them. So agile development in a startup or development in a startup, it needs to go beyond ###22. Responding to change over following a plan Just what it says in the agile manifest so there was a day 10 to 15 years ago when the myth was the way it is software project is made a plan. Plan to work, work the plan right if I heard that one more time from project manager. I was going to do something was a glass two or three felony.Depending on how annoy you got, so if if your Plan the work with the plan and you realise now this Things change too much. then responding to change is a big step forward. To say no it’s it’s not enough to follow the plan because Things just change too much. reality diverges from the plan Reality is the much less flexible than planned? There was the one fundamental point that follows the plan thing Is that reality bends a lot less than the plan bends. So saying let’s response to change our metaphor for executing the project to be responding to change Well cool mean thats, it’s a big step forward ###23. Initiating change Now you’re in a startup nothings changing yet Because nothings moving. you have to establish Momentum first. development in a startup Requires initiating change not just responding to it Now this is where my anti-responsibility hackles start going up you know where I basically I don’t want to be responsible I know it’s have to but I don’t like it and I’m like oh it’s up to me. To initiate change that because if you don’t initiate change the startup, you don’t have anything. Not even moving. the military has this is great concept of being the initiative One side has the initiative in any given engagement that’s the people who are acting in the other person’s reacting. And there are military leaders through history who’s been very good at seizing the initiative So you know you you only got, you know, you 300 people but you don’t wait for the other guy to come in Smack you take the initiative because when you have the initiative you got a whole bunch of advantages yes you’re exposed But the other guy if you can make sure the other guy is busy Worrying about what you’re going to do he doesn’t have time to think about what he’s can do to you ###24. the Civil War This happen late in the Civil War when Grant went to the To the armies the east and a reporter asked him well you know What what do you think Roberty Lee is going to do next and Grant said I don’t I don’t care what Lee is going to do next I’m going to make him worry about what I’m going to do next. And that was the point that the that the Civil War turn around. ###25. beyond the agile manifesto So that for me is beyond agile development in the startups Team vision in discipline looking at the whole. Process and not just what one not maximising one person’s output. Focusing on learning instead of producing software. So it starts with the need in move backwards discovering customers. And this change initiation process instead of Waiting for the change that happen them and then responding. I don’t know how many minutes that means have left. It’s ok. ###26. the principle of pull So the I told you I was going to talk about this the second principle so the first principle was the principle of pull. That is start with Build you start with learning that you want to have an work backwards to the building you need. ###27. the principle of flow And the second principle is a principle of flow principle of flow says If you have all other things be equal Two deliveries half-and-half is more valuable than one delivery of the whole thing. And I was talking to a guy in the Norway the other day, Senior guy in a big consulting company and he was telling me his start up strategy. What is the builder really finally polished product so nobody could possibly complain about it. You know you spend a year and you make something that’s really good and send it out. And I said well so sometimes when you do that you get the message back. Nobody wants to buy this. oh yeah happens. So could you build half the product and got an question answered Yeah sure when you been so polished okay how about you know. Three months. that’s the principle of flow at work I’m trying to think. How can we shorten the time through entire cycle. ###28. Payment gateway As came up the other day with respect to the game. so my wife is tired of me writing programs don’t make any money so one of my Really you can ask her. so so one of the things I want to validate early in this (price) of this game is Can I make money. so the game has likes a couple of natural parts of divides into two parts And I thought okay so you do the first part. And you get addicted to the game right that’s how it’s supposed to work And then there’s a button that pit uses I’ll pay to use the second part And I was talking to reengineer is working with on this and he said okay so we have to set up the payment gateway and … And I thought how could we slice this finer to me this is the engineering game of start up engineering is Taking tasks that seem monolithic cutting them into little pieces of rearranging pieces. It’s a cool puzzle to solve by the time you’ve gone through learn and measure and you get the build that’s the point All of my engineering synapses are firing I’m thinking how can we slice up so I thought Okay here’s my cool idea now in this room this is unlikely to be remarkable but I’m proud of myself so I’m just telling you. That is I thought how can we do this without having to set up the payment gateway …He said So how about if we write the first boad has the game we put the buy button And when you present buy, it just opens up the second half of the game but sends us a message. So we know whether anybody’s going to press the buy button is right now we don’t know if anybody’s going to press the buy button. but everybody press the buy button and we just get the second half of the game for free. This is whereas wonderful not having customers, so what I love not having customers because when I make mistakes like oh not implementing the payment gateway is not a big deal. If I had 1 million customers and I didn’t implement the payment gateway, I’ll be stuffed. But I just want to find out if anybody’s going to press the buy button. ###29. thnk about whole loop As a developer in a startup, I have to be thinking about that whole loop. And that’s really what’s different it is not how can I do Of the best software development it’s how can I Tightened the learn measure building loop as fast as possible. Make it shortest as possible and extract the maximum possible value added each cycle through that loop. I should do the engineering that achieves that Well sometimes that’s going to look like Mockups wanting not me software at all. you know I got an index card some sharpies I don’t like this. Now, I just saved 18 hours. Am I good engineer? Yeah. I want to impress my other engineer friends but they got their own startup problem. ###30. Good startup engineering That’s okay so sometimes it’s going to look like hackery. Base awful hackery. yeah we just copy this files of changing two lines. Is anybody going to our people gonna to play the game is more like this and like that Well we could carefully retractor …. Well I might feel good but it feels good to do good engineering But that’s not good start up engineering. not if there’s a cheaper way. To get through that loop that doesn’t mean that always in startup you just hack hack hack and so you ever do. ###31. Switch modes What happens when you hit it ,when you hits scaling. You got to switch modes from the cycle you’re going through isn’t can I attract customers. But that’s not the learning you’re trying to to create it’s like that that learning you’re trying to create is Is there any way we can handle twice as many customers that’s the learning you need at that point whole boy you need. Test driven development oh boy do you need automation and re-factoring and responsive design and being able to make your changes in parallel without disrupting stuff. And at some point when you’re saying when the learning is can we make more money from our current revenue stream. Then engineering shifts yet again to to being absolutely throughput oriented how can you reduce the cost of engineering team. How can you reduce the operational costs. And it’s something else yet again. To be an engineer good engineering in a start up. Go through all those phases and notices the need to switch Making those transition seems to be really really hard I say that because I have trouble making this transition. To see a number of other people to do also so can you realise it for a while the engineering team builds features faster and faster. ###32. We can build the future twice as fast this week as we did last And he feel Good. we can build the future twice as fast this week as we did last cool. that’s pretty cool. When you hit scaling when you’re optimal engineering strategies probably removing features. And carefully tuning the feature instead remain all of a sudden the value system has to change Right how you kept score has to change and making those transitions seems to be Very difficult because there’s much cultural as they are is there technical. ###33. Conclusion But the goal of building in the lean start up Is minimising the time through the loop and maximising the value that is extracted from us. |
|
| 123位 |
|
|||
|
23:32:37 |
(Akatsuki Inc. 所属) |
|
##sar(sysstat)とは
LoadAverageやCPU使用率、ディスクI/Oの状態を表示できるコマンド。 何より便利なのは、過去にさかのぼれる点。 ##sarのインストール ``` # yum install sysstat ``` インストールすると、ログを取り始めます。 ##sarのログ取得間隔の変更 /etc/cron.d/sysstat を編集します。 ``` $sudo vi /etc/cron.d/sysstat # run system activity accounting tool every 10 minutes */10 * * * * root /usr/lib64/sa/sa1 1 1 ``` デフォルト10分間隔(*/10)になっているので、好みの間隔に変更。 ##主要コマンド一覧 |コマンド|内容| |:-----------|------------|:------------:| |sar -q|loadaverage| |sar -u|CPU使用率| |sar -b|I/O| |sar -r|メモリとスワップ使用率| |sar -s time|指定時間以降のデータ| |sar -e time|指定時間までのデータ| |sar -f /var/log/sa/sa01| 日付別の過去データ| ##-qでLoadAverageをチェック ```sh $ sar -q -s 21:00:00 21時00分01秒 runq-sz plist-sz ldavg-1 ldavg-5 ldavg-15 21時05分01秒 1 203 0.19 0.20 0.16 21時10分01秒 0 203 0.21 0.18 0.17 21時15分01秒 2 209 0.11 0.18 0.17 21時20分01秒 1 208 0.38 0.20 0.18 21時25分01秒 0 210 0.10 0.20 0.18 平均値: 1 207 0.20 0.19 0.17 ``` ldavg-xが、過去x分間のLoadAverageを示します。 ##LoadAverageの上昇が見られたら 基本的に、 - CPU負荷(プロセス不足) - I/O負荷 のどちらかに切り分けられます。 ##-uでCPU使用率をチェック ```sh $ sar -u -s 21:00:00 21時00分01秒 CPU %user %nice %system %iowait %steal %idle 21時05分01秒 all 11.42 0.00 3.89 0.16 0.00 84.52 21時10分01秒 all 11.32 0.00 3.44 0.45 0.00 84.79 21時15分01秒 all 10.74 0.00 3.41 0.20 0.00 85.64 21時20分01秒 all 10.26 0.00 3.29 0.13 0.00 86.32 21時25分01秒 all 10.81 0.00 3.60 0.15 0.00 85.44 21時30分01秒 all 9.41 0.00 3.03 0.17 0.00 87.39 平均値: all 10.66 0.00 3.44 0.21 0.00 85.68 ``` sar -uは、CPUがどのようなモードになっていたのか、 それぞれのモードの割合を示してくれる。 |モード|内容| |:-----------|------------|:------------:| |%user|アプリケーション(ユーザプロセス)が使用している状態| |%system| カーネル(OSなど)が使用している状態 | |%iowait| ディスクI/O待ち状態| |%idle | CPUが何の処理もしない待機状態(I/O待ちの時間は除く)| ※steal、niceは省きます。詳細は下部の参考を参照ください。 ##sar -u から分かること #### %userか%systemが高いと・・・CPUボトルネック(もしくはメモリ不足) (%userならアプリケーション側。%systemならカーネル側。) #### %iowaitが高いと・・・ディスクI/Oボトルネック。 ##-bでディスクI/O状況をチェック ``` $ sar -b -s 21:00:00 21時00分01秒 tps rtps wtps bread/s bwrtn/s 21時05分01秒 15.67 0.00 15.67 0.00 363.44 21時10分01秒 15.42 0.00 15.42 0.00 389.58 21時15分01秒 16.25 0.00 16.25 0.00 355.18 21時20分01秒 13.50 0.00 13.50 0.00 313.71 21時25分01秒 15.98 0.00 15.97 0.05 351.94 21時30分01秒 14.43 0.00 14.43 0.00 313.95 21時35分01秒 13.22 0.00 13.22 0.00 296.66 ``` |項目|内容| |:-----------|------------|:------------:| |tps| 秒間I/Oリクエスト 数の合計。| |rtps| 秒間読み込みIOリクエスト数の合計。| |wtps| 秒間書き込みIOリクエスト数の合計。| |bread/s| 秒間読み込み(ブロック単位)IOリクエストのデータ量の合計。| |bwrtn/s| 秒間書き込み(ブロック単位)IOリクエストのデータ量の合計。| ##-rでメモリとスワップの使用状況をチェック ``` $ sar -r -s 21:00:00 21時00分01秒 kbmemfree kbmemused %memused kbbuffers kbcached kbswpfree kbswpused %swpused kbswpcad 21時05分01秒 655456 3388400 83.79 254316 1595220 2152620 80 0.00 0 21時10分01秒 642676 3401180 84.11 254364 1596728 2152620 80 0.00 0 21時15分01秒 598616 3445240 85.20 254376 1598160 2152620 80 0.00 0 21時20分01秒 598744 3445112 85.19 254388 1599628 2152620 80 0.00 0 21時25分01秒 681552 3362304 83.15 254396 1601256 2152620 80 0.00 0 21時30分01秒 548508 3495348 86.44 254416 1602084 2152620 80 0.00 0 ``` 重要なのは以下でしょうか。 |項目|内容| |:-----------|------------|:------------:| |kbmemfree| メモリ空き容量(kb)| |kbmemused| メモリ使用量(kb)| |%memused| メモリ使用率| |kbswpfree| スワップ空き容量(kb)| |kbswpused| スワップ使用量(kb)| |%swpused| スワップ使用率| ##過去データを日時指定して実行 例えば当月15日の23:30以降のデータを見たい時は・・ ``` $ sar -u -s 23:30:00 -f /var/log/sa/sa15 23時30分01秒 CPU %user %nice %system %iowait %steal %idle 23時35分01秒 all 7.20 0.00 2.39 0.17 0.00 90.24 23時40分01秒 all 8.35 0.00 2.65 0.12 0.00 88.89 23時45分01秒 all 6.76 0.00 2.31 0.10 0.00 90.83 23時50分01秒 all 7.27 0.00 2.36 0.12 0.00 90.25 23時55分01秒 all 8.18 0.00 2.66 0.12 0.00 89.03 平均値: all 7.55 0.00 2.47 0.12 0.00 89.85 ``` ##参考 sarコマンド - CPU・ネットワーク・メモリ・ディスク情報確認 http://www.syboos.jp/linux/doc/sar-command.html sarコマンドの見方 http://linux.alohakeakua.net/archives/488 CPU利用率: stealとは http://d.hatena.ne.jp/mir/20080407/p1 nice:優先順位を上げてプログラムを実行する http://itpro.nikkeibp.co.jp/article/COLUMN/20060228/231193/ |
|
| 124位 |
|
|||
|
21:53:01 |
(サイバーエージェント 所属) |
|
JavaScriptでは関数はすべてクロージャです。 ではそもそもクロージャってなんなんでしょうか。 #クロージャ?? クロージャの簡単な定義として 「自分を囲むスコープにある変数を参照できる関数」 が挙げられます。 言葉ではパッとしないかもしれませんが、コードを一つづつ追っていくと、 入門としてのクロージャは簡単に理解できます。 #スコープ まずJavaScriptは関数ごとにスコープが作られます。 スコープとは変数を参照できる範囲のことです。 その範囲外では変数は参照できません。 ```javascript: function func() { var value = 1; console.log(value); } func(); // 1 console.log(value); // undefined ``` #クロージャの例 ここでクロージャの例を見てみましょう ```javascript: function func() { var value = 1; function innerFunc() { console.log(value); } innerFunc(); } func(); // 1 ``` 先ほどの定義を思い出してみましょう。 「自分を囲むスコープにある変数を参照できる関数」 内側の関数が外側の関数の変数を参照できています。 ここで重要なのは、値のコピーではなく、参照ができていることです。 ですから、ここでは下記のようにvalueの変更が可能です。 ```javascript: function func() { var value = 1; function innerFunc() { value++; } innerFunc(); console.log(value); // 2 } func(); ``` #クロージャの実践例 ここで基本的なクロージャの実践例として一つ、 「モジュールパターン」を紹介します。 クロージャの一つの利点として、 変数をプライベートな変数として扱う事ができるようになります。 ```javascript: var module = (function() { var count = 0; return { increment: function() { count++; }, show: function() { console.log(count); } }; })(); module.show(); // 0 module.increment(); module.show(); // 1 console.log(count); // undefined ``` 即時関数を用いてオブジェクトを生成しています。 moduleにはreturnされているオブジェクトが代入されます。 ここで重要な事として、 即時関数内のcountに関しては内側からしか参照できないという事です。 外側からアクセスしようとした場合にはもちろん「undefined」となります。 これによりプライペードな変数として扱う事が可能になります。 # 外部アカウント 技術情報のみつぶやくアカウント作成しました。JavaScriptは最新情報も追っていきます。 [Twitterはこちら] (https://twitter.com/takeharumikami) [Feedlyのフォローはこちら] (http://cloud.feedly.com/#subscription%2Ffeed%2Fhttp%3A%2F%2Fqiita.com%2Ftakeharu%2Ffeed) # おすすめの記事 JavaScriptはオブジェクト指向?プロトタイプベースのオブジェクト指向を学ぶなら。 [JavaScriptのプロトタイプからオブジェクト指向を学ぶ](http://qiita.com/takeharu/items/809114f943208aaf55b3) JavaScriptでは関数型言語の一部の機能?実践的なJavaScriptの関数型とは。 [JavaScriptで関数型プログラミングの入門](http://qiita.com/takeharu/items/cf98d352ff574c5ac536) 関数型プログラミングを強力に後押しするライブラリ、Underscore.jsとlodashとは。 [JavaScriptで関数型プログラミングを強力に後押しするUnderscore.jsのおすすめメソッド12選(lodashもあるよ)](http://qiita.com/takeharu/items/7d4ead780710c627172e) |
|
| 125位 |
|
|||
|
15:31:39 |
(Bracket, Inc 所属) |
|
全て Windows でも使えます ``` $ ruby -v ruby 1.9.3p194 (2012-04-20 revision 35410) ``` ## Kernel.#system サクッと実行したい場合に便利 実行結果(true, false, nil)が返却される stdin : 渡せない stdout : 取れない stderr : 取れない status : 取れる($? で参照) ``` ruby system('mkdir hoge') # => true ``` ## バッククォート サクッと実行して、標準出力の内容だけ取れればOKな場合に便利 標準出力が返却される stdin : 渡せない stdout : 取れる stderr : 取れない status : 取れる($? で参照) ``` ruby `date` # => "2012年 9月 3日 月曜日 23時59分17秒 JST\n" # %x[ date ] でも同じ ``` ## IO.popen 標準入力にデータを渡して標準出力から受け取りたい場合に使う IOオブジェクトが返却される 実行できなかった場合は Errno::EXXX が発生する stdin : 渡せる stdout : 取れる stderr : 取れる(コメント参照) status : 取れる($? で参照) ``` ruby p IO.popen("cat", "r+") {|io| io.puts "foo" io.close_write io.gets } # => "foo\n" ``` ## Open3.capture3 標準入出力を使ったデータのやりとりを簡単に使いたい場合に便利 標準出力, 標準エラー, 終了ステータスが返却される 実行できなかった場合は Errno::EXXX が発生する stdin : 渡せる stdout : 取れる stderr : 取れる status : 取れる ``` ruby require "open3" o, e, s = Open3.capture3("echo a; sort >&2", :stdin_data=>"foo\nbar\nbaz\n") p o #=> "a\n" p e #=> "bar\nbaz\nfoo\n" p s #=> #<Process::Status: pid 32682 exit 0> ``` ## Open3.popen3 実行中プロセスの標準出力、標準エラー出力をリアルタイムに扱いたい場合に便利 ブロックを渡すことで、実行中プロセスの標準出力、標準エラー出力を扱える stdin : 渡せる stdout : 取れる stderr : 取れる status : 取れる ``` ruby require "open3" Open3.popen3("echo a; sleep 1; echo b; sort >&2; sleep 3") do |i, o, e, w| i.write "foo\nbar\nbaz\n" i.close o.each do |line| p line end #=> "a\n", "b\n" e.each do |line| p line end #=> "bar\n", "baz\n", "foo\n" p w.value #=> #<Process::Status: pid 32682 exit 0> end ``` ## systemu ブロック渡すとバックグラウンド実行してくれたりして色々便利 終了ステータス, 標準出力, 標準エラーが返却される stdin : 渡せる stdout : 取れる stderr : 取れる status : 取れる ``` ruby require 'systemu' date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " ) status, stdout, stderr = systemu date p [ status, stdout, stderr ] # => [#<Process::Status: pid 50931 exit 0>, "2011-12-11 22:07:30 -0700\n", "2011-12-11 22:07:30 -0700\n"] ``` ### 参考 http://doc.ruby-lang.org/ja/1.9.3/doc/method/Kernel/m/system http://doc.ruby-lang.org/ja/1.9.3/doc/spec=2fliteral.html#command http://doc.ruby-lang.org/ja/1.9.3/method/IO/s/popen.html http://doc.ruby-lang.org/ja/1.9.3/method/Open3/m/capture3.html http://doc.ruby-lang.org/ja/1.9.3/method/Open3/m/popen3.html https://github.com/ahoward/systemu http://d.hatena.ne.jp/ursm/20090625/1245947107 |
|
| 126位 |
|
|||
|
11:35:32 |
|
|
# vimライトユーザの俺が説明する実践vim入門編
* `対象者` : これからvimを使い始めようという人、開発者やwebデザイナ * `期待される成果` : vimが少しでも好きになる ## vimとは何か 私の貧弱な語彙ではとても表しきれないので一言で言うと、`高機能なテキストエディタ`だ。 使うまでに幾分かの修行を必要とすると言われているほど多機能、特殊な動作をする。 そんなツンツンのvimを使っているとそのうちデレてきて、私たちはvimからひと時も離れたくなくなるだろう。 ## 何はともあれインストール なるべく簡単に用意できる方法を選んでいます。 自前でソースコードからビルドするよっていう方は`mercurial`を使って世代管理するといいかと思います。 コマンド内の`> と $`は入力不要です * Windows [香り屋 KaoriYa](http://www.kaoriya.net/software/vim/)からダウンロードして使うのが吉。 `Windows+r`を押してファイル名を指定して実行から`cmd`と打ち込みEnter→コマンドプロンプトが起動されます。 フォルダを`C:\App`フォルダに解凍したのであれば以下のようにPATHを追加します。 ```bat > setx PATH "%PATH%";C:\App\vim74-kaoriya-win32 ``` * Mac Homebrewを使います。 Homebewのインストールはここを参考にして[パッケージ管理システム Homebrew](http://qiita.com/b4b4r07/items/6efebc2f3d1cbbd393fc) `Ctrl+Space`を押して`terminal`と入力してEnter→Terminal.appが起動されます。 ```bash $ brew update $ brew install lua $ brew install macvim --devel --with-lua $ echo $PATH #で/usr/local/binが無い場合 $ export PATH="/usr/local/bin:$PATH" ``` brew update で私が作業した時に出たxcodeのライセンス同意しろよってエラー ```bash $ brew update Warning: You have not agreed to the Xcode license. Builds will fail! Agree to the license by opening Xcode.app or running: xcodebuild -license $ sudo xcodebuild -license #文章が表示されるので agree と入力してEnter ``` * Debian,Ubuntu ```sh $ sudo aptitudo vim ``` ## 使ってみよう ```sh $ vim ~/.vimrc ``` vimにはモードという概念があります。 通常時のノーマルモードと入力時のインサートモードです。 どっちのモードか分からなくなったら即`esc`キーを入力してノーマルモードに戻りましょう。 vimではインサートモード中は普通カーソル移動しません。 入力漏れなどを発見した場合はノーマルモードに戻ってカーソルを移動しましょう。 早速使っていきます。最初は入力速度がべらぼうに落ちますが我慢します。 以下に簡単な入力操作の簡易表を用意しました。 操作|vim ---+--- ↑|k ↓|j ←|h →|l 一文字削除|x インサートモードへ|i 次行を新規行として挿入してインサートモードへ|o 現在行に新しい行を追加してインサートモードへ|O ノーマルモードへ|(インサートモードで)esc 最初は上記のカーソル移動を見ながらでもいいので打ってみてください。 ```vim:.vimrc filetype plugin indent on syntax on set nowrap set hlsearch set ignorecase set smartcase set autoindent set ruler set number set list set wildmenu set showcmd set shiftwidth=4 set softtabstop=4 set expandtab set tabstop=4 set smarttab set clipboard=unnamed ``` 最後にノーマルモードで`:w`と入力してください。 これは保存です。同じく`:q`でvimでの編集を終了します。 以下に似た操作を列挙しておきます。 操作|vim ---+--- 上書き保存|:w 名前をつけて保存|:w ファイル名 編集終了|:q 保存して終了|:wq または :x ファイルを開く|:e ファイル名 ただしvimでは上書き保存していない編集中のファイルを閉じようとすると保存してませんぜって言われます。 編集をしたけどやっぱり保存しないといった場合は`:q!`のように最後に`!`を付けることによって強制的に上述した表の操作を行なうことが出来ます。 ちなみに上で保存したファイルはvimの設定ファイルです。 自分なりにこれは必須だと思うものをあげました。 設定した内容については次回投稿する記事で取り扱う予定です。 ## でもって何が便利なのか 今のところ普通のエディタやIDE(eclipseやvisual stusioなどの統合開発環境)に比べて使いづらい印象をウケていると思います。 今のところはそれでいいと思います。 こっからvimの操作での強みであるモーション(移動)とオペレータ(操作)について説明します。 今度は移動のチートシートです。移動はノーマルモードで行ないます。 ここから登場する`C-`は`Control`と同時押しで、2文字続いているものは続けて打ってください。 vim|意味 ---+--- w|次の単語の先頭へ e|単語の最後へ b|前の単語の先頭へ 0|行頭へ $|行末へ gg|ファイルの先頭へ G|ファイルの最終行へ %|マッチする文字へ移動(対応する[]や()への移動) C-f|次のページへ C-b|前のページへ まぁこんな感じでしょうか。 続いて操作です。 vim|意味 ---+--- y|ヤンク(コピー) Y|行をヤンク p|ペースト P|現在位置にペースト x|カーソル下の一文字を削除 d|削除 D|行内のカーソル以降を削除 dd|行を削除 u|元に戻す C-R|操作を進める r|変更(一文字) R|変更(入力したもの全部) c|一文字消してインサートモードへ C|一行消してインサートモードへ A|行末に移動してインサートモードへ a|カーソルの一つ右からインサートモードへ I|行の始まり(文字の開始位置)からインサートモードへ >|右へインデント <|左へインデント gu|小文字へ gU|大文字へ .|事前の操作を繰り返す 移動も操作も他にもいっぱいあるけど私が良く使うのはこんなところです。 では実際に操作してみましょう。 テストデータを用意したのでコピーしてvimでペーストしてください。 ```vim:test_data vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim vim [indent hoge [foo hoge] hoge] (indent hoge (foo hoge) hoge) [indent hoge [foo hoge] hoge] (indent hoge (foo hoge) hoge) [indent hoge [foo hoge] hoge] (indent hoge (foo hoge) hoge) ``` 少し気持ち悪いですが気にしないでください。洗脳じゃないです。 まずは`w`,`e`,`b`などで移動を試してください。 ついでに[]()で`%`を押して対応する括弧に移動することを確認してください。 チートシートにありませんでしたが`f[アルファベット]`で行内の[アルファベット]の部分に移動します。 `/[word]`で検索を行なえます。`/vim`と打ってください。 `n`,`N`でそれぞれ次と前の検索へ移動することができます。 操作は移動と組み合わせて使うことができます。 `dw`で次の単語の先頭まで削除が行なえます。 更に操作やモーションは回数指定が可能です。 `10dd`では10行削除可能です。 もう一つ強力な機能としてテキストオブジェクトというものがあります。 例えば操作+`iw`で単語に対して操作を行ないます。 `diw`ではカーソルの位置に関係なく単語を消せます。 以下によく使いそうなテキストオブジェクトのチートシートを示します。 vim|意味 ---+--- iw|単語(以降iの部分はaでも代用可能です) iW|単語(空白含む) is|文 it|タグ i]|括弧("",'',(),<>,{})なども対応可能です) テストデータの(indentあたりで`yi)`とすると括弧内をヤンクできます。 どっかで`p`でペーストしてみてください。 `20i`で`vim `と入力した後にescでノーマルモードに戻るとこの気持ち悪いテストデータの作り方が...。 最後に`dis`とするとすごいことになるのでやってみて。 ## 最後に vimrcと追加プラグインによってvimはIDEに匹敵する、あるいはそれ以上の使い心地になる可能性を秘めています。 この記事の内容はかなり搔い摘んだ話です。 更にvimの基礎を深めたい方はターミナルで`vimtutor`と入力してvimのチュートリアルレッスンを開始できます。 テキストを読み進めながらいろいろやってみてください。 次回以降は今回扱えなかったビジュアルモードやコマンド、vimrc、プラグインの話に入っていく予定です。 [vim実践的入門初級編](http://qiita.com/himinato/items/d7fdbee03c4857b1ce20) |
|
| 127位 |
|
|||
|
20:04:28 |
(サイバーエージェント 所属) |
|
# JavaScriptのクラス?コンストラクタ??
最初に、JavaScriptにはクラスはありません。 コンストラクタからインスタンスを生成します。 なんだかなぁ・・と言う人は、 とりあえず単語は置いておいてコードから理解してください。 # コンストラクタの基本 ```javascript:コンストラクタ function Dog(name, cry) { this.name = name; this.bark = function() { console.log(cry); }; } var dog = new Dog('きなこ', 'わんわん'); console.log(dog.name); // きなこ dog.bark(); // わんわん ``` もし「new」を忘れたら、関数呼び出しになってしまって一大事です。 これがわからない方はこちらの記事へ。 [「JavaScript」の「this」は「4種類」??](http://qiita.com/items/9935ce476a17d6258e27) #newの挙動 もし「new」をつけた場合は何がおこっているのか。 実は暗黙のルールで二行加わっています。 ```javascript:コンストラクタ function Dog(name, cry) { // var this = {}; this.name = name; this.bark = function() { console.log(cry); }; // return this; } ``` このように「new」をつけた場合は上の挙動になります。 これを見たら理解は簡単ですね。 毎回新しい空オブジェクトを生成して、 それに初期プロパティを加えて「return」しています。 newをつけた場合の暗黙のルールとして… んっ、なら明示的に書いた場合はどうなるの?? ってことで別の書き方も身につけましょう。 #newなしパターンは?? ```javascript: function Dog(name, cry) { var self = {}; self.name = name; self.bark = function() { console.log(cry); }; return self; } ``` この場合はさっきのthisの暗黙のルールは適用されなくなります。 なぜなら明示的にローカル変数「self」を「return」してるからです。 なので、「new」をつけてもつけなくても挙動は一緒になります・ 暗黙のルールの「return this;」がないからです。 ```javascript: function Dog(name, cry) { return { name: name, bark: function() { console.log(cry); } } ``` これはさっきのselfとまったく同じです。 これも単純に関数なのですが、 基本的には最初を大文字として コンストラクタ呼び出しを期待するのがbetterです。 そしてもし「new」をつけ忘れたとしても大丈夫。 バグは発生せず、期待通りの動きをしてくれます。 #注意点 ここでメモリを気にする方は関数の生成についても考えたいですね☆ 上記の欠点としてはプロトタイプへのリンクがきれてしまうことです。 はて、プロトタイプって? [JavaScriptのプロトタイプ入門] (http://qiita.com/items/809114f943208aaf55b3) # 外部アカウント 技術情報のみつぶやくアカウント作成しました。JavaScriptは最新情報も追っていきます。 [Twitterはこちら] (https://twitter.com/takeharumikami) [Feedlyのフォローはこちら] (http://cloud.feedly.com/#subscription%2Ffeed%2Fhttp%3A%2F%2Fqiita.com%2Ftakeharu%2Ffeed) # おすすめの記事 入門者がつまづく、thisの挙動を4種類に分けて簡単に学ぶならこれ。Apply, callの挙動までわかる。 [JavaScriptの「this」は「4種類」?」](http://qiita.com/takeharu/items/9935ce476a17d6258e27) JavaScriptでは関数はすべてクロージャ。 [そもそもクロージャって?JavaScriptでクロージャ入門](http://qiita.com/takeharu/items/4975031faf6f7baf077a) |
|
| 128位 |
|
|||
|
01:57:33 |
|
|
知っている人には当たり前、けど本業じゃないし開発1年生なのでそんな知りません的な人には
有用かなと思い書いてみます。 思いつきで書いていくので読みづらかったらすいません。 後、しょうもない内容もあるのであまり為にならないかもしれません。 #サーバ監視って? まず、サーバ監視っていっても定常的な監視を人がやることはほぼありません。 基本はZabbixなどで監視を仕掛けて検知したアラートを電話なりメールで受け取ります。 それを人が見て、 ###やばいかも? ###出ちゃいけないエラーが出てる! などと冷や汗をかいた経験はあると思います。 そんな中でも比較的厄介(アプリの不具合も十分厄介ですが)なのが ###負荷すごい高いんだけど??サーバが瀕死?? という状態です。 そんなときインフラ屋さんへ頼るのが正解ですが、 インフラ屋さんは全部のサービス、システムの仕様、アクセスのされ方、 機能を理解していないケースの方が多い(主観)と思うのと、 場合によってはもう帰っちゃいました、電話つながりません、 という時もあると思います。 そんなとき本番へアクセスできる開発者(いいかどうかは別として)が 出来ることをちょっとだけ書きます。 #なんかした人がいないか確認する 初歩的ながら割と重要で、役割分担が難しい会社であれば(じゃなくても)必然的に いつか起きることですが、なんかやっちゃった人がいる可能性があります。 何度かやっちゃった会社なら全体に周知するよう通達が出ていることでしょう。 #サーバへログインできない(SSH接続すら出来ないとき) 書くまでもありません、データセンターへGO!です。 てのは言い過ぎですが、LB、FWの問題でなければサーバをなんとかするしかありません。 AWSを使っていれば追加でインスタンスを立てるなどの方法があります。 状況が許せばオンプレミスでの管理はなるべく避けるべきです。 ※会社の体力があって人員が整っていればその限りではありません。 #サーバへログイン出来た!なんか重い まず、ここで重要なのは ###ネットワークが重いのかDBが重いのかサーバのリソース不足なのかの切り分けです。 topコマンドなどは比較的知っている方も多いと思いますが、 こんなコマンドがあります。 ###mpstat もしコマンドが叩けない場合は、インフラ屋さんへ文句を言ってもいいレベルです。 ※ yum install sysstat とか叩けば入ったりしますが。 使い方、見方はこんな感じです。 ``` $ mpstat -P ALL 1 Linux 3.2.22-35.60.amzn1.x86_64 (xxx) 2013年11月08日 _x86_64_ (2 CPU) 00時51分11秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle 00時51分12秒 all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 00時51分12秒 0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 00時51分12秒 1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 ``` ぶっちゃけ見るべきは ###%usrと%iowait%idleだけだったりします。 ※一応誤解がないようにいうと、殆どの場合は、CPU、DISK I/O、DB、ネットワークが問題になる為、 CPUの割り込み回数などで問題になるケースはまれです。 そこまで追うのであれば素直にインフラ屋さんへ押し付けるべきです。(怒られそうだけど) ただし、あーもしかしたらあの処理かなってことがあれば素直に相談してください。 一応見方も書いておくと、 * CPU:マルチコアの場合でもコア毎の使用率が全て出てきます。 * %usr:userプロセスのCPU使用率です。簡単に言うとミドルウェアやアプリケーションの使用率です。 * %iowait:ディスクへの書き込み待ちによるCPUの処理待ちです。 * %idle:CPUの待機率です。100%ってことは仕事がない状態です。 ※仮想マシンを利用していると、%stealや%guestも変わることがあります。 これは左から、仮想マシン(複数運用)からのCPU割当率、%idleの代わりになったりします。 ここまででCPU使用率だったりtopコマンドで見たloadaverageが高ければ ほぼアプリケーションだったりDISKアクセスの問題となります。 ※ちなみに、topコマンドで見た方がいいですが、uptimeコマンドでも見れたりします。 * loadaverageが高くてiowaitが高ければほぼDISK(DBや書き込みが多い)アクセスの問題、 * loadaverageが高くiowaitが低くidleが低ければほぼアプリケーション、ミドルウェアの問題です。 * loadaverageが低くても、CPUが高ければアプリケーション、ミドルウェアの問題です。 ※まれにNFSでbig kernel lock などでアプリが高負荷状態に見える場合があります。 ``` ps -aux | grep <プロセス名> ``` だったりtopコマンドでどのプロセスの負荷が高いか見てください。 apacheだったりしたらアプリの可能性もあります。 というか、ちゃんとapacheの設定が出来ていて、急にアクセスの多くなるサービスでなければ ほぼアプリの問題です。 謝る心構えが必要です。 Tips:コマンドとしては、vmstat , dstat , iostat など目的に応じて様々なコマンドがあります。 ここまでで問題なければ次は #Database すいません、長くなるのでここはいつか書きます。 事例だけあげると、 ###重いクエリが走っていてロックが発生していたり、 ###最大同時接続数に達していたり、、 ###接続失敗が続いてDBにアクセスを拒否されていたり、、、 一度は通る道ですので言わずもがなかもしれません。 DBつながんないなーって状態であれば、上記のいずれかの可能性が高いので 即刻インフラ屋さんへ連絡です。 次は #ネットワーク ちなみに、SSHで接続した時に重くなければNW構成にもよりますが、 全体が遅延している可能性は低いです。 sar -n DEV コマンドで情報は見れますが、多分見てもよくわからないし 一開発者では対処の仕様もないので割愛です。素直にインフラ屋さんへ相談です。 あとは、 #Memory ですが、 ``` $ free -m total used free shared buffers cached Mem: 7455 6480 975 0 116 4933 -/+ buffers/cache: 1430 6025 Swap: 10239 35 10204 ``` でSwapのusedがひどい状態(この例では35MB swap落ちしています。。。) でなければ、問題ありません。 ひどい状態というのは、何百MBも落ちちゃっている場合です。 DBのチューニングやミドルウェアのチューニングが適当になっていなければ 殆ど発生しません。 #最後 かなりざっくりした内容ですが、サーバ監視についての雰囲気を少しでも感じてもらえたら幸いです。 出来れば見た内容をインフラ屋さんへ整理して伝えられるとなお良いです。 後、一番重要なことを書き忘れましたが、 ###ちょっと知ってるからってDBいじったりプログラムを直接書き換えたり勝手に再起動をしないこと です。 トラブル発生時にありがちなのが、2次災害です。 しかも人的な。 システムが落ちる程度ならまだしも、データアクセスが思いからってデータを消そうとして 間違ってテーブル毎消しちゃったり、point time リカバリなど勝手にやろうとして 失敗して戻しようがなくなったり、などは極稀だと思いますが、余計にやらなければ ならないことが増えたりします。 落ちてもちゃんと管理者と連絡をとって対処をしましょう。 本当にあせる必要があるのか今一度踏みとどまって考えてください。 インフラ屋さんへ電話が繋がらないのはあなたのせいではありません。 それでもやらなければならない事情があればその時はしょうがないんだと思います。 今回はこれで終わりですが、次は真面目にDBチューンング周りだったり、 スタックトレースの解析だったりも 気が向いたら書き記したいと思います。(忘れてきてるので残したいってのもあります) |
|
| 129位 |
|
|||
|
21:22:07 |
(Increments Inc 所属) |
|
## はじめに
チーム内でコーディング規約を作っても,ついクセで違う書き方をしたり気にしない人がいたりして形骸化しがちだと思います.またレビュー時に細かい違いを指摘するのも面倒です.そんなときは[rubocop](https://github.com/bbatsov/rubocop)を入れましょう ## インストール ```ruby gem i rubocop ``` これでrubocopコマンドがインストールされ,`rubocop foo.rb`とするとチェックできます. コマンドラインからいちいち実行したくないので,[各エディタのプラグイン](https://github.com/bbatsov/rubocop#editor-integration)をインストール.すると,以下のようにコーディングルールに合わない箇所を指摘してくれます.  ここでは「bodyが1行のときは後置ifか,&&や||を使え」と言われています. ## 設定 rubocopが準拠するコーディング規約は[同じ開発者が提案しているもの](https://github.com/bbatsov/ruby-style-guide)ですが,`.rubocop.yml`ファイルを編集することで自分達に合った形に変更することができます. うちではチームで話し合い,10箇所ぐらい変更しました. ## うちの設定 こんな感じです.(rubocop v0.9.1) 最近のバージョンだとデフォルト設定は別ファイルに切り出されたのでカスタマイズしやすい. ```yaml:.rubocop.yml # This file overrides https://github.com/bbatsov/rubocop/blob/master/config/default.yml # Use UTF-8 as the source file encoding. Encoding: Enabled: false # Limit lines to 80 characters. LineLength: Enabled: false # Avoid methods longer than 10 lines of code MethodLength: Enabled: false # Favor modifier if/unless usage when you have a single-line body. IfUnlessModifier: Enabled: false # Favor modifier while/until usage when you have a single-line body. WhileUntilModifier: Enabled: false # Preferred collection methods. CollectionMethods: Enabled: false # Avoid Perl-style regex back references. AvoidPerlBackrefs: Enabled: false # Don't interpolate global, instance and class variables directly in strings. VariableInterpolation: Enabled: false # Don't use semicolons to terminate expressions. Semicolon: AllowBeforeEndInOneLineMethods: false # Use only ascii symbols in comments. AsciiComments: Enabled: false # TODO: Change it to true when $redis and $mixpanel is removed # # Do not introduce global variables. AvoidGlobalVars: Enabled: false ``` ## 関連 obective-cなら[Objective-C - 意識の高さからかコードフォーマッター設定を公開 - Qiita](http://qiita.com/ayakix/items/3f05da9541b8e130e39f) vimから自動実行するなら [Ruby - Rubocopをsyntastic.vimから実行する - Qiita](http://qiita.com/yuku_t/items/0ac33cea18e10f14e185) |
|
| 130位 |
|
|||
|
15:38:33 |
|
|
## Objective-Cを愛してください
- Objective-CはCの拡張です。 - Cでできることはすべてできるし、Cでできないこと( **恐らく計算機では不可能なこと** )はすべてできません。 - Objective-CはJavaよりも年上です。 - 「Objective-CってJavaに似てるね」と言われると、Objective-Cを愛するものとしては少し悲しい気分になります。 - 歴史的にはJavaがObjective-Cの影響を受けています。 - メッセージングに使うブラケット`[]`はObjective-Cのチャームポイントです。 - Objective-CがSmalltalkの子であると証明するための、とってもチャーミングな形質です。間違っても「キモい」なんて言わないであげてください。 - 関数とメソッドを見た目で区別できるという利点もあります。 - メソッド名が長いのはメソッド自身がドキュメントの役割を果たしているからです。 - タイプ数が多い? Xcodeを始めとしたコード補完が効くIDEを使いましょう。もちろんvimでも可能です。 ## Objective-Cに愛されるには - Cを書けるようになりましょう。 - Cを知らなければ **本当の** Objective-Cを書くことはできません。 - 逆にCを本当に理解していれば、Objective-CとCに本質的な差がないことを発見できるでしょう。 - 他言語の常識は一旦捨てましょう。 - それらの常識はObjective-Cに通用しないかもしれません。 - 初めてプログラミングを学ぶ子供のように純真な心でObjective-Cに触れましょう。 - コンパイラを信頼しましょう。 - clang/LLVMにしろ、GCCにしろ、コンパイラは多くのプログラマよりはるかに賢く最適化します。 - Objective-Cのオブジェクトを用いた実装のランタイム性能は確かに純粋なCに比べれば低速ですが、ほとんどの場合致命的な問題にはなりません。また、 Objective-Cは純粋なCのコード、C++のコードと共存することが可能です。 - 動的なプログラミングをしましょう。 - **プロトコル** 、 **デリゲート** 、 **カテゴリ** など、Objective-Cや動的オブジェクト指向プログラミング言語に備わった機能を活用したコードはとてもエレガントで、なおかつ可読性と拡張性に優れています。 - Objective-CはC由来の静的型付けとSmalltalk由来の動的型付けを組み合わせることで、( **C以上C++未満の** )安全性と書きやすさを両立させています。 - Objective-Cのオブジェクトは、実行時にはすべて`id`として評価されることを覚えておくと、コードを書いたりデバッグをする際のヒントになるでしょう。 - Objective-Cプログラマを名乗る前に、一度は[Objective-C Runtime Programming Guide](https://developer.apple.com/library/mac/documentation/cocoa/conceptual/objcruntimeguide/objcruntimeguide.pdf)を熟読しておきましょう。 ## 特にOSX、iOSプログラマへ - OSX、iOS向けにObjective-Cを書くプログラマは、ARCを使いましょう。 - ARCはガーベジコレクタでは **ありません** 。実行時のオーバーヘッドはほとんどありません。 - 古い環境(OSX 10.5以前やiOS 4.2以前)では動作しませんが、あなたのアプリケーションは本当にそれらをサポートする必要がありますか? もしサポートする必要がないのであれば、MRC(Manual Reference Counting)を使う理由はほぼゼロに近いはずです。 - ARCはプログラミング初心者向けの機能では **ありません** 。むしろCの本質的なメモリモデルと、Objective-C伝統の参照カウント双方を理解していなければ使いこなすことはできません。 - Appleの環境では、あらゆるプログラマが書くコードは5年以内( **Objective-Cに限ればほぼ2、3年以内** )に陳腐化します。 - これはObjective-Cに限りません。Apple帝国ではCやC++のAPIもどんどん削除され、追加され、ときにはフレームワークごと削除されます。あきらめるしかありません。 - 陳腐化したコードを更新するためにプログラマの仕事が増えます。もしかしたらAppleは雇用創出をしているのかもしれません。 - **Objective-Cが陳腐化しました(オチ)** |
|
| 131位 |
|
|||
|
10:56:49 |
(Wantedly 所属) |
|
## github
* gitのスライドがまとまってる http://www.find-job.net/startup/7-git-slides * webベースでgitを勉強 http://try.github.io/ * githubフローの説明 https://gist.github.com/Gab-km/3705015 ### ゴール * add, commit, push, branch, checkoutを覚える * 自分でトピックブランチを作ってpush、その後githubでpull requestを作成することができる * githubフローを理解する ## rails * railsのチュートリアル http://railstutorial.jp/ * MVCの挙動(特にしっかり理解) http://railstutorial.jp/chapters/a-demo-app#sec-mvc_in_action * GitHubに自分のアプリをPushする http://railsgirls.jp/github/ * Heroku に Rails アプリをアップ http://railsgirls.jp/heroku/ ### ゴール * routes、model、controllerを自分で作れる * githubに自分のコードを公開する * herokuに自分のコードをアップする * railsのチュートリアルを自分で全部やる ## さらに * 新人エンジニアに読んで欲しい英語ドキュメントまとめ (Ruby/Rails編)(次はここ) http://engineer.wantedly.com/2013/12/26/getting-started.html * rails cast(半年くらい契約してた) http://railscasts.com/ * pro git(一通りgitが分かったら読むべき) http://git-scm.com/book/ja/ ## その他ブックマーク * rubyのmethodを探したいとき http://ruby-doc.org/core-2.1.1/ * railsのメソッドを探したいとき http://api.rubyonrails.org/ * gemを探したいとき(カテゴリを見るとどういう種類があるのかも勉強になる) https://www.ruby-toolbox.com/ * dash(ローカルでrailsやrubyを検索) https://itunes.apple.com/jp/app/dash-docs-snippets/id458034879?mt=12 ## インターン生・未経験者向け投稿 以下も同じように、これからスキルを身につけたい人向けに開発インターンで役立つことを書いています。 * 開発インターンで気をつける5つのポイント http://qiita.com/reikubonaga/items/d00c179957d57f48bf73 * 初めてコードレビューされる人のためのpull requestとcommitの作り方 http://qiita.com/reikubonaga/items/e3b3b19c14d4ef4efb95 * 技術面接を受ける前に確認しておくといいこと https://www.wantedly.com/companies/wantedly/post_articles/42207 * Webサービスのための7つのデータ分析基本パターン https://www.wantedly.com/companies/wantedly/post_articles/62759 |
|
| 132位 |
|
|||
|
09:49:12 |
(株式会社キュリオシティソフトウェア 所属) |
|
iOS7からステータスバーの背景とナビゲーションバーが一体化し、ステータスバーの文字列スタイルをViewControllerで制御するためのdelegateが追加されたのでメモ。 # ナビゲーションバーをカスタマイズする ## ナビゲーションバーの色を変える まず、ナビゲーションバーの背景色はbarTintColorで変更できる ```objc [UINavigationBar appearance].barTintColor = [UIColor colorWithRed:0.000 green:0.549 blue:0.890 alpha:1.000]; ``` ナビゲーションバー上の戻るボタンの色やUIBarButtonItemの色はtintColorで変更できる。 ```objc [UINavigationBar appearance].tintColor = [UIColor whiteColor]; ``` ## タイトルをカスタマイズする 次に、タイトルの文字色を変える。 iOS7ではUITextAttributeTextColorは非推奨になった。NSForegroundColorAttributeNameが使える。 ```objc [UINavigationBar appearance].titleTextAttributes = @{NSForegroundColorAttributeName: [UIColor whiteColor]}; ```  # ステータスバーをカスタマイズする ## ステータスバーの文字色を一括で変更したい ステータスバーの文字色もコードで白に出来る。 ```objc [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; ``` ただしinfo.plistで”View controller-based status bar”をNOにしておく必要がある。 ## ステータスバーのスタイルをViewController毎に変えたい場合 逆にアプリ全体を一括で変えたくない場合、info.plistで”View controller-based status bar”をYESにしておくと、UIViewControllerでステータスバーのスタイルを動的に変更することができる。 ```objc //下の2つのメソッドを呼び出す事を伝える - (void)setNeedsStatusBarAppearanceUpdate; //オーバーライドしてスタイル指定 - (UIStatusBarStyle)preferredStatusBarStyle; //オーバーライドして非表示かどうかを選択 - (BOOL)prefersStatusBarHidden; ``` setNeedsStatusBarAppearanceUpdateメソッドを実行した際に、preferredStatusBarStyleと、prefersStatusBarHiddenメソッドが実行されるのでそれぞれをオーバーライドしてスタイルの変更や非表示選択を行える。 ```objc - (void)viewDidLoad { [super viewDidLoad]; if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) { //viewControllerで制御することを伝える。iOS7 からできたメソッド [self setNeedsStatusBarAppearanceUpdate]; } } - (BOOL)prefersStatusBarHidden { //YESでステータスバーを非表示(NOなら表示) return NO; } - (UIStatusBarStyle)preferredStatusBarStyle { //文字を白くする return UIStatusBarStyleLightContent; } ``` ### 動的にステータスバーの表示・非表示を切り替える ボタン押下時にステータスバーを非表示にする場合、フラグを作ってそれをYESにする方法がiOS7 Tech Talkで紹介されていた。 ```objc @property BOOL shouldBeHidingStatusBar; - (BOOL)prefersStatusBarHidden { return self.shouldBeHidingStatusBar; } - (IBAction)hideStatusBar { self.shouldBeHidingStatusBar = YES; [self setNeedsStatusBarAppearanceUpdate]; } ``` さらにステータスバーをアニメーションさせたい場合は、setNeedsStatusBarAppearanceUpdateをUIViewのanimateWithDuration:animations:メソッドでblocksに入れ、preferredStatusBarUpdateAnimationメソッドをオーバーライドしてUIStatusBarAnimationを指定する。 ```objc - (IBAction)hideStatusBar { self.shouldBeHidingStatusBar = YES; [UIView animateWithDuration:0.5 animations:^{ [self setNeedsStatusBarAppearanceUpdate]; }]; } //スライドするアニメーションを指定 - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation { return UIStatusBarAnimationSlide; } ``` ### UINavigationControllerがあり、ViewController毎にスタイルを変えたい場合、 ステータスバーのスタイル変更(色を白にしたいなど)は、UINavigationControllerがあると途端にややこしくなる。 まずUINavigationControllerがある場合の基本動作を把握しておく #### 基本動作 - UINavigationControllerのchildViewControllerForStatusBarStyleが動作しnilを返す。 - nilの場合、自身(UINavigationController)のpreferredStatusBarStyleが動作。 - preferredStatusBarStyleはUIStatusBarStyleDefault(つまり黒)を返す - nilを返されて各ViewControllerのpreferredStatusBarStyleが動作しない UINavigationControllerに紐づけてある各ViewControllerのpreferredStatusBarStyleが動作しないので、何が起こっているのか非常にわかりづらいし、コンテナであるUINavigationController自体で問題解決しないといけないとはなかなか気づけなかった。以下が解決方法。 #### 解決方法1. ナビゲーションバーのスタイルを変える - 各ViewControllerでnavigationBar.barStyle = UIBarStyleBlack;とする - UINavigationControllerはスタイルが黒のときステータスバーを白になる #### 解決方法2. UINavigationControllerを継承かカテゴリで動作を変える - UINavigationControllerのchildViewControllerForStatusBarStyleをオーバーライドして表示しているViewControllerを返す - 各ViewControllerでpreferredStatusBarStyleをオーバーライドしスタイルを変える UINavigationControllerをカテゴリでオーバーライドする例 ```objc #import "UINavigationController+StatusBarCustom.h" @implementation UINavigationController (StatusBarCustom) - (UIViewController *)childViewControllerForStatusBarStyle { // デフォルトこいつはnilが返るので表示されたものを返すようにする return self.visibleViewController; } @end ``` 個別のViewControllerでpreferredStatusBarStyleが動作するのでステータスバーの色を白にする ```objc - (UIStatusBarStyle)preferredStatusBarStyle { //文字を白くする return UIStatusBarStyleLightContent; } ``` ### 解決方法の選定 そもそもUINavigationControllerのchildViewControllerForStatusBarStyleがデフォルトでnilを返している基本動作がおかしい気がする。同じコンテナのUITabBarControllerはnilを返しておらず、タブが選択されたViewControllerを返していて理にかなっているし、そのおかげでコンテナに邪魔されない。もしコンテナで一括で変えたい場合などはそこで設定すればいいと考えられるので自然じゃないかな。 そのため、解決方法2についてはカテゴリで既存の動作をオーバーライドするのも悪くない手ではないかと思う。むしろこういう時こそカテゴリでUIKitをオーバーライドすべきじゃないかなとすら思う。すでにアプリの設計上UINavigationControllerを継承で作りきっているならカテゴリ使う必要ないよねってな感想。 解決方法1については、UINavigationBarを黒にすることでたまたまステータスバーが白くなることを利用している風に感じてしまう。UINavigaitonBarに画像やTintColorを設定しなければほんとに黒くなるので、そうしたい場合には理にかなっているが、黒と設定しているが見た目が黒じゃないということ自体には人の書いたコードを保守する目線で見ると辛い。 ## ナビゲーションバーの非表示と同時にステータスバーを非表示にする UINavigationControllerのsetNavigationBarHidden:animated:メソッドを使いナビゲーションコントローラを非表示にしようとする場合、ステータスバー更新のためのUIViewControllerのsetNeedsStatusBarAppearanceUpdateメソッドを使う必要がない。 ```objc //フルスクリーンにするメソッドを作る //ナビゲーションバーとステータスバーを非表示にしたい - (void)changeFullScreen { //ここでナビゲーションバーを非表示にするが //同時にステータスバー更新のメソッドも呼ばれる [self.navigationController setNavigationBarHidden:YES animated:YES]; //以下は意味なし //ここでステータスバーの更新を呼んでも2度手間になる // [self setNeedsStatusBarAppearanceUpdate]; } ``` ## 分かってないこと MFMailComposeViewControllerでメールのモーダルの画面を立ち上げるとステータスバーの文字色が黒色(デフォルト)になってしまいコントロール出来ない... SKStoreProductViewControllerでAppStoreを表示するモーダルの場合は、ナビゲーションバーのtintColorをコントロールする方法をしりたい… ## 参考 WWDC2013 のsession214 iOS 7におけるUIStatusBarStyleのベストプラクティス http://cockscomb.hatenablog.com/entry/2013/11/12/190905 iOS7 Teck Talk Videos : Architecting Modern Apps,Part1 https://developer.apple.com/tech-talks/videos/index.php?id=2 |
|
| 133位 |
|
|||
|
11:29:18 |
(XFLAG STUDIO, mixi, Inc. 所属) |
|
(以前社内のLTで発表したネタをそのまま書いています)
あちこちのサーバーにsshするときに、まさか毎回パスフレーズやパスワード打ったりしてないですよね? サーバー上に秘密鍵をおいたりしていないですよね?まだssh-agentつかってない人はぜひ使いましょう。 # ssh-agentの使い方 ## Windows環境 - PuTTY, Tera Term - Pageant.exeを使う - 起動時の引数に秘密鍵のパスを与えると便利。 - ショートカットを作って、スタートアップ登録すると更に便利。 - 例: "C:\Program Files (x86)\PuTTY\pageant.exe" "C:\hoge\fuga.ppk" - ssh-agent.exeを使う - 参考: http://mattn.kaoriya.net/software/20081106192615.htm - Poderosa - メニュー「ツール」「エージェントフォワーディング」から設定をする ## Mac OS X環境 ※ssh-agentの起動は不要 秘密鍵の登録状況の確認 ``` $ ssh-add -l The agent has no identities. ``` 秘密鍵の登録 ``` $ ssh-add hoge/fuga Enter passphrase for hoge/fuga: Identity added: hoge/fuga (hoge/fuga) ``` 確認してみる ``` $ ssh-add -l 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx hoge/fuga (RSA) ``` `-A`オプション付きでsshすると、接続先でも秘密鍵の情報が引き継がれる ``` $ ssh -A ホスト名 $ ssh-add -l 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx hoge/fuga (RSA) ``` ## Linux環境 ssh-agentの起動 ``` $ eval `ssh-agent` $ ssh-add -l The agent has no identities. ``` 秘密鍵の登録 ``` $ ssh-add hoge/fuga Enter passphrase for hoge/fuga: Identity added: hoge/fuga (hoge/fuga) ``` 確認してみる ``` $ ssh-add -l 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx hoge/fuga (RSA) ``` `-A`オプション付きでsshすると、接続先でも秘密鍵の情報が引き継がれる ``` $ ssh -A ホスト名 $ ssh-add -l 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx hoge/fuga (RSA) ``` # ログアウトすると UNIXドメインソケットが消える対策 - screenやtmuxを使っている環境では、ssh-agentを起動しているホストでログアウトすることはよくある。しかしログアウト時に、UNIXドメインソケットが消えてしまう。 - そんなときは、SSH_AUTH_SOCKを固定化する。 - 参考 http://www.gcd.org/blog/2006/09/100/ - 環境変数SSH_AUTH_SOCKに設定される値 /tmp/ssh-xxxxx/agent.[sshd PID] このパスにUNIXドメインソケットが作られる。これは、ログインのたびに値は変化する。 そのため、screen, tmuxをアタッチしなおした際にソケットに接続できなくなってしまう。 - 環境変数SSH_AUTH_SOCKを固定化させれば問題は起きない。 - 毎回作られる/tmp以下のパスから固定化させたパスへシンボリックリンクを作っておき、環境変数SSH_AUTH_SOCKが参照するように.bashrcや.zshrcで設定する。 # .ssh/configの活用 ~/.ssh/configを設定しておくと便利です。 例えば、MacOS XやLinuxでの設定 ``` % vi ~/.ssh/config ForwardAgent yes Host hogehost User fuga ``` こうしておくと、hogehostへsshする際に -Aオプションとユーザー名の指定が省略できます |
|
| 134位 |
|
|||
|
07:49:07 |
|
|
バージョン24から入った`package.el`により、プラグインの導入が格段に容易になったEmacs。そこで、ELPA互換のリポジトリの一つである[MELPA](http://melpa.milkbox.net/)のダウンロードTOP50+αのパッケージについてまとめてみた。
(ランキングに関しては2014/3/26時点の情報を使用) それ単体で便利というよりも、有名なパッケージの依存関係でダウンロードされるライブラリもあるので注意。 ちなみに、個人的なオススメパッケージは、[auto-complete](#2-3)、[helm](#2-7)、[flycheck](#2-10)、[undo-tree](#2-11)、[zenburn-theme](#2-12)、[expand-region](#2-15)、[smartparens](#2-24)、[rainbow-delimiters](#2-28)、[multiple-cursors](#2-29)。 また、TOP50には入っていないが、[anzu](#2-51)、[volatile-highlights](#2-52)、[powerline](#2-53)、[git-gutter-fringe](#2-54)、[hlinum](#2-55)、[smooth-scroll](#2-56)、[yascroll](#2-57)、[undo-hist](#2-58)、[point-undo](#2-59)、[sequential-command](#2-60)もおすすめ。 お好みで[smooth-scroll](#2-56)、[yascroll](#2-57)もどうぞ。 実際に自分が利用している.emacs.dをGithub上で公開してみたので、何かの参考になれば幸い。 https://github.com/hottestseason/dotemacs `package.el`の利用方法については以下のリンクを参考に。 [package.el - Emacs JP](http://emacs-jp.github.io/packages/package-management/package-el.html) P.S. たくさんのストックありがとうございます。ストックしてくださっている方や、新しくEmacsのパッケージを探している方のためにも、随時更新していこうと思います。 何か間違いがある場合や他にもオススメパッケージがあれば教えて下さい。 ## 1. [magit](https://github.com/magit/magit) EmacsのためのGitクライアント。 標準のVCと違い、Gitに特化することによって、コンソールからしか出来なかったことがほとんど出来る。 [EmacsのGitクライアント:Magit - Qiita/takc923](http://qiita.com/takc923/items/c7a11ff30caedc4c5ba7)  ## 2. [dash](https://github.com/magnars/dash.el) elispのリスト操作のためのモダンライブラリ。 [EmacsLispを書くときに便利なライブラリ「dash.el」 - Qiita/mrkuc](http://qiita.com/mrkuc/items/0494d5f415b5681a5849) [Emacsのモダンなライブラリ4+1選 #dash.el - Qiita/pogin503](http://qiita.com/pogin503/items/01005402dc7cfd375b55#1-2) ## 3. [auto-complete](https://github.com/auto-complete/auto-complete) 言わずと知れた最強の自動補完機能。これのおかげでタイピング力が落ちたという声もちらほら。 [公式サイト](http://cx4a.org/software/auto-complete/index.ja.html)  ## 4. [popup](https://github.com/auto-complete/popup-el) Emacsでポップアップメニューとツールチップを実現するためのライブラリ。auto-completeで使われている。 ## 5. [git-commit-mode](https://github.com/magit/git-modes#git-commit-mode) Gitのコミットメッセージを書くためのメジャーモード。 Gitの`editor`を`emacs`/`emacsclient`にするのをお忘れなく。  ## 6. [s](https://github.com/magnars/s.el) elispの文字列操作のためのモダンライブラリ。 [Emacsのモダンなライブラリ4+1選 #s.el - Qiita/pogin503](https://github.com/magnars/s.el) ## 7. [helm](https://github.com/emacs-helm/helm) `anything`の後継。 補完・リストアップ・絞り込みのためのインターフェース(?)。 使ったことがない人には何のこっちゃわからないかもしれないが、超絶オススメなパッケージの一つ。 日本語のドキュメントが少ないのが玉に瑕。 [もう初心者なんて言わせない、Anything で始まる Emacs 道。 - 日々、とんは語る。](http://d.hatena.ne.jp/tomoya/20090423/1240456834) (注:Anythingの解説)  ## 8. [yasnippet](https://github.com/capitaomorte/yasnippet) TextMateからインスパイアされたテンプレートフレームワーク。 標準で[いろいろな言語](https://github.com/AndreaCrotti/yasnippet-snippets)に対応しているので、なかなか便利だが、「最近の言語はそもそもテンプレートなんていらないよね」なんて口が裂けても言えない。 [最強テンプレート補完機能: YASnippet - Ochiailab Tips](http://ochiailab.blogspot.jp/2012/12/yasnippet.html) ## 9. [git-rebase-mode](https://github.com/magit/git-modes#git-rebase-mode) `git rebase -i`で作られる`git-rebase-todo`のためのメジャーモード。`git-commit-mode`と同じく`editor`の設定をお忘れなく。 ## 10. [flycheck](https://github.com/flycheck/flycheck) エラー表示のためのライブラリであるflymakeに、様々な言語のための初期設定や自作のための便利なAPIを追加してくれるパッケージ。 flymakeでゴニョゴニョする前に、こちらを試してみよう。 [公式ユーザマニュアル](http://flycheck.readthedocs.org/en/latest/manual/index.html) [Flycheckでモダンなシンタックスチェック - Qiita/senda-akiha](http://qiita.com/senda-akiha/items/cddb02cfdbc0c8c7bc2b) [flymakeの設定する前に flycheckを確認しよう - Life is very short](http://d.hatena.ne.jp/syohex/20130123/1358950322)  ## 11. [undo-tree](http://www.emacswiki.org/emacs/UndoTree) undoの履歴をビジュアライズするライブラリ。 ぜひ、すべてのエディタに標準で付いていて欲しい機能。 これと[undohist](https://github.com/m2ym/undohist-el)があればGitなんていらないよね(嘘)。 [Emacs24.3の導入とundo-tree.elの紹介 - Qiita/takc923](http://qiita.com/takc923/items/c3d64b55fc4f3a3b0838)  ## 12. [zenburn-theme](https://github.com/bbatsov/zenburn-emacs) ローコントラストなテーマである[Zenburn](http://slinky.imukuppi.org/zenburnpage/)のEmacs版。 今までのスクリーンショットでの使用テーマがこれ。 ## 13. [pkg-info](https://github.com/lunaryorn/pkg-info.el) インストールされているパッケージのバージョンを取得するライブラリ。 `cider`や`flycheck`、`projectile`など、有名なパッケージで使用されている。 ## 14. [smex](https://github.com/nonsequitur/smex) `ido`の`M-x`版。`helm`や`anything`は使いたくないけど、あいまい検索で`M-x`を使いたい人向け。 最近使ったコマンドは上位に出てくる。 ## 15. [expand-region](https://github.com/magnars/expand-region.el) Emacsのリージョン(選択範囲)をインタラクティブに広げたり、狭めたりする機能を追加するパッケージ。 様々な言語やコンテキストに対応していて、特にlisp系言語や[multiple-cursors](#2-29)と相性がいい。 [Episode 09: expand-region - Emacs Rocks! (デモ)](http://emacsrocks.com/e09.html) [expand-region.elの紹介 - Life is very short](http://d.hatena.ne.jp/syohex/20120117/1326814127) ## 16. [markdown-mode](http://jblevins.org/projects/markdown-mode/) markdownのためのメジャーモード。 コマンド一発でプレビューが開けるらしいが、それなら[kobito](http://kobito.qiita.com/)の方がいいよね(と、ごまをすってみる)。 ## 17. [cider](https://github.com/clojure-emacs/cider) EmacsをClojure開発のためのIDE/REPL化するためのパッケージ。 [Clojure開発環境インストール手順(Emacs) - /home/matstani/weblog](http://matstani.github.io/blog/2013/04/19/clojure-dev-env-emacs/) ## 18. [projectile](https://github.com/bbatsov/projectile) Emacsでプロジェクト内のファイルを管理するためのパッケージ。 gitやmercurial、bundler、sbtなどを認識して、自動でプロジェクトのルートディレクトリを割り出してくれ、 プロジェクト内のファイルやディレクトリへのアクセスを容易にする。 helmと連携する機能`helm-projectile`もある。 ## 19. [epl](https://github.com/cask/epl) package.elにいくつか便利なAPIを追加している。 `pkg-info`で使用されている。 ## 20. [clojure-mode](https://github.com/clojure-emacs/clojure-mode) Clojureのためのメジャーモード。 ## 21. [f](https://github.com/rejeep/f.el) elispのファイル操作のためのモダンライブラリ。 [Emacsのモダンなライブラリ4+1選 #f.el - Qiita/pogin503](http://qiita.com/pogin503/items/01005402dc7cfd375b55#1-4) ## 22. [ido-ubiquitous](https://github.com/DarwinAwardWinner/ido-ubiquitous) ファイルやバッファだけではなく、ほとんどあらゆる場面でidoスタイルの補完を使えるようにするライブラリ。 helmで(ry。 ## 23. [paredit](http://www.emacswiki.org/emacs/ParEdit) lispなどで丸括弧のバランスを維持するためのパッケージ。 [ParEdit チュートリアル - Daregada Archives](http://www.daregada.sakuraweb.com/paredit_tutorial_ja.html) ## 24. [smartparens](https://github.com/Fuco1/smartparens) カッコやクォートなどの対応関係を自動挿入・管理するパッケージ。 同じような機能を持つ、autopair、textmate、wrap-region、electric-pair-mode、pareditなどの機能をすべてまとめて、さらにいくつか新しい機能を加えた(らしい)。 latexやruby、htmlのための拡張機能も付いている。 [smartparens.el での括弧処理 - 理系学生日記](http://d.hatena.ne.jp/kiririmode/20131231/p1) [デモ動画 - Youtube](http://www.youtube.com/watch?v=ykjRUr7FgoI&list=PLP6Xwp2WTft7rAMgVPOTI2OE_PQlKGPy7&feature=plpp_play_all) ## 25. [haskell-mode](https://github.com/haskell/haskell-mode) haskellのためのメジャーモード。 ## 26. [ace-jump-mode](https://github.com/winterTTr/ace-jump-mode) 移動したい文字にピンポイントで飛ぶためのパッケージ。 [公式デモ](http://dl.dropboxusercontent.com/u/3254819/AceJumpModeDemo/AceJumpDemo.htm) [ace-jump-modeの紹介 - Life is very short](http://d.hatena.ne.jp/syohex/20120304/1330822993) [Emacsで1~3ストロークで画面上の任意の場所に移動するための設定 - non-nil](http://d.hatena.ne.jp/rkworks/20120520/1337528737) ## 28. [solarized-theme](https://github.com/bbatsov/solarized-emacs) [Solarized](http://ethanschoonover.com/solarized)テーマのEmacs版。 ## 28. [rainbow-delimiters](https://github.com/jlr/rainbow-delimiters) lispのように大量の丸括弧が入れ子構造になっている時に、対応する括弧ごとに色を変え、見やすくするパッケージ。 [Emacs LispのRainbow Delimitersで,対応する括弧を虹色で示そう. - Qiita/ncaq](http://qiita.com/ncaq/items/5a1d102723fec11a8bff)  ## 29. [multiple-cursors](https://github.com/magnars/multiple-cursors.el) 複数の場所にある特定の単語の周辺を同時に編集する機能。 `Sublime Text`の`Multiple Selection`由来で、リファクタリングに便利。 [expand-region](#2-15)や[region-bindings-mode](https://github.com/fgallina/region-bindings-mode)と合わせて使うと効果バツグン。 [Episode 13: multiple-cursors - Emacs Rocks!](http://emacsrocks.com/e13.html) [multiple-cursors.elのキーバインドを少しだけ改善 - tam5917's diary](http://tam5917.hatenablog.com/entry/20121208/1354931551) ## 30. [gitconfig-mode](https://github.com/magit/git-modes#gitconfig-mode) `.gitconfig`や`.git/config`などのためのメジャーモード。 ## 31. [elisp-slime-nav](https://github.com/purcell/elisp-slime-nav) Common Lisp開発のための拡張であるSLIMEの機能の一つである「シンボルの定義場所への移動」をEmacs Lispでも使えるようにしたパッケージ。 ## 32. [js2-mode](https://github.com/mooz/js2-mode) Google所属のハッカー、Steve Yeggeによって開発されたJavaScriptのためのメジャーモード。 標準のjavascirpt-modeと比べると * Ecma-262対応 * 正確なシンタックスハイライト * 動的なエラー表示 * Rhino、SpiderMonkeyのJavaScript拡張へのサポート が強み(らしい)。 [js2-mode - 紫藤のWiki](https://sites.google.com/site/shidoinfo/Home/programing-lang/%E9%96%A2%E6%95%B0%E5%9E%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E/ecmascript/javascript-kai-fa-huan-jing/emacs-javascript/js2-mode) ## 33. [gitignore-mode](https://github.com/magit/git-modes#gitignore-mode) `.gitignore`や`.git/info/exclude`などのためのメジャーモード。 ## 34. [diminish](https://github.com/emacsmirror/diminish) パッケージをたくさん入れてると、モードラインにたくさんのマイナーモード名が表示されるようになる。 そんな時、このパッケージを利用すれば、モードラインに表示されるマイナーモード名を消したり、変更することが出来る。 [マイナーモード表示をカスタマイズ - Meadow/Emacs memo](http://www.bookshelf.jp/cgi-bin/goto.cgi?file=meadow&node=diminish) ## 35. [inf-ruby](https://github.com/nonsequitur/inf-ruby) EmacsからRubyのREPLが利用できるパッケージ。 `M-x inf-ruby-console-auto`を利用すれば、Rails(&Pry)を認識してくれる。 [inf-rubyメモ - odz buffer](http://d.hatena.ne.jp/odz/20060619/1150779522) ## 36. [gh](https://github.com/sigma/gh.el) EmacsのGithubクライアントのライブラリ。 ## 37. [web-mode](https://github.com/fxbois/web-mode) HTMLの中でJavaScriptやCSS、PHP、Rubyなどを書くためのメジャーモード。 埋め込まれたコードは正しく色付けされる。 昔はmmm-modeとか使ってた記憶があるけど、そちらは現在あまりメンテナンスされてない模様。 っていうか、そもそも今はslimとかjadeを使っているので、生のHTMLを書いていない。 ## 38. [exec-path-from-shell](https://github.com/purcell/exec-path-from-shell) MacやLinuxなどUnix系の環境でシェル以外からEmacsを起動すると、PATHの設定が上手く引き継がれない場合がある。そんな時、このライブラリを使えば、 ```el (exec-path-from-shell-initialize) ``` と書くだけで、すべてバッチリ。 [EmacsでPATHの設定が引き継がれない問題をエレガントに解決する](http://qiita.com/catatsuy/items/3dda714f4c60c435bb25) ## 39. [pcache](https://github.com/sigma/pcache) 永続的なデータをハッシュテーブルのようなデータ構造で保存するためのライブラリ。 ## 40. [php-mode](https://github.com/ejmr/php-mode) PHPのためのメジャーモード。 ## 41. [coffee-mode](https://github.com/defunkt/coffee-mode) CoffeeScript(or IcedCoffeeScript)のためのメジャーモード。 ## 42. [color-theme](http://www.nongnu.org/color-theme/) Emacsの色設定をテーマとして纏めておくことができるライブラリ。 標準でたくさんのテーマが同梱されている。 Emacs24からは標準でthemeフレームワークが入っているので、もういらない子。 [color-themeでEmacsの見た目をきれいにする - Qiita/kubosho_](http://qiita.com/kubosho_/items/17464754663936cb7895) ## 43. [gist](https://github.com/defunkt/gist.el) Emacsからgistの作成・管理を行うためのパッケージ。 ## 44. [yaml-mode](https://github.com/yoshiki/yaml-mode) YAMLのためのメジャーモード。 ## 45. [logito](https://github.com/sigma/logito) tiny logging framework for Emacs. [gh](#2-36)で利用されている。 ## 46. [ack-and-half](https://github.com/jhelwig/ack-and-a-half) Emacsから[ack](http://beyondgrep.com/)を利用するためのパッケージ。 [ack.el](http://rooijan.za.net/code/emacs-lisp/ack-el)や[full-ack.el](http://nschum.de/src/emacs/full-ack/)を参考にして作られた。 ## 47. [dired+](http://www.emacswiki.org/emacs/dired+.el) Emacs標準の`dired`の強化版。 [Dired Plus - EmacsWiki](http://www.emacswiki.org/emacs/DiredPlus) ## 48. [move-text](http://www.emacswiki.org/emacs/move-text.el) 現在いる行や選択しているリージョンを`M-up`や`M-down`で上下に移動できる。 ## 49. [slime](https://github.com/slime/slime) EmacsをCommon Lisp開発のためのIDE化してくれるライブラリ。 [公式サイト](http://common-lisp.net/project/slime/) ## 50. [ctable](https://github.com/kiwanami/emacs-ctable) Emacsで表を表示させるためのMVCなフレームワーク。 ## 56. [anzu](https://github.com/syohex/emacs-anzu) [vim-anzu](https://github.com/osyo-manga/vim-anzu)のEmacs版。 検索時にマッチした数と現在の位置を表示してくれる。 [作者による解説ページ - Qiita/syohex](http://qiita.com/syohex/items/56cf3b7f7d9943f7a7ba)  ## 57. [volatile-highlights](https://github.com/k-talo/volatile-highlights.el) undoやyankした後にどこが変わったかをハイライトしてくれる。  ## 104. [powerline](https://github.com/milkypostman/powerline) Emacsのモードラインをだいぶシャレオツにしてくれるパッケージ。 Vimの[Powerline](https://powerline.readthedocs.org/en/latest/)のEmacs版。 カスタマイズする場合には`powerline-active1`、`powerline-active2`辺りのfaceをいじると色とかが変えられる。 モードライン全体の構成を変えたい人は、`mode-line-format`を自分好みに設定しよう。 書き方に困ったら[powerline-themes.el](https://github.com/milkypostman/powerline/blob/master/powerline-themes.el)を参考にするといいかも。 [emacsにpowerlineを導入 - $shibayu36->blog;](http://shibayu36.hatenablog.com/entry/2014/02/11/160945)  ## 124. [git-gutter-fringe](https://github.com/syohex/emacs-git-gutter-fringe) Sublime Textの[Git Gutter](https://github.com/jisaacks/GitGutter)のEmacs版で、前回からの変更点を視覚的に表示してくれる機能。 [git-gutter](https://github.com/syohex/emacs-git-gutter)と違い、`emacs -nw`のようなtty frameでは動作しないが、 fringe(フレームの左右の余白)部分に表示するため、linum-modeと併用できる。 ちなみに、使ったことはないが、git-gutterから派生したプロジェクトで[git-gutter+](https://github.com/nonsequitur/git-gutter-plus)なるものが存在していて、 git-gutterの作者もオススメしているので、興味がある方は使ってみるといいかも。 [git-gutter.el - Emacs JP](http://emacs-jp.github.io/packages/vcs/git-gutter.html) [git-gutterと git-gutter+の違い - Life is very short](http://d.hatena.ne.jp/syohex/20131002/1380724469) ## 440. [hlinum](https://code.google.com/p/hlinum-mode/) 行番号を左に出してくれるlinum-modeにプラスして、現在いる行番号を強調表示してくれる。 [作者による紹介ページ - tm_tnの布団の中](http://d.hatena.ne.jp/tm_tn/20110605/1307238416) ## 483. [smooth-scroll](https://github.com/k-talo/smooth-scroll.el) スクロールを滑らかにしてくれる。 [Emacsのスクロールが滑らかになって気持ちいい! - Qiita/ShingoFukuyama](http://qiita.com/ShingoFukuyama/items/429199542c38625c5554)  ## 602. [yascroll](https://github.com/m2ym/yascroll-el) OS標準のスクロールバーなんてダサくて、速攻で消してる人にオススメなパッケージ。 スクロールバーをちょっとかっこよくしてくれる。 詳しくはリンク先か、上の[smooth-scroll](#2-56)のスクリーンショットで確認してみよう。 [作者による解説ページ](http://d.hatena.ne.jp/m2ym/20110401/1301617991) ## 696. [undohist](https://github.com/m2ym/undohist-el) バッファやEmacsを閉じた後もファイルのundoの履歴を保持してくれる。 ## 811. [point-undo](http://www.emacswiki.org/emacs/point-undo.el) カーソル位置をundo・redoする機能を追加してくれる。 ## 974. [sequential-command](#http://www.emacswiki.org/emacs/sequential-command.el) rubykitch先生が開発した、同じコマンドを連続実行することで挙動を変える機能を追加するパッケージ。 例えば、一緒にインストールされる`sequential-command-config`をrequireすると定義される`seq-home`は次のような挙動を示す。 1. まず行頭に飛ぶ 2. 次にバッファ先頭に飛ぶ 3. 次に元の場所に戻る バッファ先頭に行く`M-<`とか覚えたくない人には大変便利。 (`seq-home`を`C-a`に割り当てとけば、`C-a`を2回連打すればバッファ先頭に行ける) 同様に`C-e`に対応する`seq-end`や前の単語を次々に加工する`seq-upcase-backward-word`や`seq-downcase-backward-word`などもめっちゃ便利。 [smartrep.el](https://github.com/myuhe/smartrep.el)でも同じことが実現出来ると思うが、そちらは使ったことないので、是非いつか使ってみたい。 [作者による紹介ページ - rubykitch](http://d.hatena.ne.jp/rubikitch/20090219/sequential_command) |
|
| 135位 |
|
|||
|
11:18:35 |
|
|
あくまでも個人的な意見であって、自分がシェルスクリプトを書く時に気をつけている事を備忘録的に列挙しているだけです。
「こうするべきだ」と押しつけている訳ではありません。 勿論「私はこうしている」とか「こうすると良いよ」という意見や議論は大歓迎です。 ## *export* (1) しないシェル変数は全て小文字で書く。 `PATH` や `HOME` などは**環境変数**なので大文字で。 スクリプト内で使っているのは単なる**シェル変数**なので小文字で。 使い方をちゃんと区別する事は重要だと思う。 ## シェルの変数展開を活用しよう ```sh: [ -z "${foo}" ] && foo="FOO" ``` という書き方でも問題ないけど、 ```sh: ${foo:-FOO} ``` と記述した方がすっきり記述できるし意味が判りやすいと思う。 ## *trap* (1) を活用しよう *trap* (1) で 0 を指定するとスクリプトが正常終了した時にも指定した処理が実行されるので、プログラム中で作成した一時ファイルの削除などにはもってこい。 スクリプトの先頭で ```sh: tmpdir="${TMP:-/tmp}/`basename ${0}`.$$" mkdir -p ${tmpdir} trap 'rm -r ${tmpdir}' 0 ``` こんな処理をしてスクリプト中で使う一時ファイルなどは全て `${tmpdir}` 以下に作成する様にすると後始末も楽で便利。 ## コマンドを使う場合はコマンドの持つ機能を活用しよう *grep* (1) や *cut* (1) と *awk* (1) や *sed* (1) を組み合わせて使うのは、殆どの場合において CPU とプロセスと時間の無駄遣い。 勿論あまりにも複雑な正規表現が必要な場合や処理が煩雑になる場合は勿論この限りではないです。 ```sh: grep "^HOGEHOGE" file | awk 'BEGIN { FS = "="}; { print $2 }' ``` というコード。一見何も問題ない様に見えるし確かに動作はするが、 *awk* (1) には拡張正規表現が実装されているで、 ```sh: awk 'BEGIN{FS="="}/^HOGEHOGE/{ print $2 }' file ``` とすれば記述が簡潔になるし *grep* (1) の分だけ CPU やプロセス、時間が節約できる。 ```sh: sed -n '/^HOGEHOGE/s/^.*=//p' file ``` としても等価なのでどちらを使うかは個人の好みで。 ## エラーメッセージは標準エラー出力に出そう。 unix を使う上では基本中の基本です。 ## インデント幅はスクリプト内で統一しましょう。 Tab でも スペース何個でも OK ですが、 1ファイルの中のインデント位は合わせないと恥ずかしいです。 ## ${PATH} は信用せずにフルパスをシェル変数に代入して使う様にしましょう。 たまに ${PATH} に "." を含めるセキュリティ意識の低い困ったちゃんがいるので、スクリプト中で使用するコマンドは ```sh: foo="/path/to/foo" : ${foo} ``` な感じで使うが良いと思います。 スクリプトの開発中は ```sh: foo="echo /path/to/foo" ``` こんな感じに変更しとくと *mv* (1) や *rm* (1) の様なコマンドの実行を抑止できるので初期段階では特に重宝すると思います。 ```sh: logger="/usr/bin/logger -p daemon.err -t ${myname}" ``` なんて設定しとくと共通のロギング処理にもなるので安全便利。 一部の linux ディストリビューションの場合 /bin/sh が bash だったりするので、設定によっては *cp* (1) や *rm* (1) などが、 cp -i とか rm -i などの様な迷惑千万な alias に設定されていてもスクリプトの動作に支障をきたさなくなります。 シェルの内部コマンドも含め全てのコマンドは *execv* (2) 可能でなければならないと POSIX で定義されているので、 POSIX 準拠を詠っている OS であれば全てのコマンドが揃ってる(筈)。 ## 複数行に渡る長いメッセージや固定データは *echo* (1) ではなくてヒアドキュメントを。 ```sh: echo "………" > ${output} echo "………" >> ${output} echo "………" >> ${output} ``` などと書いていると途中行を修正する時に ">>" をついうっかり ">" にしてしまったりする間違いをする危険性が高いし、それぞれの *echo* (1) の実行ごとにファイルのオープンクローズが繰り返されてしまい非常に効率が悪いので、 ```sh: cat << EOF> ${output} ……… ……… ……… EOF ``` と記述する様にすれば安心確実だしシステムにも優しいです。 ## スペースで区切られたデータの構文解析は *set* (1) を活用しよう。 年、月、日 をそれぞれ `$year`、`$month`、`$date` に代入する処理を素直に書けば ```sh: date="`date '+%Y %m %d'`" year="`echo $date | awk '{ print $1 }'`" month="`echo $date | awk '{ print $2 }'`" day="`echo $date | awk '{ print $3 }'`" ``` な感じになるが *date* (1)、*echo* (1)、*awk* (1) の呼び出しが煩雑になる。それならば ```sh: set -- `date '+%Y %m %d'` year="$1" month="$2" day="$3" ``` の方が CPU やプロセス、時間の節約になるし、見た目もすっきりすると思う。勿論位置パラメタは上書きされるので必要な値は事前にシェル変数に保存する。 ## bash や zsh に依存した処理は絶対使わない 折角移植性が優れているシェルスクリプトなので、なるべく posix に準拠した機能だけでスクリプトを作る様に心がけたいと思う。 |
|
| 136位 |
|
|||
|
19:22:05 |
(Drivemode, inc. / DroidKaigi 所属) |
|
## アプリを作っていてありがちなこと
Android には、画面を構成するための Activity というコンポーネントがあり、概ね MVC フレームワークの Controller に相当する機能を持っています。 MVC といえば、肥大化する Controller というのがよくある問題として挙げられますが、Activity も例に漏れず、往々にして肥大化しがちです。 また、Model も、その責務を詰め込んでいくと肥大化しやすいレイヤと言えます。 この投稿では、Controller や Model の肥大化を極力防ぐためのレイヤわけを、Android アプリ向けに書いていきます。 ## Activity を綺麗に保つ Activity は、Controller として、様々な UI から受けるイベントを受けて、適切にハンドリングする役割を持っています。 OptionsMenu や ContextMenu のハンドリングを始め、ボタンが押された時や、テキストが入力された時のイベント処理もハンドリングします。 ### イベントハンドリングにおける条件分岐を整理する 例えば、OptionsMenu や ContextMenu、あるいは、onActivityResult のハンドリング処理は、switch 文等で Activity にベタ書きしていくと、項目が増えるに従って分岐も増えていきます。 ```java // メニューの項目が増えるほどに条件分岐が複雑化する例 public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_hoge: doHoge(); return true; case R.id.menu_fuga: doFuga(); return true; default: return super.onOptionsItemSelected(item); } } private void doHoge() { Toast.makeText(getApplicationContext(), R.string.message_menu_hoge, Toast.LENGTH_SHORT).show(); } private void doFuga() { Toast.makeText(getApplicationContext(), R.string.message_menu_fuga, Toast.LENGTH_SHORT).show(); } ``` Activity にすべてが詰まっているため、テストもしづらくなりますね。 以下のように整理すると、Activity に記述する内容が最小限になります。 ```java // =========== Activity public class MyActivity extends Activity { // ... {snip} ... public boolean onOptionsItemSelected(MenuItem item) { MyActivityOptionsItemAction action = MyActivityOptionsMenuAction.valueOf(item); return action.getHandler().handle(this, null) || super.onOptionsItemSelected(item); } } // =========== Options Menu Event enum // この enum に、MenuItem の id とイベントハンドラとの対応付けをもたせ、Activity から id をもとに引いてきてイベントハンドリングをデリゲートする public enum MyActivityOptionsItemAction { HOGE(R.id.menu_hoge, new HogeActionHandler()), FUGA(R.id.menu_fuga, new FugaActionHandler()), UNKNOWN(-1, new UnknownActionHandler()); // MyActivity では取り扱っていないはずの id が来た時に使う、NullObject private final int mMenuId; private final OptionsItemActionHandler mHandler; private MyActivityOptionsItemAction(final int menuId, final OptionsItemActionHandler handler) { mMenuId = menuId; mHandler = handler; } public static MyActivityOptionsItemAction valueOf(MenuItem item) { for (MyActivityOptionsItemAction action : values()) { if (action.getMenuId() == item.getItemId()) { return action; } } return UNKNOWN; // 知らない id が来るのは実装上の不具合なので、IllegalArgumentException を投げるでも良い } public int getMenuId() { return mMenuId; } public OptionsItemActionHandler getHandler() { return mHandler; } } // =========== Interface public interface OptionsItemActionHandler<E> { public boolean handle(Context context, E entity); } // =========== impl public class HogeActionHandler implements OptionsItemActionHandler<Void> { @Override public boolean handle(Context context, Void entity) { // menu_hoge を選択された時の処理 Toast.makeText(context.getApplicationContext(), R.string.message_menu_hoge, Toast.LENGTH_SHORT).show(); return true; } } public class FugaActionHandler implements OptionsItemActionHandler<Void> { @Override public boolean handle(Context context, Void entity) { // menu_fuga を選択された時の処理 Toast.makeText(context.getApplicationContext(), R.string.message_menu_fuga, Toast.LENGTH_SHORT).show(); return true; } } public class UnknownActionHandler implements OptionsItemActionHandler<Void> { @Override public boolean handle(Context context, Void entity) { return false; } } ``` ## コールバックインタフェースを減らす 特に Model の実装時に多いのが、非同期処理後のコールバックインタフェースの乱立です。 以下のように、Loader を用いた非同期処理を含む Model クラスの実装をあちこちでしていくと、モデルのコールバックが大量に出来上がります。 ```java public class MyModel implements LoaderCallbacks<Something> { private static final int LOADER_ID = 0; private Context mContext; private MyModelCallback mCallback; @Override public Loader<Something> onCreateLoader(int id, Bundle args) { return new AsyncSomethingLoader(mContext); } @Override public void onLoadFinished(Loader<Something> loader, Something result) { if (mCallback == null) { return; } if (result == null) { mCallback.onFailure(); } else { mCallback.onSuccess(result); } } @Override public void onLoaderReset(Loader<Something> loader) {} public void onCreate(Context context, MyModelCallback callback) { mContext = context; mCallback = callback; } public void load(LoaderManager manager) { manager.initLoader(LOADER_ID, null, this); } public void onDestroy(LoaderManager manager) { mCallback = null; manager.destroyLoader(LOADER_ID); } public interface MyModelCallback { public void onSuccess(Something something); public void onFailure(); } } ``` コールバックはまた、実装する Context のライフサイクルを考慮した管理をしなければなりません。 これらの面倒事を解消する策として、EventBus を使います。 以下、[このライブラリ](https://github.com/greenrobot/EventBus/)を用いた実装を例にあげてみます。 ```java public class MyModel implements LoaderCallbacks<Something> { private static final int LOADER_ID = 0; private Context mContext; private EventBus mEventBus; @Override public Loader<Something> onCreateLoader(int id, Bundle args) { return new AsyncSomethingLoader(mContext); } @Override public void onLoadFinished(Loader<Something> loader, Something result) { if (result == null) { mEventBus.post(new SomethingLoadFailureEvent()) } else { mEventBus.onSuccess(new SomethingLoadSuccessEvent(result)); } } @Override public void onLoaderReset(Loader<Something> loader) {} public void onCreate(Context context) { mContext = context; mEventBus = EventBus.getDefault(); } public void load(LoaderManager manager) { manager.initLoader(LOADER_ID, null, this); } public void onDestroy(LoaderManager manager) { manager.destroyLoader(LOADER_ID); } } ``` コールバックのインスタンスを管理する部分がごっそりなくなりました。かわりに、イベントを受け取る実装を Activity ですることになります。 より詳しく EventBus の使い方を見るには、[こちら](http://qiita.com/KeithYokoma/items/793aaac6994c9242808f)を御覧ください。 ## Model のなかの棲み分け Model と一言で言っても、その役割はたくさんあります。 ネットワークや DB 、キャッシュなどからデータを非同期で取ってきて、それをデシリアライズ、加工し、View に反映させるまでの間にも、いくらかのビジネスロジックを挟んだりします。 大まかに分類すると、それぞれ、データにアクセスするレイヤ、データへのアクセスを非同期にするためのレイヤ、データを加工したりメモリにのせたりするレイヤ、に分けるとすると、以下のような名前空間に分離することができます。 - client: データにアクセスするレイヤ - loader: Android における Loader を用いた非同期処理のレイヤ - model: データを加工したりメモリにのせたりする、所謂ビジネスロジックを含むモデルのレイヤ さらに、データそのものを表現するためのオブジェクトとして、Entity を定義することによって、Model や Controller、View などのコンポーネントを Entity が行き来するようなアーキテクチャが構成できます。 モデルも細かく分離することで、テストが容易になります。 |
|
| 137位 |
|
|||
|
21:13:29 |
|
|
すこし記事が長いため、簡単なアウトラインを書いておきます。要点だけ掴みたい場合は、最終項の「まとめ」を読むのがいいかもしれません。
- コマンドライン引数の一般的な解析手法 - それぞれの特徴 〜 getopt と getopts の違い - getopts(メリット・デメリット) - getopt(メリット・デメリット) - 自前で解析しちゃう(唯一のデメリット) - まとめ ============================= コマンドライン引数を処理する一般的な手法として、 1. getopts 2. getopt 3. shift などで自力で解析 といった具合に、上から順に考えつくかと思います。`getopt(3)` は UNIX において、コマンドの引数を処理する一般的な C 言語のライブラリ関数です。それを用いて実装されたコマンドが `getopt(1)` です。Bourne シェル系だと内部関数になりますが、同系統の `getopts` という関数があります。 `getopt` だと外部コマンドであるため、色々と面倒なことがあるので、Bourne シェル系だと `getopts` を使った方が余計なことがなくて良いとされています。 ## それぞれの特徴 〜 *getopt と getopts の違い* ### getopts $ type getopts getopts is a shell builtin **メリット** とりあえず`getopts` は bash のビルドインコマンドであることです。ゆえに bash スクリプトとの親和性が高いとされます。`getopts` は `while` ループと `case` 文を用いることが定石です。`getopts` は第一引数に、使用したいオプション文字列を受け付けます。もし、そのオプションが引数を取る場合はコロンを後に付けると、`OPTARG` 変数にその値が保持されます。疑問符が返ってきたときは、無効なオプションが渡された時です。`break` で抜けるか、使い方を表示して `exit` が一般的です。そして、最後に処理した引数の数だけ、`shift` し終了です。 説明文の羅列だけでは分かりづらいので以下、テンプレートです。 ```bash:getopts.sh #!/bin/bash usage_exit() { echo "Usage: $0 [-a] [-d dir] item ..." 1>&2 exit 1 } while getopts ad:h OPT do case $OPT in a) FLAG_A=1 ;; d) VALUE_D=$OPTARG ;; h) usage_exit ;; \?) usage_exit ;; esac done shift $((OPTIND - 1)) ``` ここでの例では、`-a` オプションは引数を取らず、`-d` オプションは引数が必要としています。また、`-h` オプションまたはその他に関しては、ヘルプ表示を行う`usage_exit` 関数を実行し終了しています。 `while getopts ad:h OPT` にてオプション解析をしていきますが、ここで指名していないオプション(例えば -b など)を指定した場合やオプションに引数が必要なのに無い場合など、 ./test.sh: option requires an argument -- d ./test.sh: illegal option -- x といったようなエラー報告が行われます。ここで `while getopts :ad:h OPT` というように先頭にコロンを置くことで、こういったエラー表示は行われなくなりますので、自前でのエラー処理が行えます。 **デメリット** * コマンドの引数のあとでオプションが使用できない bash スクリプトでオプション解析をする際には、bash 組み込みコマンドの `getopts` を使っていれば大抵の場合は問題ありません。しかし、`getopts` だと、コマンドラインの後半でオプションを指定できないことに気がつきました。 具体的には、 Usage: command.bash [-a] [-d dir] item1 item2 ... のような構文の場合に、`-d dir` を `item` の後ろに指定しても、期待した処理がされません。 * ロングオプションが使用できない `--help` や `--version` などのようなロングオプションが使用できないことです。一文字オプションでは文字の競合などが起こり、どのような規則で命名されたのか分かりにくく、これが必要なときもあります。 ### getopt $ type getopt getopt is /usr/bin/getopt **メリット** 上で挙げた `getopts` のデメリットである、引数のあとにオプションを使用できない件と、ロングオプションの件を *条件付きで* 満たしています。 *一般に* 使用される `getopt` のテンプレートは以下です。 ```bash:getopt.sh set -- 'getopt ad: "$@"' if [ $? != 0 ]; then echo "Usage: $0 [-a] [-d dir]" 1>&2 exit 1 fi for OPT in "$@" do case $OPT in -a) A_FLAG=1 shift ;; -d) B_ARG=$2 shift 2 ;; --) shift break ;; esac done ``` `getopt` の機能を拡張したものが `getopts` であるため、一般に使用する場合その優位性は `getopts` にあります。しかし、既存のシェルスクリプトでは `getopt` によって書かれたものも多く存在するので、すくなくとも読めたほうが良いです。 **デメリット** `getopt` は 外部コマンドのため、バージョンや環境によって差異があります。それが、BSD 系と GNU 系の違いです。 | | BSD系 実装 | GNU 実装 | |:-----------------------|:-----------:|:-----------:| | 主なOS | Mac OS X | Ubuntu | | ロングオプション | __不可能__ | 可能 | | 引数のあとにオプション | __不可能__ | 可能 | つまり、`getopt` は使用する環境によって大きく処理が異なります。 なら、BSD でも GNU のものを使えばいいじゃないか!ということで GNU 版 `getopt` のソースを入手しても、Mac OS X ではコンパイルは出来ません。 $ gcc -o getopt getopt.c Undefined symbols for architecture x86_64: "_main", referenced from: start in crt1.10.6.o ld: symbol(s) not found for architecture x86_64 collect2: ld returned 1 exit status exit 1 Ubuntu 上ではコンパイルも実行も可能ですが、当然ながら Mac OS X 上では作動してくれません。 $ ./getopt -bash: ./getopt: cannot execute binary file exit 126 $ file getopt getopt: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, not stripped しかし、完全に Ubuntu でしか使用しないなら `getopt` の優位性は逆転します。`getopts` のデメリットを解消できるので、以下に使用テンプレを載せておきます。 ```bash:getopt4ubuntu.sh OPT=`getopt -o ab:c --long long-a, long-b:,long-c -- "$@"` if [ $? != 0 ] ; then exit 1 fi eval set -- "$OPT" while true do case "$1" in -a | --long-a) # -a のときの処理 shift ;; -b | --long-b) # -b のときの処理 shift 2 ;; -c | --long-c) # -c のときの処理 shift ;; --) shift break ;; *) echo "Internal error!" 1>&2 exit 1 ;; esac done ``` GNU 版の `optget` は、ロングオプションも使用でき、 $ getopt ab:c -b aaa -a bbb -c -b aaa -a -c -- bbb というように、`-c` オプションの前にあるコマンドの引数が、順番が入れ替えられて正常に出力される。 結果として、 * `Ubuntu なら getopts << getopt` * `Mac OS X なら getopts >> getopt` であると言えます。 > **重大なバグに関して** > getopt には重大な落とし穴があります。それは、スペースや特殊文字が引数に含まれていた場合、正しく処理できないということです。あるシェルスクリプトで getopt を使った場合、空白や特殊文字は絶対に使うな、という注意書きが必要になります。これを何とかしのごうとするのは容易ではありません。 ## いっそ自前で解析しちゃう ## これらの組み込み関数や外部コマンドを使用しない簡単な例として、以下のように書くことが一般的であるといえるでしょう。 ```bash:shift.sh for OPT in "$@" do case $OPT in '-a' ) FLAG_A=1 ;; '-b' ) FLAG_B=1 VALUE_B=$2 shift 2 ;; esac shift done if [ "$FLAG_A" ]; then echo "Option -a specified." fi if [ "$FLAG_B" ]; then echo "Option -b $VALUE_B specified." fi ``` このままでは、ロングオプションとコマンドの引数のあとでオプション指定の件が満たされていません。BSD 系の `getopt` 状態です。これらのデメリットを解消するため、実装していきましょう。 getopt(s) をめぐる差異は非常に厄介なので、`shift` しながらゴリゴリ処理してしまうパターン改。 以下、ソース。 ```bash:shift2.sh PROGNAME=$(basename $0) VERSION="1.0" usage() { echo "Usage: $PROGNAME [OPTIONS] FILE" echo " This script is ~." echo echo "Options:" echo " -h, --help" echo " --version" echo " -a, --long-a ARG" echo " -b, --long-b [ARG]" echo " -c, --long-c" echo exit 1 } for OPT in "$@" do case "$OPT" in '-h'|'--help' ) usage exit 1 ;; '--version' ) echo $VERSION exit 1 ;; '-a'|'--long-a' ) if [[ -z "$2" ]] || [[ "$2" =~ ^-+ ]]; then echo "$PROGNAME: option requires an argument -- $1" 1>&2 exit 1 fi ARG_A="$2" shift 2 ;; '-b'|'--long-b' ) if [[ -z "$2" ]] || [[ "$2" =~ ^-+ ]]; then shift else shift 2 fi ;; '-c'|'--long-c' ) shift 1 ;; '--'|'-' ) shift 1 param+=( "$@" ) break ;; -*) echo "$PROGNAME: illegal option -- '$(echo $1 | sed 's/^-*//')'" 1>&2 exit 1 ;; *) if [[ ! -z "$1" ]] && [[ ! "$1" =~ ^-+ ]]; then #param=( ${param[@]} "$1" ) param+=( "$1" ) shift 1 fi ;; esac done if [ -z $param ]; then echo "$PROGNAME: too few arguments" 1>&2 echo "Try '$PROGNAME --help' for more information." 1>&2 exit 1 fi ``` コマンドの引数($@ にあたるもの)は `param` 変数に保持されます。ゆえにコマンドが引数の必要を絶対としない場合は、下からの5行はコメントアウトしてください。 **唯一のデメリット** >GNU 版 `getopt` と肩を並べた以上のサンプルコードですが、組み込み関数 `getopts`・BSD 版 `getopt`・GNU 版 `getopt` では問題なかった、`-ab` というようなオプションの複数同時指定ができません。`-a -b` というようにバラさなければいけないのです。もちろん実装できなくもないですが、オプション解析がスクリプト本体の行数を超えかねないので、簡潔さを優先させ断念しました。 **(以下 2014/10/10 追記)** と以前は書いていたのですが単純ですが以下の方法で `-ab`、`-ba` というようなオプションの併記も可能です。 ```bash:manually_complete_version.sh declare -i argc=0 declare -a argv=() while (( $# > 0 )) do case "$1" in -*) if [[ "$1" =~ 'n' ]]; then nflag='-n' fi if [[ "$1" =~ 'l' ]]; then lflag='-l' fi if [[ "$1" =~ 'p' ]]; then pflag='-p' fi shift ;; *) ((++argc)) argv=("${argv[@]}" "$1") shift ;; esac done ``` bash の組み込みである `[[` を使用して正規表現的に「含まれているか」を調べてオプションを設定しています。 実引数(オプションを除く引数)の数 `argc` や実引数 `argv` も取得できます。 よって直前の `shift2.sh` のスクリプトと組み合わせると、 - ショートオプション/ロングオプション対応 `$ command --long arg1 arg2` - オプションの後付け可能 `$ command -s arg1 arg2 --long` - オプションの複数併記可能 `$ command -ab -dc arg1` - 環境に依存しない これらの問題を克服して bash でオプション解析が可能になります。 ## まとめ - `getopts` は bash の組み込みコマンドである。ロングオプションは使用不可能、コマンドライン行後半でのオプション指定は不可能 - BSD 版 `getopt` は `getopts` に劣るといえる - しかし、GNU 版 `getopt` はロングオプションが使用でき、コマンドライン行後半でのオプション指定は可能であるが、BSD 環境での使用はできない - `getopt` は外部コマンドのため、以上のような環境での違いもあり、重大なバグも存在するためその使用は推奨されない - 自前での解析は、数々の問題点をすべて解消できる。 **しかし唯一、オプションの複数同時指定ができないという問題が発生する(2014/10/10 追記)** - やっぱり自前解析が一番いいとおもう(2014/10/10 現在の結論) |
|
| 138位 |
|
|||
|
01:05:36 |
|
|
JavaScript のオブジェクト作成においてクラス定義で継承を実装する方法はいくつかあります。
正しい継承はどうあるべきか、基本から検証しながら考えてみたいと思います。  ※正しくクラス定義がエコ楽にできる様に追加記事書きました。 [[JavaScript] getter/setterも使えるエコ楽なクラス定義 - もちろん継承も - private変数も] (http://qiita.com/LightSpeedC/items/3946088b58925234cc48) # 一番簡単なオブジェクトの作成方法 典型的な JavaScript のオブジェクトを簡単に作成してみて、それらを確認してみましょう。 ```js var obj1 = {x: 12, y: "ab"}; var obj2 = new Object; // または new Object() obj2.x = 34; obj2.y = "cd"; // obj < Object var obj3 = [12, "ab"]; var obj4 = new Array(34, "cd"); // obj < Array < Object ```  ## `instanceof` による確認 `instanceof` を使用すると `Object` かどうかがすぐにわかります。 ```js console.log(obj1 instanceof Object); // -> true console.log(obj2 instanceof Object); // -> true console.log(obj3 instanceof Object); // -> true console.log(obj4 instanceof Object); // -> true ``` `true` なので、全て `Object` の一種です。間違いありません。 ## `constructor` による確認 `constructor` を使用すると、オブジェクトが誰によって作成されたのか、つまりどのクラスのオブジェクトなのか確認することができます。 ```js console.log(obj1.constructor === Object); // -> true console.log(obj2.constructor === Object); // -> true console.log(obj3.constructor === Array); // -> true console.log(obj4.constructor === Array); // -> true ``` 後半の例は配列 `Array` です。思った通りですね。 ## もう一度 `instanceof` による確認 それでは `Array` なのかどうか `instanceof` で確認してみましょう。 ```js console.log(obj1 instanceof Array); // -> false console.log(obj2 instanceof Array); // -> false console.log(obj3 instanceof Array); // -> true console.log(obj4 instanceof Array); // -> true ``` 後半は配列なので `true` ですが、前半は配列ではありませんので `false` です。予想通りで良かったですね。 ## 更に `__proto__` と `prototype` による確認 クラスの `prototype` はどうなっているのか。 それは作成した各オブジェクトの `__proto__` 属性からポイントされています。 ```js console.log(obj1.__proto__ === Object.prototype); // -> true console.log(obj2.__proto__ === Object.prototype); // -> true console.log(obj3.__proto__ === Array.prototype); // -> true console.log(obj4.__proto__ === Array.prototype); // -> true // getProto(obj) var getProto = Object.getPrototypeOf ? Object.getPrototypeOf : function getProto(obj) { return obj.__proto__; }; console.log(getProto(obj1) === Object.prototype); // -> true console.log(getProto(obj2) === Object.prototype); // -> true console.log(getProto(obj3) === Array.prototype); // -> true console.log(getProto(obj4) === Array.prototype); // -> true ``` それぞれのクラスのプロトタイプ・オブジェクトをポイントしているので問題ないですね。 ## `__proto__` の `__proto__` による確認 ついでに `__proto__` の `__proto__` も確認しておきましょう。 ```js console.log(obj1.__proto__.__proto__ === null); // -> true console.log(obj2.__proto__.__proto__ === null); // -> true console.log(obj3.__proto__.__proto__ === Object.prototype); // -> true console.log(obj4.__proto__.__proto__ === Object.prototype); // -> true console.log(obj3.__proto__.__proto__.__proto__ === null); // -> true console.log(obj4.__proto__.__proto__.__proto__ === null); // -> true console.log(getProto(getProto(obj1)) === null); // -> true console.log(getProto(getProto(obj2)) === null); // -> true console.log(getProto(getProto(obj3)) === Object.prototype); // -> true console.log(getProto(getProto(obj4)) === Object.prototype); // -> true console.log(getProto(getProto(getProto(obj3))) === null); // -> true console.log(getProto(getProto(getProto(obj4))) === null); // -> true ``` `Object` の親クラスは `null` に行き着いておしまい。 `Array` の親クラスは `Object` で、更に親はいない、という事ですね。 この先祖をたぐれる様になっている仕組みがプロトタイプ・チェーンです。 ちなみに `__proto__` は非標準です。 標準では `Object.getPrototypeOf` を使う様に、との事。  ## 今度は `constructor.name` による確認 `constructor` はオブジェクトを作成する時に使用されたコンストラクタ関数を指しており、関数に名前がある場合 `name` 属性を見れば、その関数名がわかります。 ```js console.log(obj1.constructor.name); // -> Object console.log(obj2.constructor.name); // -> Object console.log(obj3.constructor.name); // -> Array console.log(obj4.constructor.name); // -> Array ``` 正しく表示できましたか。もし思った通りに表示されない場合、以下の互換性対応のおまじないコードを先に実行して、もう一度上記のコードを実行してくれますか。うまくいく事を祈ります。 ```js // 互換性対応のおまじない。主に IE。 var fnameRegExp = /^\s*function\s*\**\s*([^\(\s]*)[\S\s]+$/im; // fname: get function name function fname() { return ('' + this).replace(fnameRegExp, '$1'); } // Function.prototype.name if (!Function.prototype.hasOwnProperty('name')) { if (Object.defineProperty) Object.defineProperty(Function.prototype, 'name', {get: fname}); else if (Object.prototype.__defineGetter__) Function.prototype.__defineGetter__('name', fname); } ``` 関数の `name` 属性は非標準なので本来は使用してはいけないのですが、デバッグする時に非常に役に立つので上記のコードを実行してから使う事にします。あしからず。 # データだけでなくメソッドも定義してみる 幅(w)と高さ(h)で四角形の面積を計算してみる。 ```js var x1 = {w: 10, h: 20, calc: function () { return this.w * this.h; }}; var x2 = {w: 20, h: 30, calc: function () { return this.w * this.h; }}; console.log(x1.calc()); // -> 200 console.log(x2.calc()); // -> 600 console.log(x1.calc === x2.calc); // -> false console.log(x1.calc.toString() === x2.calc.toString()); // -> true ``` メソッドとしての関数を記憶するためのフィールドが、オブジェクトのインスタンスそれぞれに必要となりますね。同じ様に関数定義しても違う関数が登録されている様です。またメソッドの数だけ記憶領域が必要です。あまり効率が良いとは言えませんね。 ちょっとだけマシな定義にするには以下の様にすればいいのでしょうか。 ```js function calc() { return this.w * this.h; } var x3 = {w: 10, h: 20, calc: calc}; var x4 = {w: 20, h: 30, calc: calc}; console.log(x3.calc()); // -> 200 console.log(x4.calc()); // -> 600 console.log(x3.calc === x4.calc); // -> true ``` 面積をプロパティの様に扱う事も可能です。 その様な場合 getter を使います。 ```js var x5 = {w: 10, h: 20, get area() { return this.w * this.h; }}; var x6 = {w: 20, h: 30, get area() { return this.w * this.h; }}; console.log(x5.area); // -> 200 console.log(x6.area); // -> 600 ``` ここには例はあげませんでしたが setter も勉強しておきましょう。 こちらに示したサンプルは非常に簡単ですが、本格的にオブジェクトをたくさん作成するには、記憶領域の効率はあまり良くないと思います。 直接オブジェクトを作成するパターンはこれくらいにして本題に進みましょう。 # プロトタイプベースのオブジェクト指向 みなさんはクラスベースのオブジェクト指向言語として Java, C#, C++ なども勉強されているのではないかと思いますが、JavaScript はプロトタイプベースのオブジェクト指向言語なので、ちょっと変わった振る舞いをします。様々なクラス定義の例をみながら検証していきましょう。 ## シンプルなクラス定義 JavaScript ではコンストラクタ関数を定義する事で、クラス定義の様に、記述する事が可能です。 まずは `Animal` クラスを定義してみましょう。 ```js // Animal クラス定義 function Animal(name) { this.name = name; } // Animal クラスのメソッド定義 Animal.prototype.introduce = function introduce() { console.log('私は ' + this.constructor.name + ' の ' + this.name + ' です。'); }; ``` ## インスタンス作成と利用 では `Animal` クラスのインスタンスオブジェクトを作成し、利用してみましょう。 ```js // Animal クラスのインスタンスオブジェクトの作成と利用 var a1 = new Animal('Annie'); a1.introduce(); // -> 私は Animal の Annie です。 ``` `this.constructor.name` が使用できるように、上記の様に関数定義としてクラス名を関数に付ける方が良いと思います。 ## インスタンスオブジェクトの検証 では、このクラスのインスタンスがちゃんとできているか検証してみましょう。 まずはクラス定義とオブジェクトが正しく作成できているか検証するための関数を作りましょう。 ```js var CSI = '\u001b['; // ANSI Control Sequence Introducer var NORMAL = typeof window !== 'undefined' ? '' : CSI + 'm'; var GREEN = typeof window !== 'undefined' ? '' : CSI + '32m'; var RED = typeof window !== 'undefined' ? '' : CSI + '31m'; var YELLOW = typeof window !== 'undefined' ? '' : CSI + '33m'; // getProto(obj) var getProto = Object.getPrototypeOf ? Object.getPrototypeOf : function getProto(obj) { return obj.__proto__; }; // assertTrue: true であればOK、false の時はエラーメッセージを表示する function assertTrue(bool, msg) { if (!bool) { console.error(RED + 'Error: ' + msg + NORMAL); } } // verifyClassObject: オブジェクトの検証 function verifyClassObject(obj, expected, keysExpected) { var name = expected[0]; var TheClass = expected[1]; var SuperClass = expected[2]; var keys = []; for (var i in obj) { keys.push(i); } var keysActual = keys.join(','); if (keysActual === keysExpected) { console.info(GREEN + 'Success: keys = ' + keysActual + NORMAL); } else { console.error(RED + 'Error: keys = ' + keysActual + ', ' + NORMAL + YELLOW + 'Expected: keys ' + keysExpected + NORMAL); } // obj は Class のインスタンスだ (new Class で作成したからね) assertTrue(obj instanceof TheClass, name + ' は ' + TheClass.name + ' のインスタンスではない。'); // obj は SuperClass のインスタンスでもある if (SuperClass) { assertTrue(obj instanceof SuperClass, name + ' は ' + SuperClass.name + ' のインスタンスではない。'); } // obj は Object のインスタンスでもある assertTrue(obj instanceof Object, name + ' は ' + Object.name + ' のインスタンスではない。'); // obj のコンストラクタは Class だ assertTrue(obj.constructor === TheClass, name + ' のコンストラクタは ' + obj.constructor.name + ' で、 ' + TheClass.name + ' ではない。'); // Class のプロトタイプオブジェクトのコンストラクタは Class だ assertTrue(TheClass.prototype.constructor === TheClass, TheClass.name + ' のプロトタイプは ' + TheClass.prototype.constructor.name + ' で、 ' + TheClass.name + ' ではない。'); // obj の __proto__ を見てみると... assertTrue(getProto(obj).constructor === TheClass, name + ' の __proto__ は ' + getProto(obj).constructor.name + ' で、 ' + TheClass.name + ' ではない。'); // Class は SuperClass を継承しているんだね if (SuperClass) { assertTrue(getProto(getProto(obj)) === SuperClass.prototype && getProto(getProto(obj)).constructor === SuperClass && getProto(TheClass.prototype).constructor === SuperClass, name + ' の __proto__ の __proto__ は ' + getProto(getProto(obj)).constructor.name + ' で、 ' + SuperClass.name + ' ではない。'); } // obj の先祖を辿ってみる... var expectedString = expected.map(function (fn) { return typeof fn === 'function' ? fn.name : fn; }).join(' < '); var ancestors = [name]; for (var obj = getProto(obj); obj; obj = getProto(obj)) { ancestors.push(obj.constructor.name); } var actualString = ancestors.join(' < '); if (actualString === expectedString) { console.info(GREEN + 'Success: ' + actualString + NORMAL); } else { console.error(RED + 'Error: ' + actualString + ', ' + NORMAL + YELLOW + 'Expected: ' + expectedString + NORMAL); } // -> name < Class < SuperClass < Object } if (!('info' in console)) { console.info = console.log; } if (!('error' in console)) { console.error = console.log; } ``` ## a1 < Animal < Object を検証する では作成したオブジェクトを検証してみましょう。 ```js // a1 < Animal < Object かどうか検証してみる verifyClassObject(a1, ['a1', Animal, Object], 'name,introduce'); // -> Success: keys = name,introduce // -> Success: a1 < Animal < Object ``` もちろん、シンプルな例ですので、特に問題はありませんね。  ## 関数定義には名前を付ける `a1.constructor.name` で検証ができる様に関数定義には名前を付ける方が良いと考えています。エラー発生時のトレースバック情報にも関数名が含まれますので、そういう場合にも詳しい情報が表示されるので有効です。それはメソッドの関数名も同様です。 もちろん、以下の様な形式でクラス定義してもいいですが、名前が無いクラスを作っているようなものなので、私は推奨はしません。 ```js var Animal = function (name) { this.name = name; }; ``` せめて以下の様に名前を付けましょう。 ```js var Animal = function Animal(name) { this.name = name; }; ``` ## さきほどの簡単なオブジェクトもついでに検証 さきほど作成した簡単なオブジェクト `obj1`~`obj2`, `x1`~`x6` も検証してみましょう。 ```js verifyClassObject(obj1, ['obj1', Object], 'x,y'); // -> Success: keys = x,y // -> Success: obj1 < Object verifyClassObject(obj2, ['obj2', Object], 'x,y'); // -> Success: keys = x,y // -> Success: obj2 < Object verifyClassObject(obj3, ['obj3', Array, Object], '0,1'); // -> Success: keys = 0,1 // -> Success: obj3 < Array < Object verifyClassObject(obj4, ['obj4', Array, Object], '0,1'); // -> Success: keys = 0,1 // -> Success: obj4 < Array < Object verifyClassObject(x1, ['x1', Object], 'w,h,calc'); // -> Success: keys = w,h,calc // -> Success: x1 < Object verifyClassObject(x2, ['x2', Object], 'w,h,calc'); // -> Success: keys = w,h,calc // -> Success: x2 < Object verifyClassObject(x3, ['x3', Object], 'w,h,calc'); // -> Success: keys = w,h,calc // -> Success: x3 < Object verifyClassObject(x4, ['x4', Object], 'w,h,calc'); // -> Success: keys = w,h,calc // -> Success: x4 < Object verifyClassObject(x5, ['x5', Object], 'w,h,area'); // -> Success: keys = w,h,area // -> Success: x5 < Object verifyClassObject(x6, ['x6', Object], 'w,h,area'); // -> Success: keys = w,h,area // -> Success: x6 < Object ``` 当然ですが、全て問題ありません。そうなる様に引数をセットしました。 # やっちゃいけない継承、その1 それでは `Animal` クラスを継承して `Bear` クラスを定義してみましょう。 ```js // Bear クラス定義 function Bear(name) { Animal.call(this, name); } // やっちゃいけない継承、その1 Bear.prototype = Animal.prototype; // Bear クラスのインスタンスオブジェクトの作成と利用 var b1 = new Bear('Pooh'); b1.introduce(); // -> 私は Animal の Pooh です。 // b1 < Bear < Animal < Object かどうか検証してみる verifyClassObject(b1, ['b1', Bear, Animal, Object], 'name,introduce'); // -> Success: keys = name,introduce // -> Error: b1 のコンストラクタは Animal で、 Bear ではない。 // -> Error: Bear のプロトタイプは Animal で、 Bear ではない。 // -> Error: b1 の __proto__ は Animal で、 Bear ではない。 // -> Error: b1 の __proto__ の __proto__ は Object で、 Animal ではない。 // -> Error: b1 < Animal < Object, Expected: b1 < Bear < Animal < Object ``` なんとなく動いているように見えるんですけど、検証結果はどうでしょうか。 いくつか問題がありそうです。 `b1 < Bear < Animal < Object` っていうのがいいと思うけど、なんか違うね。 では、以下の様な `Bear` クラスのメソッド定義をするとどうなるでしょうか。 ```js Bear.prototype.bearMethod = function bearMethod() {}; ``` そうです。`Animal` クラスにも同じ定義ができてしまいますね。 これでは、おかしなことになりそうですね。 もし上記を実行してしまったなら以下で削除しておきましょう。 ```js delete Bear.prototype.bearMethod; ```  # やっちゃいけない継承、その2 今度は `Animal` クラスを継承して `Cat` クラスを定義してみましょう。 ```js // Cat クラス定義 function Cat(name) { Animal.call(this, name); } // やっちゃいけない継承、その2 Cat.prototype = new Animal; // Cat クラスのインスタンスオブジェクトの作成と利用 var c1 = new Cat('Kitty'); c1.introduce(); // -> 私は Animal の Kitty です。 // c1 < Cat < Animal < Object かどうか検証してみる verifyClassObject(c1, ['c1', Cat, Animal, Object], 'name,introduce'); // -> Success: keys = name,introduce // -> Error: c1 のコンストラクタは Animal で、 Cat ではない。 // -> Error: Cat のプロトタイプは Animal で、 Cat ではない。 // -> Error: c1 の __proto__ は Animal で、 Cat ではない。 // -> Error: c1 < Animal < Animal < Object, Expected: c1 < Cat < Animal < Object ``` なんとなく動いているように見えるけど、検証結果はどうでしょうか。 やはり、いくつか問題がありそうです。 `c1 < Cat < Animal < Object` っていうのがいいと思うけど、やっぱりなんか違うね。 # 結果は正しそうだけど互換性が無い継承 今度は `constructor` と `__proto__` を使って `Animal` クラスを継承して `Dog` クラスを定義してみましょう。 ```js // Dog クラス定義 function Dog(name) { this.name = name; } // 無理やり constructor と __proto__ を使って prototype オブジェクトを上書きする Dog.prototype = { constructor: Dog, __proto__: Animal.prototype }; // Dog クラスのインスタンスオブジェクトの作成と利用 var d1 = new Dog('Hachi'); d1.introduce(); // -> 私は Dog の Hachi です。 // d1 < Dog < Animal < Object かどうか検証してみる verifyClassObject(d1, ['d1', Dog, Animal, Object], 'name,introduce'); // -> Error: keys = name,constructor,introduce, Expected: keys name,introduce // -> Success: d1 < Dog < Animal < Object ``` 検証結果は正しそうでしたが `for in` で余計な `constructor` が出てきました。 また `__proto__` を使うのは非標準です。 ## ところで `ClassName.prototype = {}` って、やっていいの? なんか、これが問題になっているような気がしますね。 こういう `ClassName.prototype = {}` という記述を見たら間違いと思った方がいいでしょうね。 # 結果は正しいけど互換性が無い継承 今度は `__proto__` だけを使用して `Animal` クラスを継承して `Elephant` クラスを定義してみましょう。 ```js // Elephant クラス定義 function Elephant(name) { Animal.call(this, name); } // 結果は正しいけど互換性が無い継承 Elephant.prototype.__proto__ = Animal.prototype; // Elephant クラスのインスタンスオブジェクトの作成と利用 var e1 = new Elephant('Dumbo'); e1.introduce(); // -> 私は Elephant の Dumbo です。 // e1 < Elephant < Animal < Object かどうか検証してみる verifyClassObject(e1, ['e1', Elephant, Animal, Object], 'name,introduce'); // -> Success: keys = name,introduce // -> Success: e1 < Elephant < Animal < Object ``` 検証結果は正しそうですね。 ですが `__proto__` は標準ではありません。 互換性のないキーワードを使っている事が問題です。 # 正しそうな継承 今度は `Object.create` と `constructor` を使って `Animal` クラスを継承して `Fox` クラスを定義してみましょう。 ```js // Fox クラス定義 function Fox(name) { Animal.call(this, name); } Fox.prototype = Object.create(Animal.prototype); Fox.prototype.constructor = Fox; // Fox クラスのインスタンスオブジェクトの作成と利用 var f1 = new Fox('Gon'); f1.introduce(); // -> 私は Fox の Gon です。 // f1 < Fox < Animal < Object かどうか検証してみる verifyClassObject(f1, ['f1', Fox, Animal, Object], 'name,introduce'); // -> Error: keys = name,constructor,introduce, Expected: keys name,introduce // -> Success: f1 < Fox < Animal < Object ``` 検証結果は正しそうでしたが `for in` で余計な `constructor` が出てきました。 やはり `enumerable: false` でないとまずいですね。 # 正しい継承 今度は `Animal` クラスを継承して `Gorilla` クラスを定義してみましょう。 継承させるための関数 `inherits` として Node.js の `util.inherits` をそのまま使用してみましょう。 ```js // Gorilla クラス定義 function Gorilla(name) { Animal.call(this, name); } // console.log(require('util').inherits.toString()); より function inherits(ctor, superCtor) { if (ctor === undefined || ctor === null) throw new TypeError('The constructor to `inherits` must not be ' + 'null or undefined.'); if (superCtor === undefined || superCtor === null) throw new TypeError('The super constructor to `inherits` must not ' + 'be null or undefined.'); if (superCtor.prototype === undefined) throw new TypeError('The super constructor to `inherits` must ' + 'have a prototype.'); ctor.super_ = superCtor; Object.setPrototypeOf(ctor.prototype, superCtor.prototype); // ctor.prototype = Object.create(superCtor.prototype, { // constructor: { // value: ctor, // enumerable: false, // writable: true, // configurable: true // } // }); } // 正しい継承 inherits(Gorilla, Animal); // Gorilla クラスのインスタンスオブジェクトの作成と利用 var g1 = new Gorilla('Kong'); g1.introduce(); // -> 私は Gorilla の Kong です。 // g1 < Gorilla < Animal < Object かどうか検証してみる verifyClassObject(g1, ['g1', Gorilla, Animal, Object], 'name,introduce'); // -> Success: keys = name,introduce // -> Success: g1 < Gorilla < Animal < Object ``` 検証結果も、もちろん定義も問題ありませんね。  # おまけ1: `new` を付け忘れた時の対応 以下の様に `new` を付け忘れた場合どうなるでしょうか。 ```js try { var a2 = Animal('Annie'); } catch (err) { console.log(RED + err + NORMAL); } ``` `this` オブジェクトがグローバルオブジェクトになってしまいますので、以下の様にガードする様にしましょう。 ```js // Animal2 クラス定義 function Animal2(name) { if (!(this instanceof Animal2)) { return new Animal2(name); } this.name = name; } var a2 = Animal2('Annie'); ``` # おまけ2: Closure を使って定義する 以下のコードを見てください。 ```js var a3 = new Animal('Annie'); a3.introduce(); // -> 私は Animal の Annie です。 console.log(a3.name); // -> Annie a3.name = 'Aho'; a3.introduce(); // -> 私は Animal の Aho です。 console.log(a3.name); // -> Aho ``` `name` 属性が外から書き換えられてしまいますね。 Java, C#, C++ 等の言語でいう所の private なフィールドは無いのでしょうか。 Closure を使うとそれに似た事ができます。 ```js // Animal3 クラス定義 function Animal3(name) { this.introduce = function introduce() { console.log('私は ' + this.constructor.name + ' の ' + name + ' です。'); }; } var a3 = new Animal3('Annie'); a3.introduce(); // -> 私は Animal3 の Annie です。 console.log(a3.name); // -> undefined a3.name = 'Aho'; a3.introduce(); // -> 私は Animal3 の Annie です。 console.log(a3.name); // -> Aho ``` それぞれのオブジェクトにメソッド数だけ関数定義が必要になりますが、外部から内部の変数へのアクセスは絶対に不可能になります。公開されたメソッドでないと内部の変数にアクセスすることができないので非常に安全です。 それぞれのオブジェクトに属性を定義していくのか、コンストラクタ関数内の変数を定義し、公開メソッドを定義していくのか、全く違った設計になると思います。 記憶領域の効率は良くないですし、若干遅いのであまりお勧めはしません。 # おまけ3: クラス共通の属性定義 以下の様に `prototype` に共通の属性を定義すると、デフォルト値の様なものが定義できます。それを上書きすることもできます。 ```js // Animal4 クラス定義 function Animal4(name) { this.name = name; this.animalProp = 123; } Animal4.prototype.animalCommonProp = 'abc'; var a4 = new Animal4('Annie'); console.log(a4.animalProp + ' ' + a4.animalCommonProp); // -> 123 abc a4.animalProp = 456; a4.animalCommonProp = 'xyz'; console.log(a4.animalProp + ' ' + a4.animalCommonProp); // -> 456 xyz delete a4.animalProp; delete a4.animalCommonProp; console.log(a4.animalProp + ' ' + a4.animalCommonProp); // -> undefined abc ``` 最後の行、注意してくださいね。 # 参考文献 ## Node.js のリリースノートに正しい継承方法が記述されていました [v0.8 から v0.10 の主なAPI変更 - Node.js] (https://github.com/nodejsjp/nodejs.org_ja/wiki/API-changes-between-v0.8-and-v0.10) `EventEmitter` を継承させる時の注意点として記述があります。 ## 型判定 [JavaScriptの「型」の判定について - Qiita] (http://qiita.com/south37/items/c8d20a069fcbfe4fce85) `instanceof` を正しく使える様に定義していくべきでしょうね。 型判定には `typeof` が高速なので、まずはそれを使うべきかなぁと思います。 ## 重要な事が記述されています [Javascriptでオブジェクト指向するときに覚えておくべきこと - Qiita] (http://qiita.com/awakia/items/8ff451ca5f8ae0122be7) ## 以下のような記事を読んで奮起しました。 [【メモ】JavaScriptで継承(っぽいの)- Quiita] (http://qiita.com/ryokio0129/items/ddf354639889bf4a03e6) っぽいんですけど、やはり... [javascriptでの継承の基本パターン4つ - Quiita] (http://qiita.com/norami_dream/items/ea3827f05699afcb1cc5) 検証してみていない... |
|
| 139位 |
|
|||
|
02:11:02 |
|
|
[sidekiq](https://github.com/mperham/sidekiq)は[resque](https://github.com/resque/resque)やdelayed_jobのような非同期実行を実現するgemです。
使い方はgithubのwikiか[RAILSCASTS](http://railscasts.com/episodes/366-sidekiq?language=ja&view=asciicast)あたりを見れば分かりますが、Railsで使う場合について簡単に要約してみます。 ## 準備 バックエンドにredisが必要です。 とりあえず試すのであれば、Macならbrew install redisでインストールされます。 本番ではwebサーバと別にredisサーバを用意するべきでしょう。 ## インストール 通常通りGemfileに書いてbundle installするだけです。 ```ruby:Gemfile gem 'sidekiq' ``` ## 設定 initializersに起動時の設定を書きます。 ```config/initializers/sidekiq.rb Sidekiq.configure_server do |config| config.redis = { url: 'redis://localhost:6379', namespace: 'sidekiq' } end ``` ## Worker 非同期に実行するworkerをapp/workers以下に配置します。 workerにperformというメソッドを作成し、その中に実行されるべきロジックを書きます。 実際のロジックはモデルに記述した方がテストしやすいと思います。 引数にモデルのインスタンスを取ることもできますが、いちいちシリアライズされるためパフォーマンスが低下します。なるべくidを渡してWorker内でロードするようにした方が賢明でしょう。 sidekiq_optionsのqueueにはnamespaceを指定します。 未指定だとdefaultというnamespaceになります。 sidekiqを起動する際に必要になるので覚えておいて下さい。 ```app/workers/event_worker.rb class EventWorker include Sidekiq::Worker sidekiq_options queue: :event def perform(id) @event = Event.find(id) @event.calculate_rank! end end ``` ## キューに入れる perform_asyncを実行するとworkerがキューに入り、非同期でperformメソッドが実行されます。 通常、コントローラから呼び出されると思います。 ```app/controllers/events_controller.rb class EventsController def ranking EventWorker.perform_async @event.id end end ``` perform_asyncの代わりにperform_inを使うと指定時間だけ待ってから実行されます。 ```app/controllers/events_controller.rb class EventsController def ranking EventWorker.perform_in 1.hour, @event.id end end ``` ## sidekiq起動 キューに入れただけではジョブは実行されません。 とりあえずCUIで実行するには以下のようにします。 ``` bundle exec sidekiq -q default event ``` namespaceが複数ある場合は全て指定します。 指定しないとdefaultのキューのみが実行されます。 しばらく待ってキューが実行され、コンソールにログが流れれば成功です。 ## 起動設定 毎回上記のオプションを入力するのは面倒なので、設定ファイルを作成します。 ```config/sidekiq.yml :verbose: false :pidfile: ./tmp/pids/sidekiq.pid :logfile: ./log/sidekiq.log :concurrency: 25 :queues: - default - event ``` 設定ファイルを読み込んで起動する場合、以下のようにします。 ``` bundle exec sidekiq -C config/sidekiq.yml ``` ## デプロイ sidekiqは一度起動すれば(落ちるまで)動き続けますが、注意するのはデプロイ時です。sidekiqがロードしているコードは更新されないので、再起動を忘れると思わぬバグが発生する可能性があります。 capistranoを使っている場合、デプロイと同時に起動・再起動ができます。 ```config/deploy.rb require 'sidekiq/capistrano' set :sidekiq_role, :web ``` roleはsidekiqを走らせるroleです。複数台でsidekiqを実行すると自動的にバランシングされるので、特別な理由がなければrailsを走らせているroleを指定するのが良いでしょう。 設定ファイルはデフォルトで上記のconfig/sidekiq.ymlが使われます。 これでcap deployを実行すれば自動的にsidekiqが起動・再起動します。 ## リトライ・エラー処理 sidekiqはperformメソッドが例外を返すと自動的にリトライします。 処理に失敗した場合にリトライさせたいなら、raiseすればいいわけです。 retryに数字を指定すると、その回数だけリトライして諦めます。 ```app/workers/event_worker.rb sidekiq_options queue: :event, retry: 5 ``` デフォルトではリトライはリトライ回数の4乗+15秒間隔で行われます。 即ち15,16,31,96,271…秒の間隔を置きます。 変更する場合はsidekiq_retry_inを定義します。 詳しくは[wiki](https://github.com/mperham/sidekiq/wiki/Error-Handling)を参照して下さい。 リトライさせたくない場合、retryに数字ではなくfalseを指定します。 ```app/workers/event_worker.rb sidekiq_options queue: :event, retry: false ``` ## ダッシュボード sidekiqには[こんな感じ](https://raw.github.com/mperham/sidekiq/master/examples/web-ui.png)のかっこいいダッシュボードがビルトインされています。 有効にするにはまずGemfileにsinatraを追加します。 ```ruby:Gemfile gem 'sinatra', require: false ``` 次にroutes.rbにルーティングを追加します。 ```config/routes.rb require 'sidekiq/web' MyApp::Application.routes.draw do mount Sidekiq::Web, at: "/sidekiq" end ``` これで/sidekiqにアクセスすればダッシュボードが表示されます。 アクセス制限をかけたい時は以下のようにします。 ```config/routes.rb require 'sidekiq/web' MyApp::Application.routes.draw do authenticate :user, lambda { |u| u.admin? } do mount Sidekiq::Web => '/sidekiq' end end ``` ## テスト rspec-sidekiqを使うとrspecでテストが書けます。 ```ruby:Gemfile group :test do gem 'rspec-sidekiq' end ``` ```spec/workers/event_worker_spec.rb require 'spec_helper' describe EventWorker do let(:test_event) { create(:event) } before do subject.perform test_event.id end it "performでjobがqueueに入ること" do should be_processed_in(:event) end end ``` processed_in?の引数にはworkerのsidekiq_optionsで指定したnamespaceが入ります。 詳しくは[rspec_sidekiq](https://github.com/philostler/rspec-sidekiq)のreadmeを参照して下さい。 ## まとめ sidekiqは非常に簡単なので是非使ってみて下さい。 個人的にはdelayed_jobやresqueよりも分かりやすいと思います。 ## 追伸 rails4でも支障なく動きます。 |
|
| 140位 |
|
|||
|
11:20:17 |
|
|
「ショートカットを制するものは開発を制す」だそうです。
一つ一つは微々たる時間の節約でも毎日何度も使うと、膨大な時間が節約出来ることを 考えると、そうかもと思います。 今回は個人的に使う頻度順にまとめてみました。13inchのMBAで開発をしており、作業スペースが狭いと感じることが多いので、それを広げるショートカットが多めです。 こんなのも良く使うというご指摘あれば追加致しますので宜しくお願い致します。 その他間違いのご指摘、ご意見等頂けますと幸いです。 ★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★ ## Cmd + R - ビルドしてラン ## Cmd + . - ストップ。ランの状態の終了。 - シュミレーターで起動しているアプリを落としたい時によく使います。 ## Cmd + Q - シュミレーターの終了 - シュミレータは起動するのに、アプリは立ち上がらない・立ち上がりが異様に遅いことって多くないですか?そんな時は終了してまたランすると、上手くいくことが多いです。 ## Cmd + Shift + K - クリーン - クリーンとは?:ビルド時に作成される様々なファイルを削除する処理 - エラー発生→実装は間違っていない→とりあえずクリーン ## Cmd + Ctr + ← - 戻る的な動作のショートカット。ファイルをまたいで色々移動した後に、前に選択していた箇所(別のファイル)に戻りたいという時によく使います。 ## Cmd + Ctr + → - 進む的な動作のショートカット。 ## Ctr + E - 行の最後に移動 ## Ctr + A - 行の先頭に移動 ## Cmd + Shift + → or Ctr + Shift + E - 現在のカーソルの位置からその行の最後までを選択 - 一気に選択してコピペや削除した時に使います。 ## Cmd + Shift + ← - 現在のカーソルの位置からその行の最初までを選択 - 一気に選択してコピペや削除した時に使います。 ## Cmd + Shift + F - プロジュクト内検索 - 検索結果を一つずつ全て確認した場合は、検索結果表示画面で↑と↓で該当箇所を移動します。 ## Cmd + F - ファイル内検索 - Returnで該当箇所の次に移動。 ## Cmd + Ctr + ↓ or ↑ - ヘッダーファイルと実装ファイルの切替 ## Ctr + 6 - 現在のファイル内のプロパティとメソッドを表示します。 - 素早くメソッドを検索して移動したい時に使います。 - 合わせてpragmaの使い方を勉強すると役に立つかなと思います。 ## Cmd + B - ビルド ## Cmd + 0~7 - ナビゲータエリアの表示/非表示切替は0。 - プロジェクトナビゲータを表示したい時には1。小さいアイコンをクリックしてファイル一覧を表示して、という作業は面倒臭いので良く使います。 - 4のイシューナビゲータの表示もデバッグをしている時に良く使います。 ## Ctr + 5 - 選択中フォルダ内のファイルを一覧を表示します。 - 上下でファイルの選択を移動して、Returnで選択出来ます。 - 他にもCtr + 1~6は、まだ頻繁に使えてはいないのですが、便利機能があります。 ## Cmd + Opt + 0~7 - 0でユーティリティエリアの非表示をよくします。使う時と使わない時の差が激しいので、基本非表示にして使うときは0で出すという使い方です。 ## Cmd + Shift + Y - デバッグエリアの表示/非表示切替。 - 使う時と使わない時の差が激しいので、基本は表示せずです。デバッグモードでランすると勝手に表示されるので、それを隠す為に使う時が多いです。 ## Cmd + Ctr + Y - ブレークポイントで止まった処理をコンティニュー - ステップイン、ステップアウト等のショートカットもありますが、ここでは割愛 ## Cmd + Shift + O - プロジェクト内のファイル、メソッド、変数を検索 ## Ctr + K - カーソル位置から行端までを切り取り。 ## Cmd + Click - 選択したオブジェクトの定義に移動。 - この変数の型とかメソッドの実装なんだっけの時によく使います。 ## Cmd + T - タブの追加。 - タグは慣れるとすごく便利で、特にStoryBoardファイルは開いてからちゃんと表示されるまでに少し時間が掛かる(1.7GHz Intel Core i7・4GB 1600 MHz DDR3環境)ので、タブで開いといて、編集時にタブ移動のショートカットで移動することが多いです。 ## Cmd + Shift + [ or ] - タブの移動。 - ちなみにSafariのタブをこれで移動出来ます。ただ、Safariのタブは[]の代わりに→←でも移動出来る(カーソルが文字列を非選択の状態で)のに対して、Xcodeは[]でしか移動出来ないようです。 ## Cmd + Opt + C - コミット ## Cmd + Return - コミットのコメント挿入画面(コミット実行画面?)で、コミットの実行 - カーソルでコミットボタンまで移動してクリックが面倒臭いので、これで済ましています。取り ## Cmd + Shift + J - 現在編集中のファイルをプロジェクトナビゲーターで選択。 - その後、↓↑で選択ファイルの移動が出来ます。 ## Ctr + I - インデントの調整 ## Cmd + / - コメントアウト ## Cmd + Ctr + J - 定義に移動。 - 慣れてくるとCmd + Clickよりもこちらの方が早い気がします。 ## Opt + Click - Descriptionの表示 ## Opt + Double Click - 選択オブジェクトに関するDocumentを表示。 冒頭にも書かせて頂きましたが、まだまだ便利ショートカット等はたくさんあると思います。 皆様がよく使っているのに、書かれていないもの等あれば、ご指摘頂けましたら幸いです。 宜しくお願い致します。 |
|
| 141位 |
|
|||
|
09:43:35 |
(メルカリ/ソウゾウ 所属) |
|
このTopicはWeb ApplicationのfrontendのProjectを管理するTopicの2つ目です。
1. [node.jsのversionを管理するためにnodebrewを利用する](http://qiita.com/sinmetal/items/154e81823f386279b33c "node.jsのversionを管理するためにnodebrewを利用する") 1. [npmでnode.jsのpackageを管理する](http://qiita.com/sinmetal/items/395edf1d195382cfd8bc "npmでnode.jsのpackageを管理する") 1. [grunt.jsを使って、frontendのビルドを行う](http://qiita.com/sinmetal/items/ab34c02fe26e994876a6 "grunt.jsを使って、frontendのビルドを行う") 前提条件として、僕の環境はMac OSです。 ##npmとは? [npm](https://npmjs.org/ "npm")はnode.jsのpackageを管理するためのツールです。 node.js自体はJavaScriptで出来たWeb Serverですが、frontendを管理するためのtoolもたくさんあります。 npmはnode.jsのpackageを管理すためのツールです。 Javaが分かる方は、mavenのようなものだと想像していただければ、分かりやすいかと思います。 ##npmでpackageをインストールする npmでpackageをインストールするには、[npm install](https://npmjs.org/doc/install.html "npm install")を使います。 オプションなどは公式にありますので、その中からよく使うものをいくつか試してみたいと思います。 書いてあるcommandを実際に実行する場合は、適当な作業ディレクトリを作って、その中で実行するのが良いです。 ### npm install -g ```bash:command $ npm install -g grunt-cli ``` -g オプションは指定されたpackageをグローバルな領域にインストールするcommandです。 version関係なく、どのprojectでも利用するようなpackgeをインストールする時に利用します。 上の例だと、[grunt-cli](https://npmjs.org/package/grunt-cli "grunt-cli")をインストールしています。 grunt-cliについては次のTopicで利用しようと思っているpackageですので、今の段階ではそういうpackageをグローバルな領域にインストールされたのだなとだけ思っていてください。 ###npm init ```bash:command $ npm init name: (sample) sample version: (0.0.0) description: entry point: (index.js) test command: git repository: keywords: author: sinmetal license: (BSD) MIT { "name": "sample", "version": "0.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": "", "author": "sinmetal", "license": "MIT" } ``` npm initは対話形式でprojectの初期設定を行うためのcommandです。 実行すると対話形式で項目を入力して、package.jsonを作成します。 package.jsonはprojectのpackageを管理するためのファイルです。 mavenで言うところのpom.xmlです。 package.jsonはnode.jsのlibraryを公開する時のファイルなので、licenseなどが入ります。 ただ、ここでは公開するlibraryを作っているわけではなく、開発環境としてnpmを使いたいだけなので、ほとんど削除してかまわないでしょう。 適当に項目を削ったのが、以下のpackage.jsonです。 ```json:package.json { "name": "sample", "version": "0.0.0", "description": "" } ``` 適当に削った時に、よく最後のカンマを消し忘れるので、注意ですw ###npm install --save-dev ```bash:command $ npm install --save-dev grunt ``` --save-devはinstallしたlibraryの情報を自動でpackage.jsonに書いてくれるoptionです。 command実行後にnode_modules/gruntが増えていることが分かると思います。 -gオプションを付けずに実行した場合は、node_modulesにインストールされます。 projectごとに作られるわけですね。 [grunt](https://npmjs.org/package/grunt "grunt")については次のTopicで利用しようと思っているpackageですので、今の段階ではそういうpackageがインストールされたのだなとだけ思っていてください。 また、package.jsonにも変更が入っています。 ```json:package.json { "name": "sample", "version": "0.0.0", "description": "", "devDependencies": { "grunt": "~0.4.1" } } ``` devDependenciesが増えています。 devDependenciesというのは開発時のみ利用するlibraryのversionを管理します。 libraryとして公開するようなprojectだと、--saveになります。 今回追加したのはgruntですので、gruntが書かれています。 ###npm install ```bash:command $ npm install ``` オプション無しで、npm installを実行すると、package.jsonの内容に従って、packageをインストールします。 試しに、node_modulesディレクトリを削除して、実行してみてください。 node_modulesが再度作成されて、gruntがインストールされるはずです。 ##おまけ node_modulesはnpm installで自動生成されるディレクトリですので、VCSで管理する必要はありません。 gitを利用している場合は、.gitignoreに追加しておきます。 ```text:.gitignore # for client side development node_modules/ ``` |
|
| 142位 |
|
|||
|
09:55:30 |
|
|
##NeoBundleとは? vimのプラグイン管理ツール。 ##vundleとの違い [公式ドキュメント](http://vim-users.jp/2011/10/hack238/)からの引用 0. コマンド名が改善されている 1. vital化されている 2. shellslashがオンでも動作する 3. vimprocに対応している 4. unite.vimインタフェースの実装 5. Subversion, Mercurialへの対応 6. リビジョン指定 ##使い方 ```sh: $ curl https://raw.githubusercontent.com/Shougo/neobundle.vim/master/bin/install.sh > install.sh $ sh ./install.sh ``` 下記をvimrcに追記 ```vim: "NeoBundle Scripts----------------------------- if &compatible set nocompatible " Be iMproved endif " Required: set runtimepath^=/home/vagrant/.vim/bundle/neobundle.vim/ " Required: call neobundle#begin(expand('/home/vagrant/.vim/bundle')) " Let NeoBundle manage NeoBundle " Required: NeoBundleFetch 'Shougo/neobundle.vim' " Add or remove your Bundles here: NeoBundle 'Shougo/neosnippet.vim' NeoBundle 'Shougo/neosnippet-snippets' NeoBundle 'tpope/vim-fugitive' NeoBundle 'ctrlpvim/ctrlp.vim' NeoBundle 'flazz/vim-colorschemes' " You can specify revision/branch/tag. NeoBundle 'Shougo/vimshell', { 'rev' : '3787e5' } " Required: call neobundle#end() " Required: filetype plugin indent on " If there are uninstalled bundles found on startup, " this will conveniently prompt you to install them. NeoBundleCheck "End NeoBundle Scripts------------------------- ``` vi上から、`:NeoBundleInstall` で.vimrcのNeoBundleで指定されているリポジトリのプラグインをインストールできる。 プラグインを削除したい場合は、vimrc上からNeoBundleの記述を消して `:NeoBundleClean` でできる。 |
|
| 143位 |
|
|||
|
19:36:57 |
(Increments inc. 所属) |
|
- 続編も書きました : [
結局jQuery.Deferredの何が嬉しいのか分からない、という人向けの小話](http://qiita.com/yuku_t/items/1b8ce6bba133a7eaeb23) jQuery version1.5で導入された`jQuery.Deferred`は、無くてもコードを書けるけど、使えば少しコードが綺麗かつ見通しが良くなる、という機能。 無くても書けるという機能がなかなか使われないというのは世の常なので、jQueryクックブック(O'REILLY)の中でも言及されていない、なんとも寂しい状況だ。 ちょっとここらで一肌脱いでやるか、という趣旨で書き始めたら無駄に長くなった。 とりあえず使ってみたい、という人は下の方の「jQuery.Deferred自体の使い方」までジャンプするとよい。 ##jQuery.Deferredとはどういう場面で使うものなのか コールバックを渡して非同期処理完了時にそれを呼び出してもらうような場面。 具体的には ```js $.get('hoge', function(data) { /* hogeからGETで取ってきて、取ってきた結果を第一引数(data)にいれて実行される関数 */ }); ``` 知ってる人が多いとは思うが、以下のように書くのは間違い ```js var hoge; $.get('hoge', function(data) { hoge = data; }); alert(hoge); // => undefined ``` `undefined`とアラートされる理由は`$.get`のコールバック関数が実行される前に`alert`が呼ばれるため。 これくらいはJavaScriptを少し聞きかじった人でもだいたい知っているだろう。 この程度なら大した問題にならないと思うし、`jQuery.Deferred`をわざわざ使わなくてもなんとかなる。 しかしながら、上記の例はhoge一つだけだったのが、もう少し話を進めてhigeとhomuまで必要になったとしたらどうなるだろう? 以下のようにコールバックの雨あられとなってしまう。 ```js $.get('hoge', function(hoge) { $.get('hige', function(hige) { $.get('homu', function(homu) { // hoge, hige, homu を使った処理 }); }); }); ``` 僕的には既にアウトだが、百歩譲ってこれがぎりぎりセーフだとしても、ここから更に歩を進めて、必要なデータの数が任意個だったらどうすればよいのか。 ```js $.get(arg1, function(data) { // 任意回のコールバックのネスト???? }); ``` 加えて、これらの間に依存関係がないとしたら、順番順番に取得するのは時間のムダだ。並列的にに取ってきたいだろう。つまり、 ``` -(get hoge)->|-(get hige)->|-(get homu)->|-(hoge hige homuを使った処理)-> ``` じゃなく ``` -(get hoge)->| -(get hige)->|-(hoge hige homuを使った処理)-> -(get homu)->| ``` という感じだ。 ここでさっそうと現れるのが`jQuery.Deferred`。 具体的には次のように書く。 まずは小手調べとして、hoge, hige, homuが必要な場合。 ```js $.when($.get('hoge'), $.get('hige'), $.get('homu')) .done(function(hoge, hige, homu) { // hoge, hige, homu を使った処理 }); ``` そして、任意個のデータが必要な場合。`names`という配列に必要なのが入っているとする。 ```js deferredObjects = []; for (var i = 0; i < names.length; i++) { deferredObjects.push($.get(names[i])); } $.when.apply(null, deferredObjects) .done(function() { // arguments を使って必要な処理を書く }); ``` 一つでもエラーがあった場合は他の処理をしたい時は次のようになる。 ```js $.when.apply(null, deferredObjects) .done(function() { /* arguments を使う */ }) .fail(function() { /* エラー処理 */ }); ``` ##具体的な利用シーン キャッシュがあればそちらを使い、無かったらリモートから取ってきてキャッシュに入れて、其の後、値を使って処理をしたい、という場合は次のような感じに書く。(stack over flowにあった例を拝借) ```js var cache = {}; var getData = function(name) { return cache[name] || $.ajax(name, { success: function (data) { cache[name] = data; }); }; $.when(getData('hoge'), getData('hige'), getData('homu')) .done(function(hoge, hige, homu) { /* */ }); ``` このサンプルの味噌は`getData`が返すのが`$.ajax()`だったら遅延が生じ、単なるオブジェクト(ここでは`cache[name]`)だったら、遅延が生じないということだ。 つまり、hogeとhigeとhomuのうち、キャッシュに乗っていないものだけ取ってきて、全部がそろった段階で`done`に渡されたコールバックが実行される。 #jQuery.Deferred自体の使い方 - 参考: [jQuery.Deferredって何](http://hamalog.tumblr.com/post/5159447047/jquery-deferred) 上記のページがいろいろ書いてある。ここでは中身には踏み込まず、ぶっちゃけどうすれば使えるかだけ書く。 ##0 非同期処理を含んだ関数を用意する ```js var func = function () { async(function () { // 非同期関数 async に渡されるコールバック関数 }); }; ``` ##1 jQuery.Deferred#promise()を返すようにする ```js var func = function() { var dfd = $.Deferred(); // new はあってもなくても同じ async(function() { /* callback */ }); return dfd.promise(); }; ``` `func`を実行すると、内部の`async`の結果を待たずに`dfd.promise()`が返される。 返される値がこれ以外だった場合は、自動的に `resolve(返されたデータ)` 相当として処理される。(`resolve`については後述) ##2 コールバックの中でresolveもしくはrejectを呼ぶ ```js var func = function() { var dfd = $.Deferred(); async(function() { // 何かの処理 success ? dfd.resolve(/* 渡したいオブジェクト */) : dfd.reject(); }); return dfd.promise(); }; ``` 終わり。簡単簡単。 勘の良い人なら分かると思うが、`jQuery.ajax`は上記の仕様を満たすように設計されているため、`jQuery.when`に渡すことができる。 ##3 実行する ```js func() .done(function(data) { // resolveが実行された場合 // data には上のdfd.resolveに渡したオブジェクトが入っている }) .fail(function () { // rejectが実行された場合 }); ``` 上の方で書いたように`when`には複数渡せば並列実行できる。 ```js $.when(func(), func(), func()) .done(function(data1, data2, data3) { /* 全部 resolve だった場合 */ }) .fail(function() { /* 一つでも reject だった場合 */ }); ``` ざっとこんな感じ。 jQuery.Deferredを使って非同期コーディングを楽しみましょう。 続編 [ 結局jQuery.Deferredの何が嬉しいのか分からない、という人向けの小話](http://qiita.com/yuku_t/items/1b8ce6bba133a7eaeb23) |
|
| 144位 |
|
|||
|
00:48:24 |
(フリーランス 所属) |
|
GitHubを彷徨っていてよくあるのが、ググったりRuby Toolboxとかで見つけて「これイイじゃんよ!」と思ったら既にスター済み、という奴。
一回、自分がどんなリポジトリにスター付けたのか整理しつつ、更新止まってたり古くなったやつを削除していこうと思う。 それぞれの説明は超適当。基本的にいつか使おう的な感じでスターを付けているので、あんまり使ったことあるのが無い。 そもそも良く使うものにはスター付けてないこと多いし…。 大体rubygemsで一部JSのライブラリ、少しvimとScalaって感じ。 思い返したようにスター付けてたので、時期がバラバラだけど、基本的に下に行く程付けた時期が新しい。 リポジトリ | 説明 ----|---- [norman/friendly_id](https://github.com/norman/friendly_id "norman/friendly_id") | 数字以外のIDでActiveRecordを検索したい時に便利にしてくれるgem [thoughtbot/factory_girl](https://github.com/thoughtbot/factory_girl "thoughtbot/factory_girl") | テストデータを簡単に準備するfixture replacementの定番gem [jpmobile/jpmobile](https://github.com/jpmobile/jpmobile "jpmobile/jpmobile") | 日本の携帯向けの機能を追加するRails拡張 [apotonick/cells](https://github.com/apotonick/cells "apotonick/cells") | ビューのパーツごとにコンポーネント化して1セットにできるRails拡張 [tuupola/jquery_lazyload](https://github.com/tuupola/jquery_lazyload "tuupola/jquery_lazyload") | 画像の遅延表示を行うjQueryプラグイン [matthuhiggins/foreigner](https://github.com/matthuhiggins/foreigner "matthuhiggins/foreigner") | Railsのマイグレーションにforeign key制約を定義するDSLを追加する [igrigorik/em-proxy](https://github.com/igrigorik/em-proxy "igrigorik/em-proxy") | EventMachineを利用したプロキシサーバ。RubyのDSLでTCPのパケットをスパイしたり転送したり [plataformatec/devise](https://github.com/plataformatec/devise "plataformatec/devise") | Railsに認証機能を追加するgemの定番。便利だけど頻繁に悩まされる [jboesch/Gritter](https://github.com/jboesch/Gritter "jboesch/Gritter") | Growlっぽい通知を出すためのjQueryプラグイン [tpope/vim-pathogen](https://github.com/tpope/vim-pathogen "tpope/vim-pathogen") | Vimのプラグイン管理を助けてくれるプラグイン。最近NeoBundleを使ってるので使わない [tyru/skk.vim](https://github.com/tyru/skk.vim "tyru/skk.vim") | VimでSKK入力をするプラグイン [intridea/grape](https://github.com/intridea/grape "intridea/grape") | RESTfulなDSLでAPIサーバーを構築できるシンプルなフレームワーク [presidentbeef/brakeman](https://github.com/presidentbeef/brakeman "presidentbeef/brakeman") | Railsの脆弱性を診断するgem。Jenkinsに組み込んだり [fnichol/chef-rvm](https://github.com/fnichol/chef-rvm "fnichol/chef-rvm") | chefでrvmを入れるレシピ。 [mozilla/doctorjs](https://github.com/mozilla/doctorjs "mozilla/doctorjs") | JSの静的解析ツール詰め合わせ。jsctags以外が良く分からない [vimpr/vimperator-plugins](https://github.com/vimpr/vimperator-plugins "vimpr/vimperator-plugins") | vimperator向けのプラグインがまとまってるリポジトリ [nathanaelkane/vim-indent-guides](https://github.com/nathanaelkane/vim-indent-guides "nathanaelkane/vim-indent-guides") | Vimのインデント表示を見易くしてくれるプラグイン [mapbox/node-zipfile](https://github.com/mapbox/node-zipfile "mapbox/node-zipfile") | node.js向けのzipfile解凍ライブラリ。libzipを使うらしい [github/gitignore](https://github.com/github/gitignore "github/gitignore") | GitHubが提供してる.gitignoreのサンプル集 [samsonjs/strftime](https://github.com/samsonjs/strftime "samsonjs/strftime") | JSにstrftimeを追加する。最近だとmoment.js使った方が良さそう [codebrew/backbone-rails](https://github.com/codebrew/backbone-rails "codebrew/backbone-rails") | Railsにbackbone.jsを追加するgem。generatorとかも追加してくれる。最近使ってない [cgriego/active_attr](https://github.com/cgriego/active_attr "cgriego/active_attr") | 簡単な定義でActiveModelっぽいオブジェクトを定義できるgem。最近ActiveModel::Modelあるからそんなに要らない気もする [sosedoff/capistrano-unicorn](https://github.com/sosedoff/capistrano-unicorn "sosedoff/capistrano-unicorn") | Unicorn向けのcapistranoタスクを定義してくれるgem [faye/faye-websocket-ruby](https://github.com/faye/faye-websocket-ruby "faye/faye-websocket-ruby") | FayeというWebSocketライブラリのRuby実装 [amatsuda/active_decorator](https://github.com/amatsuda/active_decorator "amatsuda/active_decorator") | Railsにデコレーター層を追加する。オブジェクト指向なビューヘルパーが使える [bploetz/versionist](https://github.com/bploetz/versionist "bploetz/versionist") | サーバーでAPIを提供する時のバージョニングを支援するgem。こんな複雑なバージョニングしない方が良いような気もする。 [brainspec/enumerize](https://github.com/brainspec/enumerize "brainspec/enumerize") | ActiveRecordに列挙型っぽい感じでデータを保存する。ActiveRecord::Enumがかなり微妙なのでこっちの方が便利じゃないかなあ。 [pry/pry-stack_explorer](https://github.com/pry/pry-stack_explorer "pry/pry-stack_explorer") | Pryにスタックを移動したり一覧したりする機能を追加するgem [k-tsj/pattern-match](https://github.com/k-tsj/pattern-match "k-tsj/pattern-match") | Rubyでパターンマッチをするためのgem。確かSapporo RubyKaigiで聞いたものだったと思う [cldwalker/debugger](https://github.com/cldwalker/debugger "cldwalker/debugger") | Ruby1.9以下向けのデバッガ。最近は、めっきり用が無くなってしまった [jenseng/immigrant](https://github.com/jenseng/immigrant "jenseng/immigrant") | foreignerを利用するマイグレーションファイルを生成するジェネレーター [shugo/immutable](https://github.com/shugo/immutable "shugo/immutable") | immutableなデータ構造をRubyで実装したライブラリ。これもSapporo RubyKaigiで聞いたと思う [cookpad/arproxy](https://github.com/cookpad/arproxy "cookpad/arproxy") | ActiveRecordとDBの間に噛ませるプロキシ。ActiveRecordが発行するSQLを加工したりログ取ったりできる [tomykaira/rspec-parameterized](https://github.com/tomykaira/rspec-parameterized "tomykaira/rspec-parameterized") | RSpecにパラメータライズドテストをやり易くするDSLを追加する [burke/zeus](https://github.com/burke/zeus "burke/zeus") | Railsのコマンド類を高速化するやつ。最近はSpringに移行したので使ってない [r7kamura/webtail](https://github.com/r7kamura/webtail "r7kamura/webtail") | ブラウザでログのtailができるgem [jschr/bootstrap-modal](https://github.com/jschr/bootstrap-modal "jschr/bootstrap-modal") | Bootstrapのモーダル機能を拡張する [backbone-paginator/backbone-pageable](https://github.com/backbone-paginator/backbone-pageable "backbone-paginator/backbone-pageable") | Backbone.jsのページネーションプラグイン [charliesome/better_errors](https://github.com/charliesome/better_errors "charliesome/better_errors") | RailsとかRackアプリのエラー画面をリッチにするgem [discourse/discourse](https://github.com/discourse/discourse "discourse/discourse") | Rails製のコミュニケーションプラットフォーム。規模の大きいRailsアプリケーションの例 [bradphelan/jasminerice](https://github.com/bradphelan/jasminerice "bradphelan/jasminerice") | RailsでJasmineを利用したJSのテストを実行するのを支援するgem。あんま更新されてないっぽいので使ってない [ymattw/cdiff](https://github.com/ymattw/cdiff "ymattw/cdiff") | Python製のdiffの強化版 side-by-side出力とかできる [troessner/reek](https://github.com/troessner/reek "troessner/reek") | Rubyのコードのバッドスメルな部分を検出する解析ツール [seattlerb/flay](https://github.com/seattlerb/flay "seattlerb/flay") | Rubyのコードの中で似ている部分を検出してくれる [acrmp/foodcritic](https://github.com/acrmp/foodcritic "acrmp/foodcritic") | ChefのクックブックのLINTツール [yyuu/capistrano-rbenv](https://github.com/yyuu/capistrano-rbenv "yyuu/capistrano-rbenv") | capistranoにrbenvを利用してコマンドを実行する機能を追加する [thoughtbot/appraisal](https://github.com/thoughtbot/appraisal "thoughtbot/appraisal") | 複数のGemfileを切り替えてテストを実行するためのgem [lomba/schema_plus](https://github.com/lomba/schema_plus "lomba/schema_plus") | Railsのマイグレーションに色々なDSLを追加する。foreign keyとかviewとか [ahoward/systemu](https://github.com/ahoward/systemu "ahoward/systemu") | Rubyのsystemメソッドをちょっと便利にしたもの。 [comfy/comfortable-mexican-sofa](https://github.com/comfy/comfortable-mexican-sofa "comfy/comfortable-mexican-sofa") | Rails4製のCMSエンジン。Rails Engineを利用したアプリケーションの例 [nebhale/git-pivotal-tracker-integration](https://github.com/nebhale/git-pivotal-tracker-integration "nebhale/git-pivotal-tracker-integration") | gitにpivotal trackerと連携できるサブコマンドを追加する [yuroyoro/git-issue](https://github.com/yuroyoro/git-issue "yuroyoro/git-issue") | gitにGitHubのissueを操作するサブコマンドを追加する [markbates/coffee-rails-source-maps](https://github.com/markbates/coffee-rails-source-maps "markbates/coffee-rails-source-maps") | coffee-railsでコンパイルされるJSに対応するsource mapを出力してくれるgem [cldwalker/boson](https://github.com/cldwalker/boson "cldwalker/boson") | thorに似た簡単にコマンドラインツールを開発するためのフレームワーク [jpignata/temping](https://github.com/jpignata/temping "jpignata/temping") | ActiveRecordで利用できる一時的なクラスとテーブルを作成してくれるgem。モジュールのテストなんかに使えるらしい [tomykaira/qspec](https://github.com/tomykaira/qspec "tomykaira/qspec") | 複数プロセスでRSpecを並列実行するgem。redisを使ってspecのキューイングを行う [amatsuda/kawaii_validation](https://github.com/amatsuda/kawaii_validation "amatsuda/kawaii_validation") | Railsのバリデーション定義にクールなシンタックスを追加する [agoragames/stache](https://github.com/agoragames/stache "agoragames/stache") | Railsのビューテンプレートとしてmustacheかhandlebarsを利用する [ebryn/backburner.js](https://github.com/ebryn/backburner.js "ebryn/backburner.js") | Ember.jsから切り出されたイベントループのためのライブラリ。backboneに組み込んだりできるらしい [ConradIrwin/jist](https://github.com/ConradIrwin/jist "ConradIrwin/jist") | gistにコードをアップロードするコマンドを追加するgem [glebm/i18n-tasks](https://github.com/glebm/i18n-tasks "glebm/i18n-tasks") | Rubyのi18nで使われていないキーを探したり、定義されてないキーを探してくれるgem [xdite/bootstrap-helper](https://github.com/xdite/bootstrap-helper "xdite/bootstrap-helper") | Bootstrapフレンドリーなヘルパーメソッドを定義してくれるgem。でもこれbootstrapのバージョン何に対応してるんだろう [nathanl/authority](https://github.com/nathanl/authority "nathanl/authority") | cancanっぽい権限管理のためのgem。比較的新しい [deivid-rodriguez/byebug](https://github.com/deivid-rodriguez/byebug "deivid-rodriguez/byebug") | Ruby2.0以降のデバッガの定番 [solnic/virtus](https://github.com/solnic/virtus "solnic/virtus") | active_attrに似たプレーンなRubyオブジェクトを使ってモデル定義をするためのgem [wycats/rack-offline](https://github.com/wycats/rack-offline "wycats/rack-offline") | HTML5のアプリケーションキャッシュのためのmanifestファイルを出力してくれるgem [plataformatec/simple_form](https://github.com/plataformatec/simple_form "plataformatec/simple_form") | Railsでフォームを表示する時に使える便利なヘルパーを提供するgem [hayeah/rantly](https://github.com/hayeah/rantly "hayeah/rantly") | いくつかの型のランダムなデータを生成してくれるgem。QuickCheck的なテストコードも書けるらしい [mariusGundersen/Overload](https://github.com/mariusGundersen/Overload "mariusGundersen/Overload") | JSでメソッドオーバーロードできる関数を定義できるライブラリ [guilleiguaran/fakeredis](https://github.com/guilleiguaran/fakeredis "guilleiguaran/fakeredis") | インメモリで動作するredis-rb向けのドライバ。テスト時に利用できる [causes/mock_redis](https://github.com/causes/mock_redis "causes/mock_redis") | redis-rbと同じインターフェースを提供するモックオブジェクト。fakeredisと用途は一緒だが動作するレイヤーが違う [mozilla/brick](https://github.com/mozilla/brick "mozilla/brick") | Mozilla製のWebアプリ向けコンポーネント集。タブバーとかスライドするパネルとかカレンダーとかスライダーとか [mperham/connection_pool](https://github.com/mperham/connection_pool "mperham/connection_pool") | 簡単にコネクションプールを定義するためのライブラリ。RedisとかDalliとかで便利 [utgarda/sidekiq-status](https://github.com/utgarda/sidekiq-status "utgarda/sidekiq-status") | sidekiqのジョブ実行状況をjob_idを元の問い合わせることができるgem [nateware/redis-objects](https://github.com/nateware/redis-objects "nateware/redis-objects") | Redisを利用したORMを提供するgem。 [amatsuda/database_rewinder](https://github.com/amatsuda/database_rewinder "amatsuda/database_rewinder") | シンプルで高速なdatabaseクリーンアップgem [schisamo/vagrant-omnibus](https://github.com/schisamo/vagrant-omnibus "schisamo/vagrant-omnibus") | omnibus-installerを利用してchefをインストールするvagrantプラグイン [amatsuda/rfd](https://github.com/amatsuda/rfd "amatsuda/rfd") | Ruby製のターミナル用ファイラー [zilkey/active_hash](https://github.com/zilkey/active_hash "zilkey/active_hash") | HashをデータストアにしたActiveRecordっぽいオブジェクトが定義できるgem [brentd/xray-rails](https://github.com/brentd/xray-rails "brentd/xray-rails") | RailsのテンプレートやBackboneのviewで描画したものが画面上でどこに対応しているかを視覚的に表示してくれる [emadurandal/emberjs-guides-japanese-translation](https://github.com/emadurandal/emberjs-guides-japanese-translation "emadurandal/emberjs-guides-japanese-translation") | Emberjs Guidesの日本語訳 [modeset/teaspoon](https://github.com/modeset/teaspoon "modeset/teaspoon") | Railsのアセットパイプラインに対応したJSのテストドライバー [willnet/takarabako](https://github.com/willnet/takarabako "willnet/takarabako") | ゲームに使えそうなランダム名称ジェネレーター。厨二病を発揮したい時などにも [leshill/hogan_assets](https://github.com/leshill/hogan_assets "leshill/hogan_assets") | hogan.jsを使ってmustache/hamstacheでJSTを書けるようになる [grosser/parallel](https://github.com/grosser/parallel "grosser/parallel") | parallel_testsなんかの裏側で利用されてるgem [srawlins/allocation_stats](https://github.com/srawlins/allocation_stats "srawlins/allocation_stats") | Ruby2.1から追加されたObjectSpaceからオブジェクトアロケーションをトレースするAPIを使い易くしてくれるgem [kostya/eye](https://github.com/kostya/eye "kostya/eye") | プロセスを立ち上げて、状態を監視するツール。godみたいな感じ [r7kamura/sitespec](https://github.com/r7kamura/sitespec "r7kamura/sitespec") | RackアプリケーションとRspecの定義から静的なサイトを生成してくれる [meganemura/ruby-dmm](https://github.com/meganemura/ruby-dmm "meganemura/ruby-dmm") | RubyからDMMのAPIを叩くためのライブラリ [mirakui/activerecord-mysql-index-hint](https://github.com/mirakui/activerecord-mysql-index-hint "mirakui/activerecord-mysql-index-hint") | ActiveRecordの発行するSQLにFORCE INDEXを追加できるようになる [udzura/rack-spyup](https://github.com/udzura/rack-spyup "udzura/rack-spyup") | HTTPのヘッダやボディをインターセプトして表示してくれるRackミドルウェア [json4s/json4s](https://github.com/json4s/json4s "json4s/json4s") | ScalaのJSONパーサーライブラリ [pokonski/public_activity](https://github.com/pokonski/public_activity "pokonski/public_activity") | ユーザーの行動をActiveRecordとかMongoMapperを使ってトラッキングするgem [lodash/lodash](https://github.com/lodash/lodash "lodash/lodash") | underscore.jsの代替ライブラリ。高速で多機能化されてる [RailsApps/rails_layout](https://github.com/RailsApps/rails_layout "RailsApps/rails_layout") | bootstrap等のフレームワークに合わせたレイアウトテンプレートを生成してくれるジェネレーター [ianyh/Amethyst](https://github.com/ianyh/Amethyst "ianyh/Amethyst") | Mac向けのタイル型ウインドウマネージャー [dtao/lazy.js](https://github.com/dtao/lazy.js "dtao/lazy.js") | underscore.jsに似たライブラリだが、処理が遅延評価される [vvo/lazyload](https://github.com/vvo/lazyload "vvo/lazyload") | JSのスタンドアローンな遅延読み込みライブラリ [skinny-framework/skinny-framework](https://github.com/skinny-framework/skinny-framework "skinny-framework/skinny-framework") | Scalaで書かれたWebフレームワーク。Railsっぽい雰囲気がある [polygonplanet/tombloo](https://github.com/polygonplanet/tombloo "polygonplanet/tombloo") | tombloo/tombfixで利用できる追加機能パッチがまとまっている [idobata/hubot-idobata](https://github.com/idobata/hubot-idobata "idobata/hubot-idobata") | idobataでhubotを利用する時に [es-shims/es5-shim](https://github.com/es-shims/es5-shim "es-shims/es5-shim") | レガシーなJSエンジンでECMA5のメソッドをエミュレートする [moment/moment](https://github.com/moment/moment "moment/moment") | JSで時間を扱う時の便利機能をまとめたライブラリ [sciactive/pnotify](https://github.com/sciactive/pnotify "sciactive/pnotify") | BootstrapかjQuery UIのスタイルを利用して通知を表示するjQueryプラグイン [daiksy/dmm4s](https://github.com/daiksy/dmm4s "daiksy/dmm4s") | ScalaでDMMのAPIを叩くためのライブラリ [scalaj/scalaj-http](https://github.com/scalaj/scalaj-http "scalaj/scalaj-http") | ScalaのシンプルなHTTPクライアントライブラリ [dockyard/destroyed_at](https://github.com/dockyard/destroyed_at "dockyard/destroyed_at") | ActiveRecordの論理削除プラグイン [cookpad/rrrspec](https://github.com/cookpad/rrrspec "cookpad/rrrspec") | 分散RSpecを実現する [jimbojsb/launchrocket](https://github.com/jimbojsb/launchrocket "jimbojsb/launchrocket") | homebrewで入れたソフトのplistを読んで、launchctlで起動する操作を簡単にしてくれる [whitequark/parser](https://github.com/whitequark/parser "whitequark/parser") | Rubyのパーサー [zimbatm/direnv](https://github.com/zimbatm/direnv "zimbatm/direnv") | ディレクトリごとに環境変数を自動でセットできるようになる。binstubsとか利用してる時に便利 [rodreegez/powder](https://github.com/rodreegez/powder "rodreegez/powder") | powで動作させるアプリケーションを簡単に追加できる [neovim/neovim](https://github.com/neovim/neovim "neovim/neovim") | ネオなヴィム [evrone/quiet_assets](https://github.com/evrone/quiet_assets "evrone/quiet_assets") | アセットパイプラインに対するアクセスログを黙らせる [Mange/roadie](https://github.com/Mange/roadie "Mange/roadie") | RailsからHTMLメールを送る時に役立つ便利機能を提供するgem [smartinez87/exception_notification](https://github.com/smartinez87/exception_notification "smartinez87/exception_notification") | Rails/Rackアプリで例外が発生した時に、色々な方法で通知を投げるgem [sevenwire/forgery](https://github.com/sevenwire/forgery "sevenwire/forgery") | ランダムデータジェネレーター。あんまりちゃんとメンテしてないから気を付けろ、らしい [netzpirat/haml_coffee_assets](https://github.com/netzpirat/haml_coffee_assets "netzpirat/haml_coffee_assets") | hamlとcoffeeでJSTを記述できるgem [chjj/marked](https://github.com/chjj/marked "chjj/marked") | JSのmarkdownパーサー/コンパイラー [uglide/RedisDesktopManager](https://github.com/uglide/RedisDesktopManager "uglide/RedisDesktopManager") | Qt5で書かれたRedisのGUI管理ツール [cookpad/styleguide](https://github.com/cookpad/styleguide "cookpad/styleguide") | クッ社のコーディング規約 [errbit/errbit](https://github.com/errbit/errbit "errbit/errbit") | Airbrakeコンパチのエラー管理ツール [jpillora/notifyjs](https://github.com/jpillora/notifyjs "jpillora/notifyjs") | JSの通知ライブラリ。ボタンの横にちょこっと出したりする時に使えそう [wavded/humane-js](https://github.com/wavded/humane-js "wavded/humane-js") | JSの通知ライブラリ。でかめの通知が出せる [joeferner/redis-commander](https://github.com/joeferner/redis-commander "joeferner/redis-commander") | node.js製のRedis管理ツール。Webから操作するタイプ [BBC-News/wraith](https://github.com/BBC-News/wraith "BBC-News/wraith") | PhantomJSを使ってWebサイトのスクリーンショットを取り、比較して差分を検出するツール。PhantomCSSみたいな感じ [bblimke/webmock](https://github.com/bblimke/webmock "bblimke/webmock") | Rubyから外部のネットワークにHTTPでアクセスする処理をモックしてテストできるようにしてくれる [vcr/vcr](https://github.com/vcr/vcr "vcr/vcr") | 実際に一度外部にアクセスしたレスポンスを記録しておき、モックのレスポンスとしてそれを返すことができる。webmockと組み合わせると便利 [resque/redis-namespace](https://github.com/resque/redis-namespace "resque/redis-namespace") | Redisにアクセスする時にnamespaceを作ってくれる [karmi/retire](https://github.com/karmi/retire "karmi/retire") | ElasticSearchにRubyからアクセスするためのAPIとDSLを提供する [ankane/searchkick](https://github.com/ankane/searchkick "ankane/searchkick") | 同上。ActiveRecordに対応してる [sunspot/sunspot](https://github.com/sunspot/sunspot "sunspot/sunspot") | Apache SolrにRubyからアクセスするためのDSLを提供する [CanCanCommunity/cancancan](https://github.com/CanCanCommunity/cancancan "CanCanCommunity/cancancan") | CanCanの後継として開発されてる権限管理のgem [seejohnrun/ice_cube](https://github.com/seejohnrun/ice_cube "seejohnrun/ice_cube") | Rubyで繰り返し日付を表現するライブラリ [hpoydar/chronic_duration](https://github.com/hpoydar/chronic_duration "hpoydar/chronic_duration") | 自然言語とか1:01:02とかを時間の長さとしてパースして数値として返したり、逆に自然言語っぽく表示したりできる(でも英語 [julianlloyd/scrollReveal.js](https://github.com/julianlloyd/scrollReveal.js "julianlloyd/scrollReveal.js") | なんか妙にかっちょいい感じでWebページの要素を表示できるライブラリ。でもこんなお洒落なサイトに縁が無い [thoughtbot/formulaic](https://github.com/thoughtbot/formulaic "thoughtbot/formulaic") | capybaraでRailsのフォームビルダーで描画したフォームに値を入れるのを楽にしてくれるヘルパーメソッドを追加する [peter-murach/finite_machine](https://github.com/peter-murach/finite_machine "peter-murach/finite_machine") | Rubyのミニマルなステートマシーンライブラリ。この手のタイプ色々あるんだけど、あんまり上手くハマった経験が無い [toptal/chewy](https://github.com/toptal/chewy "toptal/chewy") | RubyのElasticSearchクライアント。まだElasticSearch使ったことないからどれが良いかは分からない [nulldb/nulldb](https://github.com/nulldb/nulldb "nulldb/nulldb") | 実際にはDBに接続しないActiveRecordのDBアダプター。テストで上手く活用できると、IO発生しなくなるからすげー早くなるらしい [defunkt/fakefs](https://github.com/defunkt/fakefs "defunkt/fakefs") | ファイルシステムを操作する処理をテストするためにフェイクのファイルシステムを提供する。レポートを出力するようなgemのテストコード書く時に便利そう [thoughtbot/bourbon](https://github.com/thoughtbot/bourbon "thoughtbot/bourbon") | シンプルなSassのmixin集。compassの軽量版みたいな感じだと思う [bwillis/versioncake](https://github.com/bwillis/versioncake "bwillis/versioncake") | APIのバージョニングをjbuilderのテンプレートを分けることで実現するgem [avdi/naught](https://github.com/avdi/naught "avdi/naught") | RubyでNull Objectパターンを書くのを支援してくれるgem [sdsykes/fastimage](https://github.com/sdsykes/fastimage "sdsykes/fastimage") | URLやファイルパスを渡して、画像のサイズやフォーマットを取得できるgem [metricfu/metric_fu](https://github.com/metricfu/metric_fu "metricfu/metric_fu") | Rubyの静的解析ツールがまとまったもの。Ruby2.1でも結構使える |
|
| 145位 |
|
|||
|
19:19:42 |
|
|
## Ruby Advent Calendar 2013 2日目
[るびぃあどぅべんどぅくぁれんだー2013](http://qiita.com/advent-calendar/2013/ruby)、2日目の記事となります。昨日はすうぱぁももんがさんのあくろばてぃっくな[今年こそRubyを始めたいあなたに!ももんが流・最強のRuby学習法](http://blog.supermomonga.com/articles/ruby/sugoi-learning-way.html)です。 ## 概要 三回ほど手を変え品を変えWeb APIをRubyで作ってきました。ここではそこから学んだ今の私の全力全開、最高のWeb API開発についてコード例を交えてお話したいと思います。ここで言うWeb APIとはスマホアプリから使用する、サーバに置いてあるAPIをイメージして頂ければと思います。 ここではユーザが写真をサーバに保存できるAPIを想定し、応答のフォーマットはJSONとします。 ## 使うもの ### rails 4 Rails 3ではなくRails 4を使うのは趣味です。 ### [grape](https://github.com/intridea/grape) APIを書く際に非常に楽になります。APIで使用するURLとHTTPメソッドの構造をベースとしたDSLを使用することで、該当のAPIとコードの対応が非常に分かりやすく書けます。また一つのファイルにAPIのコントローラ全てが纏めることができ、コード全体を非常に把握しやすくなります。 ### [rabl](https://github.com/nesquena/rabl) JSONのビューを書く際に非常に便利なテンプレートエンジンです。ビューを使いまわして入れ子にしたりすることも出来るので、複雑なJSONを生成する際には欠かせません。 ### [json_expressions](https://github.com/chancancode/json_expressions) RSpecでJSONをテストする際に非常に便利なmatcharです。Rubyで予期されるJSONの構造をまんま書くことで、それにマッチするか判定してくれます。マッチしなかった時のエラーも単なる文字列比較に終わるのではなく、どの属性の値が違うとか配列の要素の数がおかしいとか賢い指摘をしてくれます。 ### その他 おなじみRailsで使うgemですがGrapeでも使えます。 - kaminari - ページングに役立つgemです。 - paperclip - おなじみのRailsで画像を取り扱うgemです。 - oj - JSON生成に使うgemでなんかすっごいはやいらしい。 - factory_girl_rails - おなじみのテスト用オブジェクト作成gem。 - ffaker - faker-japanese - テスト用文字列を作成するgem。後者は日本語の文字列も生成できます。 ## コードはこんな風になります この環境での開発を始めてからテスト駆動しやすくなりました。 ### テスト it_behaves_like('201')は返ってきたレスポンスがlet(:result)で定義したJSONと一致するか、またレスポンスコードが201であるかを見ています。*_matcharというのは別所で定義した正規表現です。要はどんなURLにどんなメソッドでどんなパラメータを送ったらどんなJSONの応答が欲しいか書きます。 ```ruby:spec/requests/pictures_spec.rb # -*- encoding: utf-8 -*- require 'spec_helper' describe API do describe 'Pictures' do let(:user) { FactoryGirl.create(:user) } describe 'POST /api/pictures' do let(:image) { fixture_file_upload('sample.jpg', 'image/jpeg') } context '正常な投稿' do subject { post '/api/pictures', { api_key: user.api_key, image: image } } let(:result) do { picture: { id: /\A\d+\z/, owner: { nickname: user.name, }, hash: sha1_matchar, image_url: image_url_matchar, created_at: datetime_matchar, updated_at: datetime_matchar } } end it_behaves_like('201') end end end end ``` ### コントローラ このファイルにコントローラの全てが集約されます。URLの構造に沿ったコードの構造になっています。余談ですがRails 4で実装されたStrong Parametersに対応するためhelpersで通すパラメータを定義したメソッドを用意しています。 ```ruby:app/api/api.rb # -*- encoding: utf-8 -*- class API < Grape::API format :json formatter :json, Grape::Formatter::Rabl default_format :json helpers do def picture_params ActionController::Parameters.new(params).permit(:title) end end resource :user do # POST /api/user post do # ユーザ作成の処理が入ります end # PUT /api/user desc 'ユーザ更新' do put do # ユーザ更新の処理が入ります end end resource :pictures do # GET /api/pictures get '/', rabl: 'pictures' do @pictures = current_user.pictures end # POST /api/pictures post '/', rabl: 'picture' do @picture = current_user.pictures.build(picture_params) @picture.image = ActionDispatch::Http::UploadedFile.new(params[:picture]) if params[:picture] @picture.save! @picture.reload end resource ':picture_id' do # PUT /api/pictures/:picture_id put '/', rabl: 'picture' do # 写真の更新処理が入ります end end end end ``` ### ビュー rablを使いビューを記述します。extendsで他のビューを入れ子に出来ます。 ```ruby:app/views/api/picture.rabl object @picture attributes :hash, :image_url child user: :owner do extends 'api/users/detail' end ``` ### モデル いつものRails。 ## 終わりに ステップバイステップで冒頭のAPIを作ろうとしましたが納期に間に合いませんでした!雰囲気を感じて貰えればと思います。 この開発方法の良さを語ります!! - コントローラが見やすい - URL構造そのまま - 複数のファイルに分散しない - JSONの応答を厳密にテストできる - わずかなデグレも見逃さない - ビューが使いまわせる - JSONのフォーマットの変更が簡単に出来る - Railsの資産を思う存分使える - Ruby愛してる 以上! [Ruby Advent Calendar 2013](http://qiita.com/advent-calendar/2013/ruby)、3日目の担当はznzさんです。私はこの記事でやっと1 Contributionなのですが、znzさんを見ると・・・凄いです。勉強させて頂こうと思います!(業務中に学んだことを効率よくアウトプットする術を身につけたい・・・) ## 謝辞 以下の記事は何度も読み返し大変お世話になりました。 - [Grape + RSpec + json_expressions で Awesome な API の受け入れテスト](http://qiita.com/milkcocoa/items/5a4656ee1fd51e86e57e) |
|
| 146位 |
|
|||
|
19:40:52 |
|
|
Gradle を実際に触ってみたときのメモ。
基本的な話は vvakame さんの [Gradle入門](http://qiita.com/vvakame/items/83366fbfa47562fafbf4) を読んでいる前提。 #環境 ##OS Windows 7 64bit ##Java 1.7.0_40 ##Gradle 1.9 #インストール ##Java 割愛 ##Gradle [ここ](http://www.gradle.org/downloads) を開いて `gradle-1.9-bin.zip` をクリックして zip をダウンロード。 zip を解凍したら、 `gradle-1.9\bin` にパスを通す。 コマンドプロンプトを開いて、 `gradle --version` を入力。 バージョンの情報が出力されたら OK。 ###プロキシの設定 ```properties:HTTPプロキシ systemProp.http.proxyHost=<プロキシホスト名> systemProp.http.proxyPort=<プロキシポート番号> systemProp.http.proxyUser=<認証ユーザ名> systemProp.http.proxyPassword=<パスワード> ``` ```properties:HTTPSプロキシ systemProp.https.proxyHost=<プロキシホスト名> systemProp.https.proxyPort=<プロキシポート番号> systemProp.https.proxyUser=<認証ユーザ名> systemProp.https.proxyPassword=<パスワード> ``` 上記設定を、以下のいずれかに記述する。 - プロジェクトのルートに配置した `gradle.properties` - Gradle ホームフォルダ(`<ユーザのホームフォルダ>\.gradle`)に配置した `gradle.properties` #簡単な Java プロジェクトを作る コマンドラインから実行して `Hello Gradle!!` と出力するだけの簡単なプロジェクトを作る。 ##build.gradle を作成する プロジェクトのルートフォルダに、 `build.gradle` という名前のテキストファイルを作成する。 内容は以下。 ```groovy:build.gradle apply plugin: 'java' ``` ##Java ソースコードを作成する 次に、 Java のソースコードを作成する。 `src\main\java` がデフォルトのソースフォルダの場所なので、その下に `sample\gradle` とフォルダを作って、 `GradleMain.java` を配置する。 `GradleMain.java` の内容は以下。 ```java:GradleMain.java package sample.gradle; public class GradleMain { public static void main(String[] args) { System.out.println("Hello Gradle!!"); } } ``` フォルダ構成は以下のようになる。 ```text:フォルダ構成 │ build.gradle └─src └─main └─java └─sample └─gradle GradleMain.java ``` ###デフォルトのフォルダ構成 ```text: src ├─main │ ├─java : 製品の Java ソースコード │ └─resources : 製品のリソース └─test ├─java : テスト Java ソースコード └─resources : テストのリソース ``` 基本のフォルダ構成は Maven と同じ感じ。 ##コンパイルする コマンドプロンプトを開いて、 `build.gradle` のあるフォルダまで移動し、以下のコマンドを実行。 ```text:Javaソースコードをコンパイルする >gradle compileJava ``` すると、以下のように `build` フォルダの下に class ファイルが生成される。 ```text: │ build.gradle │ ├─build │ └─classes │ └─main │ └─sample │ └─gradle │ GradleMain.class │ └─src └─main └─java └─sample └─gradle GradleMain.java ``` ##実行する ```bat:コンパイルで生成されたclassファイルを普通に実行する >java -cp build\classes\main sample.gradle.GradleMain Hello Gradle!! ``` ##生成したファイルを消す ```text:生成したファイルを消す >gradle clean ``` `build` フォルダが削除される。 ##タスクを複数指定する ```text:タスクを複数指定して実行する >gradle clean compileJava ``` `clean` してから `compileJava` が実行される。 #コンパイルのオプションを色々設定する ##javac のオプション `javac` コマンドで指定できるオプションは、 gralde では `build.gradle` で以下のように指定する。 ```groovy:build.gradle apply plugin: 'java' compileJava { options.encoding = 'Shift_JIS' } ``` `compileJava` の中で `options.<オプション名> = <値>` とすれば指定できる。上記例は、ソースコードのエンコーディングを指定している。 指定できるオプション名については [CompileOptions - Gradle DSL Version 1.11](http://gradle.monochromeroad.com/docs/dsl/org.gradle.api.tasks.compile.CompileOptions.html) を参照。 ##ソース・class ファイルの JDK バージョンを指定する `javac` の `-source`, `-target` に対応するオプションは以下のように指定する。 ```groovy:build.gradle apply plugin: 'java' sourceCompatibility = '1.6' // -source targetCompatibility = '1.6' // -target ``` #Gradle からアプリケーションを実行する __アプリケーションプラグイン__ を使えば、 java コマンドを叩く代わりに Gradle 経由でアプリケーションを実行できる。 まず、 `build.gradle` を以下のように記述する。 ```groovy:build.gradle apply plugin: 'application' mainClassName = 'sample.gradle.GradleMain' ``` `apply plugin: 'application'` でアプリケーションプラグインの使用を宣言し、 `mainClassName` に Main クラスの FQCN を指定する。 ※アプリケーションプラグインを使えば java プラグインも利用できるようになるので、 java プラグインの宣言は不要。 あとは、以下のように `run` タスクを実行すれば OK。 ```text:runタスクでアプリケーションを実行する >gradle run ``` ##run タスクで実行する Main クラスにコマンドライン引数を渡す コマンドライン引数を渡したい場合、単純に `gradle run` の後に引数を書き連ねてもうまくいかない。 ```text:runタスクの後ろに引数を書いてもダメ >gradle run hoge fuga ``` `hoge` と `fuga` は、実行する Main クラスの main メソッドには渡されない。 [gradle-user - Command line arguments to application?](http://gradle.1045684.n5.nabble.com/Command-line-arguments-to-application-td3999977.html) ←同じ疑問を持った人がいて、どうも書き込みを見る限り普通にコマンドライン引数のノリで引数を渡すことはできず、以下のように `build.gradle` の記述を工夫する必要があるみたい。 ```groovy:build.gradle apply plugin: 'application' mainClassName = 'sample.gradle.GradleMain' run { if (project.hasProperty('args')) { // "args" というプロパティが渡されていたら args project.args.split('\\s+') // 空白文字で split して、 run タスクの args オプションにセットする } } ``` 実行するときは、以下のようにコマンドライン引数を渡す。 ```text:runタスクにコマンドライン引数を渡す >gradle run -Pargs="hoge fuga piyo" :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :run {hoge,fuga,piyo} ``` #起動用スクリプトを自動生成する 同じくアプリケーションプラグインを使えば、作成したプログラムの起動用スクリプト(Windows と Unix 用)を自動生成できる。 起動スクリプトの生成は、 `startScripts` タスクを使用する。 ```text:起動スクリプトを生成する >gradle startScripts ``` タスクが完了すると、 `build\scripts` の下に起動用スクリプトファイルが出力されている。 デフォルトでは、 `build.gradle` のあるフォルダの名前でスクリプトファイルが出力される。 スクリプトファイルの名前は `build.gradle` で以下のように指定できる。 ```groovy:build.gradle apply plugin: 'application' mainClassName = 'sample.gradle.GradleMain' startScripts { applicationName = 'hoge' } ``` `applicationName` で指定できる。 他に指定できる値については [CreateStartScripts - Gradle DSL](http://gradle.monochromeroad.com/docs/dsl/org.gradle.api.tasks.application.CreateStartScripts.html) を参照。 #作成したアプリケーションを全部 zip にまとめて出力する 同じくアプリケーションプラグインを使えば、次のファイル類をまとめた zip ファイルを、タスク一発で生成できる。 - ソースをコンパイルして固めた jar - 依存 jar ファイル一式 - 起動スクリプト タスクの名前は `distZip`。 ```text:全部入りのzipを生成する >gradle distZip ``` タスクが完了すると `build\distributions` に zip ファイルが出力される。 解凍すると、以下のようなフォルダ構成になっている。 ```text: ├─bin : 起動スクリプトが配置されている └─lib : ソースをコンパイルして固めた jar 及び依存 jar ファイル ``` こちらも `build.gradle` で zip ファイルの名前を任意の値に変更できる。 ```groovy:build.gradle apply plugin: 'application' mainClassName = 'sample.gradle.GradleMain' distZip { baseName = 'HelloGradle' } ``` `baseName` で指定する。 他に指定できる値については [Zip - Gradle DSL Version 1.8](http://gradle.monochromeroad.com/docs/dsl/org.gradle.api.tasks.bundling.Zip.html) を参照。 Maven より圧倒的に楽だ……。 ちなみに、 tar にしたい場合は `distTar` というタスクがある。 #ソース以外の任意のファイルをアプリケーションに含める `src/main/java` および `src/main/resources` の下にあるファイルは、全て jar に固められてしまう。 そうではなく、 jar の外に出しておいて、かつアプリに含まれるようにしたいファイルが存在する場合は、 `src/dist` フォルダの下に配置する。 ```text:フォルダ構成 project/ `-src |-main/java/ `-dist/ |-hoge.txt `-fuga/ `-piyo.txt ``` ```text: > gradle installApp ``` ※`installApp` は ver 3.0 から削除され `installDist` に置き換わる予定なので注意。 ```text:タスク実行後 project/ `-build/install/ |-hoge.txt |-fuga/ | `-piyo.txt |-bin/ `-lib/ ``` - `src/dist` の下に配置したファイルやフォルダが、そのままアプリケーションのルートに配置される。 #サードパーティのライブラリ(jar)を使用する Apache Commons Lang を使った例。 ##ローカルに jar ファイルがある場合 jar ファイルが `lib\commons-lang3-3.1.jar` にある場合、 `build.gradle` に以下のように記述する。 ```groovy:build.gradle apply plugin: 'java' dependencies { compile files('lib/commons-lang3-3.1.jar') } ``` `dependencies` 内の `files` で jar を指定している。 jar ファイルの指定は、次のようにもできる。 ```groovy:ローカルのjarの指定方法 dependencies { // 個別ファイル複数指定 compile files('lib/hoge.jar', 'lib/fuga.jar') // lib フォルダ直下の jar を全て指定 compile fileTree(dir: 'lib', include: '*.jar') // lib フォルダ以下、サブフォルダも含めて全 jar ファイルを指定 compile fileTree(dir: 'lib', include: '**/*.jar') } ``` ##Maven2 のセントラルリポジトリに存在する jar を指定する場合 Maven2 のセントラルリポジトリに存在する jar を指定する場合は、 `build.gradle` を以下のように記述する。 ```groovy:build.gradle apply plugin: 'java' repositories { mavenCentral() } dependencies { compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1' } ``` `group` は `GroupId`、 `name` は `ArtifactId` を指定する。 リポジトリからダウンロードした jar ファイルは、 `%ユーザのホームフォルダ%\.gradle\caches` 以下に保存される。 ライブラリをキャッシュする場所を変更したい場合は、環境変数 `GRADLE_USER_HOME` に任意のフォルダのパスを設定すれば良い。 `dependencies` の部分は、以下のようにも書ける。 ```groovy: dependencies { compile 'org.apache.commons:commons-lang3:3.1' } ``` ##セントラルリポジトリ以外の Maven リポジトリを指定する JSONIC 1.2.0 を Seasar が提供している Maven リポジトリから落としてみる。 ```groovy:build.gradle apply plugin: 'application' mainClassName = 'sample.gradle.GradleMain' repositories { maven { url "http://maven.seasar.org/maven2" } } dependencies { compile 'net.arnx.jsonic:jsonic:1.2.0' } ``` `repositories` の中の `maven` の中の `url` に使用するリポジトリの URL を設定すれば、セントラルリポジトリ以外の Maven リポジトリも使用できるようになる。 ```java:GradleMain.java package sample.gradle; import java.util.Map; import java.util.HashMap; import net.arnx.jsonic.JSON; public class GradleMain { public static void main(String[] args) { Map<String, Object> map = new HashMap<>(); map.put("hoge", "HOGE"); map.put("fuga", "FUGA"); map.put("piyo", "PIYO"); System.out.println(JSON.encode(map)); } } ``` ```text:実行 >gradle clean run :clean :compileJava :processResources UP-TO-DATE :classes :run {"fuga":"FUGA","hoge":"HOGE","piyo":"PIYO"} BUILD SUCCESSFUL ``` #Javadoc を生成する ```text:Javadocを生成するタスク >gradle javadoc ``` `build\docs\javadoc` の下に Javadoc が生成される。 ```java:GradleMain.java package sample.gradle; /** * メインクラスです。 */ public class GradleMain { /** * メインメソッドです。 * @param args コマンドライン引数 */ public static void main(String[] args) { System.out.println("Hello Gradle!!"); } } ``` __生成された Javadoc__  #JUnit を実行する JUnit4 + jMockit でテストコードを作成して実行してみる。 ```java:src/main/java/sample/gradle/GradleMain.java package sample.gradle; public class GradleMain { public String method() { return "original"; } } ``` ```java:src/test/java/sample/gradle/GradleMainTest.java package sample.gradle; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; import mockit.Mocked; import mockit.Expectations; public class GradleMainTest { @Mocked private GradleMain gradleMain; @Test public void test() { // setup new Expectations() {{ gradleMain.method(); result = "stub"; }}; // exercise String actual = gradleMain.method(); // verfiy assertThat(actual, is("stub")); } } ``` ```groovy:build.gradle apply plugin: 'java' repositories { mavenCentral() } dependencies { testCompile 'com.googlecode.jmockit:jmockit:1.4' testCompile 'junit:junit:4.11' testCompile 'org.hamcrest:hamcrest-library:1.3' } ``` JUnit とかはテストでだけ使うので `testCompile` を指定している。 ```text:テストを実行する >gradle test :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava :processTestResources UP-TO-DATE :testClasses :test BUILD SUCCESSFUL ``` デフォルトは、全ての class ファイルがチェックされて、テストクラスを見つけたら全部実行される。 ##テスト結果を HTML で確認する `test` タスクが完了すると `build\reports\tests` の下にテスト結果が HTML で出力される。  __2013/12/22 修正__ デフォルトでは HTML は出力されず、 `reports.html.enabled = true` にする必要があると記述していたが、よく見たらデフォルトは出力されるようになっていたので、特に設定なしで HTML 出力は可能でした。すみません。 #War を作成する __WAR プラグイン__ を使って war ファイルをビルドし、 Tomcat にデプロイしてみる。 サーブレットの実装は以下。 ```java:src/main/java/sample/gradle/GradleServlet.java package sample.gradle; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.annotation.WebServlet; import java.io.IOException; @WebServlet("/servlet") public class GradleServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { response.getWriter().println("Hello Gradle War Plugin!!"); } } ``` デフォルトでは、 `src/main/webapp` 以下が、ウェブアプリケーションのソースフォルダになるので、その直下に `index.html` を配置する。 内容は以下。 ```html:src/main/webapp/index.html <html> <head> <title>index</title> </head> <body> <h1>Index</h1> <a href="servlet">GradleServlet</a> </body> </html> ``` 最後に、 `build.gradle` を以下のように記述する。 ```groovy:build.gradle apply plugin: 'war' repositories { mavenCentral() } dependencies { providedCompile 'javax.servlet:javax.servlet-api:3.1.0' } war { archiveName = 'hello.war' } ``` 先頭で WAR プラグインの利用を宣言。 servlet の実装は実行環境が提供してくれるので、war ファイル内に servlet の jar を含めないように `providedCompile` を指定している。 ※`providedCompile` は WAR プラグインを使用している時にだけ利用可能。 `war` の `archiveName` は、生成する war ファイルの名前を指定している。これを指定しない場合は、デフォルトでは `build.gradle` の存在するフォルダの名前が war ファイルのベースに利用される。 war の生成は、 `war` タスクで実行できる。 ```text:warタスクを実行してwarファイルを生成する >gradle war ``` タスクが完了すると、 `build\libs` の下に war ファイルが生成される。 これを Tomcat に配備してブラウザからアクセスする。   #マルチプロジェクトを作成する ##基本 ```text:フォルダ構成 │ build.gradle │ settings.gradle │ ├─hoge │ └─src │ └─main │ └─java │ └─sample │ └─gradle │ Hoge.java │ ├─fuga │ └─src │ └─main │ └─java │ └─sample │ └─gradle │ Fuga.java │ └─src └─main └─java └─sample └─gradle Main.java ``` ルートフォルダと `hoge` フォルダ、 `fuga` フォルダがそれぞれプロジェクトとなっている。 `hoge` と `fuga` がサブプロジェクトフォルダであることを明示するためには、ルートフォルダに `settings.gradle` を配置し、以下のように記述する。 ```groovy:settings.gradle include 'hoge', 'fuga' ``` `hoge` や `fuga` がもっと深い階層に存在する場合は、次のように `:` (コロン)繋ぎで記述する。 ```groovy:settings.gradle include 'subprojects:gui:hoge', 'subprojects:web:fuga' ``` 区切り文字が `/` ではなく `:` なのには以下の理由がある。 > 以前Gradleでは、プロジェクトのパスセパレータとして「/」を使用していました。しかし、このセパレータ文字はdirectoryタスク(「ディレクトリの作成 」参照)が導入されたときに廃止されています。directoryタスクのタスク名に、「/」が含まれるためです。 [第56章 マルチプロジェクトのビルド - 56.5. プロジェクトとタスクのパス](http://gradle.monochromeroad.com/docs/userguide/multi_project_builds.html#sec:project_and_task_paths) 各プロジェクトの設定は、以下のようにルートフォルダの `build.gradle` にまとめて書くことができる。 ```groovy:build.gradle allprojects { apply plugin: 'java' } ``` `allprojects` の中には、ルートのプロジェクトも含めた、全プロジェクトに共通の設定を記述することができる。 ルートフォルダでタスクを実行すれば、各プロジェクトごとにタスクがそれぞれ実行される。 ```text:ルートフォルダでcompileJavaタスクを実行 >gradle compileJava >tree │ build.gradle │ settings.gradle │ ├─hoge │ ├─build │ │ └─classes │ │ └─main │ │ └─sample │ │ └─gradle │ │ Hoge.class │ │ │ └─src │ └─main │ └─java │ └─sample │ └─gradle │ Hoge.java │ ├─fuga │ ├─build │ │ └─classes │ │ └─main │ │ └─sample │ │ └─gradle │ │ Fuga.class │ │ │ └─src │ └─main │ └─java │ └─sample │ └─gradle │ Fuga.java │ ├─build │ └─classes │ └─main │ └─sample │ └─gradle │ Main.class │ └─src └─main └─java └─sample └─gradle Main.java ``` ##サブプロジェクトだけにまとめて設定をする ルートプロジェクト以外の全サブプロジェクトに設定を適用させたい場合は、 `subprojects` に設定を記述する。 ```groovy:build.gradle allprojects { apply plugin: 'java' } subprojects { apply plugin: 'war' } ``` 上記設定で `war` タスクを実行すると、 `hoge` と `fuga` プロジェクトだけが war にアーカイブされる。 ```text:subprojectsにだけWarプラグインを適用させた場合 >gradle war >tree /f │ build.gradle │ settings.gradle │ ├─hoge │ ├─build │ │ └─libs │ │ hoge.war │ │ │ └─src │ └─main │ └─java │ └─sample │ └─gradle │ Hoge.java │ ├─fuga │ ├─build │ │ └─libs │ │ fuga.war │ │ │ └─src │ └─main │ └─java │ └─sample │ └─gradle │ Fuga.java │ └─src └─main └─java └─sample └─gradle Main.java ``` ##サブプロジェクト毎に個別の設定をする さらにプロジェクト毎に個別の設定をしたい場合は、以下のいずれかの方法が利用できる。 - `project(String)` メソッドを使用する - サブプロジェクトのフォルダごとに `build.gradle` を配置する ###project(String) メソッドを使用する ```groovy:build.gradle allprojects { apply plugin: 'application' mainClassName = 'sample.gradle.Main' } project(':hoge') { mainClassName = 'sample.gradle.Hoge' } project(':fuga') { mainClassName = 'sample.gradle.Fuga' } ``` 全プロジェクトにアプリケーションプラグインを適用し、 Main クラスとして `sample.gradle.Main` を設定している。 ただし、 Main クラスについては、 `project(String)` でプロジェクトごとに Main クラスを再定義している。 ```text:runタスクの実行結果 >gradle run :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :run Main :fuga:compileJava UP-TO-DATE :fuga:processResources UP-TO-DATE :fuga:classes UP-TO-DATE :fuga:run Fuga :hoge:compileJava UP-TO-DATE :hoge:processResources UP-TO-DATE :hoge:classes UP-TO-DATE :hoge:run Hoge BUILD SUCCESSFUL ``` ※各クラスは、自分のクラス名を出力する実装になっている(Hoge クラスは Hoge と出力する)。 ###サブプロジェクトのフォルダごとに build.gradle を配置する ```text:フォルダ構成 │ build.gradle │ settings.gradle │ ├─hoge │ │ build.gradle │ │ │ └─src │ └─main │ └─java │ └─sample │ └─gradle │ Hoge.java │ ├─fuga │ │ build.gradle │ │ │ └─src │ └─main │ └─java │ └─sample │ └─gradle │ Fuga.java │ └─src └─main └─jaga └─sample └─gradle Main.java ``` ルートの `build.gradle` の内容は以下。 ```groovy:ルートのbuild.gradle allprojects { apply plugin: 'application' mainClassName = 'sample.gradle.Main' } ``` 各サブプロジェクトに配置している `build.gradle` の内容は以下。 ```groovy:hogeプロジェクトのbuild.gradle mainClassName = 'sample.gradle.Hoge' ``` ```groovy:fugaプロジェクトのbuild.gradle mainClassName = 'sample.gradle.Fuga' ``` ```text:runタスクの実行 >gradle run :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :run Main :fuga:compileJava UP-TO-DATE :fuga:processResources UP-TO-DATE :fuga:classes UP-TO-DATE :fuga:run Fuga :hoge:compileJava UP-TO-DATE :hoge:processResources UP-TO-DATE :hoge:classes UP-TO-DATE :hoge:run Hoge BUILD SUCCESSFUL ``` ##特定のサブフォルダ以下のサブプロジェクトにだけ設定を適用する ```text:フォルダ構成 ├─core └─web ├─hoge └─fuga ``` ルートフォルダに `core` プロジェクトがあり、 `web` フォルダの下に `hoge` と `fuga` プロジェクトが存在する。 `web` フォルダ以下に存在するサブプロジェクトにだけ設定を適用したい場合は、次のように `build.gradle` を記述する。 ```groovy:build.gradle subprojects { apply plugin: 'java' repositories { mavenCentral() } } project(':web') { subprojects { apply plugin: 'war' dependencies { providedCompile 'javax.servlet:javax.servlet-api:3.1.0' } } } ``` `web` フォルダも実は __プロジェクトの1つ__ なので、その中でまた `subprojects` を使って設定を記述すれば、 `web` プロジェクトのサブプロジェクト全体に設定を適用させることができる。 ##特定のプロジェクトだけでタスクを実行する 前述したとおり、ルートフォルダでタスクを実行すると全プロジェクトで指定したタスクが実行される。 特定のプロジェクトでだけタスクを実行したい場合は、以下のようにタスクを指定する。 ```text:特定のプロジェクトだけでタスクを実行する >gradle :hoge:compileJava ``` `:<プロジェクトへのパス>:<実行するタスク>` と指定する。 複数指定する場合は以下のようにする。 ```text:プロジェクト指定のタスクを複数実行する >gradle :hoge:clean :hoge:compileJava ``` ##プロジェクト間の依存関係を設定する ```text:フォルダ構成 │ build.gradle │ settings.gradle │ ├─hoge │ └─src │ └─main │ └─java │ └─sample │ └─gradle │ Hoge.java │ ├─fuga │ └─src │ └─main │ └─java │ └─sample │ └─gradle │ Fuga.java │ └─piyo └─src └─main └─java └─sample └─gradle Piyo.java ``` ```java:Hoge.java package sample.gradle; public class Hoge { public static void main(String[] args) { Fuga.fuga("Hoge"); Piyo.piyo("Hoge"); } } ``` ```java:Fuga.java package sample.gradle; public class Fuga { public static void fuga(String caller) { System.out.println(caller + " calls Fuga#fuga() method."); Piyo.piyo("Fuga"); } } ``` ```java:Piyo.java package sample.gradle; public class Piyo { public static void piyo(String caller) { System.out.println(caller + " calls Piyo#piyo() method."); } } ``` ```groovy:settings.gradle include 'hoge', 'piyo', 'fuga' ``` - `hoge` プロジェクトは、 `fuga` と `piyo` の2つのプロジェクトに依存している - `fuga` プロジェクトは、 `piyo` プロジェクトに依存している 上記のようにプロジェクト間に依存関係がある場合は、以下のように `build.gradle` を記述する。 ```groovy:build.gradle subprojects { apply plugin: 'application' } project(':hoge') { dependencies { compile project(':fuga'), project(':piyo') } mainClassName = 'sample.gradle.Hoge' } project(':fuga') { dependencies { compile project(':piyo') } } ``` 依存関係は各プロジェクト毎に `dependencies` 内で定義する。 ※`settings.gradle` は、プロジェクトの依存関係に関係なく、好きな順番で記述して問題ない。 ```text:Hoge#main(String[])を実行 >gradle :hoge:run :piyo:compileJava :piyo:processResources UP-TO-DATE :piyo:classes :piyo:jar :fuga:compileJava :fuga:processResources UP-TO-DATE :fuga:classes :fuga:jar :hoge:compileJava :hoge:processResources UP-TO-DATE :hoge:classes :hoge:run Hoge calls Fuga#fuga() method. Fuga calls Piyo#piyo() method. Hoge calls Piyo#piyo() method. BUILD SUCCESSFUL ``` #Eclipse で Gradle を利用する Eclipse のバージョンは Juno を前提とする。 ##プラグインのインストール ###Groovy-Eclipse for Juno `build.gradle` とかの編集は groovy エディタがあった方がいいので、 `Groovy-Eclipse for Juno` を入れておく。 マーケットプレイスからプラグインをインストールする。 ###Gradle Integration for Eclipse マーケットプレイスから、 `Gradle Integration for Eclipse` をインストールする。 デフォルトだと `Spring Dashboard` とかをインストールしようとするけど、自分の環境?では必要なファイルが見つからない的なエラーが出てインストールできなかった。 `(optional)` って書いてるので、 `Spring Dashboard` と `Spring UAA Integration` はチェックを外してインストールした。 インストールが完了したら Eclipse を再起動。 このプラグインには 2013/12/07 現在 Gradle 1.5 がバンドルされているみたいで、デフォルトではその Gradle を使おうとするっぽい。 それは困るので、 Gradle の切り換えを行う。 [ウィンドウ] → [設定] と選択し、 [Gradle] を開く。 次に [Gradle Distribution] で [フォルダー]を選択し、既にインストールしている Gradle 1.9 のフォルダのパスを設定する。  ##Gradle プロジェクトを作る [ファイル] → [新規] → [その他] → [Gradle] → [Gradle Project] を選択。 [プロジェクト名] を入力して、 [Sample project] でプロジェクトのひな形を選択する。 とりあえず、 [Java Quickstart] を選択。 作成されたプロジェクトは以下。  なんか色々生成されてる。 ##既存の Gradle プロジェクトを Eclipse にインポートする ```text:フォルダ構成 │ build.gradle │ └─src └─main └─java └─sample └─gradle GradleMain.java ``` ```groovy:build.gradle apply plugin: 'application' mainClassName = 'sample.gradle.GradleMain' repositories { mavenCentral() } dependencies { compile 'org.apache.commons:commons-lang3:3.1' } ``` 上記プロジェクトを Eclipse にインポートする。 Eclipse 上で [ファイル] → [インポート] を選択。 [Gradle] → [Gradle Project] を選択して次へ。 [ルート・フォルダー] に上記プロジェクトのフォルダパスを入力し、 [Build Model] をクリックする。 するとプロジェクトが読み込まれるので、 [完了] をクリックする。   依存ライブラリは Eclipse の Gradle プラグインが管理してくれているようで、 `.classpath` ファイルは以下のようになっている。 ```xml:.classpath <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src/main/java"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry exported="true" kind="con" path="org.springsource.ide.eclipse.gradle.classpathcontainer"/> <classpathentry kind="output" path="bin"/> </classpath> ``` Gradle の Eclipse プラグインを使って生成した Eclipse プロジェクトだと、 `.classpath` に jar のフルパスが記述されてて開発者毎の環境に依存しないように工夫をしないといけないけど、この方法だと問題なさそう。 ##Web アプリケーションのプロジェクトを WTP としてインポートする こっち参照→「[Java - EclipseのGradleプラグイン使ってるときにprovidedCompileで指定しているjarもWEB-INF/libに配備されて困ったときの話 - Qiita [キータ]](http://qiita.com/opengl-8080/items/a00fa878fdfc9f35ae04)」 ##Eclipse プロジェクト上での JRE を指定する 特に指定しない場合、ワークスペースのデフォルトの JRE を使うようになる。 任意の JRE を使用したい場合は、以下のように build.gradle を設定する。 ```build.gradle(一部) ext.jreName = 'jdk1.7.0_51' eclipse { classpath { containers.clear() containers.add("org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/${jreName}") } } ``` `jreName` は、 Eclipse に登録している「インストール済み JRE」の名前を指定する。 __参考__ - [java - Specifiy JRE Container with gradle eclipse plugin - Stack Overflow](http://stackoverflow.com/questions/11901737/specifiy-jre-container-with-gradle-eclipse-plugin) #プロジェクトの依存関係を確認する ##dependencies タスクで確認する ```groovy:build.gradle apply plugin: 'java' repositories { mavenCentral() } dependencies { compile 'commons-dbcp:commons-dbcp:1.4' compile 'com.google.inject:guice:3.0' testCompile 'junit:junit:4.11' } ``` コンパイル時の依存ライブラリに Apache Commons DBCP と Google Guice を、テストコンパイル時の依存ライブラリに JUnit をとりあえず設定している。 この状態で `dependencies` タスクを実行すると、各タスク毎での依存ライブラリの様子が確認できる。 ```text: >gradle dependencies (中略) compile - Compile classpath for source set 'main'. +--- commons-dbcp:commons-dbcp:1.4 | \--- commons-pool:commons-pool:1.5.4 \--- com.google.inject:guice:3.0 +--- javax.inject:javax.inject:1 +--- aopalliance:aopalliance:1.0 \--- org.sonatype.sisu.inject:cglib:2.2.1-v20090111 \--- asm:asm:3.1 (中略) testCompile - Compile classpath for source set 'test'. +--- commons-dbcp:commons-dbcp:1.4 | \--- commons-pool:commons-pool:1.5.4 +--- com.google.inject:guice:3.0 | +--- javax.inject:javax.inject:1 | +--- aopalliance:aopalliance:1.0 | \--- org.sonatype.sisu.inject:cglib:2.2.1-v20090111 | \--- asm:asm:3.1 \--- junit:junit:4.11 \--- org.hamcrest:hamcrest-core:1.3 (中略) BUILD SUCCESSFUL Total time: 9.731 secs ``` ##レポートプラグインを使って出力する ```groovy:build.gradle apply plugin: 'java' apply plugin: 'project-report' repositories { mavenCentral() } dependencies { compile 'commons-dbcp:commons-dbcp:1.4' compile 'com.google.inject:guice:3.0' testCompile 'junit:junit:4.11' } ``` `apply plugin: 'project-report'` を追加すると、レポートプラグインが使える。 ###dependencyReport タスク ```text:dependencyReportタスクの実行 >gradle dependencyReport ``` `build\reports\project` の下に `dependencies.txt` というファイルが出力される。 内容は、 `dependencies` タスクを実行したのと同じ。 ###htmlDependencyReport タスク ```text:htmlDependencyReportタスクの実行 >gradle htmlDependencyReport ``` `build\reports\project\dependencies` の下に色々とファイルが出力されるので、 index.html を開くと HTML 形式のレポートが見れる。   ###htmlDependencyReport タスク(マルチプロジェクトの場合) マルチプロジェクトで何も設定せずに `htmlDependencyReport` タスクを実行すると、サブプロジェクト毎に HTML レポートが出力されてしまう。 1つの HTML レポートに、すべてのサブプロジェクトの依存関係を出力したい場合は、次のように `build,gradle` を記述する。 ```groovy:build.gradle allprojects { apply plugin: 'java' apply plugin: 'project-report' repositories { mavenCentral() } dependencies { testCompile 'junit:junit:4.11' } } project(':hoge') { dependencies { compile 'commons-dbcp:commons-dbcp:1.4' } } project(':fuga') { dependencies { compile 'com.google.inject:guice:3.0' } } htmlDependencyReport { projects = project.allprojects } ``` `htmlDependencyReport ` で `projects = project.allprojects` を指定すれば、以下のようにルートの HTML レポートにすべてのサブプロジェクトのレポートをまとめて出力できる。   ただし、 `projects = project.allprojects` という指定方法は今後の Gradle のバージョンでは変更になる可能性があるらしい(2013/12/19 現在)。 > Note: This class is incubating and may change in a future version of Gradle. [HtmlDependencyReportTask - Gradle DSL Version 1.11-20131203060736+0000](http://gradle.monochromeroad.com/docs/dsl/org.gradle.api.reporting.dependencies.HtmlDependencyReportTask.html) #プロパティファイルを使う `build.gradle` があるフォルダに `gradle.properties` という名前でプロパティファイルを配置しておけば、プロパティファイルに宣言したキーの名前で通常の変数と同じように使えるようになる。 ```properties:gradle.properties hoge=HOGE ``` ```groovy:build.gradle task sample << { println "hoge=${hoge}" } ``` ```text:実行 >gradle :sample :sample hoge=HOGE BUILD SUCCESSFUL Total time: 2.566 secs ``` でも、せっかく Groovy が使えるので、後述の `ConfigSlurper` を使った方がいい気がする。 #ConfigSlurper を使う 使い方自体は普通に [Groovy で使う](http://qiita.com/opengl-8080/items/b260e4f2df75901239c7#1-11) のと同じ。 唯一気をつけないと行けないのは、 __読み込んだコンフィグ情報をトップレベルの変数で宣言してはいけない__ 、という点。 ```groovy:config.gradle hoge { name = 'HOGE' fuga = [1, 2, 3] piyo { value = false } } ``` ```groovy:build.gradle config = new ConfigSlurper().parse(new File('config.gradle').toURL()) task config << { println config } ``` ```text:実行結果 >gradle config Creating properties on demand (a.k.a. dynamic properties) has been deprecated and is scheduled to be removed in Gradle 2.0. Please read http://gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html for information on the replacement for dynamic properties. Deprecated dynamic property: "config" on "root project 'gradle'", value: "{hoge={name=HOGE, fuga...". :config {hoge={name=HOGE, fuga=[1, 2, 3], piyo={value=false}}} BUILD SUCCESSFUL Total time: 2.701 secs ``` タスクは成功するが、ガッツリ警告が表示される。 理由は以下。 > Gradle 1.0 milestone 9より、拡張プロパティを追加する際にはextを使用することが強く推奨されるようになっていますが、まだ強制はされません。 そのため存在しないプロパティに値を設定してもGradleの実行は失敗せず、警告が表示されます。 - [13.4.2. 拡張プロパティ | 第13章 ビルドスクリプトの記述](http://gradle.monochromeroad.com/docs/userguide/writing_build_scripts.html#sec:extra_properties) ということで、拡張プロパティをセットするときは `ext` に追加してあげる。 ```build.gradle ext.config = new ConfigSlurper().parse(new File('config.gradle').toURL()) task config << { println config } ``` ```text:実行結果 >gradle config :config {hoge={name=HOGE, fuga=[1, 2, 3], piyo={value=false}}} BUILD SUCCESSFUL Total time: 2.782 secs ``` #Ant タスクを実行する ```groovy:build.gradle task ant << { ant.mkdir dir: 'dir' ant.echo message: 'ant echo', file: 'ant.txt' ant.copy(todir: 'dir') { fileset file: 'ant.txt' } } ``` ```bat:実行 >dir /b build.gradle >gradle :ant :ant BUILD SUCCESSFUL Total time: 2.825 secs >tree /f │ ant.txt │ build.gradle │ └─dir ant.txt ``` - `ant` という名前の変数に ant のタスクと同じ名前のメソッドが定義されているので、それを使う。 - 属性は `<属性名>: <値>` という形式でタスクメソッドの引数に渡す。 - ネストされる要素は、タスクメソッドにクロージャーで渡す。 以下のようにもまとめて書くこともできる。 ```groovy: task ant << { ant { mkdir dir: 'dir' echo message: 'ant echo', file: 'ant.txt' copy(todir: 'dir') { fileset file: 'ant.txt' } } } ``` #Gradle Wrapper を使う Gradle Wrapper(ラッパー)を有効にすると、ビルドに必要な Gradle をコマンド1つで自動インストールできるようになる。 Gradle のバージョンを、開発者ごとに揃えることができる。 ##Gradle Wrapper を有効にする ```groovy:build.gradle task wrapper(type: Wrapper) { gradleVersion = '1.11' } ``` `build.gradle` に、上記のようにタスクを追加する。 そして、このタスクを実行する。 ```text: > gradle wrapper :wrapper BUILD SUCCESSFUL Total time: 4.854 secs ``` すると、以下のようにファイルが追加される。 ```text:追加されるファイル |-gradle/ | `-wrapper/ | |-gradle-wrapper.jar | `-gradle-wrapper.properties |-gradlew `-gradlew.bat ``` これらのファイルは、全てバージョン管理システムに追加する。 ##Gradle Wrapper を実行する 各開発者は、プロジェクトをバージョン管理システムからチェックアウトしてきたら、ルートにある `gradlew.bat` または `gradlew` を実行する。 すると、 Gradle が自動でインストールされる。 タスクは、 `> gradlew <タスク>` で実行することができる。 #参考 - [Gradle入門 - Qiita [キータ]](http://qiita.com/vvakame/items/83366fbfa47562fafbf4) - [Gradle 日本語ドキュメント](http://gradle.monochromeroad.com/docs/) - [Gradle DSL Version 1.9](http://www.gradle.org/docs/current/dsl/) - [Gradle API 1.9](http://www.gradle.org/docs/current/javadoc/) - [Gradle Goodness: Changing the Gradle User Home Directory - Messages from mrhaki](http://mrhaki.blogspot.jp/2010/09/gradle-goodness-changing-gradle-user.html) |
|
| 147位 |
|
|||
|
21:04:08 |
(ever sense, Inc. 所属) |
|
```
Failed to mount folders in Linux guest. This is usually beacuse the "vboxsf" file system is not available. Please verify that the guest additions are properly installed in the guest and can work properly. The command attempted was: mount -t vboxsf -o uid=`id -u vagrant`,gid=`getent group apache | cut -d: -f3`,dmode=777,fmode=777 /vagrant /vagrant mount -t vboxsf -o uid=`id -u vagrant`,gid=`id -g apache`,dmode=777,fmode=777 /vagrant /vagrant ``` こんなエラー。 #解決方法 vboxをリビルドする必要があるようです。 まずsshで入ります ``` vagrant ssh ``` リビルド実行します。 ``` sudo /etc/init.d/vboxadd setup Removing existing VirtualBox non-DKMS kernel modules [ OK ] Building the VirtualBox Guest Additions kernel modules Building the main Guest Additions module [ OK ] Building the shared folder support module [ OK ] Building the OpenGL support module [失敗] (Look at /var/log/vboxadd-install.log to find out what went wrong) Doing non-kernel setup of the Guest Additions [ OK ] ``` 一旦macのコンソールに戻ります。 ``` exit ``` vagrantを再起動します。 ``` vagrant halt vagrant up ``` |
|
| 148位 |
|
|||
|
10:35:13 |
|
|
checkboxやradioのチェック状態を調べる際にはattrではなくpropを使うのが良い。
attrでも取れないこともないですが、propで取得する方が処理が早いです。 特にIEの場合、inputに対するdisabledの処理がものすごく重く、attrでdisabledやcheckedの処理を沢山していると、無駄に最悪な感じで負荷がかかります。 ##attrとpropの取得の違い## またこの2つは、同じ値を取得してるようで異なる値を取得するので注意。 例えば ``` //チェックした値を取得するよー $(":checkbox").click(function() { alert($(this).prop('checked')); alert($(this).attr('checked')); }); ``` の場合、 チェック時 `prop true` `attr checked` 非チェック時 `prop false` `attr undefined` という全然違う値が返ってきます。 ##なぜ解釈が違うのか## attrは純粋に **属性における値** を取得するので、この場合は、 >checkされてない >↓ >checkedという『属性』がない! >↓ >**undefined** という認識をしています。 一方でpropは、checkedという **プロパティの真偽** を取得してくれます。なので、 >checkされてない >↓ >checkedという『プロパティ』はfalse >↓ >**falseだよー** という解釈をします。 以前のバージョンではプロパティもなんとなく取得していた雰囲気のattrですが、propが一人前に育ってきたので本格的に属性の取得に専念した印象があります。…個人的にですが。 最近のjQueryのバージョンだとこの辺が厳密になってきている印象を受けます。古いプラグインだとjQueryによるattrの処理の解釈が違うばっかりに動作しないなどがあったりするので、プラグインがバージョンによって動かない際にはその辺なども見てみるといいかもしれないです。 ##inputはprop!## HTMLの属性を取得する場合はattr、input関連の値を取得する場合はprop、と考えておくといいと思います。 また、inputのプロパティを取得する以外に、hrefを取得する際なども、propではURLの値をまるごと取得してくれたり、色々便利に使えます。 ##propのサンプル## 以下、disabledのサンプルです。参考までにあげときます。 ###チェックボックスがチェックされたら、inputを入力できるようにする### **jQuery** ``` $('#test_chk').on('change',function(){ var chk = $(this).prop('checked'), obj = $('#test_input'); (chk) ? obj.prop('disabled',false) : obj.prop('disabled',true) ; }); ``` **HTML** ``` <input name="" type="checkbox" value="" id="test_chk" /> <label for="test_chk">chk1</label> <input name="" type="text" id="test_input" disabled="disabled" /> ``` ###セレクトバージョン(indexが2のやつ(上から3番目)を選択したらdisabledを解除)### **jQuery** ``` $('#test_select').on('change',function(){ var chk = $(this).prop('selectedIndex'), obj = $('#test_input'); (chk==2) ? obj.prop('disabled',false) : obj.prop('disabled',true) ; }); ``` **HTML** ``` <select name="" id="test_select"> <option>00000</option> <option>11111</option> <option>ここが選択されたらinputのdisabledを解除するよー</option> </select> <input name="" type="text" id="test_input" disabled="disabled" /> ``` |
|
| 149位 |
|
|||
|
02:00:02 |
(メルカリ/ソウゾウ 所属) |
|
このTopicはWeb ApplicationのfrontendのProjectを管理するTopicの1つ目です。
1. [node.jsのversionを管理するためにnodebrewを利用する](http://qiita.com/sinmetal/items/154e81823f386279b33c "node.jsのversionを管理するためにnodebrewを利用する") 1. [npmでnode.jsのpackageを管理する](http://qiita.com/sinmetal/items/395edf1d195382cfd8bc "npmでnode.jsのpackageを管理する") 1. [grunt.jsを使って、frontendのビルドを行う](http://qiita.com/sinmetal/items/ab34c02fe26e994876a6 "grunt.jsを使って、frontendのビルドを行う") ##nodebrewとは? [nodebrew](https://github.com/hokaccha/nodebrew "nodebrew")は、node.jsを自分のマシン内でversion管理するためのtoolです。 最近は、Front側の開発環境として、node.jsを使うのが普通になってきました。 npm使うだけでも、node.js必要なのでWebの開発者のマシンにはほぼnode.jsが入っている感じです。 ただ、Projectごとにnode.jsのversionが違ったりすると、いちいち入れなおすのは面倒です。 そこを解決してくれるのが、nodebrewです! ##nodebrewインストール前の確認 まず、nodebrewを入れる前に、既にnode.jsがインストールされている場合削除します。 node.jsが入っているかどうかはnodeが反応するかどうかを見れば良いですね。 ```bash:command $ node ``` これで反応が無ければOKです。 反応があった場合は、node.jsがinstallされているので、削除しましょう。 ただ、すんなり削除できるものは用意されていません。 Githubにnode.jsをuninstallするためのshを作ってくれている人がいますので、使わせてもらいましょう。 後、そのshが削除してくれないdirも削除します。 ```bash:command $ curl -o uninstall-node.sh https://gist.githubusercontent.com/nicerobot/2697848/raw/uninstall-node.sh $ chmod u+x uninstall-node.sh $ ./uninstall-node.sh $ rm uninstall-node.sh $ sudo rm -rf /usr/local/include/node $ sudo rm -rf /usr/local/lib/dtrace $ rm -rf ~/.node-gyp $ rm -rf ~/.npm $ rm -rf ~/.sourcemint ``` これでnode.jsが削除されました。 ##nodebrewをインストールする インストールするのは、簡単です。 ほとんど、公式の[INSTALL](https://github.com/hokaccha/nodebrew "INSTALL")の通りです。 ```bash:command $ curl -L git.io/nodebrew | perl - setup ... ======================================== Add path: export PATH=$HOME/.nodebrew/current/bin:$PATH ======================================== ``` nodebrewは~/.nodebrewにインストールされます。 そこにpathを通してねと出てくるので、追加しましょう。 僕は~/.bash_profileを使っているので、そこに追加しました。 後は.bash_profileを読みなおして、nodebrew commandが動けばインストールは完了です。 ```bash:command $ source ~/.bash_profile $ nodebrew help ``` ##node.jsをインストール nodebrewがインストールできたので、早速node.jsをインストールしましょう。 まずはインストールできるnode.jsのversionを確認します。 ```bash:command $ nodebrew ls-remote ``` ずら〜っとversionが出てきます。 現時点での[Stable](http://blog.nodejs.org/ "Stable")になっている最新のversionはv0.10.12なので、それをインストールしましょう ```bash:command $ nodebrew install-binary v0.10.12 ``` インストールはすぐに終わります。 インストールされているversionの一覧もcommandで確認できます。 ```bash:command $ nodebrew ls nodebrew ls v0.10.12 current: none ``` currentに出てくるのが、現在利用中のversionです。 まだ、v0.10.12をインストールしただけで利用するように設定してないので、noneになっています。 後は、使いたいバージョンをcurrentにすれば、完了です。 ```bash:command $ nodebrew use v0.10.12 $ node -v v0.10.12 ``` 簡単、便利ですね! ##migrate-packageでグローバルなモジュールを移行する ```bash:command $ nodebrew migrate-package v0.10.11 ``` migrate-packageはnpm install -gでグローバル環境にインストールされているモジュールを、カレントバージョンにも適用してくれるものです。 上記ではv0.10.11でグローバルにインストールされているモジュールを、カレントのバージョンにもnpm install -gでインストールしてくれます。 これで、node.js本体以外も簡単に移行出来ますね! ##参考にしたサイト [Node.jsをアンインストールする](http://sushichop.blogspot.jp/2013/04/nodejs.html "Node.jsをアンインストールする") [node.js 入れるなら nodebrew が超簡単](http://tacamy.hatenablog.com/entry/2013/02/10/141434 "node.js 入れるなら nodebrew が超簡単") [nodebrewでバイナリからインストールできるようにした](http://webtech-walker.com/archive/2012/12/nodebrew_update.html "nodebrewでバイナリからインストールできるようにした") |
|
| 150位 |
|
|||
|
01:27:42 |
(Wantedly 所属) |
|
* 気がついたら121GBすべて使い切ってしまってた。 * 内訳を見ても、その他の項目がほとんど。。どこを削除すればいいのだろうという人は以下を試してみてください * 削除してみたら20GB以上スペースができた!!! * (問題ないと思いますが、心配な方は、いきなりrmせずにファイル名変更しながら試したほうがいいかもしれないです。。) ### iPhoneのバックアップを削除する -> MacBookAirのハードディスクに保存するのはないかな -> これは最初に昔に試していた -> バックアップをとらない設定も忘れずに -> これだけで20GBくらい軽くなった ### DeveloperのArchiveの履歴を削除する * リリースやテストを何度もしていたら自然に溜まってくる。 * いらないし、削除していいのでは! * (追記) クラッシュログをシンボルつきのログに変換する.dSYMというファイルも消えるので、気をつけて削除してください。 * https://developer.apple.com/library/iOS/technotes/tn2151/_index.html * crashlyticsやtestflightでは、自動的にdSYMのファイルをアップロードしていて、ログを見やすくしているそうです。 ``` sudo rm -rf ~/Library/Developer/Xcode/Archives ``` -> これで4GBくらい軽くなった ### DerivedDataをすべて削除 * 過去に起動したアプリも重なるとすごい量になっていた * ここはシミュレーターに過去に入れたアプリが入っている * 過去のバージョンのシミュレータのアプリとかもここに入ってるみたい * いったん削除して使うものはまた勝手に作られる * 削除していいのでは! ``` sudo rm -rf ~/Library/Developer/Xcode/DerivedData ``` -> これで11GBくらい軽くなった ### iOS DeviceSupport * iOSのDeviceが何種類も入っている。 * 6系とか7系の古いversionなど使わないものは削除していいのでは! ``` sudo rm -rf ~/Library/Developer/Xcode/iOS\ DeviceSupport/6.0* ``` -> 1GBくらい軽くなった ### ~/Library/Logs/iOS Simulator * 6GBくらいあった * 所詮ログだし、削除したらいいのでは! ``` sudo rm -rf ~/Library/Logs/iOS\ Simulator ``` ### ~/Library/Caches * 1.3GBくらいになっていた * 所詮キャッシュだし、削除したらいいのでは! ``` sudo rm -rf ~/Library/Caches ``` |
|
| 151位 |
|
|||
|
17:13:52 |
|
|
## dotfiles
.(ドット)何とかってファイルを管理する方法です。 .vimrc,.vim,.bash_profile,.wgetrc何でも来たれです。 * `対象者`:環境設定するのに毎回設定を書くのめんどくせんだよって思う人 * `前提条件`:ドットファイルの設定を行っていること。GitHubについての基礎の基礎知識がある。 * `必要なソフト`:`git` * `終着点`:環境設定がgit cloneによって一瞬で終わる。 ## GitHubにリポジトリを作成 まずは何はともあれ[GitHub](https://github.com/)上にdotfilesリポジトリが必要です。 サインインして右上の+ボタンからnew repositoryを選択して、リポジトリを作成しましょう。 先述したとおり`dotfiles`で作るのが主流みたいです。まぁなんでもいいけど。 repository nameのところにdotfilesって入力して、create repositoryボタンを押せばできます。 ## 今まで管理していたファイルを移動 ホームディレクトリに`dotfiles`ディレクトリを作成して、管理対象のファイルやディレクトリをぶちこみます。 下はmacの場合での例、自分で追加したいのとかいらないのとか取捨選択してね。 ```bash $ cd ~/ $ mkdir dotfiles $ mv .vimrc dotfiles $ mv .vim/colors dotfiles $ mv .vim/ftdetect dotfiles $ mv .vim/indent dotfiles $ mv .bash_profile dotfiles $ mv .wgetrc dotfiles $ mv Brewfile dotfiles ``` ちなみに下3つは筆者は使っていない。 みんなは使うのかなーとか思って入れてみただけです。 実際のところこの部分はコマンドでやらずにFinderやエクスプローラーでやっても同じです。 ## シンボリックリンクを張る まぁいわゆるショートカットです。 シェルスクリプトやバッチファイルにしちゃいます。(dotfilesディレクトリ内に作ってください。) * Linux ```bash:dotfilesLink.sh $ #!/bin/sh $ ln -sf ~/dotfiles/.vimrc ~/.vimrc $ ln -sf ~/dotfiles/colors ~/.vim $ ln -sf ~/dotfiles/ftdetect ~/.vim $ ln -sf ~/dotfiles/indent ~/.vim $ ln -sf ~/dotfiles/.bash_profile ~/.bash_profile $ ln -sf ~/dotfiles/.wgetrc ~/.wgetrc $ ln -sf ~/dotfiles/Brewfile ~/Brewfile ``` * Windows(Linux系のファイルは入れないようにしてください。lnコマンドと比べターゲットとリンク先の指定が逆なので注意) ```bat:mklink.bat mklink %HOMEPATH%"\.vimrc" %HOMEPATH%"\dotfiles\.vimrc" mklink /D %HOMEPATH%"\.vim\ftdetect" %HOMEPATH%"\dotfiles\ftdetect" mklink /D %HOMEPATH%"\.vim\indent" %HOMEPATH%"\dotfiles\indent" mklink /D %HOMEPATH%"\.vim\colors" %HOMEPATH%"\dotfiles\colors" exit 0 ``` スクリプトを作りましたら、まずは動かしてみてください。 シンボリックリンク(いわゆるショートカット)ができているかと思います。 以降ほかの環境でgit cloneでGitHubから持ってきてスクリプトを動かす際、同名のファイルは消すか移動してから動かしましょう。(Winのみ) ## 現在のディレクトリ構成 こんな感じです。ただしcolors,ftdetect,indentは筆者の環境下での話になります。 ``` dotfiles │ .vimrc │ .bash_profile │ .wgetrc │ dotfilesLink.sh │ mklink.bat │ Brewfile │ ├─colors │ desert.vim │ ├─ftdetect │ bas.vim │ cls.vim │ frm.vim │ └─indent css.vim html.vim javascript.vim ruby.vim scss.vim ``` ## GitHubへのpush ここの説明がめんどくさいのでコマンドだけ、 意味は[commitとpushしかできない人のためのgithubの使い方まとめ](http://myuon-myon.hatenablog.com/entry/20121114/1352906438)を参照してみて。 ```bash $ cd ~/dotfiles # if windows use this > cd %HOMEPATH%/dotfiles $ git init $ git add . $ git commit -m 'first commit' $ git remote add origin git://github.com/your_name/dotfiles.git $ git push origin master # type username & password ``` 下から2行目のyour_nameの部分は自身のgithubアカウント名ね。 ## 他のコンピュータからの取得 他のコンピュータからgit cloneして設定を持ってきます。 ```bash $ cd ~/ # if windows use this > cd %HOMEPATH% $ git clone https://github.com/your_name/dotfiles.git $ sh dotfilesLink.sh # if windows use this > mklink.bat ``` ## 更新 dotfilesのファイルを更新した際は更新をコミットした後にプッシュしてGitHubに送り込みます。 ```bash $ git add . $ git commit -m "anything" $ git push origin master ``` 新規のファイルが無い場合は以下で代用できます。 ```bash $ git commit -a -m "anything" $ git push origin master ``` ## 同期方法 ↑の更新を取得する場合はGitHubの状態と一致させます。 ```bash $ git pull ``` ## 最後に なぜ.vim以下全てを持っていかないのか->私はなるべく最新のpluginなど使いたいですし、要らんものがいっぱいついてくるので必要最低限しかもっていきません。NeoBundleInstallで入るのですからいいのです。 Windowsなら以前紹介した[chocolatey](http://qiita.com/himinato/items/11f4dc9a23afebbc242c)の設定を入れて置くのもありだと思います。 追記 もっと自動化しました[ミニマルに始めるDotfiles自動化計画](http://qiita.com/okamos/items/40966158d0271ae7198b) |
|
| 152位 |
|
|||
|
10:42:58 |
(Akatsuki Inc. 所属) |
|
##「Rubyならこう書く」あれこれ
- Arrayオブジェクト - Rangeオブジェクト - ブロックとProcオブジェクト - シンボルとハッシュ - クラスとコンストラクタ (追記) ブロックとProcオブジェクトについては、長くなったので以下に分離しました。 [Ruby基礎] ブロックとProcをちゃんと理解する http://qiita.com/kidachi_/items/15cfee9ec66804c3afd2 ##配列(Array) ####要素の操作 ```rb #配列要素の分割 > "foo bar bazz".split => ["foo", "bar", "bazz"] #配列要素の指定文字列での分割 > "fooxbarxxxxbazz".split('x') => ["foo", "bar", "", "", "", "bazz"] #配列要素の順番入れ替え > "foo bar bazz".split.reverse => ["bazz", "bar", "foo"] #配列要素の順番入れ替え > a = "foo bar bazz".split.shuffle => ["bar", "foo", "bazz"] ``` ####要素の追加 ```rb #配列要素の追加 > a.push(8) => ["bar", "foo", "bazz", 8] #配列要素の追加2 > a << 10 => ["bar", "foo", "bazz", 8, 10] #配列要素の追加3(chain) > a << 'fuga' << 'moga' => ["bar", "foo", "bazz", 8, 10, "fuga", "moga"] #配列要素の結合 > a.join => "barfoobazz810fugamoga" #配列要素の結合2 > a.join(', ') => "bar, foo, bazz, 8, 10, fuga, moga" ``` ##範囲(Range) ####範囲から配列生成 ```rb #「.」2つで後方の要素(9)を含む > (0..9).to_a => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] #「.」3つで後方の要素(9)は含まない > (0...9).to_a => [0, 1, 2, 3, 4, 5, 6, 7, 8] ``` ####配列要素を範囲で指定して取得 ```rb #文字列を配列に変換 > b = %w[foo bar baz fuga moga] => ["foo", "bar", "baz", "fuga", "moga"] #配列から指定範囲を取り出す > b[0..2] => ["foo", "bar", "baz"] ``` ####配列要素を範囲で指定して取得2 ```rb #配列へ変換 > c = (0..9).to_a => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] #配列の最後の要素指定 > c[5..(c.length-1)] => [5, 6, 7, 8, 9] #配列の最後の要素指定2 > c[5..-1] => [5, 6, 7, 8, 9] ``` ####その他 ```rb #範囲は文字列に対しても利用出来る > ('a'..'e').to_a => ["a", "b", "c", "d", "e"] ``` ##ブロック 分かりづらかったので少し丁寧に。 - ブロックとは何か - yield、Procとは何か - Proc補足 - で、ブロックやProcって何が嬉しいの? の順でいきます。 (追記) 本項は以下を参照。 [Ruby基礎] ブロックとProcをちゃんと理解する http://qiita.com/kidachi_/items/15cfee9ec66804c3afd2 ##シンボル 文字列と近いが、少々性質が異なる。 内部的な比較の際、文字列は1文字ずつ比べる必要があるが、 シンボルは一度に全体を比較可能。 →ハッシュ等のキーとして理想的な性質。 ```rb #文字列は分割できる >> "name".split('') => ["n", "a", "m", "e"] #シンボルは分割できない >> :name.split('') NoMethodError: undefined method `split' for :name:Symbol #文字列は(分割して)reverseできる >> "foobar".reverse => "raboof" #シンボルは(分割して)reverseできない >> :foobar.reverse NoMethodError: undefined method `reverse' for :foobar:Symbol ``` Ruby 1.9以降では、シンボルをキーとして扱う場合の特別な記法をサポート。 以下の二つは同義。 ```rb { :hoge => 'hoge', :fuga => 'fuga'} { hoge: 'hoge', fuga: 'fuga'} ``` よって以下二つも同義。 ```rb >> h1 = { :name => "hogefuga", :email => "foo@example.com" } => {:name=>"hogefuga", :email=>"foo@example.com"} >> h2 = { name: "hogefuga", email: "foo@example.com" } => {:name=>"hogefuga", :email=>"foo@example.com"} >> h1 == h2 => true ``` ハッシュがよりスムーズに記述できる。 ####ハッシュをブロックに渡す ```rb >> hash = { success: "It worked!", error: "It failed." } => {:success=>"It worked!", :error=>"It failed."} >> hash.each do |key, value| >> puts "Key #{key.inspect} has value #{value.inspect}" >> end Key :success has value "It worked!" Key :error has value "It failed." ``` →ブロックにはkeyとvalueの二つが渡ることになる。 ※inspectはオブジェクトを可視化して返す inspectを用いない場合、 ```rb >> hash.each do |key, value| >> puts "Key #{key} has value #{value}" >> end Key success has value It worked! Key error has value It failed. ``` こうなる。 ##Railsに現れるRuby 上のハッシュの知識をもとに、Rails内で使うRubyの記述を読み解いてみる。 ####cssを出力するタグの例 ```rb <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> ``` いくつか特徴がある。 1. メソッド呼び出しの丸かっこは省略可能。 →以下は同義 ```rb stylesheet_link_tag("application", media: "all", "data-turbolinks-track" => true) stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true ``` 1. 最後の引数がハッシュの場合、波括弧は省略可能。 →以下は同義 ```rb stylesheet_link_tag "application", { media: "all", "data-turbolinks-track" => true } stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true ``` 1. シンボルではハイフンを使用できない (mediaは「media:」という新式の記法なのに、data-turbolinks-trackは 「"data-turbolinks-track" =>」という旧式記法である理由) ```rb data-turbolinks-track: true #NG。シンボルではハイフンを使用できない "data-turbolinks-track" => true #OK ``` ####結果、出力されるタグ ```html <link data-turbolinks-track="true" href="/assets/application.css" media="all" rel="stylesheet" /> ``` ##クラスとコンストラクタ ####String ```rb >> a = "hoge" => "hoge" #明示的な書き方 >> a = String.new("hoge") => "hoge" ``` ####Array ```rb >> a = [1, 3, 2] => [1, 3, 2] #明示的な書き方 >> a = Array.new([1, 3, 2]) => [1, 3, 2] ``` ####Hash => コンストラクタが特殊 hashのコンストラクタに値を渡した場合、 「キーが存在しない場合に返されるデフォルト値」として定義される。 ```rb # 何も値を渡さずに初期化 >> h = Hash.new => {} >> h[:foo] # 存在しないキー :foo => nil # キーが存在しない場合のデフォルト値はnil # 値を渡して初期化 >> h = Hash.new(0) # デフォルト値をnilから0に変更 => {} >> h[:foo] => 0 # キーが存在しない場合のデフォルト値は0 ``` ##Module ```rb module ApplicationHelper ``` 通常(生のRuby)では、moduleはincludeを使い呼び出す(MixidIn)ことができる。 が、Railsは自動で呼び出してくれるため、定義さえすれば全てのViewで利用することが出来る。 ## もう一歩踏み込むならこちら [Ruby] 便利な組み込みクラスのメソッド達 Enumerable編 http://qiita.com/kidachi_/items/a00558cfb0a6a3e23f4b Array編 http://qiita.com/kidachi_/items/e9cb26c4e6cb36b70a1c Hash編 http://qiita.com/kidachi_/items/651b5b5580be40ad047e String編 http://qiita.com/kidachi_/items/7b355eb355b2d1390cf5 ## 以下に続く [Ruby基礎] ブロックとProcをちゃんと理解する http://qiita.com/kidachi_/items/15cfee9ec66804c3afd2 |
|
| 153位 |
|
|||
|
18:02:45 |
(LIFULL Co., Ltd. 所属) |
|
S3のアクセスコントロールをよく理解していないのでまとめる。 日本語ドキュメントは更新されてない場合が多いので英語ドキュメントを参照することをお勧めする。 ただ、S3のアクセスコントロールについては枯れているので日本語ドキュメントでも問題ないと思う。 [開発者ガイド](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/Welcome.html) [Developer Guide](http://docs.aws.amazon.com/AmazonS3/latest/dev/Welcome.html) # 前提知識 大概のクラウドストレージサービスには意図せぬ公開のリスクがある。 S3も同様にワンクリックでカジュアルな情報漏えいが可能である。 CLIはともかく、コンソールでのバケット作成時に公開するのか/しないのか選択できてもいいのにと思う。 こちらの記事でS3うっかり公開の防止や発見について書いてあるので読んでおくこと。 Trusted Advisor、AWS Config、S3アクセスチェック、などについて書かれている。 [意図せず公開しているS3バケットはありませんか?〜S3アクセス権限設定を見直そう〜](https://dev.classmethod.jp/cloud/aws/check-your-s3-access-configuration/) # 基礎知識 ## アクセスコントロールの種類 S3はアクセスコントロールのために3つの方法を提供している。 AWS全般のアクセスコントロールの種類やサービスごとの対応状況については[IAMと連携するAWSサービス](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html)が参考になる。 * ACL(アクセスコントロールリスト) * バケットポリシー * IAMポリシー | アクセスコントロールタイプ | AWSアカウントレベルの制御 | IAMユーザレベルの制御 | 形式 | |:----------------------|:---------------------:|:---------------:|:----:| | ACL | ○ | ☓ | XML | | バケットポリシー | ○ | ○ | JSON | | IAMポリシー | ☓ | ○ | JSON | [アクセスコントロールリスト(ACL)の概要](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/acl-overview.html) ### ACL バケット/オブジェクトのサブリソースとしてXMLで定義する。 AWS Management Console からは、各バケット/オブジェクトの Properties > Permissions で設定できる。 ### バケットポリシー バケット/オブジェクトのアクセス権をJSONで定義する。 AWS Management Console からは、バケットの Properties > Permissions > Add(Edit) Bucket Policy で設定できる。 ### IAMポリシー IAMのリソースに紐づき、S3を含むAWSリソースへのアクセス権をJSONで定義する。 AWS Management Console からは、IAMのホームディレクトリでユーザ/グループ/ロールのポリシー設定ができる。 ## リクエストの認可 あとで書く。 [Amazon S3 がリクエストを許可する方法](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/how-s3-evaluates-access-control.html) ## 評価論理 許可/拒否を決めるルール。ここが1番重要な気がする。 基本は下記の通りで **明示的拒否** > **許可** > **デフォルト拒否** の順に強い。 * **デフォルト拒否** (ただし自分自身からのアクセスは除く) * **許可** は **デフォルト拒否** に優先する * **明示的拒否** は **許可** に優先する * ポリシーの評価順序は重要ではない [IAM ポリシーの評価論理](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/AccessPolicyLanguage_EvaluationLogic.html) 個人的に間違えそうなのが、デフォルト拒否/明示的拒否に、許可を組み合わせた場合。 条件Aに一致するリクエストが拒否される時、設定によってどういう評価になるかを表にしてみる。 | 条件 | 可否 | 評価 | |:-----:|:----------:|:------------:| | NOT A | Allow(許可) | デフォルト拒否 | | A | Deny(拒否) | 明示的拒否 | これに評価が許可になる条件Bを組み合わせると、 > (NOT A) or B → デフォルト拒否 or 許可 → **許可** > A or B → 明示的拒否 or 許可 → **拒否** となり、評価がデフォルト拒否になっているからといって、拒否設定した気になっていると、 他のポリシーとの組み合わせでうっかり許可になってしまう場合がある。 ## 対象リソースの指定 バケットポリシーやIAMポリシーで、ある Action を許可/拒否する場合、対象 Resource を ARN で指定する。 バケットに対する Action は Resource としてバケットの ARN(arn:aws:s3:::bucket) を指定する。 オブジェクトに対する Action は Resource としてオブジェクトの ARN(arn:aws:s3:::bucket/*) を指定する。 AWS Management Console から特定のS3バケットの操作を許可する例。 ```javascript:IAMポリシー抜粋 { "Statement":[ // S3ホームディレクトリでバケット一覧を表示するために必要 { "Effect":"Allow", "Action":[ "s3:ListAllMyBuckets" ], "Resource":"arn:aws:s3:::*" }, // バケットに対する操作を許可 { "Effect":"Allow", "Action":[ "s3:ListBucket", "s3:GetBucketLocation" ], "Resource":"arn:aws:s3:::bucket" }, // オブジェクトに対する操作を許可 { "Effect":"Allow", "Action":[ "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource":"arn:aws:s3:::bucket/*" } ] } ``` ## AWS Account ID と Canonical User ID の確認方法 AWS Management Console に AWSアカウントでログイン後、 右上のメニュー > Security Credentials を開き Account Identifiers から確認できる。 ただし、AWSアカウントの管理者(rootユーザ)しか確認できない。 `aws s3api get-bucket-acl --bucket <bucket-name>` で確認したほうが手っ取り早いかも。 [AWS アカウント ID](http://docs.aws.amazon.com/ja_jp/general/latest/gr/acct-identifiers.html) # ACL アクセスコントロールリスト(ACL)は、各バケット/オブジェクトごとに付与され対象のアクセス権を制御できる。 バケット/オブジェクト作成時に、デフォルトで自分自身への `FULL_CONTROL` が付与される。 最大100個まで権限を付与できる。 バケットごと公開したい時 AWS Management Console からマウス操作だけで簡単に設定できるし、オブジェクトごとに細かくアクセス制限したい時などに使えそう。 別アカウントのバケットにオブジェクトをアップロードする場合、そのアカウントの管理者に `FULL_CONTROL` を与えたりする(CloudTrailなど)。 この場合ACL以外で許可する方法はない。バケットポリシーだけ設定したとしても許可はされない。 また、バケットポリシーは20KBの制限があるので、ACLを併用したほうがいいこともある。 [アクセスポリシーのオプションを使用するためのガイドライン](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/access-policy-alternatives-guidelines.html) ## 被付与者(Grantee) アクセス権を付与される人。 被付与者は、AWSアカウントに紐付いたメールアドレスor正規ユーザID(Canonical User ID)で指定する。 IAMユーザ単位のアクセス権は設定できない。たぶんIAMが実装される前からある機能だから。 S3には、あらかじめいくつかのグループが定義されており、グループに対してアクセス権を付与することもできる。 * **Authenticated Users** 全AWSアカウント(自分以外のアカウントも含むので注意)。 リクエストは署名(認証)されている必要がある。 <br> * **All Users** 誰でも。リクエストの署名有無は問わない。 AWS Management Console の Permissions で選択できる Everyone は多分これ。 <br> * **Log Delivery** S3のアクセスログ書き込み権限。アクセスログについては↓。 [サーバーアクセスのロギング](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/ServerLogs.html) ## アクセス許可(Permissions) `READ`、`WRITE`、`READ_ACP`、`WRITE_ACP`、`FULL_CONTROL` の5つのアクセス権がある。 *_ACP はアクセスコントロールリストへのアクセス権をあらわす。 [アクセスコントロールリスト(ACL)の概要 - 付与できるアクセス許可](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/acl-overview.html#permissions) ## 規定ACL(Canned ACL) S3には、あらかじめ被付与者とアクセス許可のセットが定義されており、APIのパラメータとして使用することができる。 [アクセスコントロールリスト(ACL)の概要 - 既定 ACL](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/acl-overview.html#canned-acl) * **private** (バケット/オブジェクト) デフォルトACL。所有者に `FULL_CONTROL` が付与される。 * **public-read** (バケット/オブジェクト) 所有者に `FULL_CONTROL` 、All Users に `READ` が付与される。 * **public-read-write** (バケット/オブジェクト) 所有者に `FULL_CONTROL` 、All Users に `READ`/`WRITE` が付与される。 * **authenticated-read** (バケット/オブジェクト) 所有者に `FULL_CONTROL` 、Authenticated Users に `READ` が付与される。 * **bucket-owner-read** (オブジェクト) オブジェクト所有者に `FULL_CONTROL` 、バケット所有者に `READ` が付与される。 バケット作成時に指定しても無視される。 * **bucket-owner-full-control** (オブジェクト) バケット所有者とオブジェクト所有者に `FULL_CONTROL` が付与される。 バケット作成時に指定しても無視される。 * **log-delivery-write** (バケット) Log Delivery グループに `WRITE`/`READ_ACP` が付与される。 # バケットポリシー JSONの定義は、IAMポリシーの項を参照。 設定の参考例は↓。 [バケットポリシーの例](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-bucket-policies.html) * ポリシーを定義できるのはバケット所有者のみ * バケット/オブジェクトレベルのアクセス権を設定できる * オブジェクトに対するアクセス権設定は、バケット所有者とオブジェクト所有者が同一の場合のみ可能 * ACLと違い、IAMユーザレベルのアクセス権設定ができる * IAMグループレベルのアクセス設定はできない。IAMグループのIAMポリシーで設定することはできる。 * 他AWSアカウントの管理者に許可を付与した場合、他AWSアカウントの管理者はそのIAMユーザに権限を譲渡することができる * バケットポリシーのサイズは20KBまで # IAMポリシー IAMポリシーとバケットポリシーはどちらでも同じようなアクセス権を設定できるが、 ユーザにアクセス権を設定したい場合はIAMポリシーを使い、 AWSアカウントやバケットにアクセス権を設定したい場合はバケットポリシーを使う、 という決まりを作ったほうが運用時に幸せになれそう。 IAMポリシーを使ったアクセス制御のチュートリアルが↓にあるので参考に。 [チュートリアル例: ユーザーポリシーを使用したバケットへのアクセスの制御](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/walkthrough1.html) ## リソース作成者のアクセス許可 AWSアカウントの管理者がバケット/オブジェクトを作成した場合、デフォルトで `FULL_CONTROL` が付与される。 しかし、IAMユーザが作成した場合、デフォルトではアクセス権は設定されない。 なので、IAMポリシーでアクセス権を設定する必要がある。 ## ポリシーの要素 ポリシーの要素で気になったものだけ紹介。 要素の中には値としてポリシー変数(後述)を指定できるものもある。 [IAM ポリシーエレメントの参照](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html) ### Version ポリシー言語のバージョンを表す。 ポリシー内にポリシー変数を含む場合は `Version` 要素を必ず指定する必要がある。 `Version` 要素のデフォルト値は `2008-10-17` なので、ポリシー変数がリテラル文字列として扱われるから。 `2012-10-17` を指定することでポリシー変数として扱われるようになる。 ```javascript:設定例 "Version":"2012-10-17" ``` ### Principal > Principal とは、ポリシーにしたがってアクセス権限を付与または拒否される 1 人以上のユーザーです。 > > [IAM ポリシーエレメントの参照 - プリンシパル](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html#Principal) `Principal` 要素にはAWSアカウントID、正規ユーザID、IAMユーザのARNなどが指定できる。 `NotPrincipal` 要素を使うことで、特定アカウント/IAMユーザを明示的に拒否することもできる。 ```javascript:設定例 // AWSアカウント単位 "Principal":{"AWS":"123456789012"} "Principal":{"AWS":"arn:aws:iam::123456789012:root"} // IAMユーザ "Principal":{"AWS":[ "arn:aws:iam::123456789012:user/Isono", "arn:aws:iam::123456789012:user/Nakajima" ]} // Webフェデレーションユーザ "Principal":{"Federated":"www.amazon.com"} ``` ### Action `Action` 要素にもワイルドカード(* or ?)が使える。 `NotAction` 要素を使うことで、特定アクション以外を明示的に拒否することもできる。 ```javascript:設定例 "Action":"iam:*AccessKey*" ``` S3の Action 一覧↓。 [ポリシーでのアクセス許可の指定](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/using-with-s3-actions.html) ### Resource 取り扱う対象のオブジェクトをARNで指定する。 [IAM をサポートする AWS サービス](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/Using_SpecificProducts.html) ```javascript:設定例 "Resource":[ "arn:aws:dynamodb:ap-northeast-1:123456789012:table/table1", "arn:aws:dynamodb:ap-northeast-1:123456789012:table/table2" ] ``` ### Condition オプショナルな要素。ポリシーの実行条件を細かく指定できる。 色々できるので詳細は↓を参照のこと。 [IAM ポリシーエレメントの参照 - 条件](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html#Condition) ## ポリシー変数 ポリシー変数を使う場合は `"Version":"2012-10-17"` をポリシーに含めること。 未指定の場合、デフォルトで旧バージョン(2008-10-17)が指定されたことになるのでポリシー変数は使えない。 その場合、ポリシー変数はリテラル文字列として扱われる。 [IAM ポリシー変数の概要](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/PolicyVariables.html) ### 変数の使用箇所 `Resource` 要素と `Condition` 要素で使える。 ```javascript:設定例 // Resource要素 "Resource":["arn:aws:s3:::bucket/home/${aws:username}/*"] // Condition要素 "Condition": { "StringLike": { "sns:endpoint": "https://example.com/${aws:username}/*" } } ``` ### キーの種類 #### リクエストから取得されるキー `Condition` のキーとして指定する場合、大文字/小文字を区別するので注意。 * **aws:CurrentTime** 日時条件 * **aws:EpochTime** 日付をエポックつまり UNIX 時間で表したもの。日時条件で使用 * **aws:principaltype** (※) プリンシパルがアカウント、ユーザー、フェデレーション、または引き受けられたロールかどうかを示す値 * **aws:SecureTransport** リクエストが SSL を使用して送信されたかどうかを示すブール値 * **aws:SourceIp** IP アドレス条件で使用するための、リクエスタの IP アドレス * **aws:UserAgent** 文字列条件で使用するための、リクエスタのクライアントアプリケーションに関する情報 * **aws:userid** (※) 現在のユーザーの一意の ID * **aws:username** (※) 現在のユーザーのフレンドリ名 ※ プリンシパルごとの `aws:username` `aws:userid` `aws:principaltype` の値を表にしたもの。 | プリンシパル | aws:username | aws:userid | aws:principaltype | |--------|:------:|:------:|:-------| | AWSアカウント | - | AWSアカウントID | Account | | IAMユーザ | IAMユーザ名 | 一意のID | User | | 委任ユーザ | - | AWSアカウントID:発信者が指定した名前 | FederatedUser | | Webフェデレーションユーザ | - | - | AssumedRole | | SAMLフェデレーションユーザー| - | - | AssumedRole | | 委任されたロール | - | ロールID:発信者が指定したロール名 | AssumedRole | | Amazon EC2 インスタンスに割り当てられたロール | - | ロールID:インスタンスID | AssumedRole | | 匿名(SQS/SNS/S3のみ) | - | - | Anonymous | [IAM ポリシー変数の概要 - ポリシー変数に使用可能なリクエスト情報](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/PolicyVariables.html#policy-vars-infotouse) #### Webフェデレーションユーザに関するキー どのプロバイダでログインしたかは `aws:FederatedUser` で取得できる。 * **aws:FederatedUser** * Amazonでのログイン認証時に `www.amazon.com` を返す * Facebookでのログイン認証時に `graph.facebook.com` を返す * Googleでのログイン認証時に `accounts.google.com` を返す アプリケーションIDとユーザIDも取得できる。 プロバイダごとのキーは下記の通り。 | プロバイダ | アプリケーションIDのキー | ユーザIDのキー | |----------|---------------------------|-------------------------| | Amazon | www.amazon.com:app_id | www.amazon.com:user_id | | Facebook | graph.facebook.com:app_id | graph.facebook.com:id | | Google | accounts.google.com:aud | accounts.google.com:sub | #### SAMLベースのフェデレーションユーザに関するキー 下のほうに一覧が書いてある。 [IAM ポリシーエレメントの参照 - 条件](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html#Condition) #### S3固有のキー サービス固有のキーもある。S3で利用可能なキーは↓。 [ポリシーでの条件の指定](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/amazon-s3-policy-keys.html) # クロスアカウント このチュートリアルは順を追って関係者が増えて複雑になっていくので理解しやすい。 (例1はクロスアカウントではない) [チュートリアル例: Amazon S3 リソースへのアクセスの管理](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-walkthroughs-managing-access.html) * [例 1: バケット所有者がユーザーにバケットのアクセス許可を付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-walkthroughs-managing-access-example1.html)  * [例 2: バケット所有者がクロスアカウントのバケットのアクセス許可を付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-walkthroughs-managing-access-example2.html)  * [例 3: バケット所有者が自分の所有していないオブジェクトに対するアクセス許可をユーザーに付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-walkthroughs-managing-access-example3.html)  * [例 4: バケット所有者が自分が所有しないオブジェクトへのクロスアカウントアクセス許可を付与](http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-walkthroughs-managing-access-example4.html)  とにかくIAMユーザ(子)はAWSアカウントの管理者であるrootユーザ(親)の許可がないとダメ。 クロスアカウントの場合、親であるAWSアカウントの管理者が2人になるので、子はそれぞれに親に許可を得る必要がある。 |
|
| 154位 |
|
|||
|
14:17:41 |
(M3, Inc. 所属) |
|
多種多様な構成のサーバーを Ansible で管理する場合、単一のベストプラクティスツリーに押し込むのは管理が大変すぎて現実的ではないなとおもい、どうしたものかなと悩んでいました。で、最近やっとこれかなという構成ができたので共有してみます。
## 何が問題か? * ロールには共用できるものとできないものがある、それがいっしょこたに混ざるのが嫌 * 無理に共用できるようにと変数を多用するととても管理が大変。変数も覚えられないし、テストが大変 * 読み込むファイルのパスが大元のymlからの相対パスであり、`include` ではディレクトリ階層での整理が難しい * `-l` で対象サーバーを絞り込んでも全てのタスクが表示され、skipped, skipped, skipped と関係ない task 表示がターミナルが埋まって見づらい ## そして、たどり着いたオレオレベストプラクティス まとめて管理したいサーバーグループ毎にベストプラクティスツリーを作り、共用可能な、共用したいロールをその外に出すという構成です。次のような Playbook となります。 ```yaml:site.yml - hosts: all sudo: yes vars_files: - ../vars/common.yml # 共通変数 - ../private_vars/common.yml # リポジトリにコミットしない共通変数 roles: - ../common-roles/base # 共通処理の role は外側にある - ../common-roles/iptables - { role: ../common-roles/apache24, httpd_service_name: "httpd", httpd_mpm: "worker" } - apache-config - snmp - users ``` 心置き無く汎用性のないロール、タスクを書くことができ、共用可能なものは多重に管理する必要がありません。 変数は site.yml のあるディレクトリの host_vars や group_vars の値を外の共通 role でも参照可能です。 全体は次のような感じ ``` . ├── common-roles/ │ ├── base/ │ │ ├── defaults/ │ │ │ └── main.yml │ │ ├── files/ │ │ │ ├── ... │ │ │ └── ... │ │ ├── handlers/ │ │ │ └── main.yml │ │ ├── templates/ │ │ │ ├── ... │ │ │ └── ... │ │ └── tasks/ │ │ └── main.yml │ ├── role-A/ │ │ : │ └── role-B/ │ : ├── service-A/ │ ├── group_vars/ │ │ ├── all │ │ ├── development │ │ ├── production │ │ └── staging │ ├── host_vars/ │ │ └── host1 │ ├── hosts-development │ ├── hosts-staging │ ├── hosts-production │ ├── roles/ │ │ ├── role-X/ │ │ │ ├── defaults// │ │ │ ├── files/ │ │ │ ├── handlers/ │ │ │ ├── meta/ │ │ │ ├── tasks/ │ │ │ └── templates/ │ │ └── role-Y/ │ │ ├── defaults/ │ │ ├── files/ │ │ ├── handlers/ │ │ ├── meta/ │ │ ├── tasks/ │ │ └── templates/ │ └── site.yml ├── private_vars/ │ └── common.yml ├── vars/ │ └── common.yml : ``` インベントリファイルは実行時に指定するので一つのファイルにすることもできますが、シンプルに保ちたいのでそれぞれのディレクトリに置きます。 全サーバーに対して ansible コマンドで何か実行したい場合もあるかとは思いますが、そんな時でも ansible なら実行権限をつけたスクリプトなどをインベントリファイルとして指定すればその出力を使ってくれるので、各ディレクトリのファイルをマージするスクリプトを指定してあげれば解決です。ファイル名も hosts-development, hosts-staging, hosts-production などルールがあれば、そのスクリプトも単純化できます。 いかがでしょうか、もっと良い方法があるよとか、私はこうやってるとかコメントいただけると幸いです。 |
|
| 155位 |
|
|||
|
05:07:02 |
(Moneyforward 所属) |
|
そもそもqiita見ているデザイナー&&vimmerっているんですか(´;ω;`)ねー
会社の情報共有を兼ねて、使い方から導入方法まで。 紹介するプラグインは以下の通りです。 - **emmet-vim** あまりにも有名なhtml/CSS入力補助プラグイン - **surround.vim** vimのテキストオブジェクトを拡張するプラグイン - **open-browser.vim** URLを開いたり、ググったり出来る - **vim-browsereload-mac** ブラウザを自動更新するプラグイン(Mac Only) - **vim-css3-syntax** html5のコードをシンタックス表示する - **vim-javascript** vimにjsのシンタックスを追加する - **vim-coffee-script** coffee scriptのサポート - **sass-compile.vim** sassのサポート ## インストール vimのプラグイン管理にはNeoBundleを使います。 使い方は以下を参照。 [ NeoBundleの導入 ]( http://qiita.com/items/1c32d3f24cc2919203eb ) 下記のコードをNeoBundleでインストールしてください ```vimrc NeoBundle 'mattn/emmet-vim' NeoBundle 'taichouchou2/surround.vim' NeoBundle 'open-browser.vim' NeoBundle 'mattn/webapi-vim' NeoBundle 'tell-k/vim-browsereload-mac' NeoBundle 'hail2u/vim-css3-syntax' NeoBundle 'taichouchou2/html5.vim' NeoBundle 'taichouchou2/vim-javascript' NeoBundle 'kchmck/vim-coffee-script' ``` ### emmet-vim emmet-vimはzencodingの後継プラグインです。 [Zen Codingとは?動画]( http://vimeo.com/7405114 ) **HTML / CSS 爆速コーディングプラグイン** vimmer + Web業界な人で、これを入れていない人はいない程有名なプラグインです。 `ul>li*3>a>img`と打ち込んで、`<C-Y>,`を押すと下記のように展開されます。 ```html:index.html <ul> <li><a href=""><img src="" alt="" /></a></li> <li><a href=""><img src="" alt="" /></a></li> <li><a href=""><img src="" alt="" /></a></li> </ul> ``` 最低限、htmlを日本語に指定する設定をすると良いと思います。 [ オススメの設定 ](https://gist.github.com/6879605) ### surround.vim surround.vimはvimのテキストオブジェクトを拡張するプラグインです。 #### text-objectsとは? vimには、テキストオブジェクトという機能があります。 `w`でワード移動が出来たり、 `"abc"` というテキストに対して `di"` と打つと -> delete inner " でダブルクオーテーションの中を削除して`""`となります `<a href="">abc</a>` というテキストに対して `dat`と打つと -> delete a tag タグごと消えます テキストオブジェクトについて知りたい人は、`:h text-objects`とコマンドを打てばヘルプが出てきます。 #### surround.vimは何をするか その名の通り、surround(周りを取り囲むもの)というテキストオブジェクトを追加します。 例えば、 `<h2>abc</h2>`というコードのh2をdivに変えるとします。 `cst<div>`と打つと -> change surround tag <div> で`<div>abc</div>`と書き換えることが出来ます 他にも、 `abc`というテキストに対して、ビジュアルモードで選択した状態から、`s"`と入力すると`"abc"`と一発で囲むことも出来ます。 プラグインをインストールすれば、設定は不要です。 ### open-browser.vim URLに対してコマンドを打つ事で、ブラウザを表示させたり キーワード上でコマンドを打つ事で、ググったりする事が出来ます。 [ オススメの設定 ]( https://gist.github.com/3668363 ) ### vim-browsereload-mac 保存する度にブラウザを自動リロードしてくれます。 僕は、デュアルディスプレイで片方にブラウザを起動させて、もう片方でvimを起動させて作業をするようにしています。 [ オススメの設定 ]( https://gist.github.com/3668388 ) > 正直もうこれが無いとvimでコーディングをするのは辛い! と昔は言っていましたが、Livereloadというchromeのextensionの方が便利でした。えへへ。 機能はほぼ同じなので、誰かまとめ記事を書いて、編集リクエスト送ってください! ### vim-css3-syntax, vim-javascript, html5.vim vimは標準でjsやcss3のシンタックスを用意していません。htmlにjsコードを挿入するて、インデントが崩れた経験がある方は必須です。 こちらは、インストールするだけでインデントや色付けを行ってくれるので、必ず入れましょう。 先ほどNeoBundleで示したvim-javascriptは、jQueryも色付けするようにカスタマイズしてあります。 html5のコードも対応していないので、プラグインを追加する必要があります。もし、プラグインを追加しても色がつかない場合は、下記のコードを.vimrcに追加してください [html5]( https://gist.github.com/3668961 ) ### sass-compile.vim sassやscssを編修するときに、色づけ、インデント、自動コンパイルを行ってくれるプラグインです。 下記設定を追加するだけで、保存と同時にコンパイルを行ってくれます。(compassのプロジェクトなら、設定を読み込んで正しいディレクトリに保存してくれます) [ オススメの設定 ]( https://gist.github.com/3668482 ) デザイナーならば、Vimではなく[GUIで操作してもいいですね]( http://liginc.co.jp/designer/archives/11623 ) ### vim-coffee-script coffee-scriptは、jsをより簡単に書く事が出来る言語です。 coffee-scriptまで触れるデザイナー/コーダーはとっても頼もしいですよね。 そういう人がいるかどうかは、社風によるのでしょうか。 詳しくは、[ 正しいcoffee-scriptの環境づくり vim ]( http://qiita.com/items/fb442cfa78f91634cfaa )をご覧ください ## まとめ 一括で設定したい場合は、NeoBundleの設定をした後に下記を追加するといいと思いますZ! [ オススメの設定 ]( https://gist.github.com/3668558 ) やっぱり、codaもいいですけど本気スピード + js,scss,coffeeをコーディングしようと思ったらvimですね! 爆速爆速^^ |
|
| 156位 |
|
|||
|
02:46:19 |
|
|
[経験ゼロでもできるプログラミング現場の単体テスト](http://www.amazon.co.jp/%E7%B5%8C%E9%A8%93%E3%82%BC%E3%83%AD%E3%81%A7%E3%82%82%E3%81%A7%E3%81%8D%E3%82%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E7%8F%BE%E5%A0%B4%E3%81%AE%E5%8D%98%E4%BD%93%E3%83%86%E3%82%B9%E3%83%88-%E7%89%87%E6%A1%90-%E4%B8%80%E5%AE%97/dp/4798118915)を読んだので、そのまとめ
## はじめに 著者曰く、 > 初めて導入する単体テストの指南書 を目指した一冊。 単体テストを書いたことが無かったり、書いたことがあっても書き方の方針が定まっていない人にはおすすめの本。 逆に既にバリバリテストコードを書いてる人に、再確認的な内容になるかも。 # 単体テストについて基礎知識 1. アプリケーション開発では設計に時間がかかりすぎて、テストに時間をかけられないことがある。 1. 致命的な障害発生する可能性がある。 1. とはいえすべてを想定してテストを実施することはできない。 1. テストケースを絞る必要がある 1. **各テストフェーズの礎となる単体テストが重要になる** ### 高品質なシステム > ユーザーを満足させ、不安や不満をいだかせないこと * システムエラー:データの破損などの心配が生まれる * 応答が遅い:いらいら・二度と使わなくなる ### まとめてテストはダメ * 調査時間がかかる・問題箇所が切り分けづらい * 現場のモチベーションが下がる * リスクの先送り プロジェクト終盤で行うテストは再現・原因調査・修正・再テストと時間がかかるため、プロジェクト序盤から実施して置くべき。 ### テストクラスのメリット * 繰り返し実施が可能 * 結果が分かりやすい * テスト実行された箇所が分かる * デグレの早期発見 ### テストの種類と目的 | テスト | 内容 | 詳細・具体例 | |:-----------|:------------|:------------| |単体テスト|プログラム単体の振る舞い確認|引数のデータの組み合わせ計算結果が正しいか、DBに正しいデータが登録されるか、例外が発生したさいの振る舞いが正しいか| |結合テスト|単体テストが完了したプログラム間のデータの受け渡しを確認|登録ボタンを押下したら、フォームの値をDBに登録し次の画面に遷移するか| |システムテスト|本番稼働にデータ、状態でシステム用件を満たすか確認|外部システムとの連携、日次/週次/月次/年次などのタイミングに発生する処理、システムダウン| |負荷テスト|システムに負荷がかかっても問題なく稼働するか確認|大量のデータを持つDBに大量のリクエストを送信し、想定時間内にレスポンスを返すか、どれくらいのレスポンスで返せなくなるか、長時間稼働によるメモリーリーク確認| |システムテスト|システムの脆弱性を突かれてシステムダウンや機密情報の閲覧をされないか確認|バッファーオーバーフローなどのセキュリティホール、SQLインジェクションなどの不正アクセス| ### 代表的なテスト手法 ### ホワイトボックステスト ソースコード中の文、分岐、条件などを網羅的に実行してバグを見つけるテスト。 | テスト | 内容 | |:-----------|:------------| |ステートメントカバレッジ(命令網羅)|ソースコードの全命令・文のうち、1回でも実行されたステートメントの割合| |ブランチカバレッジ(分岐網羅)|ソースコードの全分岐のうち、1回でも実行された分岐の割合| |コンディションカバレッジ(条件網羅)|ソースコードの分岐に設定されている1つ1つの条件が成立・不成立の両方が実行されたの割合| ### ブラックボックステスト 入力された値に対し、仕様通り結果が返るかテストする。 次の両方を行う。 | テスト | 内容 | |:-----------|:------------| |同値分割法|プログラムが期待する「有効同値」、「無効同値」を入力とし結果を確認| |境界値分割法|プログラムの「有効同値」と「無効同値」の境界となる値を入力し結果を確認| ### バグの発見は難しい 単体テストに限らずバグを発見するには、豊富な知識や経験が必要。 入力順番を変えた場合、異なるユーザが同じ処理を行った場合など、テストだけでは分からないバグや意図しない動作をたくさん発生させることができる。 プロジェクトメンバーの経験が不足している場合には、熟練者のレビューや他のプロジェクトのバグ対応を共有させてもらう。 時間が掛かるが、単体テスト段階からこのように高いレベルでテストを実施できると修正コストも低く抑えることができる。 ### 負荷テストのタイミング 通常結合テストが終了してから行うことが多いが、ボトルネックになる原因はだいたいパフォーマンスの出ないSQLか、メモリを湯水のごとく使う実装。 つまり、実装段階で作り込んでいる。 早い段階で負荷テストを行えば、後々その場しのぎの対応を防げる。 # 具体的な単体テスト方法・注意すること ## テストクラスの基本系 テストクラスは、JUnitの規約に従って作成すると、開発者ごとにばらつかず、可読性が高くなる。 1. テストの前提条件をそろえる 1. テスト実行 1. 実行結果からテストの成否を判定 ### 単体テストを楽にするには * テスト範囲を狭くする * 作った側からテストを行う ### JUnit4のアノテーション |アノテーション|説明| |:-----------|:------------| |@Test|テストメソッド| |@Before|全てのテストメソッド実行前に行われる処理| |@After|全てのテストメソッド実行後に行われる処理| |@BeforeClass|テストクラスのテスト実行前に1度だけ行われる処理| |@AfterClass|テストクラスのテスト実行後に1度だけ行われる処理| ### テスト結果を判定するメソッド * assert*** * fail などなど ### テストケースの考え方 様々な引数のパターンを考える * 引数にnull * 配列サイズが0 * byteの値がByte.MAX_VALUE * byteの値がByte.MIN_VALUE * byteの値が0 ## DBアクセスの単体テスト DBのテストには注意することが沢山ある。 * 見る権限の無い人に個人データを見せていないか * 間違ったデータを登録していないか * DBの状態を常に等しいか * O/Rマッピングフレームワークを使用している場合、SQLが正しく発行されているか こうした作業を大幅に軽減、自動化するツールが「DbUnit」。 ### DbUnitを使用したテスト手順 1. DbUnitでDBとコネクションを確立(@Before/@After) 1. テスト前のDBの状態をバックアップ(@Before) 1. テストの前提となるデータの設定(@Before) 1. テスト実施(@Test) 1. 2.でバックアップしたデータをDBに戻す(@After) ### テストデータの注意点 * SELECTのテスト * 1件だけ取得 * 全てのテストデータを別にする:selectされたデータを明確にするため * 条件を満たさない場合取得できないことを確認する * 複数件取得(検索条件に主キーを指定しない場合も含む) * データは3件以上(検索条件に合致2件以上、合致しない1件以上):複数件取得、データマッピングが正しいか確認するため * 「<」「>=」を使用している場合、同値ケースのテストをする * AND、ORをつなげてる場合、その組み合わせも網羅的にテストする * INSERTのテスト * null以外のデータを登録 * nullを許可するカラムにnullを登録 * カラム全てに最大桁のデータを登録 * 一意制約に違反するデータを登録 * UPDATEのテスト * null以外のデータを登録 * nullを許可するカラムにnullを登録 * カラム全てに最大桁のデータを登録 * 更新対象のレコードがないときの更新 * 複数券のレコード更新 * DELETEのテスト * 存在しないレコードの削除 ## DBアクセスのテストケースを減らす方法 WebアプリケーションではSQLのテスト不足によるバグがボトルネックになることが少なくない。 何十行もあるSELECT文のテストや親レコード有無を考慮したテストデータ作成は面倒。 **テーブル設計段階からSQL文のテストしやすさを意識する。** ### テストしやすいテーブル設計 * アイデンティファイアの追加:IDカラムを追加することでテーブル結合に必要なカラムを減らす * DB制約を使う:制約上DBに存在しないデータのテストが必要なくなる ### 保守面からテスト容易性を高める * SQL文とメソッドの粒度を1対1に:対象が分かりやすい * 大作SQLはほどほどに:テストケースが作りづらい * SQL文での計算は控える:バグの原因がプログラムかSQL文か切り分けやすくなる ## 外部リソース、システムのテスト ### Mockオブジェクト使用時の注意点 * Mockオブジェクトを作成するには設計が必要:振る舞いを真似るため仕様を理解する必要がある * Mockオブジェクトを作成してテストしてもバグは出る * 対象クラスの仕様変更があるとMockオブジェクトの修正が必要になる ### 外部リソースにアクセスするテストの注意点 * ファイル入出力などの例外が発生する処理と、ロジックを分割する:余計な例外を考慮する必要がなく処理が単純になる * メール送信などの外部システム連携が必要な場合には、連携部分と、ロジックを分割する ### 外部システム連携と画面側のテスト 単体テストフェーズでは、外部システムと連携できな場合も多いので、Mockオブジェクト、フレームワークを利用する。 ### 画面側の単体テスト * EMMAでカバレッジを測定する * キャプチャリプレイツールを使用する ### よいテストの条件 * 目的が明確 * 繰り返し実行が可能 * 環境に依存しない(ローカル/サーバ、OS、実行環境、コンパイル環境を意識する) * テストが独立している * 処理の依存関係が疎である # 継続的にテストするために > テストは行程で作られる 設計段階からテストを考慮し、プロジェクトメンバー内で共通認識とすることでクラスの粒度をあわせ、テストしやすいソースコードを作成する。 品質を意識するのはプロジェクト内の誰かではなく、全員でなければならない。 ## テストを意識した開発のポイント ### 回帰テストを自動化するために * SQL文の中で使用する日付はすべて引数で受け取る * システム日付は全てユーティリティクラスより取得する:日付の形式や実行環境依存を防げる ### 発見しづらいバグを作らない * 単体テストでは確認困難なスレッドセーフを控える(シングルトンオブジェクトには結果を持たせないなど) * 文字エンコードに注意(エンコードを指定するなど):OSに依存するため ### 静的解析ツールを使用 * コーディング規約の作成 * CheckStyleによる規約チェック * FindBugsによる危険なコーディングの排除 * フォーマッターの設定 ### 車輪の再開発を避ける 単体テストは仕様とことなる実装やバグを見つけ出すために行いますが、そもそも外部で公開されているライブラリを導入することで、テストの必要性すら排除できる。 ### 開発環境は早めに準備 早期に準備することで、コーディング規約やCheckStyle、FindBugsのルールが決定できる。 プロジェクトが進んでからだと、開発環境、コードの修正など余計な手戻りが発生する。 ## ビルド&テスト自動化する環境の構築 ### 継続的インテグレーションの実現 開発プロセスの後になってからバグを見つけて修正するのではなく、継続的にビルド、テストを実行し、問題を早期に発見する。 * バージョン管理システムからソースコードを取得 * ビルド * 単体テスト * jarやwarにパッケージ * 結合環境にデプロイ これらをCIツールを使用し自動的に行う。 # 最後に ここではだいぶ割愛しているが、著書の中ではJUnit、DbUnit、Mock、Apache Commonsの記述方法や各メソッド、アノテーション、Hudson(現Jenkins)の使い方 など細かく説明しているので、使用経験が無ければ一読の価値あると思います。 他にもテスト関係の2冊([現場で使えるソフトウェアテスト](http://qiita.com/disc99/items/ebe939f8cc203d59f27e), JUnit実践入門)を読みましたが、一番基礎的な内容が書かれています。 |
|
| 157位 |
|
|||
|
16:24:53 |
|
|

http://japan.unity3d.com/unity/ #はじめに !注意! この記事郡が最初に投稿されたのは、2014/01/02です。 Unity4時に作成されたものですから、情報が古い場合があります! 14/12/18記 リンク先アドレスを更新しました。 ゲームの作り方についてはまずこちらをご覧ください! 「ゲーム 作り方」テラシュールウェア http://tsubakit1.hateblo.jp/entry/20140218/1392651856 全体的に編集中箇所多いです。 ご指摘いただけると助かります。 ##参照サイト この記事群は公式リファレンスと 以下のサイトを参考にさせて頂いております。 どれもとても参考になる素晴らしいUnity及びC#コンテンツです。 まずはこちらをご訪問ください。 ・ActionScript入門Wiki@rsakane氏 Unity http://www40.atwiki.jp/spellbound/pages/1303.html ・ドットインストール Unity入門 http://dotinstall.com/lessons/basic_unity ・WisdomSoft@赤坂玲音氏 Unity入門 http://www.wisdomsoft.jp/35.html →本を出版されたようです! →[こちら](http://www.amazon.co.jp/%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AEUnity%E5%AE%9F%E8%B7%B5%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9-%EF%BD%9E-%E3%82%B2%E3%83%BC%E3%83%A0%E9%96%8B%E7%99%BA%E3%81%AB%E3%81%99%E3%81%90%E3%81%AB%E5%BD%B9%E7%AB%8B%E3%81%A4%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E5%85%A5%E9%96%80-%E8%B5%A4%E5%9D%82-%E7%8E%B2%E9%9F%B3/dp/4774173037/ref=sr_1_4?ie=UTF8&qid=1429668426&sr=8-4&keywords=unity+%E5%85%A5%E9%96%80) ・ゲームクリエーター特訓@尾形薫氏 http://www.ws.ic.kanagawa-it.ac.jp/GameCreator/ogata/ ・GClue Unity blog http://unitygclue.blogspot.jp/ ・窓を視ればアマグラマ > C# 階梯 @長尾氏 http://www.wgag.net/csharp/index.html#flow ・C# によるプログラミング入門 @岩永信之氏 http://ufcpp.net/study/csharp/#index-intro ##筆者のQiita記事でUnityと関係ありそうなもの よく使いそうな関数をまとめました。 ・Unity使いそうな関数まとめメモ http://qiita.com/hiroyuki_hon/items/4c3bd290c795b2481ebd UnityへのMMDモデルの取り入れについてはこちらを御覧ください。 ・MMD4Mecanimを使用してMMDモデルにUnityで踊って頂いてみた。 http://qiita.com/hiroyuki_hon/items/931c79164b0ffe19517f ・C#私的超入門#1 http://qiita.com/hiroyuki_hon/items/9ce9709b4a6f8c797a43 安価で没入度の高いヘッドマウントディスプレイ、Oculus Riftに関する記事です。 ・Oculus Rift を手に入れたらこれを遊ぼう! Oculus Rift とはヘッドマウントディスプレイの名称です。 安価(300$)で視野が広く、Unityと簡単に連動させることができます。 http://qiita.com/hiroyuki_hon/items/d8b240d1e4e327fc6b48 ・Oculus Rifter はこれを見よう! http://qiita.com/hiroyuki_hon/items/de2b8194d5a6703fd83a ・Unity/Oculus関連素敵記事等まとめ http://qiita.com/hiroyuki_hon/items/8917ff250bd0452e7e05 また筆者の投稿記事のまとめはこちらです。 http://qiita.com/hiroyuki_hon/items/00dbac79351137509149 #UnityをC#で超入門してみる ##UnityをC#で超入門してみる #0 超目次 目次詳細 http://qiita.com/hiroyuki_hon/items/c4da01e1c94bbba2c071 ### #1.Unity入門の章 ・はじめに ・変数 ・メソッド ・クラス ### #2.ゲームオブジェクトの章 ・カメラ ・ゲームオブジェクト http://qiita.com/hiroyuki_hon/items/5211a1853647eee38d51 ### #3.計算の章 ・C#の文法 ・mathfクラス http://qiita.com/hiroyuki_hon/items/3326206dcedb4e5013d4 ### #4.GUIの章 ・GUI ・シーン遷移 http://qiita.com/hiroyuki_hon/items/af0a52c1cb9e856f32b2 ### #5.時間と入力の章 ・時間 ・入力 http://qiita.com/hiroyuki_hon/items/17e573842faf39bb3d21 # #1 Unity入門の章 目次 はじめに・変数・メソッド・クラス ####はじめに Unityの入門書籍について Unityでスクリプトを書いてみよう Hello World! コメントアウトをしよう ####変数 変数を使ってみよう コンポーネントを変数の型として使おう Inspectorを使って変数を操作してみよう(1)(2) ####メソッド メソッドを使ってみよう メソッドの引数を取ろう メソッドの呼び出し時にウェイトをかけてみよう ####クラス クラスを作ってみよう ある時に呼ばれる、呼ばれ続ける関数達を知ろう(オーバーライド関数) void Updateを使って継続的にプログラムを再生しよう 衝突判定をみてみよう トリガーを処理しよう VisualStudioを用いたC#の入門について #Unityの入門書籍について(14/01/07 記) [Unity本まとめメモ 2015/4/15時点](http://qiita.com/hiroyuki_hon/items/6781cd3e7dbca66d8fda)を作成しました。ご参考まで ###筆者のおすすめライン結論 1.Unity4入門 2.Unityで作るスマートフォン3Dゲーム開発講座 3.Unityライブラリ辞典&ゲームの作り方 Unityで覚える遊びのアルゴリズム ###おすすめUnity系書籍 筆者の読んだUnity系書籍でお勧めのものを紹介します。 Unityの入門書はいくつか出版されているので、自分の肌に合うものがよいと思います。 私がまったくの初心者の方にUnity4系を始めるのにあたってお勧めの書籍は、 以下です。 #####■Unity4入門 最新開発環境による簡単3Dゲーム製作 @浅野 祐一氏, 荒川 巧也氏, 森 信虎氏 次に、上記の書籍から一歩進みたい時にお勧めなのが以下です。 #####■Unityで作るスマートフォン3Dゲーム開発講座 Unity4対応 @夏木 雅規氏, 寺園 聖文氏 #####■Unity 4マスターブック―3Dゲームエンジンを使いこなす @和泉 信生氏 注意点:3Dゲーム開発講座内のソースコードの商用利用は、 株式会社翔泳社様への一報が必要となります。 次に読む書籍はライブラリ辞典がおすすめです。 #####■Unityライブラリ辞典 ランタイム編 @安藤 圭吾氏 値段が入門系の書籍類と比べると高く、 読みにくそうな印象を受けるかもしれません。 ですが、もし続けてUnityを触るならば是非購入を検討してください。 まずは、55ページにあるDestroyを写経したり、 例えば451ページのTimeを以下のように使ってみたりして この本を使うコツを掴みましょう。 ```csharp public float i; public float j; void Update () { i = Time.time; print ( i ); j += (Time.deltaTime); print ( j ); } ``` また、サンプルコードが非常に豊富な書籍として #####■ゲームの作り方 Unityで覚える遊びのアルゴリズム @加藤 政樹氏 があります。 ソースコードを自由に使わせていただくことが出来ます。 アセットは不可です。 様々なゲームのアルゴリズムを学ぶ事ができます。 この書籍をランタイム辞典を片手に引くことで、 ゲーム制作するに必要な多くのことが学べるかもしれません。 これ以降は他のUnity本や、Unityで作成したいプログラムの ジャンルに応じた書籍を読んでいく事になると思います。 例・子供用ゲームが作りたい→児童書、教育系書籍等。 ###Unity系書籍の言語の混在について 書籍によって使用する言語がC#とJavaScriptで混在しますが、 気にしないで進むのが良いです。 理由はどちらの言語で書いても(プロジェクト内で混在させても)スクリプトは動くということと、 理解が進めばどちらかの言語で自分で書き直せば良いからです。 参考:多くのUnityユーザはC#に落ち着くようです。 #Unityでスクリプトを書いてみよう Hello World! まずレイアウトを2by3にします。(任意です。) 最初は何もないのでこうなります。  おもむろに Project > Create > C# Script を選択し NewBehaviourScriptを作成します。  メニューバーから GameObject > CreateEmpty で空のゲームオブジェクトを作成します。 空のゲームオブジェクトにスクリプトをドラッグ&ドロップします。 こうなります。  NewBehaviourScriptをダブルクリックすると、 以下の様な文字列が出てきます。 画像のエディタはSublimeText(http://www.sublimetext.com) です。  Unity標準のエディタはMonoDevelopです。 通常はMonoDevelopで問題ありません。 標準エディタの変更は、 メニューバーからUnity > Preferences を選択、 External Tools > External Script Editor で行えます。 では、NewBehaviourScriptを編集します。 void Start () {}の{}の中に書きます こう書きます。 Debug.Log("Hello World!");  再生するとコンソールにこう出ます。 コンソールの表示方法は Window > Console です。  ""の中を書き換えることで表示する文字列を変更出来ます。   #コメントアウトについて C#のコメントアウトは // 行末までのコメント と /* コメント */ になります。 ##変数 #変数を使ってみよう こう書くと  こう出ます。  int が型、valueが変数名ですね。 ```csharp int i = 10; //整数型 double d = 1.0; //実数型 bool b = true; //論理値型 string s = "str"; //文字列型 void Start () { print(i); //出力:10 print(d); //出力:1 print(b); //出力:True print(s); //出力:str } ``` #コンポーネントを変数の型として使おう コンポーネントは変数の型としても使用することが出来ます。 ```csharp Transform trans; Rigidbody rigid; Audio audio; Camera camera; ``` コンポーネントを型に取り、publicにすることで 容易にTargetとして利用出来るようになります。 エディタ上でターゲットとしたいオブジェクトをD&Dします。 ```csharp public Transform target1; public GUIText target2; ``` #Inspectorを使って変数を操作してみよう public Transform trans; と書き、 Debug.Log(trans.position); と書きます。 trans は変数名ですので書き換えが可能です。 .position が変数に指定したもののポジションを出すとする何かです。 すなわち 変数名.position のように書きます。  InspectorにTransと書かれた箱が出てきます。 public Transform trans; と書いたためです。  この箱に、MainCameraをドラッグ&ドロップで入れてみます。  再生してみると、なんとコンソールにMainCameraのポジション情報が出ます。  変数名は名前なので書き換えが出来ます。 例えばtransをtfに書き換えます。  するとInspectorに表示される名前が変わります。  ただし、Debug.Log(trans.position); は、transの変数名が無くなったので実行が出きなくなります。 ここを、Debug.Log(tf.position); に書き換える必要があります。 #Inspectorを使って変数を操作してみよう(2) int型の変数を表示してみます。 public int value; と書いて、 Debug.Log(value); と書いてみます。  すると、Valueが出ます。  というわけで、おもむろに10000と入れて再生してみると、 コンソールにも10000と出ます。  ##メソッド #メソッドを使ってみよう ```csharp //helloWorld()メソッド void helloWorld() { Debug.Log("HelloWorld"); } void Start () { //helloWorld()メソッドの呼び出し helloWorld(); } //出力:HelloWorld ``` ##メソッドの引数を取ろう ```csharp int test(int j){ if ( j == 0){ return 0; } else{ return 1; } } // Use this for initialization void Start () { int k1; k1 = test(1); print(k1); //出力:1 int k2; k2 = test(0); print(k2); //出力:0 } ``` #メソッドの呼び出し時にウェイトをかけてみよう Invokeを使用します。 設定したメソッドを1度だけ呼び出します。 ```csharp void Start () { Invoke("Hello",3); } void Hello (){ print("Hello!"); } ``` #クラスを作ってみよう class bookを作成して使ってみます。 ```csharp public class NewBehaviourScript : MonoBehaviour { void Start () { Book book1 = new Book(); book1.title = "タイトル"; book1.name = "山田太郎"; book1.lent = false; print(book1.title); //出力:タイトル print(book1.name); //出力:山田太郎 print(book1.lent); //出力:false } } class Book { public string title; public string name = "山田"; public bool lent = true; } ``` #ある時に呼ばれる、呼ばれ続ける関数達を知ろう(オーバーライド関数) ある時に呼んだり、呼び続けたりする関数である オーバーライド関数は便利です。 いつ、何回呼びだすかに注目すると理解が進みます。 デフォルトのスクリプトの中に書かれている Start() と Update() は、オーバーライド関数です。 ```csharp void Start(){ //ここに処理を書く } ``` Start() はじめに一度だけ呼び出します。 ((いつ)はじめに、(何回)一度だけ) Update() 毎フレーム呼び出します。 (いつ)Start()の後に(何回)毎フレーム) 主なオーバーライド関数 Invoke() 設定したメソッドを、 設定した秒数後に、一度だけ呼び出します。 Awake() スクリプトが読み込まれるときに、一度だけ呼びだします。 On系 OnMouse◯◯()系 マウスが乗った時等に呼び出します。 OnTrigger◯◯()系 トリガー状態のオブジェクトとの衝突状態によって呼び出します。 OnCollision◯◯()系 オブジェクトの衝突状態によって呼び出します。 OnControllerColliderHit() キャラクターコントローラーの衝突の際に呼び出します。 OnEnable() オブジェクトが有効状態になった時に一度呼び出します。 OnDestroy() オブジェクトが破棄される直前に呼び出します。 OnApplicationQuit() アプリの終了直前に呼び出します。 OnGUI() GUIの描画やイベントを処理する関数です。 以下のようにして使います。 ```csharp void OnGUI(){ GUILayout.Label("Label"); } ``` #void Updateを使って継続的にプログラムを再生しよう これまではvoid Startの中に書いてきました。 オーバーライド関数 Start() は、 名前の通りのスタート時に一度だけプログラムを実行します。 プログラムを継続的に実行したい場合は、 オーバーライド関数 Update() の中に書きます。 オーバーライド関数 Start() は、 オーバーライド関数 Update() の前に一度だけ呼び出されます。 こうです。  この状態で再生してみます。 こうなります。 連続してポジションが取られているのが分かります。  カメラの位置を変えてみます。 適当にMainCameraを選択して矢印を引っ張ります。  めでたくコンソールに表示されるポジションの情報が変わりました。  #衝突判定をみてみよう 以下のようにして衝突したら破壊してみよう。 オーバーライド関数 OnCollisionEnter() は、 衝突判定があった時に呼び出されます。 ```csharp void OnCollisionEnter(Collision collision) { Destroy(collision.gameObject); } ``` 破壊するオブジェクトをタグで選別してみよう。 オブジェクトのTagからAdd tagでタグを生成します。 switch文を用いて "testtag": タグを選択すれば、 このタグが選択されているオブジェクトのみを破壊します。 ```csharp void OnCollisionEnter(Collision collision) { switch(collision.gameObject.tag) { case "testtag": Destroy(collision.gameObject); break; ``` ただしキャラクターコントローラーの衝突判定については 別に専用のものがあるので注意が必要です。 オーバーライド関数 OnControllerColliderHit() を用います。 ```csharp void OnControllerColliderHit(ControllerColliderHit hit) { // 衝突した時の処理を書く } ``` #トリガーを処理しよう トリガーを設定するとすり抜けられるようになります。 イベントの発生する場所の設定などにつかえます。 オーバーライド関数 OnCollisionEnter()を用います。 ```csharp function OnCollisionEnter(Collision collision) { //トリガーの処理を書く } ``` #ここは内容が古いです!!現在はUnityVSを用いましょう! ##普通にMicrosoft Visual Studio Express 2013 でC#を始める Microsoft Visual Studio Express 2013 for Windows Desktop をDL 登録が必要、規約に同意してインストール、起動。 http://www.visualstudio.com/downloads/download-visual-studio-vs#d-express-windows-desktop 新しいプロジェクト>C#>コンソールアプリケーション static void Main(string[] args) { Console.WriteLine("HelloWorld!"); } で開始で出る。 ~~##VisualStudioを用いたC#の入門について~~ VisualStudioを用いたC#の入門動画については ・【C#】魔理沙が計算機の魔法に挑戦 導入編【流体力学】 http://www.nicovideo.jp/watch/sm22224128 の”プログラム編”がオススメです。 上記動画のコードを書き起こさせていただく形で記事を書きました。 ・C#プログラミング入門してみる、ただし忍者を実際基軸に、いいね? http://qiita.com/hiroyuki_hon/items/02ea13ed48cac96129bd //以下は参考まで、初めての方は、ここを抜いて次の章に移動してください。 UnityでC#を使うにあたりVSを用いるなら、以下の記事を参考にすると良いです。 http://naichilab.blogspot.jp/2013/05/unityvisual-studio-express-2012.html Unityインストール先のEditorフォルダに Start "" %1 とだけ書いた .bat ファイルを作成し、 Unityのprefarenceから呼び出すだし、Assets→Sync MonoDevelop ProjectでOKです。 (プロジェクト名-csharp.sln っていうソリューションファイルをあらかじめ開いておくことでインテリセンスが効く) Sublimeで行うなら、まずパッケージコントロールをここを参考にDLします https://sublime.wbond.net/installation#st3 終了したら再起動し、Ctrl+Shift+P で installと打ち、 Package Controll: Install Package Unity Snippets and Completes は自動での補完が可能となります。 こちらをご参考下さい。 http://qiita.com/mikamikuh@github/items/22a760a679025166c9f1 個人的に Settings-Userで "font_face": "Consolas", を足します。 #つづきます UnityをC#で超入門してみる #2 ゲームオブジェクトの章 http://qiita.com/hiroyuki_hon/items/5211a1853647eee38d51 |
|
| 158位 |
|
|||
|
22:38:39 |
|
|
# Go の Test に対する考え方
この記事は Go Advent Calendar 2013 の 9 日目の投稿です。 今回は、 Go の testing というパッケージの使い方を解説しようと思ったのですが、 それだとつまらなすぎるので、合わせて Go が test というか assert についてどういうスタンスをとっているかを書いてみます。 ## Go でテスト さて、「テストのないコードはレガシーコード」などと言われて久しく、様々な言語が test (主に Unittest) について言語レベルでサポートしたり、デファクトなライブラリが確立したりといった状況が、今日では至って普通のこととなっています。 そんな言語や環境で、息をするようにテストを書いてきたみなさんが、はじめて Go でコードを書く時に見るべきは testing パッケージです。 [http://golang.org/pkg/testing/](http://golang.org/pkg/testing/) ## 命名規則 では、余計な説明がいらないように Sum() 関数のテストを書き、その実装を sum.go にしてみます。 この場合、テストは TestSum() 関数として sum\_test.go に書くというのが Go の命名規則になります。 sum_test.go ```go package MyMath import ( "testing" ) func TestSum(t *testing.T) { actual := Sum(10, 20) expected := 30 if actual != expected { t.Errorf("got %v\nwant %v", actual, expected) } } ``` testing パッケージを import したら、テスト関数を書きます。引数は必ず *testing.T を引数に取るのがルールです。失敗した場合のために、 t.Errorf() にエラーメッセージを記述します。 fmt.Printf() と同じようにプレースホルダが使えます。 ## 実行 実行は go test コマンドを使います。 ```sh $ go test sum_test.go # command-line-arguments ./sum_test.go:8: undefined: Sum FAIL command-line-arguments [build failed] ``` では実装を。 ```go package MyMath func Sum(i, j int) int { return i + j } ``` ```sh $ go test sum_test.go ok command-line-arguments 0.050s ``` 通りました。 (本当はちゃんとディレクトリ構造作ってパスを通す必要があります) ## Go には標準 assert がない さて、ここまでコードを読飛ばさずにきて下さった方は気づいたと思いますが、、 ```go func TestSum(t *testing.T) { actual := Sum(10, 20) expected := 30 if actual != expected { t.Errorf("got %v\nwant %v", actual, expected) } } ``` testing.AssertEqual() とかありません、ただの if 文です。 実は、 Go には testing パッケージがありながら、 assert 的なものが標準では提供されていません。 testing にあるのは、基本的にはテストを落とす関数や、 Skip する関数です。 そして、実行のために go test コマンドが標準で提供されています。 充実しまくった標準ライブラリに定評のある Go がいったいどうしたのでしょう? ## わかった、作れば良いのか! 「なるほど、 Assert は自分で実装することが推奨されているんだな」 「よし、ならば俺が考える最強の Assert パッケージを」 と思ったあなた、まあそう考えるのが普通かもしれません。 「無ければ作る」が OSS の流儀です。 実際、そう考えて巷?にはいくつかの assert パッケージがあります。 そして、「きっと、誰かが良い感じの assert パッケージを作っているだろう」という調査結果もちらほら。 - [Go言語のテスト用ライブラリとGospel](http://r7kamura.hatenablog.com/entry/2013/10/06/231236) - [go言語のテスティングフレームワークについて](http://blog.satotaichi.info/testingframeworks-for-golang) 二つ目の記事でも、以下のように言及されています。 ``` 「これはつまり、Goの標準組み込みのtestingパッケージというのはテスティングフレームワークを実装する為の基盤なのだな…というのが僕の理解。」 ``` 自分も最初触ってみて、「あー自分で作るのか」と思ったんですが、その前に「なぜ無いのか」を調べてみました。 Go は言語自体や、標準パッケージの開発には、開発者達の強い思想が反映されています。 Assert が無いのにも理由があるはず。 ## Assert が無い理由 ずばり FAQ にあります。 - (原文)[Why does Go not have assertions?](http://golang.org/doc/faq#assertions) - (訳)[アサート(assert)がない理由は?](http://golang.jp/go_faq#assertions) 要するに、「Assert は便利だけど、頼り過ぎてエラーのレポートが適当になる。エラーレポート重要だからきちんと書こう。議論の余地はあるけど、新しい試みとしてやってみる。」とのこと。 さらに詳細が同じく FAQ の別の項にあります。 - (原文)[Where is my favorite helper function for testing?](http://golang.org/doc/faq#testing_framework) - (訳)[テスト用のヘルパー関数がみあたりません](http://golang.jp/go_faq#testing_framework) 要するに、「みんなテストを書くためにすぐミニ言語作りたがるけど、必要な機能はだいたい Go 本体に揃ってるから、覚えること増やす必要無い。そうやって自動生成したエラーレポートより、ちゃんと自分で意図を書こう。面倒なのは分かるけど、そのコードに全く詳しくない人や、後のデバッグする時にそのコストは回収できるはず。」といった感じでしょうか。 じゃあ、本家はどうやってるのか。サンプルとして標準パッケージのテストを見てみましょう。 ## Go の標準パッケージのテスト 適当に net/http あたりを見てみます。 [http://golang.org/src/pkg/net/http/serve_test.go#L224](http://golang.org/src/pkg/net/http/serve_test.go#L224) それぞれのケースでちゃんとエラーメッセージを書いてますね。 「何が落ちたか」よりも、「なんで落ちたか」が書かれています。 これは、確かに Assert.Equals() 的なもので自動生成した場合にはできません。 書くのは面倒ですが、はじめてこのコードを見る人でも、きっと意味がわかるでしょう。 次は、 fmt パッケージのテストを見てみます。 [http://golang.org/src/pkg/fmt/fmt_test.go#L520](http://golang.org/src/pkg/fmt/fmt_test.go#L520) ここでは、 t.Errorf() は、 "want a but b" 的な画一的なものですね。でもこれは、その [前に作られた巨大な促成 struct](http://golang.org/src/pkg/fmt/fmt_test.go#L113) を range で回した結果です。つまりパラメタライズ的なテストですね。 ちなみに、ちょうどいい例はありませんでしたが、 struct などの同値検査には [reflect.DeepEqual()](http://golang.org/pkg/reflect/#DeepEqual) が使えます。 ここまで全て一応標準機能です。 ## Go Test コマンド test コマンドはどうでしょう。もちろんテストが実行できるのですが、以下のオプションがあります。 ``` -bench regexp -benchmem -benchtime t -blockprofile block.out -blockprofilerate n -cover -covermode set,count,atomic -coverpkg pkg1,pkg2,pkg3 -coverprofile cover.out -cpu 1,2,4 -cpuprofile cpu.out -memprofile mem.out -memprofilerate n -outputdir directory -parallel n -run regexp -short -timeout t -v ``` 並列実行や、各プロファイルの取得、以前解説した[ベンチの取得](http://d.hatena.ne.jp/Jxck/20131123/1385189088)、 Go1.2 からはカバレッジの取得も追加されています。 基本的な情報は、実行時にフラグを足すだけで取れます。その知識はどのプロダクトを弄っても同じで、Go の実行環境だけで可能です。 (Go は、言語そのものをシンプルに保つ一方、コマンド系は割とどん欲に色々取り入れているイメージがあります。) ## Go が選んだもの Go は、 assert やその他テストを構造化する仕組みなどを標準で提供しない代わりに、最小の API と十分なコマンドを提供することでテストを限りなく「ただの Go プログラム」で書けるようにしています。 これは、今まで Ruby や JS などで、 DSL レベルに整備された潤沢な API を使ってテストを書いてきた人にするとかなり戸惑うところが多いのが事実です。 すぐにでも、俺々モジュールを作ってしまいたくなるところで、自分もその誘惑には何度もかられました。 しかし、せっかく言語が新しいことに挑戦しているので、だったら乗ってみようと思い、 Assert を探すのはやめました。 まだ、そこまで大したものは書いていないから、なんとかなってはいます。コマンドにも特に不足を感じていません。 (とか言っておきながら、 Errorf() のコメントが面倒で結構適当になっている部分はあるのは否定できない。。) また、これを実践するなかで今までの整備されたテスト環境や、蓄積されたノウハウの良さも見えてくるし、 逆に、過度な部分、あと書き始める前の「今一番アクティブなテスティングフレームワークはなんだろう?」とか「環境はどうやって作るのが今のデファクトなんだろ?」など調べるところから始める部分の煩わしさを再認識したりと、色々思う事があるのでそれだけでも得るものがあるかなと。 ということで、しばらくは基本的には Go 標準の方法でテストを書いていってみようと思っています。 ## おまけ ちなみに、 Go にはジェネリクスが無いので、汎用な Assert を作ろうとすると interface + reflect ゴリゴリになって結構めんどうです。 あと、過度な抽象化は Go にはあまり合いません。そういった実装面の理由も、 Assert を提供しない理由のにあるかもしれません。 |
|
| 159位 |
|
|||
|
23:12:49 |
(ShouldBee 所属) |
|
モナドについて勉強していて見つけた英語記事を翻訳してみました。 誤訳等あれば編集リクエストやコメントください :bow: 原文: [Functors, Applicatives, And Monads In Pictures - adit.io](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html) ------------ ここに単純な値(value)があります。 ![value.png] ```haskell Prelude> 2 2 ``` そして、私たちはこの値に関数を適用する方法を知っています。 ![value_apply.png] ```haskell Prelude> (+3) 2 5 ``` とても簡単ですね。それでは、ある状態(context)に値が入っていると仮定してみましょう。これからみなさんは値を入れられる状態のことを、箱だと考えてもらって構いません。 ![value_and_context.png] ```haskell Prelude> Just 2 Just 2 ``` そして、この値に関数を適用したとき、 **状態によって** 異なる結果が返ります。この考え方に、Functor、Applicative、Monad、Arrowなどがもとづいています。`Maybe` データ型は2つの状態を定義できます。 ![context.png] ```haskell data Maybe a = Nothing | Just a ``` ```haskell Prelude> :type Just Just :: a -> Maybe a Prelude> :type Nothing Nothing :: Maybe a ``` 次に、`Just a` と `Nothing` それぞれに関数を適用したときどのような違いが出るか見てみましょう。先に Functor について話してみようと思います。 # Functor 箱に入っている値に対して、普通の関数を適用することはできません。 ![no_fmap_ouch.png] ```haskell Prelude> (+3) (Just 2) ERR - No instance for (Num (Maybe a0)) ``` ここで `fmap` が登場します。`fmap`はその道の出身で、状態についてよく知っています。`fmap` は箱に入っている値にどのような関数を適用しなければならないか分かっています。例えば、 `Just 2` に `(+3)` を適用してみましょう。 `fmap` を使ってみると… ```haskell > fmap (+3) (Just 2) Just 5 ``` ![fmap_apply.png] __ポン!__ `fmap` がやってくれました。では、どのように `fmap` は関数を適用する方法を知っていたのでしょうか? # Functorとは一体何だろうか? `Functor` は [typeclass] です。ここにその定義があります: ![functor_def.png] `Functor` は fmap を適用する方法を定義したデータ型です。ここにどのように fmap が動作するのかが出ています。 ![fmap_def.png] したがって、私たちは次のようにすることができます。 ```haskell > fmap (+3) (Just 2) Just 5 ``` そして `Maybe` もまた `Functor` なので、`fmap` は魔法のように関数を適用してくれました。どのように `fmap` が `Just` と `Nothing` に適用されるかについてが次に出ています。 ```haskell instance Functor Maybe where fmap func (Just val) = Just (func val) fmap func Nothing = Nothing ``` ここに `fmap (+3) (Just 2)` と打ったとき、裏でどんなことが起こるかが現れています。 ![fmap_just.png] それでは、 `(+3)` を `Nothing` に適用してみてください。 ![fmap_nothing.png] ```haskell > fmap (+3) Nothing Nothing ``` ![bill.png] _Maybe functorも知らないBill O'Reilly_ マトリックスのモーフィアスのように、`fmap` は何をしなければならないか知っています。 `Nothing` で始まり、`Nothing` で終わる! `fmap` は禅です。では、`Maybe` データ型がなぜ存在するのか考えてみましょう。例えば、 `Maybe` がない言語で、データベースのレコードを扱おうとしたらこうなります。 ```ruby post = Post.find_by_id(1) if post return post.title else return nil end ``` しかし、Haskellは次のようにできます。 ```hakskell fmap (getPostTitle) (findPost 1) ``` もしも、`findPost` が投稿(post)を返すならば、`getPostTitle`でタイトルを得ることができます。 `Nothing` を返したら、`Nothing` を得るのです。とっても簡単じゃないですか? `fmap` の周囲表記(infix) バージョンは `<$>` で、次のように書くこともできます。 ```haskell getPostTitle <$> (findPost 1) ``` ここにもう一つの例があります。リストに関数を適用したらどんなことが起こるでしょうか? ![fmap_list.png] リストもやはり functor だったのです! ここにその定義があります。 ```haskell instance Functor [] where fmap = map ``` ではでは、最後にもう一つ例を。関数にまた別の関数を適用しようとしたらどんなことがおこるでしょうか? ```haskell fmap (+3) (+1) ``` ```haskell Prelude> :type fmap (+3) (+1) fmap (+3) (+1) :: (Functor ((->) b), Num b) => b -> b ``` ここにある関数があります。 ![function_with_value.png] 関数にまた別の関数を適用してみようと思います。 ![fmap_function.png] 結果もやはり関数でした。 ```haskell > import Control.Applicative > let foo = fmap (+3) (+2) > foo 10 15 ``` したがって、関数も Functor なのです。 ```haskell instance Functor ((->) r) where fmap f g = f . g ``` 関数に fmap を使用すると、関数の合成(function composition)をしたのと同じです。 # Applicative 次の段階のApplicativeに進んでみましょう。Functorでやってみたように、箱に値を入れます。 ![value_and_context.png] そして、関数もまた箱に入れられるのです! ![function_and_context.png] はい、じゃあ詳しく見てみましょう。Applicativeもバカじゃありません。`Control.Applicative` は `<*>` を定義していて、 **箱の中の** 値に、 **箱の中の** 関数を適用する方法を知っています。 ![applicative_just.png] なので、次のようなことができます。 ```haskell Just (+3) <*> Just 2 == Just 5 ``` `<*>` を使うと少し面白い結果を得ることができます。例えばこんな。 ```haskell > [(*2), (+3)] <*> [1, 2, 3] [2, 4, 6, 4, 5, 6] ``` ![applicative_list.png] ここでFunctorにはできなかったけど、Applicativeにはできることが出てきます。どうやって、中に値が入ってる2つの箱に、2つの引数を取る関数を適用するのでしょうか? ```haskell > (+) <$> (Just 5) Just (+5) > Just (+5) <$> (Just 4) ERROR ??? WHAT DOES THIS EVEN MEAN WHY IS THE FUNCTION WRAPPED IN A JUST ``` Applicativeなら: ```haskell > (+) <$> (Just 5) Just (+5) > Just (+5) <*> (Just 3) Just 8 ``` `Functor` を横に追いやりながら `Applicative` は言います。 「大物は引数をいくつ取る関数だって扱えるんだぜ?」 「`<$>` と `<*>` を使ってオレは、箱を開いてみることができない関数どもに、箱の中の値を渡してやって、しまいに値を箱にいれて返すこともできるのさ! ぷははは!」 ```haskell > (*) <$> Just 5 <*> Just 3 Just 15 ``` ![TaTdV.gif] _functorが関数を適用するのを見ていたapplicative_ でもちょっと待って! ここに同じことをする `liftA2` もいます。 ```haskell > liftA2 (*) (Just 5) (Just 3) Just 15 ``` # Monad モナドを学ぶ方法: 1. 情報工学の博士号を取る 2. ここでは博士号は要らないので取らない モナドは新しい解法を提示しました。 Functorは箱に入っている値に関数を適用できます。 ![fmap.png] Applicativeは箱に入っている値に、箱に入っている関数を適用できます。 ![applicative.png] モナドは箱に入っている値に、 **箱に入った値を返す** 関数を適用できます。 モナドはこのようなことをする("bind"と呼ばれる) `>>=` という関数を持っています。 例を見てみましょう。これまでに見てきた `Maybe` はモナドです。 ![context.png] _遊んでたJust aモナド_ 偶数に対してだけ動作する関数、`half`があると仮定してみましょう。 ```haskell half x = if even x then Just (x `div` 2) else Nothing ``` ![half.png] 値が入っている箱を入れてみるとどうなるでしょうか? ![half_ouch.png] 関数に値が入っている箱を押し込めたいなら、`>>=` が必要です。ここに `>>=` の写真があります。 ![plunger.jpg] どんなふうに動作するのか確認してみましょう。 ```haskell > Just 3 >>= half Nothing > Just 4 >>= half Just 2 > Nothing >>= half Nothing ``` 内部でどんなことが起こっているのでしょうか? `Monad`はまた別の[typeclass]です。ここに定義の一部が載っています。 ```haskell class Monad m where (>>=) :: m a -> (a -> m b) -> m b ``` `>>=`は、 ![bind_def.png] したがって、`Maybe`はモナドです。 ```haskell instance Monad Maybe where Nothing >>= func = Nothing Just val >>= func = func val ``` ここに `Just 3` についてどうやって動作しているか載っています! ![monad_just.png] `Nothing` を入れるともっと単純になります。 ![monad_nothing.png] このようにチェーン(chain)で呼び出すこともできます。 ```haskell > Just 20 >>= half >>= half >>= half Nothing ``` ![monad_chain.png] ![whoa.png] カッチョイイです。もう私たちは `Maybe` が `Functor` であり、`Applicative` であり、`Monad` であることが分かりました。 それではそろそろ、`IO`モナドの例に行ってみましょう。 ![io.png] > 訳注: 数字の10ではなく、英語のIO 3つの特別な関数があります。 `getLine` は引数を取らず、ユーザの入力を受け取ります。 ![getLine.png] ```haskell getLine :: IO String ``` `readFile` は文字列(ファイル名)を受け取り、ファイルの中身を返します。 ![readFile.png] ```haskell readFile :: FilePath -> IO String ``` `putStrLn` は文字列を受け取り出力します。 ![putStrLn.png] ```haskell putStrLn :: String -> IO () ``` ここに出てきた3つの関数は普通の値を受け取り(または何も受け取らず)、値が入っている箱を返します。したがって、私たちはこれを `>>=` でつなぐことができます。 ![monad_io.png] ```haskell getLine >>= readFile >>= putStrLn ``` なんということでしょう! モナドのショーが巻き起こりました! 付け加えると、Haskellはこのモナドについて、`do` というシュガーシンタクスを提供してくれています。 ```haskell foo = do filename <- getLine contents <- readFile filename putStrLn contents ``` # まとめ 1. functorは `Functor` 型クラスを実装したデータ型。 2. applicativeは `Applicative` 型クラスを実装したデータ型。 3. モナドは `Monad` 型クラスを実装したデータ型。 4. `Maybe` はこの3つをすべて実装しており、functorでありapplicativeでありモナドでもある。 この3つの違いは何か? ![recap.png] * functor: `fmap` や `<$>` を使って、箱の中の値に関数を適用できる * applicative: `liftA` や `<*>` を使って、箱のなかの関数を箱の中の値に適用できる * モナド: `liftM` や `>>=` を使って、箱に入った値を返す関数を箱の中の値に適用できる わたしたちはみんな、モナドというものが簡単で粋な概念だということについて、同意することと思います。このガイドで満足のはまだ早いです。[LYAH’s section on Monads]をチェックしてみてください。Miranがすでにモナドに関する深く素晴らしい働きをしているので、だいぶざっくり説明してきました。 [ロシア語バージョン](http://habrahabr.ru/post/183150/)もあります。もっとモナドと絵を見たい方は [three useful monads]を見てみてください。 > 訳注: [韓国語版](https://github.com/netpyoung/netpyoung.Notes/tree/master/external/haskell/functors_applicatives_and_monads_in_pictures)、[中国語版](http://jiyinyiyong.github.io/monads-in-pictures/)もあります。 [applicative_just.png]: https://qiita-image-store.s3.amazonaws.com/0/889/35f914f4-56ab-1886-54ac-ac1c9140c6d1.png [applicative_list.png]: https://qiita-image-store.s3.amazonaws.com/0/889/ccb74a3b-510c-26bf-5cb2-f3b70a709a25.png [applicative.png]: https://qiita-image-store.s3.amazonaws.com/0/889/a2f405ee-921d-fe04-7e20-520e2a3f94c4.png [bill.png]: https://qiita-image-store.s3.amazonaws.com/0/889/5f588ef0-c059-60c1-30ec-b130677d56ee.png [bind_def.png]: https://qiita-image-store.s3.amazonaws.com/0/889/da5452e0-306c-4b0b-2cb5-cab6b1face03.png [context.png]: https://qiita-image-store.s3.amazonaws.com/0/889/08655d17-653d-0970-ad5f-17f284e6b94e.png [fmap_apply.png]: https://qiita-image-store.s3.amazonaws.com/0/889/5ccd102b-091c-2eec-51d5-f27b3af08299.png [fmap_def.png]: https://qiita-image-store.s3.amazonaws.com/0/889/5f652556-4e92-b40d-058f-5fdcf7745361.png [fmap_function.png]: https://qiita-image-store.s3.amazonaws.com/0/889/9e661dfa-9bff-5a69-567e-414173d192d1.png [fmap_just.png]: https://qiita-image-store.s3.amazonaws.com/0/889/0619238e-831d-c62a-d1fc-cb221873c906.png [fmap_list.png]: https://qiita-image-store.s3.amazonaws.com/0/889/c1bd7404-87e8-6d02-8783-ba2ddc6eccc8.png [fmap_nothing.png]: https://qiita-image-store.s3.amazonaws.com/0/889/371d95b1-e307-0b32-69bc-d167fdb7d58e.png [fmap.png]: https://qiita-image-store.s3.amazonaws.com/0/889/f8c8ccd7-8e4c-d183-f1eb-c5eaec1411da.png [function_and_context.png]: https://qiita-image-store.s3.amazonaws.com/0/889/ee849c4c-9808-2433-21ad-954975583e5c.png [function_with_value.png]: https://qiita-image-store.s3.amazonaws.com/0/889/57fdb86f-d720-20a2-42a7-6d03ac59a3f2.png [functor_def.png]: https://qiita-image-store.s3.amazonaws.com/0/889/ae77308c-fc46-831a-657e-0e8dc73130f2.png [getLine.png]: https://qiita-image-store.s3.amazonaws.com/0/889/7d4a4b1d-93d0-7b4c-9b68-4dcb73593737.png [half_ouch.png]: https://qiita-image-store.s3.amazonaws.com/0/889/93a3be8d-da9b-8831-2eb3-5844de13c703.png [half.png]: https://qiita-image-store.s3.amazonaws.com/0/889/29ec2195-592c-992b-5e78-6114e000cb23.png [io.png]: https://qiita-image-store.s3.amazonaws.com/0/889/4288a533-09fa-c3d2-b63b-a9ee1e4c5dcb.png [monad_chain.png]: https://qiita-image-store.s3.amazonaws.com/0/889/65a8d729-7b44-91b4-532d-715bcea5b87e.png [monad_io.png]: https://qiita-image-store.s3.amazonaws.com/0/889/dba34d28-5e39-af76-2005-f563a6e31102.png [monad_just.png]: https://qiita-image-store.s3.amazonaws.com/0/889/7371afcc-a3be-aced-9178-5d6939389a57.png [monad_nothing.png]: https://qiita-image-store.s3.amazonaws.com/0/889/b773a289-b2e9-38a5-47b5-e85568990b6f.png [no_fmap_ouch.png]: https://qiita-image-store.s3.amazonaws.com/0/889/6a9b961e-8cec-8451-bb4f-139691f657e8.png [plunger.jpg]: https://qiita-image-store.s3.amazonaws.com/0/889/e4c6d1f0-d900-acd9-eef6-6842e504868b.jpeg [putStrLn.png]: https://qiita-image-store.s3.amazonaws.com/0/889/e669ea2e-c8c4-2e35-0360-0199f36eba1d.png [readFile.png]: https://qiita-image-store.s3.amazonaws.com/0/889/d07d4397-70f0-b91a-af45-b7d34585ebe3.png [recap.png]: https://qiita-image-store.s3.amazonaws.com/0/889/8eda652f-189f-2d22-ca3f-cc90e6e7737d.png [TaTdV.gif]: https://qiita-image-store.s3.amazonaws.com/0/889/188d4be3-36f0-f990-d87c-ef7b404556ce.gif [value_and_context.png]: https://qiita-image-store.s3.amazonaws.com/0/889/e08fabf4-8abc-95a8-dc28-30e762ee29f8.png [value_apply.png]: https://qiita-image-store.s3.amazonaws.com/0/889/b0a88333-3794-e575-89b8-7d683d5b3d36.png [value.png]: https://qiita-image-store.s3.amazonaws.com/0/889/82c3dfde-98f4-f137-8457-34fbb532b11d.png [whoa.png]: https://qiita-image-store.s3.amazonaws.com/0/889/99e38ffb-1d72-ab05-32e8-9482baa30993.png [typeclass]: http://learnyouahaskell.com/types-and-typeclasses#typeclasses-101 [function composition]: http://en.wikipedia.org/wiki/Function_composition [three useful monads]: http://adit.io/posts/2013-06-10-three-useful-monads.html [LYAH’s section on Monads]: http://learnyouahaskell.com/a-fistful-of-monads |
|
| 160位 |
|
|||
|
15:16:32 |
|
|
自分用メモ。ごちゃごちゃすると忘れるので、なるべくシンプルにまとめたい。
誤り、不備などあれば、随時追加修正します(ご指摘ありがとうございます)。 --- ## クロスサイトスクリプティング(cross site scripting、XSS) ### 概要 訪問者に目的のサイトとは別の罠サイトを踏ませて不正な処理を実行させる行為。 ### 原因 フォームから受け取った値を、エスケープせずに画面に出力するために発生 (偽のフォームを作成する手法も有るので、JavaScriptの対策だけでは不足) ### 対策 * HTMLの実体参照を用い、& を &amp; に、< を &lt; に、> を &gt; に、" を &quot; に、それぞれ置換する。 PHPではhtmlspecialchars関数を用いれば、一括で対策できる (ただしENT_QUOTESを設定しないとシングルクォーテーションはエスケープされない) ```php <?php echo htmlspecialchars($val, ENT_QUOTES); ?> ``` * タグの属性値は必ず "~" (ダブルクオート)で括る。また属性値中のエスケープを忘れない。 かつ、「`style` `href` `src` `onclick` `onload` などの危険な属性に対してユーザー入力を用いない」 * cookieを使う際の、HttpOnlyを指定(JavaScriptからクッキーを読み込まれないようにする) ``` php <?php ini_set('session.cookie_httponly', true); ?> ``` ## SQLインジェクション(SQL Injection) ### 概要 アプリケーションの意図しないSQLを実行させる。 ### 原因 外部から受け取った値を、エスケープせずにSQLを実行するために発生。 ###対策 * プレースホルダを使用する(推奨)。 ## ディレクトリトラバーサル(Directory Traversal) ### 概要 意図しないファイルにアクセスされる。 ### 原因 外部から受け取った値を、エスケープせずにファイル名を取得するために発生。 ### 対策 * 外部からファイル名を要求する仕様を避ける * 要求する場合はファイル名からディレクトリ情報を取り除く * PHPではbasename関数を用いれば、対策できる ```php <?php /*ファイルを開く場合は、固定ディレクトリ+ファイル名にする 参考URL//http://www.websec-room.com/2013/03/21/694 */ $DIRECTRY_PATH = "/var/files/"; //ファイル名取得 $fileName = basename("../../../etc/passwd"); //パス結合($pathの中身 /var/files/passwd) $path = $DIRECTRY_PATH . $fileName; ?> ``` * 備考…… $_FILES 変数に関しては、 PHP5.3.7 以降はディレクトリトラバーサル対策済み。 ## セッションハイジャック ### 概要 本利用者のセッションを攻撃者に乗っ取られる ### 原因 セッションIDの推測、強要、盗み出し etc... ### 対策 * 推測されにくセッションIDを生成する * 認証後にセッションIDを変更する ```php // PHP5.1.0以降 <?php session_start(); session_regenerate_id(TRUE); ?> ``` PHP5.1.0までは、古いセッション情報が削除されずにそのまま残っているため書き方が変わる ```php // PHP5.1.0以前 <?php session_start(); $old_id = session_id(); session_regenerate_id(); unlink(session_save_path() . 'sess_' . $old_id); ?> ``` * パスワードの再入力(保険的対策) ## OSコマンドインジェクション(OS command injection ) ### 概要 アプリケーションの意図しないシェルを実行させる (system(), passthru(), proc_open(), shell_exec(), )。 ### 原因 外部から受け取った値を、エスケープせずにシェルコマンドを実行させるために発生。 ### 対策 * 外部から受け取った値をコマンドラインのパラメータに渡さない(推奨)。 * 使用する場合は以下の文字列をサニタイジングする (想定されているパラメータの指定が英数字限定であれば、受け取る値をそれのみにしてやれば漏れが発生しづらい) ## クロスサイトリクエストフォージェリ(Cross Site Request Forgeries) ### 概要 正規の利用者に意図せず何らかの操作(書き込み、パスワードの変更etc..)を行わせる。 ### 原因 以下のWebの性質から起因する form要素のaction属性にはどのドメインのURLも使用できるため。 クッキーに保管されたセッションIDは、対象サイトに自動的に送信されるため。 ### 対策 決済実行など、必要な処理の画面の直前に、以下の対策をとる * トークンを埋め込む ```html:html側 //HTML側のhiddenにトークンを埋め込む <input type="hidden" name="token" value="<?php echo htmlspecialchars(session_id(),ENT_COMPAT, 'UTF-8'); ?>" ``` ```php:php側 //送られるトークンが想定されているものか検証する if(session_id() !== @$_POST['token']){ //不正な処理 } ``` * Refererのチェック * パスワードの再入力 ### 備考 ####XSSとの違い * CSRFはサーバ側の処理を悪用する。 * XSSはブラウザ上の処理を悪用する(JavaScriptの使ってサーバ側の機能の悪用も可能) *参考にされて、脆弱性が発生しても、責任は取れません ## 参考文献 書籍 * 『体系的に学ぶ 安全なWebアプリケーションの作り方』著 徳丸 浩 ネット * ウィキペディア * 『体系的に学ぶ 安全なWebアプリケーションの作り方』著 徳丸 浩 * 覇王色を求めて * http://d.hatena.ne.jp/ryster/20080229/1204304780 * Webセキュリテリの小部屋 * http://www.websec-room.com/2013/03/21/694 * yohgaki's blog * http://blog.ohgaki.net/session_regenerate_id_wo * KIAI@PG * http://d.hatena.ne.jp/owa-tayo/20100129/1264727922 * to-R * http://blog.webcreativepark.net/2007/02/03-013141.html * Shoulder.jp * http://canalize.jp/archives/009281.php |
|
| 161位 |
|
|||
|
00:38:12 |
(is-lab 所属) |
|
### 更新履歴 2015年2月2日 :MacYosemiteから再設定をしました。 2015年2月9日 :MacYosemite1から構築し、補足関連を追加 2015年12月21日:一部環境の変化があった為、補足関連などを更新 2016年4月30日 :新MacOS El Capitanに対応 筆者環境 - | 環境 |バージョン |備考 | |:-----------|:----------------------|:---------------| | MacOSX | 10.10.5 |MacBookProRatina 13インチ,macbook air| | Homebrew | 0.9.5 || | Ruby | 2.1系、2.2系、2.3系 |最新バージョン| | Rails | 3.1系,4.0系,4.1系、4.2系 |3系,4系ともに高負荷(月間500万PV)にて実績あり| ```前提条件 【重要】Mac環境を前提としております。Windowsでは現状残念ながらunicornのmakeに失敗するので、うまく行きません。 Windowsの場合は、VMwareをご使用下さい。vagrant 環境でも可能です。 ``` 1.macportを削除すること 2.Xcodeをインストールしてあること。 3.Xcodeのコマンドラインツールをインストールしてあること 4.rvmを使っている人は「rvm」を削除すること 5..bash_profile に変なことが書いてある人は消すこと。 6.万が一の為にタイムマシンでバックアップをとっておいた方が幸せ。 7.MYSQLを使う人というかほとんどだと思いますが、MYSQLのインストール 8.MacOSが「El Capitan」の場合は「SIP」を無効化する必要性があります。 手順は以下参照 ■URL [SIPの無効化](http://rcmdnk.github.io/blog/2015/10/10/computer-mac/) ## Xcodeのインストール XcodeをAppstoreから落としてきて下さい。 ■コマンドラインツールのインストール 1.「Xcodeを起動」 2.「Xcode」メニューから「Preferences」を選択。 3.「General」パネルで「Downloads」をクリック。 4.「Downloads」ウィンドウで「Components」タブを選択。 5.「Command Line Tools」の横の「Install」ボタンをクリックします。 ----------------------------- リストにない人はapple Developerからダウンロード ※ 最近はHomebrew本体のインストールで一緒に入ります。 ■URL [apple Developer](https://daw.apple.com/cgi-bin/WebObjects/DSAuthWeb.woa/wa/login?&appIdKey=891bd3417a7776362562d2197f89480a8547b108fd934911bcbea0110d07f757&path=%2F%2Fdownloads%2Findex.action) 1.ログイン 2.「Command Line Tools (OS X Mavericks) for Xcode」をダウンロード 3.dmgパッケージを開いてインストール ----------------------------- ■rvmをインストールしている人は切腹して消しましょう。 ```Terminalコマンド rvm seppuku # => rvm削除コマンド rm -rf ~./rvm # => ディレクトリも消しましょう! ``` ↑↑↑↑↑↑ここまで前提条件↑↑↑↑↑ - ↓↓↓↓↓↓ここから、インストール↓↓↓↓↓ - ## Homebrew本体のインストール ```Terminalコマンド ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" # => 途中パスワードを入力必要があればログイン時パスワードを入力してください。 ``` ```Terminalコマンドコメ ※ このコマンドはやりたい人だけやってください。 sudo mv /opt/local ~/macports # => macportが原因なので、上記コマンドで削除。 # => こちらは失敗する可能性もありますが、無視して大丈夫です。 ``` ```Terminal brew doctor # => Your system is ready to brew. # => これが出れば成功 ``` ``` brew update brew -v => Homebrew 0.9.9 (git revision fdf5; last commit 2016-08-10) => Homebrew/homebrew-core (git revision df62; last commit 2016-08-10) この結果が出るイメージを想定してください。 ``` ## rbenvのインストール ```コマンド brew install rbenv 必要に応じて実施してください。 echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile brew install ruby-build brew install rbenv-gemset brew install rbenv-gem-rehash ``` ## rbenvの更新 ・念の為、やっておきましょう。 ```コマンド rm -rf ~/.rbenv/plugins/ruby-build git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build ``` ## 開発用ツールインストール ・念の為、やっておきましょう ``` brew install readline ※ xcode 4.2.1 〜 gccが別のものになっているので以下コマンドを実行する。 brew search gcc brew tap homebrew/dupes brew install apple-gcc42 ``` ### 確認 ```rbenvコマンド一覧 rbenv --version # => バージョン確認 rbenv install --list # => インストール可能なバージョン一覧の表示 rbenv install [バージョン] # => rubyのインストール rbenv rehash # => rbenv の再読み込み rbenv global [バージョン] # => defaultで使うrubyのバージョン ``` Yosemiteの人で上手く行かなかったら以下のコマンドを試してみてください。 ``` export CC=/usr/bin/gcc rbenv install 2.3.1 => 結構時間かかります。 ``` 最後にターミナルを再起動しましょう。 ※ PATH系のエラーが出た場合、PATHに設定を見直すことを推奨します。 少し知識がないと難しいですが、ちなみに私の「.bash_profile」は以下の通り ```Terminalコマンド vim ~/.bash_profile # => プロファイルの編集 # これを書いてください。 export PATH=/usr/local/bin:$PATH if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi # ここまでーーーー source ~/.bash_profile # => プロファイルの再読み込み which ruby # => /Users/*******/.rbenv/shims/ruby # こんな感じのが帰ってきたらOKです。 ``` ```参考:私の.bash_profileの中身 # MySQL Path Setting export PATH=$PATH:/usr/local/mysql/bin => mysqlコマンドのパス export PATH=/usr/local/bin:$PATH if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi ``` ### 最新バージョンのRubyを入れてみよう ``` rbenv install 2.3.1 # => rbenvでrubyをインストールしていきます。時間かかります。 rbenv global 2.3.1 # => rbenvのdefaultのバージョンを2.0.0にしていきます。 gem install bundler # => bundle コマンドを使いためのGemをインストールします。 gem install rails # => 最新版Railsをインストールします。現在は4.1.5がインストールされます。時間かかります。 # 4.1系はライブラリなど一部対応していないものもある為、3.2系の最新バージョンが良いかもしれません。 rails new piyopiyo # => piyopiyoというプロジェクト名でrailsを構築します。 cd piyopiyo # => piyopiyoというフォルダができるので移動します。 bundle install # => ライブラリ関連をインストールしましょう! rails s # => 早速上げてみましょう!設定関連をしていない為、もしかしたら上がらないかも ``` 通常Rails4系が入ります。 Rails3系を入れたい場合 ``` gem list rails ■結果 rails (4.1.0, 4.0.4, 3.1.0) rails-erd (1.1.0) rails-footnotes (3.7.9) rails3-generators (1.0.0) rails3_acts_as_paranoid (0.1.4) rails_admin (0.6.2) ``` この場合 rails(4.1.0,4.0.4,3.1.0) がインストール可能です。 4.0.4をインストールしたい場合、以下コマンド実行にて可能となります。 rails _4.0.4_ new piyopiyo |
|
| 162位 |
|
|||
|
12:10:35 |
|
|
オライリージャパンから[『実践 Vagrant』](http://www.oreilly.co.jp/books/9784873116655/)が発売されるなど注目を集めているVagrantですが、Vagrantをより便利に使うためにプラグインを利用することができます。私が個人的に使用して、便利だと思ったプラグインについてまとめました。 ## 便利なプラグイン6選 1. sahara 2. vagrant-cachier 3. vagrant-global-status 4. vagrant-omnibus 5. vagrant-vbguest 6. vagrant-vbox-snapshot ### 1. sahara - [jedi4ever/sahara · GitHub](https://github.com/jedi4ever/sahara) サンドボックスモードを有効にするプラグインです。サンドボックスモード内で実行した操作はロールバック機能で実行前の状態に戻すことができます。実行した操作を確定したい場合はコミットを実行します。Chefのレシピをいろいろ試したい場合などに便利に使えます。 #### インストール方法 ``` $ vagrant plugin install sahara ``` #### 操作方法 ```sh # sandboxモード実行 $ vagrant sandbox on # ロールバック $ vagrant sandbox rollback # コミット $ vagrant sandbox commit # sandboxモード終了(コミットしていない変更は削除) $ vagrant sandbox off # sandboxのステータス確認 $ vagrant sandbox status ``` ### 2. vagrant-cachier - [fgrehm/vagrant-cachier · GitHub](http://fgrehm.viewdocs.io/vagrant-cachier) `yum`や`apt-get`などパッケージ管理システムのパッケージをローカルにキャッシュすることでプロヴィジョニングの実行を高速化するプラグインです。仮想端末を頻繁にスクラップ・ビルドする場合は時間の節約になります。 正直に言うと、個人的に使っている分にはあまり高速化の実感がないのですが、無いよりは有った方が良いかなと思っています。 #### インストール方法 ```sh $ vagrant plugin install vagrant-cachier ``` #### 最低限の設定 ```rb # Vagrantfile Vagrant.configure("2") do |config| config.vm.box = 'your-box' if Vagrant.has_plugin?("vagrant-cachier") config.cache.scope = :box end end ``` ### 3. vagrant-global-status - [fgrehm/vagrant-global-status · GitHub](https://github.com/fgrehm/vagrant-global-status) ホスト端末内のすべての仮想端末の情報をまとめて一覧表示してくれるプラグインです。気がつくと仮想端末があちらこちらに散在しているという状況を防ぐことができます。地味にとても便利です。 #### インストール方法 ```sh $ vagrant plugin install vagrant-global-status ``` #### 操作方法 ```sh $ vagrant global-status --all ``` カレントディレクトリはどこでもOKです。 ### 4. vagrant-omnibus - [schisamo/vagrant-omnibus · GitHub](https://github.com/schisamo/vagrant-omnibus) 仮想端末のChefのインストール状況を確認して、必要があれば自動的にインストールしてくれるプラグインです。手動でChefをインストールする手間から解放されます。 #### インストール方法 ```sh $ vagrant plugin install vagrant-omnibus ``` ### 5. vagrant-vbguest - [dotless-de/vagrant-vbguest · GitHub](https://github.com/dotless-de/vagrant-vbguest) 仮想端末がVirtualboxの場合、仮想端末のVirtualbox-guest-addtionのインストール状況を確認して、必要があれば自動的にインストールしてくれるプラグインです。手動でGuest addtionをインストールする手間から解放されます。 #### インストール方法 ```sh $ vagrant plugin install vagrant-vbguest ``` ### 6. vagrant-vbox-snapshot - [dergachev/vbox-snapshot · GitHub](https://github.com/dergachev/vagrant-vbox-snapshot) 仮想端末のスナップショットを管理するプラグインです。先に紹介したsaharaと一部の機能がかぶっているように見えますが、saharaが主に作業中の環境を管理するのに対して、こちらはポイントごとに取得したスナップショットを管理するために使用できます。 #### インストール方法 ```sh $ vagrant plugin install vagrant-vbox-snapshot ``` #### 操作方法 ```sh # スナップショットの取得 $ vagrant snapshot take <snapshot-name> # 直前のスナップショットの復元 $ vagrant snapshot back # 任意のスナップショットの復元 $ vagrant snapshot go <snapshot-name> # スナップショットの削除 $ vagrant snapshot delete <snapshot-nam> # スナップショットの一覧表示 $ vagrant snapshot list ``` 以上、Vagrantプラグインのまとめでした。 |
|
| 163位 |
|
|||
|
15:39:56 |
|
|
# はじめに
わたしがObjective-C を勉強し始めた時にこういう記事に出会えていれば… と思った記事を作りたいということで書きました。 著者はもともと Java ユーザーで、 Objective-C は1~2年目のドシロートです。 <del>記事内に誤りや改善点がありましたら、編集リクエストいただければ随時反映いたします。 (編集理由を併記していただけると嬉しいです)</del> **2014/03/17時点で本稿の修正は打ち切りました。閲覧時点で情報が古い・または不正確な可能性がありますことを予めご了承下さい。** # クラスと型 Objective-C 側はなるべく流行りの記法を意識しています。 ## 整数値 #### プリミティブ型 **Java** ```java int num1 = 10; long num2 = 20L; ``` **Objective-C** ```objc NSInteger num1 = 10; NSInteger num2 = 20L; ``` 型やバイト長を特別強く意識しない限りは int も long も NSInteger でOK #### オブジェクト型 **Java** ```java Integer num1 = 10; // Integer.valueOf(10); Long num2 = 20L; // Long.valueOf(20L); ``` **Objective-C** ```objc NSNumber *num1 = @10; NSNumber *num2 = @20L; ``` `@10` は古い記法でいうと `[NSNumber numberWithInt:10]` に相当。 各型に対応した様々な書き方があります。 * `@10` (int) * `@10U` (unsigned int) * `@10L` (long) * `@10UL` (unsigned long) * `@10.5f` (float) * `@10.5` (double) * `@YES` `@NO` (BOOL) 詳細な仕様は下記ドキュメントを参照。 →[Objective-C Literals](http://clang.llvm.org/docs/ObjectiveCLiterals.html) 概要はこちらのブログ記事がわかりやすいです。 →[Modern Objective-Cで実現するシンプルコーディングのススメ](http://moomindani.wordpress.com/2012/08/19/modern-objective-c/) ## 文字列 **Java** ```java String str1 = "hello!"; String jpStr = "こんにちは!"; // 連結 String str2 = str1 + "world!"; System.out.println(str2); // hello!world! // 置換 String str3 = str1.replaceAll("l", "L"); System.out.println(str3); // heLLo! ``` **Objective-C** ```objc // NSString リテラル記法 NSString *str1 = @"hello!"; NSString *jpStr = @"こんにちは!"; // 連結。他言語のように加算演算子 + は利用できない NSString *str2 = [str1 stringByAppendingString:@"world!"]; NSLog(@"%@", str2); // hello!world! // 置換 NSString *str3 = [str1 stringByReplacingOccurrencesOfString:@"l" withString:@"L"]; NSLog(@"%@", str3); // heLLo! ``` **文字列を操作するだけでこんなにたくさんキーが打てます。** **そう、 Objective-C ならね。** また、次のようにしてC言語の文字列からも NSString を得られます。 ```objc // C言語の文字列 "..." char *cStr = "これはC言語の文字列です"; NSString *strFromC = @(cStr); // 古い記法 // NSString *strFromC = [NSString stringWithUTF8String:cStr]; ``` ## 論理値 **Java** ```java boolean done = true; boolean isEmpty = false; ``` **Objective-C** ```objc BOOL done = YES; // 1 BOOL isEmpty = NO; // 0 ``` 実体が数値な点にご注意。 ## 汎用のオブジェクト型 **Java** ```java Object obj = new Object(); Object foo = null; ``` **Objective-C** ```objc id obj = [[NSObject alloc] init]; id foo = nil; ``` `nil` は Java の `null` とは違い、例えば上記サンプルの `id foo` に メッセージを送信(メソッド呼び出し)しても例外にはならない点にご注意。 詳細は Apple のドキュメントを参照 →[Objective-Cによるプログラミング](https://developer.apple.com/jp/devcenter/ios/library/documentation/ProgrammingWithObjectiveC.pdf) * p.41「nilの取り扱い」 * p.102「nilをNSNullで表す」 ## コレクション | Java | Objective-C | |---|---| | List | NSArray, NSMutableArray | | Map | NSDictionary, NSMutableDictionary | | Set | NSSet, NSMutableSet | Objective-C のコレクションクラスの要素は id 型で扱うので、 一つのコレクションに複数の異なる型を入れることができます。 逆に、取り出すときの型にも注意してください。 また、 **各種コレクションは nil を格納できません。** 値がないことを要素として格納したい場合は `NSNull` を使います。 ### List **Java** ```java List<Integer> nums = new ArrayList<>(); nums.add(10); nums.add(25); nums.add(38); for (Integer i : nums) { System.out.println(i); } System.out.println(nums.size()); // 3 System.out.println(nums.isEmpty()); // false ``` **Objective-C** ```objc // リテラル記法 @[ ... ] で作った NSArray は Immutable(不変) // NSArray *nums = @[ @10, @25, @38 ]; // 要素の追加や削除がある場合は NSMutableArray を使う NSMutableArray *nums = [NSMutableArray array]; [nums addObject:@10]; [nums addObject:@25]; [nums addObject:@38]; for (NSNumber *i in nums) { NSLog(@"%@", i); } NSLog(@"%d", [nums count]); // 3 NSLog(@"%d", ([nums count] == 0)); // 0 ``` `[nums count]` は `nums.count` でもOK。 `Collection#isEmpty()` にあたるメソッドがないので、空かどうかの検査は要素数を調べます。 ### Map **Java** ```java Map<String, String> params = new HashMap<>(); params.put("Content-Type", "text/html"); params.put("Connection", "keep-alive"); Iterator<String> keysIt = params.keySet().iterator(); for (String key : keysIt) { String value = params.get(key); System.out.println(String.format("%s = %s", key, value)); } ``` **Objective-C** ```objc NSMutableDictionary *params = [NSMutableDictionary dictionary]; params[@"Content-Type"] = @"text/html"; params[@"Connection"] = @"keep-alive"; // 古い記法 // [params setObject:@"text/html" forKey:@"Content-Type"]; // [params setObject:@"keep-alive" forKey:@"Connection"]; for (NSString *key in params) { NSString *value = params[key]; NSLog(@"%@ = %@", key, value); // 古い記法 // NSString *value = [params objectForKey:key]; } ``` ### Set **Java** ```java Set<String> names = new HashSet<>(); names.add("john"); names.add("mary"); names.add("bob"); for (String name : names) { System.out.println(name); } ``` **Objective-C** ```objc NSMutableSet *names = [NSMutableSet set]; [names addObject:@"john"]; [names addObject:@"mary"]; [names addObject:@"bob"]; for (NSString *name in names) { NSLog(@"%@", name); } ``` Set にはリテラル記法がありません。 # 文法とか ## 命名規則 [Cocoa向けコーディングスタイルガイドライン](https://developer.apple.com/jp/devcenter/ios/library/documentation/codingguidelines.pdf) を参照。 人気のオープンソースライブラリなどを見てみても、 これに準拠したスタイルが多く見受けられます。 基本的には iOS SDK のコンポーネントを真似すればだいたいOKです。 外部に公開するフレームワーク等、名前が衝突する可能性があるコードを書く場合は 自作するクラスに **大文字3文字** の接頭辞を付与します。 なお、大文字2文字の接頭辞はAppleによって予約されています。 [Programming with Objective-C: Conventions](https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html) > In order to keep class names unique, the convention is to use prefixes on all classes. […] **Two-letter prefixes like these are reserved by Apple** for use in framework classes. […] **Your own classes should use three letter prefixes.** 自作アプリなどの閉鎖されたコードであれば、接頭辞は無くても構いません。 ちなみに本稿の各サンプルコードは MySampleApp から MSA という接頭辞にしてみました。 ## メソッドの文化 `[obj someMethodWithArg:myArg1 andSecondArg:myArg2];` Java から移ってくるとどうしてもメソッド呼び出しに違和感がありますが やってればそのうち慣れます。こればっかりは慣れです。 例えば文字列型のところで使ったサンプルコードについて、 ```objc [str1 stringByAppendingString:@"world!"]; ``` これは、ちょっと強引ですが次のように読めます。 * str1 を使って * string を得たい。どんな string かというと(By) * 何かをつけたしたものだ。(Appending) * つけたすのは文字列 @"world!" だ。(String:@"world") "stringByAppending" が「文字列を足した結果を得るメソッド」を表し、 `@"world!"` の直前にある "String" は第1引数の型を示唆しています。 区切って並べると英文のように読めるのが特徴ですね。 ## クラスの定義 **Java** ```java:Parson.java public class Parson { private String name; public Parson(String name) { setName(name); } public void setName(String name) { this.name = name; } public String getName() { return name; } public void greet() { String message = buildGreetingMessage(getName()); System.out.println(message); } @Override public String toString() { return "Parson{name=" + name + "}"; } private static String buildGreetingMessage(String name) { return String.format("hi, my name is %s", name); } } ``` **Objective-C** ```objc:Parson.h #import <Foundation/Foundation.h> @interface Parson : NSObject @property (nonatomic, copy) NSString *name; - (void)greet; @end ``` ```objc:Parson.m #import "Parson.h" // private なメンバは無名カテゴリを利用して定義する @interface Parson() + (NSString *)buildGreetingMessage:(NSString *)name; @end @implementation Parson - (void)greet { NSString *message = [self buildGreetingMessage:self.name]; NSLog(@"%@", message); } // JavaのObject#toString()に相当 - (NSString *)description { return [NSString stringWithFormat:@"Parson{name=%@}", self.name]; } + (NSString *)buildGreetingMessage:(NSString *)name { return [NSString stringWithFormat:@"hi, my name is %@", name]; } @end ``` ややムリヤリ感はありますが、基本文法を詰め込んでみました。 `@property` の属性については次の投稿がわかりやすいです。 →[Objective-C のプロパティ属性のガイドライン](http://qiita.com/uasi/items/80660f9aa20afaf671f3) @uasi よく使う型に関して、次の点に気をつけておけばとりあえず前に進めます。 * 基本的に `nonatomic` を指定 * NSString や NSArray など、NSMutable~ なサブクラスがある型は `copy` を追加 * Java/Android開発でWeakReferenceを使いたくなるようなモノには `weak` を追加 * 例外的なケースがいくつかあるので注意。→[weak 参照してはいけない Cocoa のクラス一覧](http://qiita.com/uasi/items/ee71a476ec3ef9c3c10d) @uasi アクセサに副作用を持たせたりする必要がなければ、プロパティの定義は ヘッダーファイルの `@property` による宣言だけでOKです。 この場合の内部動作はこちらの記事で分かりやすく解説されています。 →[Modern Objective-Cでのシンプルなプロパティ記述方式](http://qiita.com/KUMAN/items/8bcd083e59d68ad94927) @KUMAN 詳しい言語仕様は Apple のドキュメントを参照。 →[Objective-Cによるプログラミング](https://developer.apple.com/jp/devcenter/ios/library/documentation/ProgrammingWithObjectiveC.pdf) ## 列挙の定義 **Java** ```java:Issue.java public class Issue { private String title; private String message; private Status status = Status.NEW; public static enum Status { NEW, IN_PROCESS, CLOSED, } // getter, setter, constructor... } ``` **Objective-C** ```objc:MySampleApp/MSAIssue.h #import <Foundation/Foundation.h> typedef NS_ENUM(NSInteger, MSAIssueStatus) { MSAIssueStatusNew, MSAIssueStatusInProcess, MSAIssueStatusClosed, }; @interface MSAIssue : NSObject @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *message; @property (nonatomic) MSAIssueStatus status; @end ``` 列挙定義のところは、 ```objc typedef enum MSAIssue : NSInteger { MSAIssueStatusNew, ... }; ``` と書いても **ほぼ** 同じですが、 NS_ENUM を使って定義した場合は switch 文で利用するときに case がすべての値を網羅しているかチェックされるようになります。 ## 行の折り返し位置 ```objc [obj someMethod:arg1 withFoo:foo withBar:bar]; ``` Obejctive-C では、メソッドシグネチャの ":" の高さを合わせます。 ## 改行位置 ```objc - (void)myMethod { // メソッドのブラケットは新しい行 if (condition) { // ステートメントのブラケットは行内 } // 辞書は任意で読みやすいように NSDictionary *dict = @{ @"foo": @"bar", @"hoge": @"huga", }; NSDictionary *otherDict = @{ @"foo": @"bar", @"hoge": @"huga", @"sample": @"test", }; } ``` ## ドキュメンテーションコメントと文書生成 HeaderDoc が利用できます。 →[HeaderDoc User Guide : Introduction](https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/HeaderDoc/intro/intro.html) サードパーティー製の appledoc も人気のようです。 こちらの記事で詳しく解説されています。 →[appledocでドキュメント生成](http://qiita.com/mtgto/items/c97c59d93fe67d748055) @mtgto ### Q. 文書生成しなくていいからとりあえずコードアシストに説明出したい **A. 行コメントはトリプルスラッシュ、ブロックコメントはJavadocと同じでOK。** ```objc #import <Foundation/Foundation.h> @interface Hoge : NSObject /// なまえ @property (nonatomic, copy) NSString *name; @end @implementation Hoge - (void)test { self.na/* ここでコードアシストを起動 */ } @end ``` **結果**  ## グローバルな定数の定義 **Java** ```java:MyUrlConstants.java package com.example.myapp; public class MyUrlConstants { /** 基底URL */ public static final String BASE_URL = "http://qiita.com"; /** ログインページのパス */ public static final String PATH_LOGIN = "/login"; } ``` ```java:SomeLogic.java package com.example.myapp.foo; import com.example.myapp.MyUrlConstants; // あるいは // import static com.example.myapp.MyUrlConstants.BASE_URL; // import static com.example.myapp.MyUrlConstants.PATH_LOGIN; public class SomeLogic { public void someMethod(){ System.out.println(MyUrlConstants.BASE_URL); System.out.println(MyUrlConstants.PATH_LOGIN); // import static の場合 // System.out.println(BASE_URL); // System.out.println(PATH_LOGIN); } } ``` **Objective-C** ```objc:MySampleApp/MSAMyUrlConstants.h #import <Foundation/Foundation.h> @interface MSAMyUrlConstants : NSObject /// 基底URL UIKIT_EXTERN NSString *const MSAMyUrlBaseUrl; /// ログインページのパス UIKIT_EXTERN NSString *const MSAMyUrlPathLogin; @end ``` ```objc:MySampleApp/MSAMyUrlConstants.m #import "MSAMyUrlConstants.h" @implementation MSAMyUrlConstants NSString *const MSAMyUrlBaseUrl = @"http://qiita.com"; NSString *const MSAMyUrlPathLogin = @"/login"; @end ``` ```objc:MySampleApp/MSASomeLogic.h #import <Foundation/Foundation.h> @interface MSASomeLogic :NSObject - (void)someMethod; @end ``` ```objc:MySampleApp/MSASomeLogic.m #import "MSASomeLogic.h" #import "MSAMyUrlConstants.h" @implementation MSASomeLogic - (void)someMethod { NSLog(@"%@", MSAMyUrlBaseUrl); NSLog(@"%@", MSAMyUrlPathLogin); } @end ``` Objective-C (というか C言語)の `static` 修飾子は Java と意味が違うのでご注意。 `UIKIT_EXTERN` は基本的に extern と同じですが、 下に引用したマクロ定義からも分かる通り C と C++ の差を吸収してくれます。 ```objc:UIKitDefines.h #ifdef __cplusplus #define UIKIT_EXTERN extern "C" __attribute__((visibility ("default"))) #else #define UIKIT_EXTERN extern __attribute__((visibility ("default"))) #endif ``` C++ を使う可能性がある、または特に理由がなければ、 `UIKIT_EXTERN` の方が安全です。 こちらの stackoverflow の記事も参考になります。 →[When to use UIKIT_EXTERN vs just extern](http://stackoverflow.com/a/17669269) ### Q. #define MY_CONST_STR @"hoge" とかじゃダメ? **A. マクロを定数として使うのは好ましくない場合が多いようです。** プリプロセッサマクロの定義は、ソース上のマクロ名の部分を文字列置換するようもので、 Java 系で例えるならば Gradle で定数を ReplaceTokens によって埋め込むような行為に近いです。 さらに再定義によって変更できるため、定数として使うのは良くない場合が多いです。 ### Q. コレクション型の静的定数が作れないんだけど **A. 仕様です。** 静的メソッドとして定義し、GCD dispatch_once を使って初期化します。 ```objc:MySampleApp/MSAMyAppConstants.h #import <Foundation/Foundation.h> @interface MSAMyAppConstants : NSObject + (NSArray *)MSAMyAppSearchEngines; @end ``` ```objc:MySampleApp/MSAMyAppConstants.m #import "MSAMyAppConstants.h" @implementation MSAMyAppConstants + (NSArray *)MSAMyAppSearchEngines { static NSArray *searchEngines; static dispatch_once_t onceToken; dispatch_once (&onceToken, ^{ searchEngines = @[ @"Google", @"Yahoo!", @"Bing" ]; }); return searchEngines; } @end ``` メソッド内の static は、「そのメソッド内で静的」。 dispatch_once() は、渡された実行ブロックを一度だけ実行します。 動作的には同一視できないものですが、イメージ的には Java でいうと 静的初期化子を使って凌ぐ書き方に近いですね。 ## (unsigned) int, long と NSInteger, NSUInteger 特別な理由がない限り、整数型は NS~ を使っておけばOK。 NS~ は、実行環境のアーキテクチャに応じて実際の型が適切に変化する模様。 お手元の Xcode で適当に `NSInteger` の変数を⌘+クリックして マクロ定義を見るとわかりやすいです。 ```objc:objc/NSObjCRuntime.h #if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64 typedef long NSInteger; typedef unsigned long NSUInteger; #else typedef int NSInteger; typedef unsigned int NSUInteger; #endif ``` 32bit環境と64bit環境で自動的に適切な大きさの型を採用しています。 ## float, double と CGFloat int vs NSInteger とほぼ同じ理由で、 CGFloat を使っておけばOK。 ```objc:CoreGraphics/CGBase.h #if defined(__LP64__) && __LP64__ # define CGFLOAT_TYPE double # define CGFLOAT_IS_DOUBLE 1 # define CGFLOAT_MIN DBL_MIN # define CGFLOAT_MAX DBL_MAX #else # define CGFLOAT_TYPE float # define CGFLOAT_IS_DOUBLE 0 # define CGFLOAT_MIN FLT_MIN # define CGFLOAT_MAX FLT_MAX #endif ``` これも実行環境によって double, float どちらを型とするか判断しています。 "CG~" の名が示すように CoreGraphics フレームワークの一部ですが iOSアプリケーションプロジェクトを生成するとデフォルトでリンクされているので 特に使用をためらう必要はないと思います。 UIViewのサイズ指定などでよく目にすることになります。 --- 以上です。 思いついたことがあれば随時書き足していこうと思います。 |
|
| 164位 |
|
|||
|
01:49:49 |
(レジュプレス株式会社 所属) |
|
# Rails4のproduction環境でのみ、画像が表示されない問題
## 問題の概要 先日、弊社サービス **[STORYS.JP](http://storys.jp)** をRails3からRails4.0.2にアップデートしました。 **Rails4はRails3に比べて、development環境でのページロードにかかる時間が1/10ぐらいになりました。** 読み込みに **1500ms** ほどかかっていたのが、Rails4にするだけで本番並みの **150ms** で返ってくるようになり、本当に非常に快適に開発ができるようになりました。 しかし、便利なものには弊害も多く、様々な問題がおきます。 その中でも、解決に苦しんだ問題が、 **これまでは正常に表示されていた画像が、Rails4のproduction環境でのみ表示されない問題** です。 ## 問題の原因 問題の原因は、 `app/assets/images` 以下に置いてある画像を直接パスを指定して読み込んでいることでした。 [STORYS.JP](http://storys.jp) では基本的に、HTML, SCSSで画像 `app/assets/images/hoge.png` を参照したいときは次のように書いていました。 ```html+erb:hoge.html.erb <img src="/assets/hoge.png" /> <div style="background-image:url(/assets/hoge.png)" /> ``` SCSSではこんな感じです。 ```scss:hoge.css.scss .hoge { background-image: url(/assets/hoge.png); } ``` しかしこの `/assets/hoge.png` という書き方では、Rails4からは表示されなくなりました。 ### Rails4での assets:precompile productionにdeployするときに実行する `assets:precompile` ですが、 Rails4からは **digest無しのjs, css, image は作成しない仕様になりました** digestとはjs, css, 画像等を `assets:precompile` するとファイル名の後ろにつく文字列のことです。 ``` /assets/application-d34a3eba396a045b8c71d1605256e1a2.css ``` つまり、今までは `app/assets/images/hoge.png` を `assets:precompile` したときに得られる成果物は、 ```:rails3 public/assets/hoge.png public/assets/hoge-○○○○.png ``` であったのに対し ```:rails4 public/assets/hoge-○○○○.png ``` となるようになりました。 したがってproduction環境では `/assets/hoge.png` と指定したファイルを参照することはできません。 ## 解決法1 asset_path関数, image-url関数を使う 一番順当な解決法です。画像のパスの指定をそのまま書くのではなく、Railsが提供している関数を呼ぶことで、productionのときにはdigestを勝手に付与してくれます。 ```html+erb:hoge.html.erb <img src="<%= asset_path "hoge.png" %>" /> <div style="background-image:url(<%= asset_path "hoge.png" %>)" /> ``` SCSSではこんな感じです。 ```scss:hoge.css.scss .hoge { background-image: image-url("hoge.png"); } ``` [Rails GuideのAsset Pipelineの項](http://guides.rubyonrails.org/asset_pipeline.html#coding-links-to-assets)にもう少し詳しく載っています。 ## 解決法2 public/assets 以下に画像を直接置く HTML,SCSSをわざわざRailsの関数を使って書きなおすのはめんどくさい・・・そう考えていた時にあることを思いつきました。 そもそもdigestを付与するのは、あるファイルを更新したときに、ブラウザ側で持っているキャッシュとの相違が無いようにするためのものです。 それはjavascriptやstylesheetならば話はよくわかります。application.jsは良く更新されるので。 しかし、画像ファイルで名前を変更せずに中身を変更することはほとんどありません。そうするならば名前も変更するか、新しいファイルを作るからです。 ならばそもそも `app/assets/images` 以下はdigestを付与しないオプションは無いのかと思い、調べてみたところ、 `assets:precompile` を管理している **[sprockets-rails](https://github.com/rails/sprockets-rails#changes-from-rails-3x)** に次のような記述がありました。 > Only compiles digest filenames. Static non-digest assets should simply live in public/. つまり、コンパイルする必要のないものは、そのままpublic以下に置けと言っています。 確かに、 `assets:precompile` のそもそもを考えればその通りだと思い、今まで `app/assets/images` 以下にあったものを `public/assets` 以下に移動しました。 ```bash cp -r app/assets/images public/assets/ ``` これならば、既存のHTMLやSCSSを書き換える必要すらなく完璧である・・・と思ったのですが、1つ特定の環境において問題が起こりました。 それは **capistranoでのdeploy** です。 ここについては詳しくは調べていませんが、gitの管理下に `public/assets` を含めていても、deploy時には `public/assets` 以下は削除されてしまうようです。 したがって、git管理下に `public/assets/hoge.png` が存在したとしても、deployされた先には `public/assets/hoge.png` は存在しないので画像は表示されません。 もしかしたらcapistranoは public/assets 以下がgit管理下になることを想定されていないのかもしれません。 ## 解決法3 public/images 以下に画像を置く そこで [STORYS.JP](http://storys.jp) ではこの解決法3を採用しました。 画像は基本的に `public/images` 以下に置くようにしました。 ```html+erb:hoge.html.erb <img src="/images/icons/hoge.png" /> <div style="background-image:url(/images/icons/hoge.png)" /> ``` ```scss:hoge.css.scss .hoge { background-image: url(/images/icons/hoge.png); } ``` これの欠点は、既存のファイルを `/assets/icons/hoge.png` -> `/images/icons/hoge.png` に書きなおす必要があること、またproduction環境の nginx, apache 等に記述されているであろう /assets/ へのキャッシュのポリシーを /images にも設定しなくてはいけないことです。 しかし、こうすることでproduction環境でも、無事画像が表示でき、capistranoでのdeployも問題ありませんでした。 この解決法3の良い所はもう一つあります。 それは、完全に `app/assets/images` を使わないことで、間違って `/assets/hoge.png` の記述をしにくいところです。 解決法1では、development環境で `/assets/hoge.png` のような記述をしても問題なく表示されます。 **しかし、production環境では動きません** これは結構やっかいで、コードレビュー等をしていたとしても、軽微な違いなだけに気づきにくく、deployしてから表示されないことに気が付きます。 ```html+erb:hoge.html.erb <img src="/assets/icons/hoge.png" /> <img src="<%= asset_path "icons/hoge.png" %>"/> <!-- この違いを見分けるのは結構難しい !> ``` しかし、常に `app/assets/images` を使わないようにすることで、 `<%= asset_path "icons/hoge.png" %>` のような記述はできなくなり、問題発見が容易になります。 *** **Rails4のproduction環境でのみ画像が表示されない問題の解決策** は以上になります。 もしこの問題が起きた場合は、自分たちの状況、必要な物に応じて好きな選択肢を選んでください。 |
|
| 165位 |
|
|||
|
12:57:57 |
|
|
FrogApps 技術ブログ始めました!
RailsやiOS、HTML5の情報を発信中!! → http://qiita.com/teams/frogapps ---- ここ数年、位置情報を使ったアプリ・サービスが増えましたが、GPSから取得出来る緯度経度だけではデータとして使いにくい事があります。 GoogleのGeocodingサービスなどで、緯度経度から住所への変換ができますが、件数や速度の問題があります。 そこで、国土交通省のデータを元に、緯度経度から住所への変換を行ってみましょう。 ## 国土数値情報ダウンロードサービス - http://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N03.html から全都道府県を選択。最新の情報を全都道府県分選択します。 ## PostGISのセットアップ ``` http://trac.osgeo.org/postgis/wiki/UsersWikiPostGIS20Ubuntu1204 を参考にPostGISをインストールしてください。 sudo -u postgres psql template1 CREATE USER vagrant WITH PASSWORD 'vagrant'; CREATE DATABASE gis WITH ENCODING = 'UTF-8' LC_CTYPE = 'en_US.utf8' LC_COLLATE = 'en_US.utf8' TEMPLATE template0; GRANT ALL PRIVILEGES ON DATABASE gis TO vagrant; sudo -u postgres psql gis -f /usr/share/postgresql/9.1/contrib/postgis-2.0/postgis.sql sudo -u postgres psql gis -f /usr/share/postgresql/9.1/contrib/postgis-2.0/spatial_ref_sys.sql ``` ## shpファイルからPostGISへ取り込み ``` sudo aptitude install unzip nkf # すべてのZIPファイルを解凍 find ./ -name "*.zip" -type f -print0 | xargs -0 -I {} unzip {} # shpファイルからSQLへ変換 shp2pgsql -c N03-13_01_130401.shp shapes > geo.sql find ./ -name "*.shp" -type f -print0 | xargs -0 -I {} shp2pgsql -W "SHIFT-JIS" -a {} shapes | nkf -W >> geo.sql # 取り込み psql gis < geo.sql ``` ## 検索 #### 時計台の位置情報 ``` psql gis SET client_encoding = 'UTF8'; SELECT n03_001,n03_002,n03_003,n03_004 FROM shapes WHERE ST_Within(ST_GeomFromText('POINT(141.35358861111 43.062555)'), geom); n03_001 | n03_002 | n03_003 | n03_004 ---------+---------+---------+--------- 北海道 | | 札幌市 | 中央区 ``` ## テーブルへ変換 PostGISを使えば比較的簡単にできるのですが、変換の為にProduction環境にPostGISを配置するのは面倒です。 なので、先に緯度経度から行政区の変換マップを作りました。 https://github.com/FrogAppsDev/jpcities からダウンロードしてください。 検索がしやすいようにGeohashでインデックスしています。精度は荒いですが大体の位置を把握するには役にたつと思います。 |
|
| 166位 |
|
|||
|
21:31:43 |
(neuro, Inc. 所属) |
|
Railsのテスト環境の定番といえば
+ Rspec + Guard + FactoryGirl + Spork このへんの組み合わせが定番だったんではないでしょうか。 Sporkでテスト環境をプリロードして、Guardでファイルを監視してガンガンテストを回してと。 今回はこのSporkを最近メキメキと頭角を現してきているSpringに置き換えて よりモダンな高速テスト環境の作り方を説明します。 ## Springのいいところ このSpringなにがいいって、設定がすごく簡単。 おまけにGuard+Rspec以外にもrails generateやrake routesなど他のコマンドも高速化してくれます。 一度体験したらもう戻れません。 ## 必要なGem + rspec-rails + guard-rspec + factory_girl_rails + spring ```rb:Gemfile group :development, :test do gem 'rspec-rails' gem 'guard-rspec' gem 'factory_girl_rails' gem 'spring' # これを新しく追加 end ``` ## spec_helperの設定 基本的にいつもどおり。 FactoryGirlを使用する場合は、この記述がないとfactoryの変更が反映されません。 ```rb:spec_helper.rb config.before(:all) do FactoryGirl.reload end ``` 参考までに全文掲載。 ```rb:spec_helper.rb require File.expand_path('../../config/environment', __FILE__) require 'rspec/rails' require 'rspec/autorun' require 'factory_girl' Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) RSpec.configure do |config| config.use_transactional_fixtures = true config.infer_base_class_for_anonymous_controllers = false config.order = 'random' config.include FactoryGirl::Syntax::Methods config.before(:all) do FactoryGirl.reload # これがないとfactoryの変更が反映されません end end ``` ## Guardの設定 これもほとんど今まで通りでOKです。 変更箇所は、下の一行目のspring: trueの部分だけ。 ```rb:Guardfile guard :rspec, spring: true do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } watch('spec/spec_helper.rb') { 'spec' } watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } watch(%r{^spec/factories/(.+)\.rb$}) { 'spec/factories_spec.rb' } watch(%r{^spec/support/(.+)\.rb$}) { 'spec' } watch('config/routes.rb') { 'spec/routing' } watch('app/controllers/application_controller.rb') { 'spec/controllers' } end ``` ## それでは実行 ``` bundle exec guard ``` よきテストライフを! ## おまけ 途中で触れたように、このへんのコマンドも高速に実行できます。 ``` bundle exec spring rails g model Hoge bundle exec spring rake routes ``` ## さらにおまけ Rails4っぽくbinstubつくっておくと、より便利 ``` bundle binstub spring ./bin/spring xxx ``` *** 弊社ブログ記事からの転載です [Rails4時代の高速テスト環境 Rspec+Guard+FactoryGirl+Spring[NEW!]](http://lab.heathrow.co.jp/2013/09/17/3421) @ [heathrow.lab](http://lab.heathrow.co.jp) |
|
| 167位 |
|
|||
|
14:48:33 |
|
|
テキストベースでちょっとしたディレクトリ構成図を書きたい時、いつもあの記号どうやって出すっけ??となる。なので、いい加減メモすることにした。
以下のようなのを書きたい > root/ > ├ bin/ > ├ etc/ > ├ usr/ > │ └ local/ > │ └ bin/ > └ lib/ コピペ用記号集 ``` ┣ ┠ ┝ ├ ┫ ┨ ┥ ┤ │ ┃ ─ ━ ┌ ┏ ┓ ┐ └ ┗ ┛ ┘ ``` ちなみに、MSIMEだと以下の変換で出せる (引用元:http://support.microsoft.com/kb/883172/ja) | 読み | 文字 | |:--------|:--------| | たて | │ ┃ | | たてひだり | ┨ ┥ ┤ ┫ | | たてみぎ | ┣ ┠ ┝ ├ | | ひだりうえ | ┌ ┏ | | ひだりした | └ ┗ | | ふとわく | ┗┻━┛ ┏┳━┓ | | ほそわく | └┴─┘ ┌┬─┐ | | まんなか | ┼ ╋ ┿ ╂ | | みぎうえ | ┐ ┓ | | みぎした | ┘ ┛ | | よこ | ─ ━ | | よこうえ | ┻ ┷ ┸ ┴ | | よこした | ┳ ┯ ┰ ┬ | |
|
| 168位 |
|
|||
|
18:53:07 |
(DeployGate Inc. 所属) |
|
「画面」のデザインは、エンドユーザーから見た「プロダクト」との唯一の接点。超大事。
そんな画面のデザインにまつわる、エンジニアが「いじる」ときに気をつけると、もしかしたら面倒が減って争いが減ってみんなが幸せになれるんじゃないかなあ、とか、そもそもの設計上で考慮できると、もしかしたら使う人たちが幸せになれるんじゃないかなあ、というポイントを、思い付きで書いていくので、あとは誰か整理してほしい的な投げやり感あふれるアレコレ。デザインとコーディングの話を混ぜて書いてます。 # 空白の理由を考える編 ## その1. 空白にまつわる認識の相違  例えば、[Tumblrのダッシュボード](http://www.tumblr.com/dashboard)。右肩のメニューの隅までちゃんとレイアウトされてるなーって感じがします。 でも、もしあなたが「空白を理解しないエンジニア」だった場合、こんな感じにコーディングしてしまうかもしれません。  (※画像はイメージです) 「なーんか、素人感があるなあ…」と感じたら良い傾向です。具体的に、どこが違うのでしょう。 * リスト要素の頭にスペースが入ったり入らなかったりして頭が揃わなかったり * アイコン画像のセンターがふらついていたり * メニュー全体のブロックの左右のマージンが合ってなかったり(右に寄りすぎ) * 行間が本文要素と同じになってしまっていて、クリッカブルな要素として認識しづらくなってたり つまり順序や色や機能に問題があるわけではなく、ただ、空白の扱いが雑なだけです。 もしかすると、これを見て、 > いやー細かすぎでしょ、つか空白とか動作には関係ないですよね?ましてや仕様じゃないし と思っちゃう人もいるかもしれません。 ## エンジニアにとっても空白は大事 でも、空白にはちゃんと意味があります。しかも、コードを書くなら、あなたもそれを毎日意識しているはずです。 あなたはエンジニアで、普段とてもエレガントなコードを書いています。そんなある日、新しいエンジニアがチームにやってきました。さっそく意気揚々とコードを書いて、自信満々にプルリクエストをしてきた彼のコードを見て、あなたは絶句します。 ```rb:え、でも空白とか動作には関係ないですよね?って感じのアレ def admin_user_filter user = target_user unless user.admin? render :text => 'not admin',:status =>:forbidden end end ``` なんと彼は、タブ2で揃っていた美しい世界に、突然タブ3だか4だかわからないランダムインデントを突っ込んできたのです。不安定なインデントを突っ込んでもまったく気にならない、修羅の世界を生きていたタフな彼。インデント以外の空白もバラバラ。ちょっとさすがにこれは、と突っ込んでみても、 > いやー細かすぎでしょ、つか空白とか動作には関係ないですよね?ましてや仕様じゃないし と、聞く耳を持ってくれません。 …なーんてことがあったら、おそらく即日人事に詰め寄りたくなるぐらいには発狂できるんじゃないかと思います。つまるところ、空白を気にしないエンジニアというのは、そんな感じの存在なのです。 ## 人間の目にとっての空白の役目 では、インデントやスペースは、なぜ大事なのでしょう。一般的には「コードの可読性と保守性を高めるため」と説明されます。構造が追いやすいとか、目grepしやすいとか、そんな感じですね。 インデントの深さや空行の存在で大まかなコードの構造が把握しやすくなるのは、行頭が揃って一文脈の手続きがまとまっていると、構文を理解する前に塊(ブロック)を認識できるようになるから、という理由が大きいと考えられます。実際、人間は無意識のうちに近接したブロックを認識できる能力を持っています。 <small>※携帯でご覧のお忙しい方へ:ここから先は横画面にしていただくと快適にご覧いただけます</small> ```text:空間上の塊の存在を一瞬で認識できる THISIS YOUCAN WITHOU ITHOTH ASAMPL READTH TINTER ERBLOC EBLOCK ISTEXT FEREDW KSHAHA ``` コードじゃなくてあからさまなブロックで恐縮ですが、上の例では、中の文章を読む前に瞬時に4つの塊の存在を認識することができるはずです。 ```text:間隔が変わると、塊の認識が変わる THISIS YOUCAN WITHOU ITHOTH ASAMPL READTH TINTER ERBLOC EBLOCK ISTEXT FEREDW KSHAHA ``` ブロックの間隔が変わって、今度は 1 + 3 個という形に見えると思います。間隔が変わっただけですが、今度はすべてが同じレベルの情報ではなく、なんとなく、最初の一つとほかの三つで異なる種類の情報を持っているように見えます。空間上で近接しているものをグループとして認識する力は、[プレグナンツの法則](http://ja.wikipedia.org/wiki/%E3%82%B2%E3%82%B7%E3%83%A5%E3%82%BF%E3%83%AB%E3%83%88%E5%BF%83%E7%90%86%E5%AD%A6#.E3.83.97.E3.83.AC.E3.82.B0.E3.83.8A.E3.83.B3.E3.83.84.E3.81.AE.E6.B3.95.E5.89.87)などで知られています。 ```text:間隔がばらけてしまうと THISIS YOUCAN WITHOU ITHOTH ASAMPL READTH TINTER ERBLOC EBLOCK ISTEXT FEREDW KSHAHA ``` しかし、間隔が均一でなくなってしまうと、今度は 4 つなのか、 1 + 2 + 1 なのか、どれが一番大事な部分なのか、あるいは全部同じなのか、一瞬では判断がつかなくなります。 このように、人間の認識能力は空白によって支配されています。私たちはこの認識を、コードであればタブや空行、画面レイアウトであればマージンで制御しています。  空間上に配置されている要素が整って見えるとき、その背景には、同じグリッド上に整列されているから、等間隔に配置されているから、要素の大きさが一貫しているから、などといった理由が隠されています。一部分を目立たせたいのか、情報盛りだくさんに見せたいのか、整理整頓されてるように見せたいのか、空白をうまく使い分けることでそれぞれの伝え方を設計することができます。 一方で、その配置は綿密な設計によって成り立っているため、たった一部分が崩れてしまうだけで崩壊してしまいます。  同じレベルの情報をフラットにひとかたまりで提示しているつもりが、ひとつ要素の位置がズレるだけで同一のかたまりには見えなくなり、制作者の意図していない解釈が行われることになります。ズレによって空間周波数が局所的に高まり、脳が受け取る情報量が増え、思考を伴った理解というプロセスが必要となってしまうのでしょう。 強引にまとめると、Webページや画面レイアウトで空白を尊重するということは、ノイズなく情報の可読性を高めるという目的において、コードをインデントや空行を使って整形するのと同じように大事なことです。新しい要素を増やしたり編集するときに、元のレイアウトの意図をくみ取りつつ配置することは、元のコードとインデントを合わせるのと同じように気遣いが必要な部分なのです。 ## 1ピクセルの違いが解るエンジニアになろう コードにおけるインデントが保守性に直結するように、画面レイアウトにおけるマージンはエンドユーザーにとっての可用性に直結します。UIパーツのような局所的なものから、全体のレイアウトまで、デザイナーはスペースではなく「ピクセル単位」でマージンを揃えています。呼吸するかのように「あれ、ここのマージン1ピクセル左に寄ってない?」と気づけるエンジニアになると素敵ですね。 なお、Apple信者としてSteve Jobsを崇拝する方は、「1ピクセル未満編」として、フォントのカーニング調整まで辿り着く必要がありますし、Microsoft信者の方もClearTypeのサブピクセルレンダリング特許の話をするためには、やはり1ピクセル未満編の理解が欠かせません。CSS3マスターを目指される方は当然 `font-kerning` プロパティの効果をレンダリング結果から判断できる必要がありますし、これを読んでいただいたデザイナーのM氏の言葉を引用すると、 > 72dpiの世界だと1pxって結構大きいんですよね。神の世界だとその更に10分の1の単位で制御できちゃう という世界だって存在するのです。…ん、え、神じゃなくて紙だった?…あ、なるほど! そんなピクセル単位のミクロな話も大事ですが、それだけだと局所最適で終わってしまうので、マクロな話ももちろん重要です。「その2. 繰り返しのデザインと学習曲線」に続く、といいな。 |
|
| 169位 |
|
|||
|
11:12:03 |
|
|
最近Vimを始めてvimrcを育てています。
GitHubで公開されている他の方のvimrcを読んで勉強しているのですが、個人的に特に参考にしているvimrcを紹介します。 日本語のコメントが中心 - https://github.com/yuroyoro/dotfiles - https://github.com/SpringMT/dotfiles - https://github.com/rhysd/dotfiles (2014/02/26追加) - https://github.com/glidenote/dotfiles (2014/03/03追加) すべて英語、もしくは英語のコメントが中心 - https://github.com/deris/dotfiles - https://github.com/toupeira/dotfiles - https://github.com/cocopon/dotfiles - https://github.com/thoughtbot/dotfiles (2014/03/13追加) いずれもコメントが丁寧に書かれていて初学者にはとても助かります。 特にyuroyoroさんのvimrcはかなり細かく日本語でコメントが書かれています。 vimrcのファイル分割やコメントを使ったインデントの付け方、グルーピング、構造化の方法などもすごく勉強になります。 |
|
| 170位 |
|
|||
|
13:28:35 |
|
|
## 1. Vagrant のインストール 公式サイト http://www.vagrantup.com/ 上記公式サイトから、 vagrant_1.8.1.dmg をダウンロードし インストール ``` bash $ vagrant --version Vagrant 1.8.1 ``` ## 2. VirtualBox を公式サイトからインストール VirtualBox 公式サイト https://www.virtualbox.org/ VirtualBox-5.0.20-106931-OSX.dmg をダウンロードし インストール ## 3. 初期化と起動 Centos6.7の例です。 ```bash $ mkdir -p ~/Vagrant/CentOS67/ $ cd ~/Vagrant/CentOS67/ $ vagrant init bento/centos-6.7 $ vagrant up --provider virtualbox ``` その他のBoxは下記にあるので、他のBoxを探して利用することも可能です。 https://atlas.hashicorp.com/boxes/search 例)ubuntuの例 ```bash $ vagrant init ubuntu/trusty64 $ vagrant up --provider virtualbox ``` 以前からある vagrantbox.es からBoxを探して利用することも可能です。 http://www.vagrantbox.es/ 例) ```bash $ vagrant box add centos65 https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box $ mkdir -p ~/Vagrant/CentOS65 $ cd ~/Vagrant/CentOS65 $ vagrant init centos65 $ vagrant up ``` ## 4. 仮想マシンの操作 ### 起動するには、up ```bash $ vagrant up Bringing machine 'default' up with 'virtualbox' provider... [default] VirtualBox VM is already running. ``` すでに起動してます。 ### 状態を見るには、 status ```bash $ vagrant status default running (virtualbox) ``` ### 一時停止するには、suspend ```bash $ vagrant suspend [default] Saving VM state and suspending execution... ``` ## 5. 仮想マシンへの接続 ```bash $ vagrant ssh Welcome to your Vagrant-built virtual machine. [vagrant@localhost ~]$ ``` ## 6. ネットワークの設定 ```bash $ vim Vagrantfile # config.vm.network :private_network, ip: "192.168.33.10" ↑ ここのコメントアウトを削除 ``` vagrant のリロード ```bash $ vagrant reload ``` これで、192.168.33.10 というIPでアクセスできる ```bash % ping 192.168.33.10 PING 192.168.33.10 (192.168.33.10): 56 data bytes 64 bytes from 192.168.33.10: icmp_seq=0 ttl=64 time=0.383 ms 64 bytes from 192.168.33.10: icmp_seq=1 ttl=64 time=0.624 ms 64 bytes from 192.168.33.10: icmp_seq=2 ttl=64 time=0.794 ms ``` ## 7. プロビジョニング Vagrantには chef や ansible のようなサーバーの初期設定をするしくみが備わっています。 大きく分けて、Vagrantfile に直接記述する inlineの方法と、外部ファイル(shell script)に記述する方法があります。 ### 7.1 プロビジョニング/inlie Vagrantfile に直接記述します。 ``` Vagrant.configure("2") do |config| config.vm.provision "shell", inline: <<-SHELL sudo yum update sudo yum install -y git screen tree wget SHELL end ``` 変更したら、provisionを実行 ``` $ vagrant provision ``` ### 7.2 プロビジョニング/外部ファイル 外部のshell scriptを実行します。 ``` Vagrant.configure("2") do |config| config.vm.provision "shell", path: "script.sh" end ``` このscript.sh はホストOS側にあります。 ### 7.3 その他 他にも、指定したURLのスクリプトを実行したり、いろいろできます。 詳しくはこちら https://www.vagrantup.com/docs/provisioning/shell.html ## 補足 Vagrantのsharedfolder使ってて良くはまるのが、CSSとかJSとかHTMLといった静的なコンテンツを更新しても 反映されない事があります。 そんな現象が起きた時は下記を参考にしてapacheやnginxの設定を見直すと良いかもです。 VagrantでCSSの更新が反映されない場合の対処法 http://qiita.com/shoyan/items/12389d5beaa8695b1a53 |
|
| 171位 |
|
|||
|
01:32:35 |
|
|
## はじめに
Ruby on Rails や同種のフレームワークを使っていると、《REST思想》と《リソース指向》と《Webページ》がごちゃまぜになったWebアプリケーションをつい設計してしまいます。 三つの違いを意識し、適切なWebアプリケーションを作成するようにしましょう。でないと後悔することになります。 なお、この三つの用語は本来の意味とずれているかもしれません。 「コメント」、「編集リクエスト」大歓迎です。 ## 解説 `http://yourhost/books` のURLで本の一覧が取得できるようなWebサービスを提供するとします。 では `/books` を含めた各URLはどのように振る舞うべきなのでしょうか。 (URLと言っている部分でも実際はpathを指している場合があります。ご了承ください) ### REST思想 本に対してCRUD操作を行うインタフェースとして、以下のようなものを提供します。 |役割|method + URL| |----|----| |本一覧を取得|GET /books| |一つの本を取得|GET /books/1| |新しい本を追加|POST /books| |既存の本を修正|PUT /books/1| |既存の本を削除|DELETE /books/1| 各本の情報はJSONで表現されます。`/books` にGETリクエストした場合はレスポンスとして本の一覧がJSONとして返されます。 本の追加や削除を行う場合は、本情報をJSON形式でPOSTリクエストのボディとして送ります。`application/x-www-form-urlencoded`形式で送ることは避けるべきです。 更新に失敗した場合、失敗した理由をJSON形式で返します。 ステータスコードは、取得時・更新成功時には`200 OK`を、更新失敗時には4XX番代を使うのがよいでしょう。 情報はJSON形式と書きましたが、XMLなどでも構いません。 しかし、正常系・異常系ともにアプリケーション内で統一された形式を利用してください。 ### リソース指向 本の情報は`/books`にあるとして、拡張子を変えることで、様々なフォーマットでデータを利用できるようにします。 例えば本の一覧をJSON形式で欲しければ`/books.json`にリクエストし、csv形式で欲しければ、`/books.csv`にリクエストします。 `/books.xls`のようにバイナリを提供してもかまいません。 更新用のAPIは用意しない方が良いでしょう。 `/books.csv`に対する更新を行う場合は、はPOSTリクエストのボディとして何を送ればよいのでしょうか。CSV形式で良いのでしょうか。JSONでしょうか。 また、更新に失敗したときのエラーはどう返すべきでしょうか。もし`/books.csv`にリクエストししたにも関わらず、JSON形式でエラーを返ってくることになると、クライアントは苦労するでしょう。 ### Webページ サーバはHTMLを返し、ブラウザはAjaxを使わずHTML FormでデータのCRUDを行います。 Webページの場合、RESTと違い以下のようなインタフェースを提供すべきです。 |役割|URL| |----|----| |一覧画面を表示|GET /books| |詳細画面を表示|GET /books/1| |追加formを表示|GET /books/new| |追加アクション|POST /books/new| |編集formを表示|GET /books/1/edit| |編集アクション|POST /books/1/edit| |削除確認画面を表示(任意)|GET /books/1/delete| |削除アクション|POST /books/1/delete| REST同様にCRUD用のAPIを提供しますが、Webページの場合は更新Formを表示するためのページが必要になります。 さらに更新に失敗した際、適切にエラーを見せるには - 更新FormページのURLとPOST先のURLを同じにする - 成功した場合、一覧ページあるいは詳細ページにリダイレクトする - 失敗した場合、エラーを表示し、Formを直前の入力で埋める のがよいでしょう。 URLを同じにすることで、CSSなどのリソースのパスや、リクエストパスに依存したコードを書きやすくなります。 成功時にリダイレクトすることで、`F5`対策になります。リダイレクトには`303 See Other`を利用してください。 失敗時にリダイレクトせずそのまま表示することで、エラーや直前の入力をFormに表示しやすくなります。 HTML FormではリクエストメソッドとしてGETかPOSTしか利用できません。取得はGET、更新はPOSTを利用しましょう。わざわざ`_method=put`のようなリクエストパラメタを利用しても、得られるものはありません。 ## まとめ 同じURL `/books` であっても様々な設計があるということを理解してもらえたと思います。 この三つ全てをまとめて提供することは可能です。しかしControllerのコードは複雑になり、一部の機能はほとんど使われず、削除したくても一度提供したので消せないという結末に終わります。 もし複数提供する必要がある場合、URLを別にするのが良い解決案です例えば以下のように分けることができます。 (※ ただしドメイン直下は常にWebページを提供するのが良い) |目的|URL| |----|----| |REST|http://yourhost/api/books| |リソース|http://yourhost/books| |WEBページ|http://yourhost/web/books| 要件にあわせ、素敵なアプリケーションを設計してください。 |
|
| 172位 |
|
|||
|
23:10:20 |
(SHIFTBRAIN Inc. 所属) |
|
案件で「作業の差分を納品してくれ」とか言われることってよくあります。
今までは手作業でディレクトリ作って、ファイルをコピーしてましたが、 もう、そんなうんざりする作業とはおさらばできそうです。 `git archive` と `git diff` の合わせ技で差分を出力できる事がわかったからです。 例えば、一個前のコミットから現在のコミットまでの差分を取り出したい時は、 ```bash git archive --format=zip --prefix=root/ HEAD `git diff --diff-filter=d --name-only HEAD^ HEAD` -o archive.zip ``` まずは、`git archive` について。 `--format=zip` を付けるとzipで固めてくれます。 `--prefix=root/` は抽出したファイルをrootディレクトリに入れた状態にしてくれます。 `-o archive.zip` で出力先と出力名を指定しています。 `HEAD` は抽出元のコミットで、 抽出したいファイルやディレクトリを`git diff`を使って指定しています。 `git diff` は `--name-only` を付けると、ファイルのパスを返してくれるのです。 `--diff-filter=d` オプションで不要な削除の差分をフィルタリングすると、 差分ファイルに必要なファイルパス一覧が取得できます。 例では、`HEAD^` と `HEAD` のコミット間での差分のファイル一覧が返るので、 それを `git archive` がzipにしてくれるというわけです。 ## 関数化した 毎回コマンド書くのは辛いので、 関数化してみました。 zshとbashで動作を確認しました。 ```bash function git_diff_archive() { local diff="" local h="HEAD" if [ $# -eq 1 ]; then if expr "$1" : '[0-9]*$' > /dev/null ; then diff="HEAD~${1} HEAD" else diff="${1} HEAD" fi elif [ $# -eq 2 ]; then diff="${2} ${1}" h=$1 fi if [ "$diff" != "" ]; then diff="git diff --diff-filter=d --name-only ${diff}" fi git archive --format=zip --prefix=root/ $h `eval $diff` -o archive.zip } ``` --- 引数なしで `git_diff_archive` と呼ぶと `HEAD` を丸っとzipにします。 ```bash git_diff_archive ``` --- 引数に数値を指定すると、`HEAD` と `HEAD~数値` の差分を抽出します。 つまり、数値分前のコミットからの差分になります。 ```bash git_diff_archive 数値 ``` --- 引数にコミット識別子(IDとかHEADとかHEAD^とかdiffの引数として使えるもの)を指定すると、 `HEAD` と コミット識別子 の差分を抽出します。 IDで一発指定したい時とか便利です。 この時、HEADよりも新しいIDとかは渡さないようにしてください。 想定している抽出物を得られない可能性が高いです。 ```bash git_diff_archive コミット識別子 ``` --- コミット識別子を2つ渡すと、その2つのコミットの差分を抽出します。 ちょっと前のバージョンの差分が欲しいとか言われたときに使えます。 渡す順番は新しいコミット、古いコミットの順番で渡してください。 間違うと、想定している抽出物を得られない可能性が高いです。 ```bash git_diff_archive コミット識別子1 コミット識別子2 ``` --- 何かおかしなところがありましたら、 やさしいツッコミお待ちしております。 ## 修正履歴 *2015/06/25* 引数を1つだけ渡すときに、数字で始まるコミットIDを渡すとエラーが起きていました。 *2015/06/24* 差分にファイルの削除が含まれると、それも差分として一覧に追加されてしまい、 zipに固めるときにファイルがなくてエラーになってました。 *2016/11/05* `git diff` に渡すコミット識別子の順番を入れ替えました。 以前の順番だと、新しいコミットから古いコミットを比べた差分になってしまい、 差分が上手く取得できないケースがありました。 それにともない `--diff-filter` も `D` から `d` に変更しました。 |
|
| 173位 |
|
|||
|
01:45:43 |
(Nepula,Inc. 所属) |
|
【2017年9月時点の情報で更新しました。】
Xamarin(ザマリンと読みます) とはなんぞや、個人開発者として使う時にどうなるの、的な事をさらっと書いてみようと思います。 Xamarin は 2016年2月、Microsoft に買収され、 Visual Studio に無償で同梱されることになりました。 * [【速報】Xamarin のこれからについて! - Xamarin 日本語情報](http://ytabuchi.hatenablog.com/entry/ms-xamarin) * [Xamarin が Microsoft に買収された結果 - Qiita](http://qiita.com/amay077/items/6e5c40abe0c21fc79e6a) Xamarin 自体は元企業名であり、その歴史は .NET の Linux 版を開発していた Ximian という企業が Novell に買収されて、その後レイオフされて作った企業で・・・した。 このあたりの歴史については [@atsushieno さん](http://atsushieno.hatenablog.com/entry/2013/12/24/213950) や [ちょまどさん](https://blogs.msdn.microsoft.com/chomado/xamarin/xamarin-history-as-a-company/) のブログが(読み物としても)おもしろいです。 Microsoftに買収されたことにより企業としての Xamarin はなくなりますが、現在のところ Xamarin という開発ツールの名称は、Visual Studio や、Xamarin Studio の中に見ることができます。 * [Xamarin - Official site](http://xamarin.com/) * [Xamarin - Wikipedia](http://ja.wikipedia.org/wiki/Xamarin) で、同社が開発した、 .NET技術で iOS や Android アプリが作成できる SDK が、Xamarin.iOS だったり、Xamarin.Android だったりするわけですが、それらに Mac アプリを開発できる Xamarin.Mac や、Xamarin Studio という統合開発環境を加えたツール群をまるごとひっくるめて Xamarin と呼んでいます。 (Xamarin社は他にも、クラウド上で実機テストができる [Xamarin Test Cloud](https://www.xamarin.com/test-cloud), C#のPlaygroundツール[Xamarin Workbooks](https://developer.xamarin.com/guides/cross-platform/workbooks/) などのプロダクトがあります) ## 作成できるアプリケーション 開発ツールの Xamarin を利用して作成できるアプリは、まず以下のものがあります。 * Mono を利用したアプリケーション、クラスライブラリ * iOSアプリ(Xamarin.iOS) * Androidアプリ(Xamarin.Android) * macOSアプリ(Xamarin.Mac) * 複数のプラットフォームで再利用可能なクラスライブラリ(.NET Standard または PCL) さらに、 Xamarin.Forms というフレームワークによって、以下のアプリも作成可能です。 * Windows(UWP) アプリ * Tizenアプリ(※開発中) * Linuxアプリ(※開発中) Windows 用の .NETアプリ(Windows.Forms や WPF)は、Xamarin 自体では作成できません。それは Visual Studio の役割です。ただ、.NET Standard(または PCL)と呼ばれる、プラットフォームを問わず動作するアセンブリ(DLL) を作成できますし、そもそも Mono と .NET の API はほとんど同じなので、書いたコードは Windows でも流用できます。 ## どこが共通化できる? まず、Xamarin.Android, Xamarin.iOS, Xamarin.Mac が提供するのは、 **各プラットフォーム(PF)版の.NET API + 各PFのAPIの.NETラッパクラス** です。 「各PF版の.NET API」とは、いわゆる基本クラスで、基本的な型だったり、文字列処理だったり LINQ だったりその他もろもろです。 一方、「各PFのAPIの.NETラッパクラス」とは、Android なら Android SDK、iOS なら CocoaTouch の API を .NET で記述できるラッパーです。ここにプラットフォーム間の互換性はありません。 なので、画面を作るのに Xamarin.Android なら ``Activity`` クラスを使いますし、Xamarin.iOS なら ``ViewController`` クラスを使います。 GPS を使うのに、.Android なら ``LocationManager`` を使いますし、.iOS なら ``CLLocationManager`` を使います。 つまり、共通にできるのは「コア」な部分だけで、「画面」と「各PF固有の機能」は共通化することができません。従って、各プラットフォーム の API は理解しておく必要があります。それから .NET Framework の基本クラスライブラリも。 上司に言うと「なんだその程度か」と返されると思いますが、コア部分だけでも、 **Javaと Swift でそれぞれ実装しなくて良い** というのは十分に価値があると思うんですよね僕は。 * [XamarinでWindows / Mac OSX 両対応のデスクトップアプリを作る](http://qiita.com/okajima/items/8ca53ff00a825f28dbc6) は、異なるプラットフォームで共通化できる箇所が具体的に示された有益な情報です。 ほかのクロスプラットフォーム開発可能な SDK(Titanium とか Abobe AIR とか)との比較はこちら → かきました : [Xamarin vs 他のクロスプラットフォーム開発ツール](http://qiita.com/amay077/items/01917ef1be3da9259348) ## 画面まで共通化できる [Xamarin.Forms](https://www.xamarin.com/forms) これはいわゆる Titanium Mobile や Adobe Air, Delphi XE, 最近では React Native と同じアプローチで、共通の画面定義体から、各種プラットフォーム(Android, iOS, Windows Phone, UWP<Universal Windows Platform >)向けの画面を生成します。Adobe Air, Delphi XE と異なるのは、それぞれのプラットフォームが提供するUIパーツが使用されるという点です。 画面定義体は XAML(ザムル, WPF のそれとは違います)か C# のコードで記述します。他にも ValueConverter, バインディング可能なプロパティなど .NET アプリ開発者に馴染みのある要素を使って開発できます。 XAML のグラフィカルなエディタはないので手書きするしかありませんが、[XAML Previewer for Xamarin.Forms](https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-previewer/)や、[Gorilla Player](http://gorillaplayer.com/)を使って、リアルタイムにプレビューさせることができます。 このXamarin.Formsフレームワークの活用は広がりを見せていて、以下のものに対応(または目下対応中)します。 * Android * iOS * UWP * macOS ※ver3.0で対応予定 * Tizen ※ver3.0で対応予定 * Windows(WPF) ※ver3.0で対応予定 * Linux(GTK#) ※ver3.0で対応予定 2016年11月に、[TizenがXamarin.Forms対応プラットフォームに加わると発表された事](http://nakasho-dev.hatenablog.jp/entry/2016/11/18/030310)は、ちょっとした驚きとともに迎え入れられました。今後は Xamarin.Forms 3.0 で Tizen の他に macOS、そしてなんと WPF や GTK# を使った Linux アプリ開発にも対応する予定です([Unity での実装だ・・・と!?](https://twitter.com/migueldeicaza/status/909507003894177792))。 Xamarin.Formsは発展途上でも何でもありません。**実戦投入できるフレームワーク** です。 複数プラットフォームで辻褄を合わせて共通なインターフェースとして公開しなければならないので、提供されるUIコントロールは少ないですが、Xamarin.Android, Xamarin.iOS の上に成り立っているので、 **ネイティブの部品をXamarin.Forms化することは容易** です。なぜならネイティブのAPIも同じ言語で同じIDEを使って開発・デバッグできるからです(例えば React Native では、AndroidネイティブのAPIをJavaScript側にブリッジする箇所は Java で**書か**なければなりません)。 ## .NET製のライブラリを Java や Swift から呼び出せるようにする Embeddinator-4000 これまで、 XamarinでJava製のライブラリを使うことはできるが、その逆、つまり **「C#で作成したライブラリを Java-Android や Swift-iOS から呼び出すこと」** はできませんでした。 が、その常識が覆されます。 [Embeddinator-4000](https://github.com/mono/Embeddinator-4000) は、 .NETで作成したライブラリをJavaやSwiftなど他のプラットフォーム+言語から呼び出せるようにするための仕組みです。詳しくは開発者の資料を見てください。 * [Embeddinator-4000から学ぶXamarinの基礎 // Speaker Deck](https://speakerdeck.com/atsushieno/embeddinator-4000karaxue-buxamarinfalseji-chu) Embeddinator-4000は、絶賛開発中のツールで実用はまだまだ先になりそうですが、近い将来、クロスプラットフォームアプリ開発の重要な選択肢のひとつになると思います。 ## Macの人は Visual Studio for Mac、Win の人は Visual Studio で開発しますよ Mac 向けの Visual Studio、[Visual Studio for Mac](https://www.visualstudio.com/ja/vs/visual-studio-mac/) がリリースされました。 中身は実質 Xamarin Studio で、 Windows版の Visual Studio の機能がすべて搭載されているわけではありません。 Xamarin Studio は Visual Studio for Mac とほぼ同じ機能を搭載していますが、起動時に「Visual Studioを使ってね☆」と案内されるなど、もはや敢えて選択する意味はなくなってきています。 Windows の場合、Visual Studio 2017 に同梱されるようになり、また無料化されたため、 Visual Studio Community でも利用できるようになりました。 (これまで提供されてきた Windows版の Xamarin Studio は、提供終了となりました。) Visual Studio 2015 でも使用可能ですが、環境構築でハマることが多いので Visual Studio 2017 を推奨します。Visual Studio 2017 では、Xamarin のインストールが簡単になっています。それでも既定の選択だとかなりの容量が必要なので、次のリンクを参考に、定義オプションを外すとよいでしょう。 * [Xamarin やりたい人向け Visual Studio 2017 インストール手引書 - Xamarin 日本語情報](http://ytabuchi.hatenablog.com/entry/visualstudio2017) * [Xamarin の為だけに Visual Studio 2017 をミニマムインストールする - Qiita](http://qiita.com/amay077/items/43cd700e4d125a6517b7) Visual Studio 2015 と Visual Studio 2017 は共存が可能です。 その他の開発ツールには [JetBrains Rider](https://www.jetbrains.com/rider/) があります。また開発中の製品ですが、現時点でも開発に使用することができます。 * [JetBrains Rider で Xamarin.Android プロジェクトのデバッグが行えるようになりました - Qiita](http://qiita.com/amay077/items/c22f608c5ec4c2e39d88) また iOS アプリを開発する場合は、ビルドや実行・デバッグのために Mac が必要なので、現実的には Mac 必須です。これは買収された後も変わっていません。 Remoted iOS Simulator (for Windows) が搭載され、Windows の画面でアプリを実行することができるようになりましたが、iOS アプリのビルドには Mac がどうしても必要になります。 ## だからiOSアプリを作るならMacは必要なんだってば! [Xamarin Live Player](https://www.xamarin.com/live) が発表されて、次のような日本語記事が公開されています。 * [WindowsでiOSアプリを開発、テスト、公開できる――MicrosoftがXamarin Live Playerを発表 | TechCrunch Japan](http://jp.techcrunch.com/2017/05/12/20170511microsoft-now-lets-ios-developers-deploy-run-and-test-their-apps-directly-from-windows/) * [iOSアプリ開発でMac OSが不要に、Windowsで開発・テスト・実機配備可能な「Xamarin Live Player」 -INTERNET Watch](http://internet.watch.impress.co.jp/docs/news/1059302.html) ※タイトル修正されたようです :thumbsup: が、 「macOS不要でiOSアプリが **公開** できる」というのは間違い(誤訳)です。 Xamarin Live Player(XLP)の実行形式は、 * [Xamarin Live Playerの仕組みを想像してた - ぴーさんログ](http://ticktack.hatenablog.jp/entry/2017/05/13/022611) で解説されているとおり、XLP用のiOSアプリがソースコードをインタープリタ形式で実行しているに過ぎません。つまり、実際のアプリが動作しているわけではなく、アプリを配布・公開するには、これまで通りmacOSでビルドする必要があります。あと [これ](https://twitter.com/amay077/status/862838700568334336)。 ## Xamarin のコアライブラリはオープンソースになりました Xamarin の基盤となっている [Mono](http://www.mono-project.com/) は元々オープンソースです。 Xamarin.Android, Xamarin.iOS, Xamarin.Mac, Xamarin.Forms のこれまでプロプライエタリだったライブラリ群は、いずれもMITライセンスによるオープンソースになりました → [Xamarin Open Source SDK](http://open.xamarin.com/) Xamarin Studio の IDE としての機能は元々オープンソースである MonoDevelop なので、純粋な Mono アプリケーションを作成するのにも使用されます。Xamarin Studio の Xamarin固有のプラグイン部は、オープンソース化されないとのことです。つまり、Microsoftとしては「開発ツールが収益源」ということになります。 ## 価格 Xamarin自体は無償になりましたが、企業利用の場合、大抵は Visual Studio の購入が必要になります。 ### 個人の場合 Windowsの人は、[Visual Studio Community](https://www.visualstudio.com/ja-jp/products/visual-studio-community-vs.aspx) で開発・配布ができるようになりました。 Macの人は、Visual Studio for Mac で(もちろん [Xamarin Studio](https://www.xamarin.com/studio) でも) 開発・配布ができるようになりました。 ### 企業の場合 Windowsの人は、Visual Studio のサブスクリプションの購入が必要です。 Macの人は、Visual Studio for Mac で開発を行いますが、Windowsと同様、Visual Studio サブスクリプションの購入が必要です。 詳しくは、 [Xamarin の開発環境を整理しよう - Xamarin 日本語情報](http://ytabuchi.hatenablog.com/entry/2016/04/02/163106) を参考に、あるいは Microsoft へ問い合わせてください。 [@nuits_jp](https://twitter.com/nuits_jp) さんが「絶対わかるXamarinライセンスの判断方法」を書いてくれました! * [絶対わかるXamarinライセンスの判断方法 - nuits.jp blog](http://nuits.hatenadiary.jp/entry/2016/07/11/231323) ## 日本語情報について 2017年9月現在、Xamarin の日本語情報、書籍もだいぶ増えてきましたね。 ### 書籍(商業出版:発売日が新しい順) #### [Xamarinプログラミング入門 C#によるiOS、Androidアプリケーション開発の基本 (マイクロソフト関連書)](http://amazon.jp/dp/4822253503/?tag=oku2008-22) ※10/5発売予定 2014年に発売された [C#によるiOS、Android、Windowsアプリケーション開発入門](https://www.amazon.co.jp/dp/B00MN5P6PY/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1) を基に最新情報で刷新した書籍。2014年当時には存在していなかった Xamarin.Forms にも触れています。 #### [Xamarinネイティブによるモバイルアプリ開発 C#によるAndroid/iOS UI制御の基礎](http://amazon.jp/dp/4798149810/?tag=oku2008-22) Xamarin.Android、Xamarin.iOS の ”Xamarinネイティブ” にフィーチャーした本。 Xamarin.Forms を扱うには、Xamarinネイティブの理解は必須なので、この本から読むと「急がば回れ」にならずにすむかも知れません。 #### [Essential Xamarin ネイティブからクロスプラットフォームまで モバイル.NETの世界 (技術書典シリーズ(NextPublishing))](http://amazon.jp/dp/4844397915/?tag=oku2008-22) 技術書典2で頒布した Essential Xamarin Yin/Yang(後述) が、なんとインプレスR&Dより商業本として発売されました! 同人版に比べて最新情報での加筆修正、Xamarin.iOS入門の章が追加されています。 #### [基礎から学ぶ Xamarinプログラミング](https://www.amazon.co.jp/gp/product/4863542240?tag=oku2008-22) 開発環境の構築やC#のポイントも解説し、Xamari.iOS、Xamari.Android、Xamarin.Formsに加え、XAMLやMVVMでの開発手法も解説しています #### [プログラミングXamarin 上 Xamarin.FormsとC#によるクロスプラットフォームモバイルアプリ開発](http://amzn.to/2pEHd38) ついに登場、Xamarin.Formsのバイブル「[ペゾルド本](https://developer.xamarin.com/guides/xamarin-forms/creating-mobile-apps-xamarin-forms/)」の日本語訳書籍、@atsushieno さんが [XamarinFormsBookReading](https://xamarinformsbookreading.connpass.com/) で巡回されてるやつですね。下巻も期待! #### [Xamarinエキスパート養成読本 (Software Design plus)](http://amzn.to/2nExEQ0) 「エキスパートになる」というよりは、「初心者の人がとりあえずXamarin.Formsでアプリを作れるようになる」ところまでが書かれています。 #### [Xamarinではじめるスマホアプリ開発](http://amzn.to/2nEBKrD) こちらも初心者向け。Visual Studio for Mac が使われていてどちらかというとiOSアプリ寄りの解説がされています。 #### [C#によるiOS、Android、Windowsアプリケーション開発入門](http://www.amazon.co.jp/dp/B00MN5P6PY/?tag=oku2008-22) 内容は少し古いですが、基礎の理解には問題ありません。 #### [.NET開発テクノロジ入門2016年版 Visual Studio 2015対応版](http://www.amazon.co.jp/dp/4822298612/?tag=oku2008-22) .NET開発全般についての書籍ですが、Xamarinについての章があります。C#/.NET自体もこれから勉強するという方にはふさわしいでしょう。 ### 書籍(個人または同人出版:発売日が新しい順) **※これら以外にも Xamarin について(一部でも)書かれた個人/同人出版本がありましたらお知らせください** #### [Essential Xamarin Yin/Yang (陰/陽)](https://atsushieno.github.io/xamaritans/tbf2.html) @atsushieno さん主宰のサークル「Xamaritans(6名、私も参加しております)」が執筆したXamarin技術誌です。 陰(Yin) と 陽(Yang) の2冊となっており、[技術書典2](https://techbookfest.org/event/tbf02)、[超技術書典](https://techbookfest.org/event/cho01) で販売しました。 現在は [Boothにて製本版またはダウンロード版を購入](https://xamaritans.booth.pm/) できます。 また、[加筆や最新の情報で更新された商業本化](http://atsushieno.hatenablog.com/entry/2017/05/17/234629) が進行中です、ご期待下さい! #### [かずきのXamarin.Forms入門(Kindle版)](http://amzn.to/2l7oQNK) 元MVPで現Microsoftの中の人 @okazuki さんによる自費出版本です。Xamarin.Formsの日本語での入門にはピッタリです。 そしてなんと同内容のものが Slideshare で無料配布されております!ありがたい! * [Xamarin.forms入門(Slideshare版)](https://www.slideshare.net/okazuki0130/xamarinforms-70553057) ### Webサイト、ブログ * [ものがたり](http://atsushieno.hatenablog.com/) - Xamarin の中の人である [@atsushieno](https://twitter.com/atsushieno) 氏のブログ * [ちょ窓帳 – 千代田まどか(ちょまど)のブログ](https://blogs.msdn.microsoft.com/chomado/) - Microsoftエバンジェリストのちょまどさんのブログ * [Xamarin 日本語情報](http://ytabuchi.hatenablog.com/) - Xamarinコミュニティ(JXUG)主宰の @ytabuchi さんのブログです。 * [Xamarin逆引きTips - Build Insider](http://www.buildinsider.net/mobile/xamarintips) - Xamarinに関するTipsが集まっています * [Qiita の "Xamarin" タグ](http://qiita.com/tags/xamarin/items) * [Experiments Never Fail](http://blog.amay077.net/) - ワタシのブログです😅 * [teratail の "Xamarin" タグ](https://teratail.com/questions/search?q=Xamarin) - 分からない事はここで聞いてみると大抵回答があります ### コミュニティ * [Japan Xamarin User Group (JXUG)](http://jxug.org/) - @ytabuchi さんが代表と務められているコミュニティ。定期的に勉強会、ハンズオンなどを開いています。 * [JXUG - connpass](https://jxug.connpass.com/) - 登録しておくとXamarinのハンズオンや勉強会の通知を受け取れます Microsoft に買収され、より多くの人に知ってもらえ、使ってもらえるようになった Xamarin、事実上クロスプラットフォーム開発の最良の選択肢になったと言えますね。 ## 最後に注意点 Visual Studio + Xamarin はクロスプラットフォームアプリ開発が行える非常に強力な開発ツールですが、Android や iOS のアプリ開発の知識は **必須** です。特に開発中に期待通りに動作しない事象が発生した場合、基本であるネイティブに立ち返って調べる必要があります(トラブルシューティングの方法を「[Xamarin を使用したアプリ開発での、問題解決の方法](http://qiita.com/amay077/items/abb872c3650f65f09b8f)」に書きました)。 また、Visual Studio は最強のIDEであるのはその通りですが、Androidアプリ開発には Android Studio、iOSアプリ開発にはXcodeという公式な開発ツールがあり、それらの方が便利な機能もあります。 そのため、Android Studio や Xcode で Android、iOSアプリを開発できるようになった上で、Visual Studio+Xamarin でクロスプラットフォーム開発をするのが、最大の効果を得られる方法です。 **Java も Swift も学んで C# で D.R.Y(Don't Repeat Yourself) するのが Xamarin です** |
|
| 174位 |
|
|||
|
21:02:52 |
|
|
今まで mysql... 系を使用していましたが、PHP5.5以降は非推奨となり、将来的には削除される予定らしいので、PDOの使用に変更しようと思い、まとめてみました。
プリペアドステートメントでINSERTすると安全に値を渡せるとか、結構便利みたい。 Manualとか色々読んだけど、分かりにくい言葉が多かったので、自分なりに解釈を書いています。 ####PDOとかプリペアドステートメントの説明 [こっちのページ](http://labo-iwasaki.com/code/pdo-index.html)に詳しく書いています。 言葉の意味分からんわー。みたいな時は是非読んでみてください。 ##PDOでMySQLを色々やる。 まずメソッドや引数をちょっとまとめました。 今後増やしていこうと思っています。 | メソッドや引数| 内容 | :----------------------|:----------------------| | execute() |準備したprepareに入っているSQL文を実行 | prepare |値部分にパラメータを付けて実行待ち| | query |prepareを使わずにSQL文を実行| | PDOException |エラーを投げる | | bindParam |与えられた変数を文字列としてパラメータに入れる| | bindValue |与えられた変数や数値を型を指定してパラメータに入れる※1| | PDO::PARAM_STR|変数の値を文字列として扱う| | PDO::PARAM_INT|変数の値を数値として扱う| | :nameなど|パラメータ(:の後に任意の文字)| | PDO::FETCH_ASSOC|連想配列として取得します。※2| ※1 INSERTの項目でもう少し詳しく触れています。 ※2 よく「カラム名で添字を付けた配列を返す」みたいな事を書かれてますが、これManualのコピーですよね?意味分からん。つまり連想配列として取得するって事。 ##データベースに接続する。 PDOでMySQLに接続します。 現在はPHP5.4.13なのでcharsetが有効。5.3.6以前のバージョンでは無効。 try の中に処理などを書き、catch でエラーを返します。 ```php:DBに接続 <?php try { $pdo = new PDO('mysql:host=ホスト名;dbname=DB名;charset=utf8','ユーザー名','パスワード', array(PDO::ATTR_EMULATE_PREPARES => false)); } catch (PDOException $e) { exit('データベース接続失敗。'.$e->getMessage()); } ?> ``` ###ヘルプ ●ATTR_EMULATE_PREPARES がよく分からない。 静的プレースホルダに設定しているらしい…。 ●そもそも静的とか動的の意味がよく分からん。調べても日本語の意味書いてない。 ニュアンスは分かるけど…。 ##データを呼び出し。 queryを使う時はexecute()を使わない。 ```php:データを呼び出す <?php $stmt = $pdo->query("SELECT * FROM テーブル名 ORDER BY no ASC"); while($row = $stmt -> fetch(PDO::FETCH_ASSOC)) { $ttitle = $row["title"]; $tr = $row["r"]; $tk = $row["k"]; $tt = $row["t"]; $tm = $row["m"]; echo<<<EOF ヒアドキュメント内の表示部分 EOF; } ?> ``` ##INSERT PDOでデータをINSERTする。 プリペアドステートメントで挿入すると値が文字列になるのでSQLインジェクションにも安心。 ```php:INSERT $stmt = $pdo -> prepare("INSERT INTO テーブル名 (name, value) VALUES (:name, :value)"); $stmt->bindParam(':name', $name, PDO::PARAM_STR); $stmt->bindValue(':value', 1, PDO::PARAM_INT); $name = 'one'; $stmt->execute(); ``` ###解説 ●bindParam は PDO::PARAM_INT を指定しても文字列として扱われる。 SQLインジェクションとかは心配なさそう。 ●bindValue は値を数値で指定します。 この場合は PDO::PARAM_INT 型指定が必要。 ##UPDATE すでに入っている値を変更する。 ```php:UPDATE <?php $sql = 'update テーブル名 set name =:name where id = :value'; $stmt = $pdo -> prepare($sql); $stmt->bindParam(':name', $name, PDO::PARAM_STR); $stmt->bindValue(':value', 1, PDO::PARAM_INT); $stmt->execute(); ?> ``` ##DELETE テーブルの値を削除する。 ```php:DELETE <?php $sql = 'DELETE FROM テーブル名 where id = :delete_id'; $stmt = $pdo -> prepare($sql); $stmt -> bindParam(':delete_id', $value, PDO::PARAM_INT); $stmt -> execute(); ?> ``` ##COUNT ```php:COUNT $stmt = $pdo -> query("SELECT * FROM テーブル名"); $count = $stmt -> rowCount(); ``` もっとイイやり方があるかも…。 mysql...系みたいに SELECT COUNT(dd) FROM... にしたらエラー出た。 ##SUM テーブルのa1の合計を求める。 ```php:SUM $stmt = $pdo -> prepare("SELECT SUM(a1) as a1 FROM テーブル名 WHERE y=:y"); $stmt -> bindParam(':y', $y, PDO::PARAM_STR); $stmt -> execute(); if($row = $stmt -> fetch()){ $kei = $row['a1']; } ``` 複数フィールドa1,a2,a3の値を合計する。 ```php:SUM $stmt = $pdo -> prepare("SELECT SUM(a1 + a2 + a3) as goukei FROM テーブル名 WHERE y=:y"); $stmt -> bindParam(':y', $y, PDO::PARAM_STR); $stmt -> execute(); if($row = $stmt -> fetch()){ $kei = $row['goukei']; } ``` ##テーブルを作成する。 テーブルが存在していなければ作成する。 ```php:テーブル作成 <?php $sql = "CREATE TABLE IF NOT EXISTS `テーブル名`" ."(" . "`dd` INT auto_increment primary key," . "`y` INT," . "`m` INT," . "`d` INT," . "`youbi` INT," . "`yokin` INT," . "`a1` INT," . "`a2` INT," . "`a3` INT," . "`a4` INT," . "`a5` INT," . "`i_date` DATETIME" .");"; $stmt = $pdo -> prepare($sql); $stmt -> execute(); ?> ``` ##テーブルを削除する ```php:DROP $sql = "DROP TABLE IF EXISTS テーブル名"; $pdo -> exec($sql); ``` |
|
| 175位 |
|
|||
|
22:16:59 |
|
|
`git fetch`と`git pull`の違いをわかりやすく書いてあるのが見つからなかったので。
##git fetch リモートからデータを取ってくる。 ただし取ってくるだけで、それ以外は何もしない。 例えば A->B # master A->B->C->D # origin/master だった場合,`git fetch`はCとDをダウンロードしてきて、origin/masterとして保存する。 しかしローカルのmasterには一切手を触れないため、masterとorigin/masterは別のコミットを指す。 この状態で`git checkout master`とかすると、多分こんな感じのメッセージが出る Switched to branch 'master' Your branch is behind 'origin/master' by 2 commit, and can be fast-forwarded. これは、このあと2回コミットされてるけど早送り(衝突が無いのでそのまま最新版に)できるよ! ってことらしく,`git merge origin/master`を実行することで、ローカルのmasterもorigin/masterを指す。 ##git pull `git pull`は、上の`git fetch`と`git merge origin/master`までを 全部まとめてやってくれる。 らくちん。 ただし、mergeしたくない時にやってしまうと大変なことになる。 そんな時は、戻したいコミットのIDを調べて、`git reset --hard ID`で元に戻せる。 |
|
| 176位 |
|
|||
|
15:26:11 |
(株式会社キュリオシティソフトウェア 所属) |
|
クックパッドさんが月イチで行っている第3回potatotips(ポテトチップス)というTips共有会がYahoo!Japanさんで開かれたので参加させてもらい、『やはりお前らのiOS7対応は間違っている』という刺激的&挑戦的なタイトルで発表させてもらいました。 発表時には時間もなかったせいかツッコミがなかったのですが、おそらく公開したらツッコミがあると思うのでいくつかの補足を付けて公開します([発表時の資料はSlideShareに上げてます](http://www.slideshare.net/YoshinoriImajo/ios7-30039408)が、Qiitaのほうが編集リクエストもあるし直しやすいかと思います)。 # 何を間違っているか 最近、[iOS7でUINavigationBarにself.viewが潜り込む](http://techblog.yahoo.co.jp/ios/ios7yahoo/)という話をよく目にすると思います。この対応方法として「edgesForExtededLayoutプロパティをUIRectEdgeNoneとする(StoryboardではUnder Top Barsのチェックを外す)ことで潜りこみを回避できますよ」とあり、デメリットとして「これのデメリットはすりガラス表現が失われることです」とあります。 コードでは次のように書かれていたりしますね ```objc self.edgesForExtendedLayout = UIRectEdgeNone; //良い方法じゃないぜ? ``` これは長くプロジェクトを続けていく上では決して良い方法ではないし、デメリットも微妙に表現が間違っています。 この表現を多くのブログが引用していることから、おそらくこれらの設定の理解が出来てないのではないかと感じたわけです。 また、Storyboardを使えばコード上でedgesForExtededLayoutを使う必要はなく、Extend Edges項目のUnder Top Barsのチェックになるので以降の説明にはこれを使います。  ほとんどの場合このチェックはすべきです。チェックしてUINavigationBarにself.viewが潜り込むのなら、それは他の方法でずれを直すべきですよといのうがこの文章の結論です。 ## そもそもすりガラス表現は失われてなかったんだぜ Under Top Barsのチェックを外した場合、背景にはUIWindowしかないことから真っ黒な背景に対してすりガラス表現が適用されているだけです。 ビューを立体的に見てみると分かりやすいでしょう。  RootViewControllerと書かれたUINavigationBarの下にピンクのビューが無いため、すりガラス効果が黒の部分に対して適用されています。 逆にUnder Top Barsのチェックをオンにした場合は次のようになりピンクの背景色に対してすりガラス表現が適用されてます。  ## 新しいXcodeに移行した途端、ずれているからといってViewの開始をUINavigationBarの下からにするのは良いやり方じゃない ずれているのはiOS7から考え方が全画面表示になったからで、それに伴ってView上のコンポーネントを調整しましょう。はい、調整するとiOS6がずれますね、そしたらiOS6/7 Deltaで座標値を変えてiOS6用に調整すればよいです。 # パターン別のしっかりとした解決方法 解決方法の分類のため3パターン考えてみました。 - UITableViewControllerを使う場合 - UIViewControllerを使う場合 - UIScrollViewやUICollectionViewで全画面写真表示したい場合 表で設定をまとめるとわかりやすくなります。 ||Adjust Scroll View Insets|Under Top Bars|iOS6対応のため| |:-----------|:-----------:|:-----------:|:------------| |UITableViewController|チェックする|チェックする|| |UIViewController|チェックする|チェックする|まずiOS7用にコンポーネントを配置し直し、その上でiOS6/7 Deltasで座標値調整| |UIScrollViewなどで全画面写真表示|チェック外す|チェックする|Use Full Screenをチェック| 基本的にUnder Top Barsは全てチェックをしたままが良いでしょう。 UINavigationBarがない場合はチェックを外してもいいかもしれませんが、なければこのチェックが意味を成さないだけです。 自分の経験上、あえてUnder Top Barsのチェックを外さなきゃならないパターンが見当たらないので他にあれば教えてください。 ## Adjust Scroll View Insetsにチェックしないパターン イレギュラーなのが先述の3パターン目だけです。Adjust Scroll View InsetsとUnder Top Barsをチェックしてしまうとその設定によって、View自体はちゃんとウインドウの左上から始まるのですがcontentInsets.topが調整されてしまいナビゲーションバーの下からscrollViewがはじまり全画面ぽくないですね(図の右側)。  # その他 ## Storyboard使えるならStoryboardで設定するのがオススメです Storyboard(Interface Buildr)の設定はわりとよく考えられていて、iOS7だけにしか効果が無い設定やiOS6だけにしか効果が無い設定があるので、コード上で分岐させる必要もなくなる上に、将来的にiOS8などが出てiOS6が非対応になった際にもその設定は変更する必要がなくなるわけです。おそらく表面的にも表示されなくなるでしょう。ソースコードで書いてしまうと精神衛生上それらを消さないってわけにも行かないし、消しても良いコード悪いコードの判別を強いられるの嫌ですよね。 ```objc //ViewControllerのloadViewなどでの処理 if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) { //ここにiOS7用のプロパティへの代入を書かなくてもStoryboardへの設定が反映される //例)self.edgesForExtendedLayout = UIRectEdgeAll; //Storyboardに設定した値と違う値をセット出来てしまって保守しづらい //iOS7以上でStoryboardで設定できない処理があるなら書くしかないが //そうすると消しても良いStoryboardで設定できるコードと //そうでないコードがViewControllerに混在して判別しづらい } ``` ## potatotipsで発表した資料 SlideShareにアップロードしてます http://www.slideshare.net/YoshinoriImajo/ios7-30039408 ## 設定が反映されるviewについて Adjust Scroll View InsetsやUnder Top Barsは設定したViewController直下のviewにだけ効果があると思っていましたが、直下のviewに対してaddしたview(view.subviews[0])についても設定が反映されるようです。 |
|
| 177位 |
|
|||
|
19:45:21 |
(Increments 所属) |
|
## ~/repo1 と ~/repo2 をまとめる
2つのリポジトリをまとめて1つのリポジトリにしたいとき。 ~~~ cd ~/repo1 git remote add repo2 ~/repo2 git fetch repo2 git merge repo2/master ~~~ ## ~/repo1/subdir に ~/repo2 を入れる あるリポジトリのサブディレクトリに別のリポジトリの中身を入れたいとき。たとえば、あるリポジトリのサブディレクトリを切り出して別のリポジトリとして管理しているものを、元のリポジトリに合流させたいとき。 ~~~ cd ~/repo1 git remote add repo2 ~/repo2 git fetch repo2 # サブディレクトリの内容に repo2 の内容をマージする # (repo2 と内容が似ているサブディレクトリを自動で判別) git merge -s subtree repo2/master # ↑でうまくいかないときにはパスを指定する↓ git merge -X subtree=subdir repo2/master # そもそも ~/repo1/subdir が存在しないときには↓ git read-tree --prefix=subdir/ repo2/master git checkout -- . ~~~ ## ~/repo1/dir1 と ~/repo1/dir2 をマージする (タイトルからは外れるが)同一リポジトリ内の2つのサブディレクトリをマージしたいとき:http://qiita.com/uasi/items/545665066619d8cb8dbe ## ~/repo1/subdir を ~/repo2 として切り出す あるリポジトリのサブディレクトリを別のリポジトリとして管理したいとき。 ~~~ git clone ~/repo1 ~/repo2 cd ~/repo2 git filter-branch --subdirectory-filter subdir HEAD ~~~ 余談:ここで `git clone` の代わりに `git-new-workdir` を使ってはいけない([コメント参照](http://qiita.com/uasi/items/77d41698630fef012f82#comment-441deaa42dc8087ca414)) 参考にしたページ: - [git、複数のリポジトリをまとめる - <s>gnarl,</s>技術メモ”’<marquee><textarea>¥](http://d.hatena.ne.jp/gnarl/20120111/1326270674) - [gitリポジトリのサブディレクトリを別のリポジトリとして抽出する方法 – 拡張現実ライフ](http://akio0911.net/archives/3421) |
|
| 178位 |
|
|||
|
18:18:31 |
(Freelancer 所属) |
|
他人から引き継いだプロジェクトの大まかな全体設計を把握したい場合、規模の大きいライブラリの一部だけ取り出して使用したい場合、複雑になってしまったコードをリファクタリングしたい場合等、プロジェクト内のクラスの依存関係が一望できると有益な場面は多いのではないでしょうか。
objc_depというスクリプトを用いると、下記のように、Xcodeプロジェクト内のクラスの依存関係を示す図をコマンド一発で生成することができます。  このスクリプトの使い方、生成される図の見方等を紹介します。 ##スクリプトの実行方法 次のURLからダウンロードしてきて解凍すると、objc_dep.py というファイルが入っています。 [https://github.com/nst/objc_dep](https://github.com/nst/objc_dep) そのスクリプトを適当な場所に置き、ターミナルから次のように実行します。 $ python objc_dep.py {プロジェクトのパス} > {出力ファイル名}.dot たとえばホーム直下にDemoというプロジェクトフォルダがあり、demo.dotというファイル名で出力したい場合は、 $ python objc_dep.py ~/Demo > demo.dot というコマンドになります。 ##依存関係図を表示 出力される.dotという形式のファイルは、[Graphviz](http://www.graphviz.org/) または [Omnigraffle](http://www.omnigroup.com/) というアプリケーションで表示できます。(Omnigraffle は定評のあるダイアグラム作成ソフトですが、なにぶん有料なので、とりあえず objc_dep を試してみたいという方は、オープンソースの Graphviz をオススメします。) ##依存関係図の見方 ここでは試しに、 "Facebook iOS SDK" に付属している "Hackbook" というサンプルアプリの依存関係を出力してみました。  出力された図を見ると、青い矢印で結ばれているところがあります。  ソースコード内のコメントによると、「相互にimportしている」場合に青い矢印で結ばれるようです。 コードの保守性の観点から、クラスの相互依存はなるべく避けたいところかと思います。もし青い矢印が存在した場合、プロトコルを使用して直接参照を片方向からのみにできないか検討する、といったようにリファクタリングの指針とすることができます。 またこの例には出てきませんでしたが、赤い矢印が出てくることがあります。  赤い矢印は、.pchファイルからのimportを意味します。 .pch ファイルから import されたヘッダは全クラスから参照される(この参照の矢印は描画されない)ため、このような使用になっているものと思われます。 こちらも、.pch で import しているのにさらに個別のクラスでも import している、.pch で import すべきではないクラスを import している、といったように不要な依存性がないかのチェックの指針となります。 同じくこの例には出ていませんが、カテゴリは(他のクラスに依存していない場合は)依存関係図から切り出して右上の方に列挙してくれます。カテゴリはその性質上多くのファイルから参照されていても設計上の問題とはならないため、図がゴチャゴチャしないようにこういう仕様になっているようです。 |
|
| 179位 |
|
|||
|
13:09:36 |
(Trashfeed 所属) |
|
Androidアプリ作成時、毎回、アイコンに苦悩しています。フリーのアイコン探しやライセンス確認、多機種に合わせたリサイズ作業に疲れ果てています。
もう悩みたくない。 アイコン探ししたくない。 PhotoShop使いたくない。 そんな自分が、Androidで __[Font Awesomeの約369個のアイコン](http://fortawesome.github.io/Font-Awesome/cheatsheet/)__を簡単に利用する事ができる __Android-Bootstrap__ を導入した時のチップスです。 # したいこと - Androidアプリで毎回使える汎用的なアイコンが欲しい(ランチャーアイコン以外で) - 全機種・解像度共通でアイコンを利用したい(画像リサイズ作業を極力避けたい) # 最終的な目標 - Android-Bootstrapを利用してアプリをリリースする(した) - [EveryEver(エブリー・エバー) 圏外でも快適なEvernoteライフを。](http://android.trashfeed.net/) # 利用するライブラリ ## __Android-Bootstrap__ - [https://github.com/Bearded-Hen/Android-Bootstrap](https://github.com/Bearded-Hen/Android-Bootstrap) # Android-Bootstrapって? - TwitterBootstrapライクなボタン、アイコンが利用できるAndroidライブラリ。 - AndroidでFontAwesomeの[約369個のアイコン](http://fortawesome.github.io/Font-Awesome/cheatsheet/)が利用できる(Bootstrap Button、Font Awesome Text)。 - (Font Awesome Textの場合)実態がTextViewなので、アイコンサイズ、アイコンの色が自由に変更できる(機種別に画像を用意しなくても良い)。 - アイコンの他に角丸ボタン、角丸サムネイル画像など、4種類のビューがサポートされている。 # Font Awesomeって? - 約369個のアイコンを表示する事ができるWEBフォント。 [http://fortawesome.github.io/Font-Awesome/](http://fortawesome.github.io/Font-Awesome/) - 非デザイナーの自分がWEB開発でお世話になっている[TwitterBootstrap](http://getbootstrap.com/)でも利用されています。 - Android-BootstrapのFontAwesomeTextでは、このWEBフォントがandroidで簡単に利用出来る。 # Android-Bootstrapのダウンロード先 - [https://github.com/Bearded-Hen/Android-Bootstrap](https://github.com/Bearded-Hen/Android-Bootstrap) # 利用できる5つのビュー - __Font Awesome Text__ (FontAwesomeアイコン、アニメーション) - __Bootstrap Button__ (アイコン付きのボタン、角丸ボタン。アイコン色の変更は出来ない模様) - __Bootstrap Edit Text__ (入力テキスト、角丸、枠色の変更) - __Bootstrap Thumbnail__ (サムネイル画像、Drawableリソースのみ指定可能。Bitmap、Uri等の指定はできない模様) - __Bootstrap CircleThumbnail__ (円形サムネイル画像、Drawableリソースのみ指定可能。Bitmap、Uri等の指定はできない模様) # 5つのビューの表示例 - ##__Font Awesome Text__  - ##__Bootstrap Button__  - ##__Bootstrap Edit Text__  - ##__Bootstrap Thumbnail__  - ##__Bootstrap CircleThumbnail__  # 利用方法 ## Android-Bootstrapのインストール 1. ライブラリをダウンロード [https://github.com/Bearded-Hen/Android-Bootstrap](https://github.com/Bearded-Hen/Android-Bootstrap) 2. メインのAndroidアプリから、ライブラリプロジェクトとして、ダウンロードしたAndroid-Bootstrapプロジェクトを参照。 3. ダウンロードしたライブラリのルートに「fontawesome-webfont.ttf」があるので、これをメインプロジェクトのassetsフォルダにコピー。 ## レイアウトファイルでの宣言 - ルート要素にAndroid-bootstrapのView宣言する - `xmlns:android-bootstrap="http://schemas.android.com/apk/res-auto"` を記述する。 - `android-bootstrap`の箇所は自由。 ### 例) レイアウトファイルでの宣言 ``` activity_example.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:android-bootstrap="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="fill_parent" android:orientation="vertical" > ``` ## Viewの設定 - Viewの定義には以下の名前空間を使用する | ビュー | パッケージ名 | |:-----------|:------------| | Font Awesome Text |com.beardedhen.androidbootstrap.FontAwesomeText| | Bootstrap Button |com.beardedhen.androidbootstrap.BootstrapButton | | Bootstrap Edit Text|com.beardedhen.androidbootstrap.BootstrapEditText | | Bootstrap Thumbnail |com.beardedhen.androidbootstrap.BootstrapThumbnail | | Bootstrap CircleThumbnail |com.beardedhen.androidbootstrap.BootstrapCircleThumbnail| ### 例) Bootstrap Buttonの場合 ``` activity_example.xml <com.beardedhen.androidbootstrap.BootstrapButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="アイコンボタン。角丸にも出来ます。" app:bb_icon_left="fa-smile-o" app:bb_type="default" /> ``` # よく使った属性 ## Font Awesome Text | 属性名|説明|例| |:-----------|:------------|:------------| |android:textColor|アイコン色の変更。Textviewと同様の属性。|android:textColor="#0094FF"| |android:textSize|アイコンサイズの変更。フォントなので大きくしても汚くならない。Textviewと同様の属性。|android:textSize="20sp"| |app:fa_icon|アイコンの指定。[FontAwesomeのクラス名](http://fortawesome.github.io/Font-Awesome/cheatsheet/)|app:fa_icon="fa-tags"| ## Bootstrap Button | 属性名|説明|例| |:-----------|:------------|:------------| |android:textSize|アイコンサイズの変更。フォントなので大きくしても汚くならない。Textviewと同様の属性。|android:textSize="20sp"| |app:bb_roundedCorners|角丸にする場合はTrue。|app:bb_roundedCorners="true" |app:bb_icon_left|左にアイコンを表示する。[FontAwesomeのクラス名](http://fortawesome.github.io/Font-Awesome/cheatsheet/)を指定する。|app:bb_icon_left="fa-trash-o"| |app:bb_icon_right|右にアイコンを表示する。[FontAwesomeのクラス名](http://fortawesome.github.io/Font-Awesome/cheatsheet/)を指定する|app:bb_icon_right="fa-trash-o"| |app:bb_type|ボタンの外観。[TwitterBootstrapのButtonのOptionsクラス名](http://getbootstrap.com/css/#buttons-options)を指定する|app:bb_type="danger"| |app:bb_size|ボタンの大きさ。[TwitterBootstrapのButtonのSizesクラス名](http://getbootstrap.com/css/#buttons-sizes)を指定する|app:bb_size="large"| ## Bootstrap Edit Text | 属性名|説明|例| |:-----------|:------------|:------------| |app:be_roundedCorners|角丸にする場合はTrue。|app:be_roundedCorners="true" |app:be_state|入力エリアの外観。[TwitterBootstrapのクラス名](http://getbootstrap.com/css/#buttons)を指定する|app:be_state="success"| ## BootstrapThumbnail | 属性名|説明|例| |:-----------|:------------|:------------| |app:bt_image|DrawableリソースID|app:bt_image="@drawable/cat"| |app:bt_roundedCorners|角丸にする場合はTrue。|app:bt_roundedCorners="true" |app:bt_height|画像の高さ。|app:bt_height="148sp"| |app:bt_width|画像の幅。|app:bt_width="148sp"| |app:bt_inside_padding|ボーダーの幅|app:bt_inside_padding="8sp"| ## BootstrapCircleThumbnail | 属性名|説明|例| |:-----------|:------------|:------------| |app:bct_image|DrawableリソースID|app:bt_image="@drawable/cat"| |app:bct_size|サムネイルの大きさ。small medium large xlargeから指定する|app:bct_size="xlarge"| |app:bct_minimal|ボーダーを表示しない場合はtrue||app:bct_minimal="true" ## Bootstrap Buttonの使用例 - Bootstrap Buttonを代表としてひと通り設定してみる ``` activity_example.xml <com.beardedhen.androidbootstrap.BootstrapButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="アイコンとボタン" bootstrapbutton:bb_icon_left="fa-smile-o" bootstrapbutton:bb_type="default" /> ``` ### アイコンの位置 - `bootstrapbutton:bb_icon_left`にFontAwesomeのクラス名[http://fortawesome.github.io/Font-Awesome/cheatsheet/](http://fortawesome.github.io/Font-Awesome/cheatsheet/)を指定する。 - アイコンを左に表示するには`bootstrapbutton:bb_icon_left`、右に表示するには`bootstrapbutton:bb_icon_right`にアイコンを指定。左右両方に指定する事もできる。  ### ボタンの外観 - `bootstrapbutton:bb_type`に、[TwitterBootstrapのButtonと同じクラス名](http://getbootstrap.com/css/#buttons)を指定する。  - ボタンの文字色を変更する事は出来ない。実態はtextviewなのでライブラリ側のプロジェクトを自分で修正して外部に公開するなどごちょごちょする事が必要。 ### ボタンのサイズ - ボタンのサイズもTwitterBootstrap同様で`bootstrapbutton:bb_size`に、`large, default, small, xsmall`等を指定する。  - 文字色と同じく文字サイズの設定は出来ない。 ### 角丸ボタン - `bb_roundedCorners` に `true`を指定する事で角丸ボタンとなる。  # 雑感 ## Font Awesome Text(アイコン)は自由にアイコンサイズが変更できて便利 -Font Awesomeのアイコンを表示できる「Font Awesome Text」は実態がTextViewの為、android:textSize="32sp" 等と設定するだけで大きさを変更できます。しかも大きくしてもギザギザしてません。また、アイコンの色を変更するのも android:textColor=#ddd" といった感じです。 \# 一方、Bootstrap Button(ボタン)はアイコン色の変更ができないのが少し残念。 ## 多機種のリソースを用意する必要がない。 Font Awesome Text(アイコン)やBootstrap Button(ボタン)に表示されるアイコンは只のフォントなので、多機種のリソースを用意する必要がありません。多機種の解像度に合わせたアイコン画像などを用意する手間がなく非常に簡単です。 ## スタイルが利用できない - 例えばBootstrap Buttonの共通プロパティとして`bb_roundedCorners`をスタイルで設定したい場合、以下のような `<item name="bootstrapbutton:bb_roundedCorners">true</item>` という名前空間 __bootstrapbutton:__ を含めた記述ではなく・・・、 ``` style.xml <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"> <style name="BootstrapButton"> <item name="bootstrapbutton:bb_roundedCorners">true</item> </style> </resources> ``` - 以下のように `<item name="bb_roundedCorners">true</item>` name属性を指定します。 ``` style.xml <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="BootstrapButton"> <item name="bb_roundedCorners">true</item> </style> </resources> ``` ## Bootstrap Button(ボタン)でアイコン色が指定できない - 「Font Awesome Text(アイコンビュー)」は自由にアイコン色・アイコンサイズの変更はできるが、「Bootstrap Button(ボタンビュー)」は変更できません。`bb_type`で外観は変更できてもアイコン色は自動で設定されるようです。アイコン色色を変更する属性は公開されていません。ライブラリ側のソースをなんだかんだする必要があります。 ## Bootstrap Thumbnail(サムネイル画像)でBitmapが指定できない - 「Bootstrap Thumbnail(サムネイル画像)」にはDrawableのリソースIDしか指定できません。ネット上の画像やユーザーのギャラリー画像などを表示する為には、Bootstrap Button同様にハックする必要がありそうです。 ## (Eclipse)Graphic Layout で見た目が分からない - 当然、通常のボタンのようにグラフィカルレイアウトでは表示できません。エラーログはisInEditModeで対応したとしても、ボタンの見た目は実機等で確認する必要があります。 # 実装イメージとリリースアプリ 上記を踏まえ、Android-Bootstrapを利用したアプリ[「EveryEver(エブリー・エバー) 圏外でも快適なEvernoteライフを。」](http://android.trashfeed.net/)をリリースしました。 ## 実装イメージ    |
|
| 180位 |
|
|||
|
05:02:00 |
|
|
##正体
ログインシェル(PCを起動したときにデフォルトに指定されるシェル)をbashにしている場合はログイン(起動)時に以下のような順番で設定ファイルが読み込まれ、PATHを通す、エイリアスを定義する、プログラムを実行するなどの設定が自動で設定される。 ####1. /etc/profile 全ユーザーに適用されるデフォルトの設定ファイル。 ユーザー単位では編集しない。(つまり、ほとんどいじらない) ####2. ~/.bash_profile 存在していれば読込まれる設定ファイル。 ユーザー単位での設定を書く。 無くても良い。 ####3. ~/.bash_login ログインして~/.bash_profileが存在しない場合にのみ、存在していれば読込まれる設定ファイル。 無くても良い。 ####4. ~/.profile ログインして~/.bash_profile ・ ~/.bash_loginが存在しない場合にのみ、存在していれば読み込まれる設定ファイル。 無くても良い。 ####5. ~/.bashrc シェル(bash)ログインで毎回読込まれる設定ファイル。 無くても良い。 #### ~/.bash_logout というのもある。 シェル(bash)ログアウトに毎回読込まれる設定ファイル。 無くても良い。 ##どう使い分ければいいか! ####~/.bash_profile ログイン時に一度設定すればいいもの ####~/.bashrc bashを起動するたびに設定する必要のあるもの ##その他は? 基本的に上の2つで事足りる。 しかし、以下のような特別な場合があれば利用する価値がありそう。 全ユーザーでログイン時に起動したいアプリケーションがある場合などは/etc/profileにその処理を書いておくことで全ユーザーでログイン時に処理が実行される。 ログイン時には必ずこの処理をしてほしいなどということがあれば、~/.bash_loginにその処理を書いておくことで~/.bashrcを削除しても処理が実行される。 |
|
| 181位 |
|
|||
|
14:13:44 |
(freee K.K. 所属) |
|
Backbone.jsで書き始めたら「Backboneどう?」と聞かれることがあったので、自分ではあんま語れるほど知らないけど「ここらへんの記事は素晴らしいよ!」というものをまとめておいたヽ(・ω・´)ゝ
## まずは読んでおくべきもの ### [Backbone.js Advent Calendar 2011](http://qiita.com/advent-calendar/2011/backbone) なにはともあれまずは[Backbone.js Advent Calendar 2011](http://qiita.com/advent-calendar/2011/backbone) ある程度まで書けるようになる情報は総ざらいで書いてあると思う。 中でも[Backbone.js入門](http://qiita.com/items/16b799d0ec0a0ae3f78e)はありがたい。読んでおけば基本の仕組みを知ることが出来る。 Backbone.jsが依存している[underscore.jsの情報](http://qiita.com/items/78b81fb50f0ce995ed7d)なんかもあったりしてありがたい。 ちなみに今年(2012)のAdvent Calendarは[こちら](http://www.adventar.org/calendars/15)。今はまだ始まったばかりだから情報少ないけどこれから充実してくるだろうし楽しみ(*´ω`*) ### [Backbone 入門](http://www.ibm.com/developerworks/jp/web/library/wa-backbonejs/index.html) うっかり忘れていたものを追記(2012-12-09) ibmのdeveloperworksの記事。SyncやRouterなどの解説も細かくしてあって基本としてはかなり大事かと。 とても骨太な内容なのでこれも読んでおくとより深くしっかりと身につくと思う。 ### [Backbone.js Tutorials](http://backbonetutorials.com) [Backbone.js Tutorials](http://backbonetutorials.com)はAdvent Calendar 2011とほどじゃないけどありがたい。 ここは初歩的な情報が多めかな。ビデオチュートリアルもあるみたいだけど有料っぽいのでまだ観てない。 ## とにかくまずは動くものを書きたいなら ### [Hello Backbone.js Tutorial](http://jasongiedymin.github.com/hello-backbonejs/) 初めてのBackbone.jsアプリを書いてみようと思ったらなんといっても[Hello Backbone.js Tutorial](http://jasongiedymin.github.com/hello-backbonejs/) [こんな感じ](http://jasongiedymin.github.com/hello-backbonejs/js/docs/1.html)に5段階のステップを踏んでとりあえず画面を表示するところからモデルを別にしてビューを部分によって分けたりしていく。これに沿って書いていけば「ああ、こうなると便利な気がする」と思える。 なんと今見たら[coffee sctiptでのチュートリアル](http://jasongiedymin.github.com/hello-backbonejs/coffeescript/docs/1.html)もあった。ここから一気にBackbone.jsを学びながらcoffee scriptも学べちゃいそう。 ### [todo.js](http://documentcloud.github.com/backbone/docs/todos.html) あとはこの[todo.js](http://documentcloud.github.com/backbone/docs/todos.html)。 こっちはもっと本格的なTODOアプリを実際に作ってみている。LocalStorageも使ってたりしてHTML5な雰囲気を感じますなぁ。 ちなみにBackbone.js以外にも様々なMVCフレームワークで書かれたTODOアプリのサンプルを集めている[TodoMVC](http://todomvc.com/)は「同じアプリを違うフレームワークで書くとこうなるのかー」と見てて楽しい。[Angular.js](http://angularjs.org)とか心惹かれちゃう。 ## 実際に書き始めたら ### [Backbone patterns](http://ricostacruz.com/backbone-patterns/) チュートリアルや入門の情報だけだと実際に作り始めた時に「こういう場合どうすんの?」ってことがある。 そういう時にありがたいのが[Backbone patterns](http://ricostacruz.com/backbone-patterns/)。 「アプリケーション読み込み時にこう書いたらいいよ」という初歩的なものからビューのデレゲートやアンチパターンまで含まれる。 まだちょっとパターンが少ないけど充分ありがたいヾ(`・ω・´)ノ ### [Backbonification: migrating a large JavaScript project from DOM spaghetti to Backbone.js](http://www.ofbrooklyn.com/2012/11/13/backbonification-migrating-javascript-to-backbone/) タイトルの通り、でかいJavaScriptで書かれたプロジェクトをこんな風にBackbone.js化したよという記事。 これは苦労しただろうなと思いつつ、こんなきれいに書けたら素敵だなーと思った(`・ω・´) ### [The client-side templating throwdown: mustache, handlebars, dust.js, and more](http://engineering.linkedin.com/frontend/client-side-templating-throwdown-mustache-handlebars-dustjs-and-more) これはちょっと番外編。 実際に書いてるとビューのテンプレートエンジン使った方がいいよねということになるのでここの記事を見て参考にした。 ずいぶん詳細に比べてて骨が折れただろうなーと思わせる記事。この中ではdust.jsの勝ちと結論付いてるけど僕はhandlebarsにしました。 Ember.jsでも組み込まれてる?みたいだしdust.jsより情報が多そうでいいなと思ったので。 ## その他 その他Backbone.jsに関する英語ドキュメントのまとめもあるので良かったらどうぞ [【あえて英語記事オンリー】Backbone.jsを学べるページまとめ](http://qiita.com/items/9724455eeca505dedfc3) |
|
| 182位 |
|
|||
|
23:30:22 |
|
|
# はじめに この投稿は、Mac OS XをJISキーボードで使っている時、プログラミングでよく使われるバックスラッシュ(\)を入力するための方法を記しています。 # 環境 * Mac OS X 10.8.4で動作確認を行っています。 * Google 日本語入力は、1.11.1516.1(2013年8月21日時点のもの)で動作確認を行っています。 # やり方(Mac OS X標準の場合) Mac OS XをJISキーボードで使っている場合、バックスラッシュ(\)の入力は * optionキーを押しながら、¥キーを押す の方法でできます。 毎回、optionキーを押しながら入力するのは面倒くさい人は、Mac OS XについているIME「ことえり」の設定を変えることで対応可能です。 1. 画面右上に、黒地で白文字の「A」が出るように選択 2. 「環境設定を表示」を選択 3. 「入力文字」の「JISキーボードの¥キーで入力する文字」を「\(バックスラッシュ)」に変更 # やり方(Google 日本語入力の場合) Google日本語入力の場合でも、バックスラッシュ(\)の入力は、 * optionキーを押しながら、¥キーを押す の方法でできます。 毎回、optionキーを押しながら入力するのは面倒くさい人は、Google日本語入力の設定を変えることで対応可能です。 1. 画面右上に、青字で白文字の「A」が出るように選択 2. 「環境設定...」を選択 3. 「一般」の「¥キーで入力する文字」を「\(バックスラッシュ)」に変更 |
|
| 183位 |
|
|||
|
08:26:08 |
(フリーランス 所属) |
|
ちょうどいくつかプレゼン用の資料を作らなきゃと思っていてなんかいいテンプレートないかな〜って探していたらので[reveal.js](https://github.com/hakimel/reveal.js)が超絶に良かったのでメモ [reveal.js](https://github.com/hakimel/reveal.js) htmlで綺麗でカッコいいプレゼンテーションを作れるフレームワークです。 ## 3Dでグィングィン動きます [デモ画面](http://lab.hakim.se/reveal-js/#/)見ればどんな風にグィングィン動くかわかります(開いたら右スクロールボタンを押してみてください) ## Escキー押すとプレゼンテーションのドキュメント一覧が見れます それがまたカッコよく3Dでみれます。 ## PDFへのエキスポートが楽チンです 書いたドキュメントをプリントしてメンバーに配布とかってシーンがあるとおもいますが reveal.jsなら綺麗にできます。 ## 使い方 ### zipパッケージで落としてくる [hakimel/reveal.js](https://github.com/hakimel/reveal.js)にいって 「Download ZIP」をポチッと押します。 ### 書きたい内容をindex.htmlで編集 コレだけです。 ## プレゼン内容の書き方 ### HTMLでゴリゴリ書きたい場合 プレゼン内容は ```<div class="slides">``` 以下に ``` <section> ```区切りで一枚のページになります。(右スクロールを押すたびに次のページに移動する) ### markdowon形式でゴリゴリ書きたい場合 公式にも説明がありますがこんな感じで ```html:index.html <section data-markdown> <script type="text/template"> ## Page title A paragraph with some text and a [link](http://hakim.se). </script> </section> ``` ### 下スクロールでのページを作成したい場合 ``` <section> ```をネストさせましょう ```html:index.html <section> <section> <h3> 題名 </section> </section> ``` コレで下スクロールのページが作れます ## PDFに変換する方法 以下がやり方(MAC) * 1. chromeで開きます * 2. URLにindex.html?print-pdfを付与します * 3. comannd + p で印刷画面を表示させます * 4. 「レイアウト」を横にして「pdfにして保存」を選びます 他にも色々オプションがあるのでgithubのドキュメントを読んでみてください |
|
| 184位 |
|
|||
|
13:37:27 |
|
|
# はじめに YouTubeやFacebookでも利用されているという噂のFFmpegは、開発が非常に活発で、ここ1年だけでもバージョン1.0から2.1まで上がっています。 ちょっと前まではFFmpegでエンコードした動画をストリーミングに対応させるのは一手間かかったのですが、今では-movflags faststartオプションを付けるだけで対応できるようになりました。(…というのも新しい機能のほんの一部で、他にもたくさんあります) ここでは、FFmpegの簡単な使い方をメモします。 内容は基本的なことですが、実運用で得たTipsもできるだけ混ぜていきたいと思います。 また、内容に誤りなどあれば、是非ご指摘頂けると有難いです。 # インストール インストールについては公式サイトに丁寧な解説があるので、そちらをご参照ください。 https://trac.ffmpeg.org/wiki/CompilationGuide 基本的には書いてある通りに進めば問題なく動きますが、動画エンコード時に使うフィルタによっては、configure時に何らかのオプションを付ける必要があります。 例えば、映像内に文字を書き込むにはdrawtextというフィルタを使いますが、これを利用するためにはconfigure時に--enable-libfreetypeオプションを追加する必要があります。 ※ビデオフィルタに関するドキュメント http://ffmpeg.org/ffmpeg-filters.html # 使い方 FFmpegは豊富なエンコードオプションが用意されていますが、何も指定しなくても十分に使えます。 ## H.264でmp4形式に変換する H.264でのエンコードは、以下のように行います。 ``` ffmpeg -i [元動画] -movflags faststart -vcodec libx264 -acodec libfaac [新しい動画] ``` [元動画]は、FFmpegが対応しているものであれば自動判別してくれます。 ちなみに、H.264はWebでの動画配信では非常に多く利用されている動画コーデックです。ライセンスの問題があり、ブラウザによっては採用されていないのですが(その場合でもFlash経由なら使える)、つい先日CiscoがH.264のオープンソース化を発表し、それに伴ってFirefoxもH.264をサポートすることを発表しました。 ※CiscoとMozilla、H.264のオープンソース化とFirefoxでのサポートを発表 http://www.itmedia.co.jp/news/articles/1310/31/news064.html ## 特定の時間帯を切り出す 特定の時間帯を切り出すのも簡単です。 ``` ffmpeg -ss [開始位置(秒数)] -i [元動画] -t [切り出す秒数] [新しい動画] ``` なお、動画の切り出し方は複数パターンあり、基本的にはスピードと精度のトレードオフになっています。このあたりについては[FFmpegで素早く正確に動画をカットする自分的ベストプラクティス](http://qiita.com/kitar/items/d293e3962ade087fd850)という実験メモも書いておりますので、もしよろしければご参考ください。 ## 特定の範囲を切り出す 動画の隅などに不要なものが映っていたりして、特定の範囲だけ切り取りたいこととかありますよね。 ``` ffmpeg -i [元動画] -vf crop=x=[切り取りを開始するx座標]:y=[切り取りを開始するy座標]:w=[切り取る範囲の幅]:h=[切り取る範囲の高さ] [新しい動画] ``` ## ビデオフィルタをかける:ぼかし処理 何か隠したいものがあることってありますよね。 ``` ffmpeg -i [元動画] -vf boxblur=2:1 [新しい動画] ``` ## ビデオフィルタをかける:文字を入れる 字幕を入れたいこととかありますよね。 ``` ffmpeg -i [元動画] \ -vf drawtext="[TrueTypeフォントのパス]: \ text='あのイーハトーヴォのすきとおった風': \ x=0: y=0: fontsize=20: box=1: boxcolor=white@0.7" [新しい動画] ``` 冒頭にも書きましたが、drawtextを使うためにはffmpegコンパイル時に--enable-libfreetypeオプションが必要です。 ## 動画から画像を切り出す サムネイル画像とか作ることもありますよね。 ``` ffmpeg -ss [切り出し位置(秒数)] -i [元動画] -vframes 1 -f [切り出す画像] ``` ## 音声部分をを取り出す 音声部分だけ加工したい(ノーマライズなど)こともありますよね。 FFmpegはノーマライズはできないので、音声ファイルだけ取り出して別ツールで処理する必要があります。 以下の例では、16bitのwavファイルに切り出しています。 ``` ffmpeg -i [元動画] -acodec pcm_s16le [取り出す音声] ``` ## 映像と音声を結合する バラバラの映像と音声を結合するには以下のようにします。 ``` ffmpeg -i [元映像] -i [元音声] -map 0:0 -map 1:0 \ -movflags faststart -vcodec libx264 -acodec libfaac [新しい動画] ``` -mapの「0:0」や「1:0」は、どのファイルを何番目のストリームに配置するのかを指定しています。 なお、この時に映像や音声のコーデックを変換する必要がない場合、-vcodec copyや-acodec copyのように値にcopyを指定すると再エンコードする必要がないため余計な処理が走りません。(ncaqさんコメントありがとうございます!) ## 動画のメタデータを確認する 動画の長さや、サイズ、ビットレート、動画や音声などトラックごとの情報などを表示します。 ``` ffprobe [元動画] ``` --- 以上、基本的な使い方の部分をご紹介いたしました。 本家のドキュメントも充実しているので、是非あわせてご覧ください。 http://ffmpeg.org/documentation.html ## その他の記事 * [ffmpegで動画の中に動画を表示する(実況中継の隅っこにある解説者の映像のような)](http://qiita.com/kitar/items/c4da996e358e39aa7c9b) |
|
| 185位 |
|
|||
|
16:16:34 |
|
|
## What's Google Guava?
Googleが開発しているOSSのjava core libraryで、Googleの多くのJavaベースのプロジェクトで使用されており、 Java 1.5 以上で使用できる。 昔、Google Collectionとして開発されていた。 Apache Commons の Lang、Collectionsなどに替わる機能を提供していて、パッケージ名を見れば大体の機能について想像がつくと思います。 * com.google.common.annotations * com.google.common.base * com.google.common.cache * com.google.common.collect * com.google.common.escape * com.google.common.eventbus * com.google.common.hash * com.google.common.html * com.google.common.io * com.google.common.math * com.google.common.net * com.google.common.primitives * com.google.common.reflect * com.google.common.util.concurrent ※2013/10/27現在 要は`できることを増やすライブラリではなく、普段やっていることを短く書くためのライブラリ` この中で、使ってみたものを紹介。 気が向いたら、追加してくかも、、 ## com.google.common.collect パッケージ 旧google collections library。標準のJDKには含まれていないCollectionの実装クラスをまとめられていて、便利なCollection操作クラスが多数ある。 このクラスを使うために、Guavaを使用するだけでも十分な価値がある。 ### Lists / Maps / Sets 冗長なインスタンス化の排除、配列のような柔軟な初期化。 ```java:Example // Java → 長い、冗長 List<TypeThatsTooLongForItsOwnGood> listJava = new ArrayList<TypeThatsTooLongForItsOwnGood>(); // Guava → シンプル(ただしJava7からは右辺のジェネリクスは省略できるので、、、) List<TypeThatsTooLongForItsOwnGood> listGuava = Lists.newArrayList(); Map<KeyType, LongishValueType> mapGuava = Maps.newLinkedHashMap(); // Guava → 配列ライクな初期化 List<String> theseElements = Lists.newArrayList("alpha", "beta", "gamma"); Set<String> theseSet = Sets.newHashSet("alpha", "beta", "gamma"); // Guava → 定数とか final List<String> constList = ImmutableList.of("dog", "cat", "pig"); final Map<String, String> constMap = ImmutableMap.of("dog", "犬", "cat", "猫"); // k,v,k,v... ``` ### HashMultiset setの中に含まれる、単語検索。 ```java:Example Multiset<String> wordsMultiset = HashMultiset.create(); wordsMultiset.add("word1"); wordsMultiset.add("word2"); wordsMultiset.add("word1"); wordsMultiset.count("word1"); // result 2 ``` ### Multimap keyが含まれていなければ、、というチェック無しで、単にputするだけでCollectionに追加される。 ```java:Example // Java if (!map.contains(k)) { map.put(k, new ArrayList()); } map.get(k).add(v); // Guava MultiMap map.put(k, v); ``` ### Table 2次元配列のような、2つのキーを持つテーブルを作成 ```java:Example // Java Map<KeyA, Map<KeyB, Value>> nestedMap = ... Map<KeyB, Value> innerMap = nestedMap.get(keyA); Value value; if (innerMap == null) { value = null; } else { value = innerMap.get(keyB); } // Guava 面倒なnullチェックが不要 Table<KeyA, KeyB, Value> table = ... Value value = table.get(keyA, keyB); ``` ### Joiner データを区切り文字で連結して一つの文字列にする。配列もOK。 ```java:Example String[] animals = new String[]{"dog", "cat", null, "pig"}; Joiner.on(", ").skipNulls().join(animals); // dog, cat, pig Map<String, String> dictionary = new HashMap<String, String>(); dictionary.put("米", "rice"); dictionary.put("パン", "bread"); dictionary.put("うどん", null); MapJoiner joiner = Joiner.on(", ").withKeyValueSeparator(":").useForNull("登録なし"); joiner.join(dictionary); // 米:rice, パン:bread, うどん:登録なし ``` ### Splitter / CharMatcher(baseパッケージ) データを区切り文字で分割して一つのリストにする。 ```java:Example String telnum = "090 1234 5678"; // もしくは090-1234-5678に対応 CharMatcher matcher = CharMatcher.WHITESPACE.or(CharMatcher.is('-')); Iterable<String> splits = Splitter.on(matcher).split(telnum); // 090,1234,5678という長さ3のリスト ``` ※ただし、単純な分割ならString#splitで十分。 ### ComparisonChain compare/compareToを書くときに、メソッドチェインで書けるので、シンプル。 ```java:Example public int compareTo(Foo that) { return ComparisonChain.start() .compare(this.aString, that.aString) .compare(this.anInt, that.anInt) .compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast()) .result(); } ``` ## com.google.common.primitives パッケージ Booleans、Ints、Chars、Doublesなどプリミティブ型に対するユーティリティメソッドを提供するクラスが含まれています。 ### Ints プリミティブ型の操作ユーティリティ。 ```java:Example int[] ary = new int[] { 1, 2, 3 }; List<Integer> list = Ints.asList(ary); // 配列からListへの変換 int max = Ints.max(ary); // 3 String joinedStr = Ints.join(" : ", ary); // 1 : 2 : 3 ``` ## com.google.common.base パッケージ ### Strings 文字列操作ユーティリティ。 ```java:Example Strings.isNullOrEmpty(str); // nullか空文字? Strings.repeat("*", 10); // ********** Strings.padStart("7", 2, '0'); // 07 Strings.padStart("12", 2, '0'); // 12 Strings.padEnd("7", 3, '0'); // 700 ``` ### Objects ```java:Example // nullを気にせずにオブジェクトの比較 Objects.equal("a", "a"); // returns true Objects.equal(null, "a"); // returns false Objects.equal("a", null); // returns false Objects.equal(null, null); // returns true // 安全なハッシュが簡単に作成 Objects.hashCode(Object...) // toStringメソッド作成の簡略化 @Override public String toString(){ // Returns "ClassName{x=1}" return Objects.toStringHelper(this) .add("x", this.x) //変数名とその値 .toString(); //or // Returns "MyObject{x=1}" return Objects.toStringHelper("MyObject") .add("x", 1) .toString(); } ``` ## com.google.common.io パッケージ ### Resources リソースの取得をシンプルに。 ```java:Example // Java URL resourceJava = this.getClass().getClassLoader().getResource("sample.txt"); // Guava URL resourceGuava = Resources.getResource("sample.txt"); ``` ### Files BufferedReaderの作成や、ファイル入出力の簡略化。 ```java:Example // Java BufferedReader reader = new BufferedReader(new FileReader(getFile())); // Guava BufferedReader reader = Files.newReader(getFile(), Charsets.UTF_8); // 全行リスト取得 List<String> lines = Files.readLines(getFile(), Charsets.UTF_8); // 「a」から始まる行のみを抽出 String lines = Files.readLines(getFile(), Charsets.UTF_8, new LineProcessor<String>() { private List<String> lines = Lists.newArrayList(); @Override public boolean processLine(String line) throws IOException { if(line.startsWith("a")) { lines.add(line); } return true; } @Override public String getResult() { return Joiner.on(",").join(lines); } }); ``` ## com.google.common.annotations パッケージ ### @VisibleForTesting メソッドやフィールドに付与するもので、テストをしやすくするためにアクセス修飾子のレベルを緩くしているということを明示するために付与する。 ## 使い方 セントラルリポジトリに登録されているので、Mavenのdependencyを追加。 ``` <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>r09</version> </dependency> ``` ##参考 * [guava-libraries(公式)](https://code.google.com/p/guava-libraries/) * [Google製Javaライブラリ Guavaを使ってみた。](http://shikajiro.github.io/blog/2013/03/03/java-guava/) * [Guavaのススメ](http://www.akirakoyasu.net/2011/01/15/218/) * [[Java]guava](http://d.hatena.ne.jp/mtoyoshi/20100725/1280040233) |
|
| 186位 |
|
|||
|
17:08:14 |
(Kabuku Inc. 所属) |
|
全国のGASジャンキーの皆様こんにちは 大橋です。 GASでこれできないーとかこういう時の開発方法がわからない~というのをよく見かけるので僕なりな開発方法をまとめます。 ざっくり思いついたのモノを書くので暇な時に足していきますし、 こういうのもあるよとかリクエストがあったらコメントにでも書いて下さい。 ``` なお超絶書き途中ですが、疲れたのでとりあえず公開します。 ``` 書いていくのは * スクリプトの種類(どこに書く?Spreadhsheet?Drive上?) * 開発環境(何を使って書く?) * 便利ツール(どんなライブラリを使ってる?) * 開発 * テスト・デバッグ を書いていきます。 # スクリプトの種類 GASでは大きく分けて2種類のスクリプトがあります。 * Container Bound Script * Standalone Script ## Container Bound Script Container Bound ScriptはSpreadsheetやSites、Formなどのメニューから「スクリプトエディタ」を起動して作成するスクリプトです。  ## Standalone Script Drive上に直接つくるScriptです。  ## 比較 | | Container Bound Script | Standalone Script| |---:|:---:|:---:| | 作成方法 | 各コンテナ(Sheets,Forms,Sites)のメニューから作成| Drive上またはhttps://script.google.com へアクセス| | Sitesでリンクから呼び出し | Sites上に作成すれば可能|不可能| | Spreadsheetでの独自関数 | Spreadsheet上に作成すれば可能|不可能| | onOpenトリガーの自動登録 |可能 |不可能| | Webアプリケーションとして動作 | 可能|可能| | ソースコードのダウンロード | 不可能|可能| | ScriptEditor以外での開発 | 不可能|可能| 経験則で書くと、Container Bound ScriptはどこのSpreadsheetに書いたを忘れて迷子になりがちだったりします。 またソースコードのダウンロードが不可能なのも時々痛い場合があります。 個人的な感覚では、 ``` 独自関数などContainer Bound Scriptでないとできない場合以外はStandalone Script ``` が良いかなーと思っています。 # 開発環境編 ざっと感覚値を書くと、 * あまり複雑でない、小さめなアプリケーションや、ローカルで開発するメリットがあまり感じられない場合 * ScriptEditorで作ったほうが良いです。 * どうしてもバージョン管理したい、altJSで開発したい、Container Bound Scriptの開発じゃない * Eclipseやその他サードパーティツールなどローカルで開発 初めてならやめておいたほうが良い。 ## ScriptEditor GASの開発環境は通常は[ScriptEditor](http://script.google.com)になると思います。 僕もよくGoogleのAPIを利用するスクリプトを書く場合はScriptEditor上で開発します。 ScriptEditorでは各種ショートカットがありますので、手前味噌ですが[この記事](http://qiita.com/soundTricker/items/c9091a63de5401f2d49e)を見ておくと良いと思います。 ## Eclipse  統合開発環境として有名なEclipseでもGASの開発は可能で、公式にGoogleから開発用のPluginがリリースされています。 既に作成済みのStandalone Scriptをダウンロードしてきて、コードを保存するたびに自動アップロードしたり、上記のキャプチャのような補完も可能です。 ※ScriptEditorに比べて補完リストが更新されるスピードは遅いですが。。。 またEclipseで開発可能なのは ***Standalone Script*** のみなので注意して下さい。 インストール方法はこちら...とか日本語記事へのリンクを作りたかったのですがどこにも記事が見当たらないですね。。。 そのうち書きます。 基本的には`Google Plugin for Eclipse`に含まれている機能なのでGoogle Plugin for Eclipseのインストール方法を調べると使えるようになります。 僕は使っていません。( ー`дー´)キリッ ## その他サードパーティ製エディタ、開発ツール ざっと並べて書いていきますが 個人的にはScriptEditorで書けばゲフンゲフン * [Editey](https://chrome.google.com/webstore/detail/editey/hjmchndlheccnejbaamnbdmehmabemen) * Drive上で利用可能なWeb Editorです。 * 補完とかはできませんが、ScriptEditorよりHTMLとかは書きやすいです。 * [Neutron Drive](https://www.neutrondrive.com/) * Editey同様Drive上で利用可能なWeb Editorです。 * 補完とかはできませんが、ScriptEditorよりHTMLとかは書きやすいです。 * [Sublimetext-Google-Apps-Script](https://github.com/revolunet/sublimetext-google-apps-scripts) * Sublime Text2向けのPluginです。 * 補完はできませんが大量のSnippetsが登録されています。 * 一応アップロードとかは可能ぽい? * [GAS-Manager](https://www.npmjs.org/package/gas-manager) * コマンドラインでGASのコードをダウンロード/アップロード可能なツールです。 * というか僕が作りました ちなみに僕はCoffeeScriptで書くことが多いのでScriptEditor開きながら、Chrome Extensionの[CoffeeConsole](https://github.com/snookca/CoffeeConsole)でコード書いてScriptEditorに貼り付けるって方法をやってます。 ただ1000行近いコードになるとScriptEditorの保存が超遅くなるので注意が必要です。  # 便利ツール(どんなライブラリを使ってる?) GASの開発に慣れてくると色々ライブラリを使いたくなります。 ※GASのライブラリって何?な人は[こちらの記事](http://qiita.com/soundTricker/items/7bbd86425ae8d0641d50)を読んで下さい。 一覧を出そうと思えば出せるのですが、ありすぎて気が遠くなるので、 非常によく使うものだけ出したいと思います。 * [Moment](http://momentjs.com) * ライブラリKey: MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48 * JS用の日付ライブラリ 文字列からDateに変換したりn日後、m秒前などかなり多彩な日付操作、チェックが可能 大体のGASプロジェクトで使っています。 * 上記ライブラリKeyのプロジェクトはGoogleの中の人がGAS用に提供しなおしているものです。 * [Underscore](http://underscorejs.org/) * ライブラリKey: MGwgKN2Th03tJ5OdmlzB8KPxhMjh3Sh48 * JS用の汎用ライブラリ この名前で検索すれば結構解説が出てくるので解説はそちらに任せます。 * これもGoogleの中の人がGAS用に提供しなおしているものです。 上記2つは非常に使うシーンが多いので覚えておくと良いと思います。 # 開発 TODO:あとでScriptEditorのハックとか書く TODO:補完きかなくなった場合の話とか # テスト・デバッグ TODO: あとで各Triggerごとのテスト方法を書く ## Form#onFormSubmit ## Spreadsheet#onFormSubmit ## Spreadsheet#onEdit ## Spreadsheet独自関数 ## Webアプリケーション(doGet) ## 時間トリガー |
|
| 187位 |
|
|||
|
13:21:30 |
|
|
いつも忘れるのでsshポートフォワーディングについてメモ
## ローカルフォワード localの8081番にアクセスするとremoteからアクセスできるtarget:80に繋げる例  ```bash # localから実行 ssh -L 8081:target:80 remote ``` * local外からの接続を許可する場合以下のオプションを付ける * -g : ローカル以外からローカルの8081番にアクセスする場合に必要 ## リモートフォワード remoteの8081番にアクセスするとlocalからアクセスできるtarget:80に繋げる例  ```bash # localから実行 ssh -R 8081:target:80 remote ``` * remote外からの接続を許可する場合はremoteで以下の設定を行う ```bash vi /etc/ssh/sshd_config # => GatewayPorts yesを追加 sudo /etc/init.d/sshd restart ``` ## 繋ぎっぱなしにする場合 * 以下のオプションを付ける * -f : バックグラウンドで動作 * -N : コマンド実行無し ## 特権ポート 1023番以下の特権ポートに対するフォワーディングは一般ユーザでは行えない。 (-R、-Lオプションのすぐ右に指定するポート) |
|
| 188位 |
|
|||
|
19:48:18 |
(Consensus Base Inc. 所属) |
|
DynamoDBを大雑把にさくっと日本語で理解したい方向けの説明。 (まだ書き途中) API Version 2012-08-10 を元に書いています。 ## 印象と感想 * 管理が楽! * 容量の増加を気にしなくていい! * スループットやパフォーマンスの監視、管理が楽! * ソーシャルゲームでは、一部のデータではすごく良さそう * 検索や集計は弱いから、MySQLと併用 * レイテンシが低いと書いてあるが、memcache の方が当然早い * テーブル設計の理解と指定方法がちょっと面倒 * 料金体系の理解がちょっと面倒 ## DynamoDBとは何か? 大雑把に * NoSQL, スキーマレスなAWS上のデータベースサービス * スケールに関して何も気にしなくていい まずは、公式サイトを読むと概要はわかります。 [Amazon DynamoDB (フルマネージドNo SQLデータベースサービス) | アマゾン ウェブ サービス(AWS 日本語)](http://aws.amazon.com/jp/dynamodb/) ### どんなもの? 以下の初心者向けのスライドは、技術的なことも一番わかりやすかったです! [Amazon DynamoDB(初心者向け 超速マスター編)JAWSUG大阪](http://www.slideshare.net/shimy_net/amazon-dynamodb-23315068) ## 理解に必要な基礎単語 * KVS, NoSQL, スキーマレス * プライマリキー (PK, Primary Key) * インデックス (index) * ハッシュ(HASH) と レンジ(RANGE) * 型 (type), 属性の型 (Attribute Type) * スカラーデータ型 (Scalar data types) N,S,B (Number, String, Binary) * マルチバリュー型 (Multi-valued types) Number Set, String Set, Binary Set * ローカルセカンダリーインデックス (Local Secondary Indexes) * 投影された (projected) データ内容がインデックスに反映された的な意味で使われていると思う 設定では、次の3つから選ぶ KEYS_ONLY, INCLUDE, ALL * プロビジョニングされた (Provisioned) * 「リソースが事前に用意されている」という意味 * プロビジョニングは、「必要なリソースをすぐに使える体制」という意味 * プロビジョンドスループット (Provisioned Throughtput) * Read Capacity Units, Write Capacity Units * DynamoDB Local DynamoDBのローカル環境 ## 大まかな流れ * 何にどのように使うのか?の方針決めや設計 (お試しの場合不要) * テーブルの設計をする * プライマリキーの決定 (HASH か HASH + RANGE) * インデックスの決定 * DynamoDB環境の構築 * ローカル環境 (DynamoDB Local)をインストールするか * AWS Managemment Console で DynamoDBをセットアップするか * テーブルを作る ## プライマリキー (PK) ハッシュかハッシュ&レンジの2つの方法から選ぶべし ### ハッシュ型プライマリキー HASH を一つのカラムで。 このキーだけで一意に決定して検索できるようにする ### ハッシュとレンジ型プライマリキー HASH のキー & RANGE のキーの2つを使う HASHとRANGEの2つのキーで一意にレコードを決定できるようにする RANGE で範囲指定して検索できる ## ローカル・セカンダリ・インデックス クエリーのためのインデックスをオプションで追加できるようになった。 特徴は、下記サイトからの引用です。 [おちラボ:教育システム研究開発BLOG: DynamoDB: ローカルセカンダリインデックスを使ってみた(DynamoDBMapper利用編)](http://ochi-lab.blogspot.jp/2013/04/dynamodb-dynamodbmapper.html) >テーブル毎に最大5つのローカルセカンダリインデックスを作成可能 >あくまでも従来のレンジキーに対する代替 >複数のローカルセカンダリインデックスを組み合わせた検索は不可 >従来のレンジキーとの組み合わせも不可 >紐付けるAttributeによってコストが増える? どうやら、3つ以上の条件(Key Conditions)では検索できないようです。 HASHだけか、HASH & RANGE の2つの条件しかできないようです。 ### LSIのメリット・デメリット * メリット: * クエリーが早くなる * 多く取ってきてフィルターしなくて良い * デメリット * インデックス用の容量が増える * 書き込み時のインデックスの更新コストがかかる ## テーブルの設計 ### DBの大まかな構造 テーブルがあって、アイテムがある。 アイテムを探すのにプライマリキーがある。 ### テーブルの作成 現状では、後からインデックスの追加ができません。 以下を指定してテーブルを作成する (API経由でcreateTableする時) * テーブル名 (TableName) * 属性の設定 (AttributeDefinitions) * 属性名(AttributeName)と属性の型(AttributeType) * キースキーマ (KeySchema) * 属性名とキーの型 (KeyType: HASH か RANGE) * ローカルセカンダリーインデックス (LocalSecondaryIndexes) * インデックス名 ## ちなみに * SSD で色々なリージョン、サーバに分散して保存 * phpLiteAdmin で DynamoDB のデータ操作ができる * ハッシュキーで別々のサーバに保存するっぽいので、一つのハッシュキーに処理が集中すると遅くなる。ので、分散するように設計しよう * 既存のLSI でない DynamoDB テーブルを LSI のテーブルにしたい時は、 Elastic Map Reduce を使って既存のデータをエクスポートしてから、新しいLSIなテーブルへインポートするといい (by Amazon CTO, Werner Vogels) * SQL系DBのトランザクションのようなものはない → キューに突っ込む? AWSのSQSなど * query の応答一つあたりのサイズ上限は1MB * Item 64KBまで ## 参考URL ### 公式 公式の日本語FAQ [Amazon DynamoDB FAQs | アマゾン ウェブ サービス(AWS 日本語)](http://aws.amazon.com/jp/dynamodb/faqs/#What_are_local_secondary_indexes?) [Redshift の話を聞きに「AWSのビッグデータサービスを使いこなせ!」セミナーに行ってきた - #garagekidztweetz](http://d.hatena.ne.jp/garage-kid/20130716/awsbigdataseminaratmeguro) 公式なローカルセカンダリーインデックスの説明 [Amazon Web Services ブログ: 【AWS発表】 Amazon DynamoDB でローカルセカンダリインデックスを作成可能に](http://aws.typepad.com/aws_japan/2013/04/local-secondary-indexes-for-amazon-dynamodb.html) ### 利用事例 ソシャゲでの利用事例。SQSのキューで一貫性を担保する話 [DynamoDBによるソーシャルゲーム実装 How To](http://www.slideshare.net/itoyusaku/jawsug-201303) ### 感想 使った感想 [DynamoDBのメモ書き+使ってみた雑感 - アルパカDiary](http://d.hatena.ne.jp/toritori0318/20120623/1340466684) [DynamoDB と Redshift について - ようへいの日々精進](http://inokara.hateblo.jp/entry/2013/08/31/184953) ### すぐに理解 [Amazon DynamoDB(初心者向け 超速マスター編)JAWSUG大阪](http://www.slideshare.net/shimy_net/amazon-dynamodb-23315068) ## 検索用 AWS, DynamoDB, PK, primary key, local secondary index, provisioned throughtput, |
|
| 189位 |
|
|||
|
22:39:24 |
|
|
教えてください。
\#追記 [これ知らないプログラマって損してんなって思う汎用的なツール 100超 まとめ](http://qiita.com/items/2183) suinさんがまとめてくれました。 |
|
| 190位 |
|
|||
|
13:47:51 |
(Abby 所属) |
|
# Docker TIPS あれこれ Docker を使っているとアレどーすんだっけ?って探しまわることが多いのでここにまとめておこうと思います。 随時更新予定です。 先日のまとめの分も再度記載しておきます。 基本、ホストは ubuntu-server 12.04、コンテナ側は普段使いしている13.10前提で記述しています。 ## docker のイメージ格納先を変更したい apt で docker をインストールしている人がほとんどだと思います。 デフォルトでは `/var/lib/docker` になります。 ``` /etc/default/docker ``` ここに設定があるのでそこで変更できます。 DNSの設定もここでできます。 格納先は `-g` オプションで設定できます。 ``` # Docker Upstart and SysVinit configuration file # Customize location of Docker binary (especially for development testing). #DOCKER="/usr/local/bin/docker" # Use DOCKER_OPTS to modify the daemon startup options. DOCKER_OPTS="-dns 8.8.8.8 -dns 8.8.4.4 -g /opt/docker" # If you need Docker to use an HTTP proxy, it can also be specified here. #export http_proxy="http://127.0.0.1:3128/" # This is also a handy place to tweak where Docker's temporary files go. #export TMPDIR="/mnt/bigdrive/docker-tmp" ``` ## コンテナを一気に削除したい Dockerfile を書いているとゴミコンテナが大量にできてしまいます。 以下で使用していないコンテナを一度に削除してしまいましょう。 ``` $ sudo docker rm $(sudo docker ps -a -q) ``` 起動中のコンテナは削除されません。 強制的に削除する場合には`-f`オプションを使って下さい。 ## イメージを一気に削除したい 同様にイメージも一気に削除できます。 本当にまっさらになってしまうので注意しましょう。 依存関係によってはうまく消えないので依存しているコンテナを削除してから実行しましょう。 ``` $ sudo docker rmi $(sudo docker images -q) ``` 依存関係などは`docker images` の `--tree`オプションを使うとわかりやすくてよいでしょう。 ``` $ sudo docker images --tree ``` ## aufs のリミット Docker での aufs layer の制限は 0.7 移行、42から127に引き上げられていますが、それでも足りない場合には export -> import することでまとめることができます。 土台になるbaseイメージなどでごっそり必要な設定、インストールを済ませ、export -> importすることで layer 数を稼ぐことができます。 export する際にはコンテナIDが必要になるので注意して下さい。 まずコンテナIDを取得してから作業しましょう。 ``` $ container_id=$(sudo docker run -d <REPOS:TAG> /bin/bash -c "") $ sudo docker export $container_id > tmp.tar $ cat tmp.tar | sudo docker import - <REPOS:TAG> ``` import 時に TAG を貼り直してしまっているのですが、心配な方は指定せずに最新のコンテナに TAG を振りなおしましょう。 ## private-repository でイメージを共有、バックアップする private-repository を立てることでイメージの共有を行うことができます。 S3 へのアップロードはそのうち試しますが、今回は push したイメージをファイルシステムに落とし込む方法 です。 private-repository は registry という名前で提供されています。 そのため即試すことができます。 ``` $ sudo docker run -d -p 5000:5000 -v /opt/registry:/tmp/registry:rw registry ``` Docker のイメージで提供されているのでそのままだと registry が落ちると push した内容も吹き飛んでしま います。 そのため、ホストの/opt/registry をマウントして push したイメージを永続化しています。 (デフォルトで/tmp/registryへ保存されるので) registry は github にあるので興味のある方は見てみるといいです。 構成は `gunicorn + gevent + flask` です。 そのうち詳しく書くかも知れません。 # Dockerfile ## apt-get が遅いんだけど apt の参照先が本家なので国内からだと遅めです。 ミラーを設定しましょう。 たまにミラーの設定のことをちらっと書いてる人がいますが、フルで書いておいた方がよいと思います。 ``` RUN echo "deb http://jp.archive.ubuntu.com/ubuntu/ saucy main restricted\n\ deb-src http://jp.archive.ubuntu.com/ubuntu/ saucy main restricted\n\ deb http://jp.archive.ubuntu.com/ubuntu/ saucy-updates main restricted\n\ deb-src http://jp.archive.ubuntu.com/ubuntu/ saucy-updates main restricted\n\ deb http://jp.archive.ubuntu.com/ubuntu/ saucy universe\n\ deb-src http://jp.archive.ubuntu.com/ubuntu/ saucy universe\n\ deb http://jp.archive.ubuntu.com/ubuntu/ saucy-updates universe\n\ deb-src http://jp.archive.ubuntu.com/ubuntu/ saucy-updates universe\n\ deb http://jp.archive.ubuntu.com/ubuntu/ saucy multiverse\n\ deb-src http://jp.archive.ubuntu.com/ubuntu/ saucy multiverse\n\ deb http://jp.archive.ubuntu.com/ubuntu/ saucy-updates multiverse\n\ deb-src http://jp.archive.ubuntu.com/ubuntu/ saucy-updates multiverse\n\ deb http://jp.archive.ubuntu.com/ubuntu/ saucy-backports main restricted universe multiverse\n\ deb-src http://jp.archive.ubuntu.com/ubuntu/ saucy-backports main restricted universe multiverse\n\ deb http://security.ubuntu.com/ubuntu saucy-security main restricted\n\ deb-src http://security.ubuntu.com/ubuntu saucy-security main restricted\n\ deb http://security.ubuntu.com/ubuntu saucy-security universe\n\ deb-src http://security.ubuntu.com/ubuntu saucy-security universe\n\ deb http://security.ubuntu.com/ubuntu saucy-security multiverse\n\ deb-src http://security.ubuntu.com/ubuntu saucy-security multiverse\n"> /etc/apt/sources.list ``` ## apt でインストール中、ダイアログの選択で落ちる apt でインストールするものの中にはユーザーと対話をしてくるものがいます。 (選択したものを設定に反映したり、パスワードを入れたりするもの) Docker だと選択肢に答えようがないので以下をセットしてから`apt-get install`しましょう。 ``` ENV DEBIAN_FRONTEND noninteractive ``` またその後使う場合、対話に戻しておきたいことがあるかも知れません。 その場合には一連の操作終了後、以下をセットしておきましょう。 ``` ENV DEBIAN_FRONTEND dialog ``` ## upstart への登録、upstart 関連で落ちる 有名な workaround なのであまり書かれてないのかも知れません。 インストールするものの中にはデーモン(サービス)で動作するものもあります。 ubuntu の性質上デーモンは upstart にひっかけて OS 起動時に起動するような設定を行います。 ですが、Docker では /sbin/init が書き換わっており upstart がうまく動きません。 インストール時の設定の時点で失敗、落ちてしまう事が有ります。 (upstartを動かす方法もありますが…) その場合の対処法として以下のように設定してから `apt-get install` しましょう。 ``` RUN dpkg-divert --local --rename --add /sbin/initctl && rm -f /sbin/initctl && ln -s /bin/true /sbin/initctl ``` ## fuse のインストール apt でいろいろ入れてると fuse 関連が上手く入らず落ちてしまう事があります。 一応以下で回避できますがきっともっといい方法があるでしょう。 ``` RUN chmod go+w,u+s /tmp RUN apt-get install libfuse2 RUN mkdir /tmp/fuse && \ cd /tmp/fuse && \ apt-get download fuse && \ dpkg-deb -x fuse_* . && \ dpkg-deb -e fuse_* && \ rm fuse_*.deb && \ echo -en '#!/bin/bash\nexit 0\n' > DEBIAN/postinst && \ dpkg-deb -b . /fuse.deb && \ dpkg -i /fuse.deb && \ cd / && \ rm -rf /tmp/fuse /fuse.deb ``` ## 容量を少しでも減らす `apt-get install` 時に `--no-install-recommends` をつけましょう。 一連のインストールが終わったら` apt-get clean` を実行しましょう。 ## git clone 時の known_hosts dotfiles や ソースなど git clone してくる際に落ちてるしまうことがあります。 known_hosts がまっさらなので確認してくるのですがそこで止まってしまいます。 ssh/config などで確認が入らないようにしておきましょう。 ``` Host github.com StrictHostKeyChecking no Host bitbucket.org StrictHostKeyChecking no ``` # ソフトウェアのインストール、個別設定 ## supervisor で複数のデーモンを立ち上げる 公式にもあるので書きませんが、sshd があるとコンテナ内のファイルを救い出す事ができたりするので設定しておくといいでしょう。 もちろん、起動時に`-v`でマウントしておくともっと楽かも知れません。 ## Docker 0.9 での tty 現在は解消されているようです。 https://github.com/dotcloud/docker/issues/4605 SSH server を立ち上げてログイン後、pty がねえよとか言われる奴です。 ログインするとプロンプトが出てこず壊したくなる workaround でも書かれてますが run する際に`-t`オプションをつけましょう。 ``` $ sudo docker run -t -p 40000:22 xxxxxx /usr/sbin/sshd -D ``` ## saucy の sshd の設定 ssh でログイン後、即切断される場合の対処法です。 13.10 では pam を見るのでこの設定を書き換えておく必要があります。 ``` RUN sed -i 's/.*session.*required.*pam_loginuid.so.*/session optional pam_loginuid.so/g' /etc/pam.d/sshd ``` ## nvm による node のインストール RUN コマンドは `/bin/sh -c` として実行されるので source を叩く場合の例。 virtualenv なども同様にすればできるはず。 ``` RUN git clone git://github.com/creationix/nvm.git /home/ma2/.nvm RUN /bin/bash -c 'source ~/.nvm/nvm.sh && nvm install 0.10.26 && nvm alias default v0.10.26 && npm install -g grunt grunt-cli bower gulp' ``` ## Oracle JDK のインストール 奇特な方が Java 8 使いたいとかいうことがあるのでその人向け。 定番の`webupd8team/java`を使います。 途中でライセンスに同意しないといけないタイプなので注意です。 ``` RUN echo oracle-java7-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections RUN apt-get install -y software-properties-common RUN add-apt-repository ppa:webupd8team/java -y \ && apt-get update \ && apt-get install -y oracle-java7-installer oracle-java6-installer oracle-java8-installer oracle-java8-set-default ``` また jdk8 ですがすこぶる落ちてくるのが遅かったり、落ちてこなかったりするのでその場合は再度トライして 下さい。 キャッシュが効いているのですぐやり直しできるはずです。 # 心霊・怪奇現象 ## Permission Denied の話 ### レア度 ☆☆☆ ユーザーを変更して作業する Dockerfile を書いていると build 時に稀に起きます。 明らかに問題がない場合でもおきます。 霊の仕業でしょうか? - 再度 build し直す - --no-cache で build し直す で __Dokerfile未編集__ でうまく行ったりします。 それでもダメな場合は本当に Dockerfile が間違っている可能性があるので見なおしてみましょう。 ## 消せないコンテナの話 ### レア度 ☆☆☆☆ ファイルが消せなくなります。 Stale NFS file handle ってやつですね。 適当にファイルを消すと発生します。 霊の仕業でしょうか? https://github.com/dotcloud/docker/issues/643 まだ直ってない様子、原因がイマイチ不明な感じがします。 勇気を持ってOSを再起動しましょう。それしか解決方法はありません。 |
|
| 191位 |
|
|||
|
22:25:17 |
|
|
[Docker Cheat Sheet](https://gist.github.com/wsargent/7049221)というDockerの基礎のまとめが良かったので翻訳してみた([Docker 虎の巻](https://gist.github.com/tcnksm/7700047)).このまとめは説明は十分にあるが,例がほとんどない.実例を使って,コンテナとイメージに関する基礎コマンドをまとめてみる.
## OS XでDockerを使う場合 vagrantを使ってる場合は今すぐ1.4にバージョンを上げる. [Download Vagrant - Vagrant](http://www.vagrantup.com/downloads.html)より.dmgをダウンロードしてきてインストール. ``` bash vagrant init precise64 http://files.vagrantup.com/precise64.box ``` Vagrantfileを以下のようにすれば,すぐにDockerを使える. ``` rb Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "precise64" config.vm.provision :docker do |d| d.pull_images "ubuntu" end end ``` ログイン ```bash vagrant ssh ``` ## 準備 コンテナのIDをいちいち保持しておくのは面倒,忘れるので,以下のaliasをホスト側で設定しておくと直近に起動したコンテナのIDを呼び出すことができるようになり便利 ([15 Docker tips in 5 minutes](http://sssslide.com/speakerdeck.com/bmorearty/15-docker-tips-in-5-minutes)). ``` bash alias dl='docker ps -l -q' ``` ## コンテナ コンテナを作成する.`-d`オプションでバックグラウンドで実行する. ``` bash docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done" ``` コンテナを停止する. ``` bash docker stop `dl` ``` コンテナを起動する. ``` bash docker start `dl` ``` コンテナを再起動する. ``` bash docker restart `dl` ``` 起動中のコンテナに接続する. ``` bash docker attach `dl` ``` コンテナ内のファイルをホストにコピーする. ``` bash docker cp `dl`:/etc/passwd . ``` ホストのディレクトリをコンテナにマウントする. ``` bash docker run -v /home/vagrant/test:/root/test ubuntu echo yo ``` コンテナを削除する. ``` bash dockr rm `dl` ``` ## コンテナの情報 起動中のコンテナを表示する.停止中のコンテナも表示するには,`-a`オプション. ``` bash docker ps ``` コンテナの情報(IPなど)を表示する. ``` bash docker inspect `dl` ``` コンテナのログを表示する. ``` bash docker logs `dl` ``` コンテナのプロセスを表示する. ``` bash docker top `dl` ``` ## イメージ コンテナからイメージを作成する.タグ名は\<username>/\<imagename\>が[奨励されている](http://docs.docker.io/en/latest/use/workingwithrepository/#committing-a-container-to-a-named-image) ``` bash docker run -d ubuntu /bin/sh -c "apt-get install -y hello" docker commit -m "My first container" `dl` tcnksm/hello ``` Dockerfileからイメージを作成する. ``` bash echo -e "FROM base\nRUN apt-get install hello\nCMD hello" > Dockerfile docker build tcnksm/hello . ``` イメージ内にログインする.`-rm`オプションをつけるとコンテナの停止後にそのコンテナは破棄される. ``` bash docker run -rm -t -i tcnksm/hello /bin/bash ``` イメージをリモートレポジトリにアップロードする.あらかじめ[Docker index](https://index.docker.io/)にアカウントを作成しておく必要がある.今回の例は[こちら](https://index.docker.io/u/tcnksm/hello). ``` bash docker login docker push tcnksm/hello ``` イメージを削除する. ``` bash docker rmi tcnkms/hello ``` ## イメージの情報 イメージ一覧を表示する. ``` bash docker images ``` イメージの詳細情報(IPなど)を表示する. ``` bash docker inspect tcnksm/hello ``` イメージのコマンド履歴を表示する. ``` bash docker history tcnksm/hello ``` |
|
| 192位 |
|
|||
|
01:13:30 |
|
|
@takehiro に教えて貰ったCircle CIを使ってみるともの凄く良くて、とてもお勧めなので記事を書きました!
Circle CIって何? --- Travis CIと同機能でWebでのUIが若干違うサービスです。 CIとしての仕事はきちんと行えます。 Circle CIの使い方 --- https://circleci.com/ にアクセスします。  Githubアカウントでサインアップを行います。 画面に従って進むとプロジェクトをfollowする画面が表示されます。  CIしたいプロジェクトをfollow後、"Done Managing Repos"をクリックします。 ここでは"camelmasa/spree"を選択します。 するとfollowしたプロジェクト一覧のテスト結果の画面が表示される様になります。  これでCIが出来る環境が整いました! 後はgithubにpushする毎にCIが実行される様になります!簡単! Circle CIのお勧めポイント -- princingがとにかく安い! https://circleci.com/pricing private repositoryでも最安19ドルから! それでいてTravisと遜色無い機能を持っている。 Circle CIの細かいTips --- ドキュメントのURLはこちら。 https://circleci.com/docs 環境が整ったは良いが、テストが失敗する…。 そうです、プロジェクト直下にcircle.ymlを置かない限り、恐らくテストは失敗すると思います。(一応Webからもテストコマンドの設定も可能) circle.ymlのsampleはこちらのURLにあります。 https://circleci.com/docs/configuration また細かいところなのですが、Travisと同じでgithubのcommitページにテスト結果が表示されます。  まとめ --- 超おすすめ!!! |
|
| 193位 |
|
|||
|
01:23:47 |
|
|
iOS Advend Calender 2013 iOS second stage一日目担当の[@keroxp](https://twitter.com/keroxp/)です。普段は大学の研究室でインターフェースの研究をしていたり、仕事先の会社でiOSアプリの開発を行っていたりします。
さて、今回私が取り上げるテーマは「Xcodeと自動化」です。予告のタイトルでは「XcodeとContinuous Integration」だったのですが、CIを含めた自動化という題材でお送りしたいと思います。 # 自動化の意義 iOSに限らず、ソフトウェア開発の日常には様々なイライラが存在します。一つ一つは小さく、手間も少ないように思える事柄でも、手動でやらなくてはいけないことがひとつふたつ...と増えていったり、開発期間が長引くにつれて毎日毎日一定時間同じ作業を手動で行わなくてはならなくなります。 **Don't Repeat Yourself!!** そこでこの記事ではXcodeを用いたiOSアプリケーションの開発で発生しがちな面倒な人力処理を自動化するためのTipsを実験的にまとめてみます。 # ビルド設定の自動化 XcodeはIDEとしては優れている部類だと思いますが、それはビルドの複雑な処理をうまい具合に隠蔽しているからであって、ビルドのプロセス自体はとても複雑です。Xcodeのビルドオプションは設定項目が多すぎて何がどういうオプションなのかまったく把握できません。初期設定のままなら問題ありませんが、少し手を加えたりすると途端にビルドできなくなったりして困ります。 **XcodeのBuild Settingの画面**  そこでおすすめするのがxcconfigファイルです。xcconfigはXcode4から導入されたBuild Configurationのまとめ設定ファイルで、xcconfigファイルをプロジェクトにaddしておくと、プロジェクトのinfoタブから設定できます。Configurationを分ける必要があるのは主にDebugビルドとReleaseビルドですが、ONLY_ACTIVE_ARCHやCOPY_PHASE_STRP、ARCHSといった闇の深そうな設定をうまくまとめてくれているxcconfigファイルがあります。 [https://github.com/jspahrsummers/xcconfigs](https://github.com/jspahrsummers/xcconfigs) これはCocoa界の偉人[Justin Spahr-Summers](https://github.com/jspahrsummers)氏が公開しているファイルで、Mantleやoctokitなどのgithub謹製のプロジェクトで用いられている秘伝のconfigファイルです。ありがたく使わせてもらいましょう。このxcconfigファイルには他にもClang Static Analyzerの設定が入っており、かなりstrictな開発環境になります。 たとえば、このconfigurationをいれていると普通ならWarningになってビルドが成功する場面でもビルドできなくなります。こういった横着的な実装はあとあと良くないことを引き起こす危険性があるのでプレビルドの段階で弾いておくべきでしょう。 **普通ならwarningで済む場面が...**  **エラーになる!**  また、xcconfigファイルは#includeを使うことができるので、project specifiedな設定も上記のベースファイルと一緒に使うことができます。 # パッケージ管理の自動化  iOS開発の現場では外部のライブラリの使用が普通ですが、その管理もできるだけ自動化したいところです。[Cocoapods](http://beta.cocoapods.org/?q=)は言わずと知れたCocoaのパッケージマネージャです。パッケージマネージャを使うことで、依存パッケージの一括管理とアップデートを自動化することができます。使い方は「Cocoapods 使い方」でググればいくらでも出てくると思うので、ここではCocoa Podsを使っているときに起こりがちな問題と、あまり知られていないPodfileの書き方を紹介します。 ## Podfileの書き方 一般的にはPodfileはこんな書き方をすると思います。 ```ruby:Podfile platform :iOS, "7.0" pod "AFNetworking" pod "BlocksKit" ``` この手軽さがCocoaPodsの持ち味なのですが、もっとプロジェクトが複雑になったときに困る場合があります。たとえば、アプリのiPhone版とiPad版でターゲットを分けた場合など、 **プロジェクトの中に複数のターゲットが存在する** ときです。[CocoaPodsのソース](https://github.com/CocoaPods/Core/blob/master/lib/cocoapods-core/podfile/dsl.rb)によれば、CocoaPodsは明示的な宣言が無い限り、プロジェクトの先頭にあるターゲットをリンク先として選択します。  例えばこのようなターゲットがプロジェクトにある場合、上記のPodfileは先頭のXcodeCIというターゲットにリンクされますが、XcodeCI iPadというターゲットにはリンクされません。また、テストターゲットでのみ[Kiwi](https://github.com/allending/Kiwi)などのテスト用フレームワークを使いたい場合はどうすればいいでしょう? またインストール前にかならずCocoaPodsのアップデートを行いたいときは?... Podfileでは上記の他に以下のようなDSLを用いることができます。 ```ruby:Podfile # プラットフォームの指定 platform :ios, "7.0" # Xcodeプロジェクトまたはワークスペースの指定 xcodeproj "XcodeCI" workspace "XcoeeCI" # Podパッケージをリンクさせるターゲット link_with "XodeCI", "XcodeCI iPad" # 使用するパッケージとバージョンの宣言 pod "AFNetworking", "~> 2.0.0" # インストール前に行われるアクション pre_install do |installer_representation| system "gem update --system" system "gem update cocoapods" p "install started" end # ターゲット別の使用パッケージの宣言 # :exclusiveオプションはtarget外で宣言されたpodパッケージを継承しないことを表す target "XcodeCITests", :exclusive => true do pod "Kiwi/XCTest" end # インストール後に行われるアクション post_install do |installer_representation| p "install finished!" end ``` これで ```zsh pod ``` と実行すると  のようにセットアップが完了します。 また、pre_installのブロックでgemのアップデートを実行していますが、systemはrubyでコマンドを実行するためのKernelオブジェクトのメソッドです。つまり、 **Podfileはrubyファイルなのです。** なので、Podfileの中には他にも任意のrubyスクリプト+コマンドを書くことができます。 ## Podfile.lockってなんなの? Podfileを作成し、 ```zsh pod install ``` したあとに生成されるPodfile.lock。なんかよく分からないけど怪しそうだから触らないことが多いと思いますが、実はこのファイルとても重要なのです。中身を見てみると、なにやらPodfileに書いてあることとあまり変わらないことが書いてあるように見えますが、重要な点がふたつ。  ひとつはパッケージのバージョン。Podfileにパッケージのバージョンが指定されていなかった場合、最初にインストールされた際のバージョンがここに保存されているのです。なので、別の環境でpod installしてもきちんと同じバージョンが入るんですね。 ふたつめはcocoapods gemのバージョン。これは最後にpodを使った際(詳しくはわからないですがinstall, updateとか)の環境のcocoapodsのバージョンが記されています。なので、複数のマシンで開発をしている場合に、誰かがいじって別の人がpod installしようとしたとき、ローカルのcocoapodsのバージョンが違った場合、コンソールにwarningが出るはずです。なので、warningがでたら ```zsh gem update cocoapods ``` として最新バージョンを使うようにしましょう。 このようにPodfile.lockはとても大事なファイルなので、 **必ずgit treeにいれておきましょう。** # Xcodeのカスタムビルドアクション Xcodeにはビルド時にシェルスクリプトのカスタムアクションをいれることができます。 例えば、Debugビルド時以外のビルドの場合のみ、ビルド番号を上げたいときは以下のようなシェルスクリプトをBuild PhaseからEditor-Add Build Phase-Add Run Sctipt Build Phaseから追加することで実現可能です。  地味ですがとても便利な機能です。他にも良い使い方をご存じの方がいれば教えてください。 # GitHub + Travis CIを用いたContinuous Integration Continuous Integrationの詳しい説明は[http://en.wikipedia.org/wiki/Continuous_integration](http://en.wikipedia.org/wiki/Continuous_integration)を見るといいと思います。iOSにおいてCIはあまり一般的ではなく浸透していませんが、webサービスなどでは頻繁なアップデートと安定性の両立という点において重要な位置づけになっています。Xcode5ではOSX Serverと連携してCIが行えるようになりましたが、OSX Serverが普及していないのであまり流行っていません。そこでおすすめしたいのが[Travis CI](travis-ci.org)です。githubとの連携が必須ですが、無料でセットアップも必要ないのでおすすめです。(※Travis CIはつい最近Xcode5でのビルドができるようになりました。 [Xcode 5 and iOS 7 now available for Mac and iOS Builds!](http://about.travis-ci.org/blog/2013-11-18-xcode-5-now-available-for-mac-ios-builds/)) クライアントサイドアプリケーションにおけるテストとCIの意義は個人的には必須だとは思っていないのですが、(UIと密接に関係するクライアントサイドはTry&Errorでテストするしかない部分が多いため)UIと完全に分離したデータモデルやコントローラのクラスではできるだけ作るようにしたほうがいいと思います。CIを行う理由はいくつかありますが、そのひとつに **テストの数が多い、非同期処理がいくつもある、などの理由でテストに時間がかかる場合** があげられると思います。 また、ビルド環境の違いを吸収するという目的もあると思います。チームで開発を行う場合ツールやバージョンを合わせるのが一般的ですが、何かの違いで依存のある変更がgitにコミットされていない場合などに、その起こりうる人的ミスを早期に発見することができます。(例:Podfileを作成したがgit treeにいれずにcommit pushしてしまった、など) というけでCIのためのセットアップをしてみましょう。 基本的な設定に関してはこちらの記事などを参考にするといいと思います。 [iOSのライブラリだってTravis CIとかCoverallsとか使うべき](http://www.tokoro.me/2013/07/09/objc-travis-coveralls/) CIは基本的にマシンが自動的に行うため、Xcodeなどを操作することはできません。そこで用いられるのがコマンドラインでのビルドツールです。Xcodeでビルドを行う場合⌘+Bが普通ですが、Command Line Toolでインストールされるxcodebuildを用いることでもビルドができます。 ```zsh xcodebuild -project XcodeCI.xcodeproj -scheme XcodeCI ``` ##xctool [https://github.com/facebook/xctool](https://github.com/facebook/xctool) xctoolはfacebookが開発したxcodebuildのようなツールで、基本的な機能は同じながらテストの場面で便利な機能があるのでおすすめです。 **インストール** ```zsh brew install xctool ``` **使い方** ```zsh xctool -project XcodeCI.xcodeproj -scheme XcodeCI build ``` Xcodeのschemeは以下のようにManage Schemesからsharedにチェックを入れないと他の環境でビルドすることができないので設定しておきましょう。  ##Makefileと.travis.ymlを作る Travis CIがビルドとテストを行うためのファイルを作ります。 今回の場合、テストが行われるマシンは普通のMacなので、スクリプトであれば何でも構わないのですがMakefileが一番簡単だと思います。 ```mf:Makefile PROJECT=XcodeCI.xcodeproj SCHEME=XcodeCI defualt: clean build test clean: xctool \ -project ${PROJECT} \ -scheme ${SCHEME} \ clean build: xctool \ -project ${PROJECT} \ -scheme ${SCHEME} \ build \ -sdk iphonesimulator test: xctool \ -project ${PROJECT} \ -scheme ${SCHEME} \ test \ -test-sdk iphonesimulator \ -parallelize ``` ```yaml:.travis.yml language: objective-c script: - make ``` ##Rakefileを使う さて、小規模なプロジェクトならば上記のようなMakefileを作るだけでいいのですが、場合によっては何らかの事情でプロジェクトの中に結構な数のターゲットや別々のテストユニットが混在する場合があります。そんなとき、すべてのターゲットをビルドしてテストしたい場合、xctoolではすべてのスキームを選択することはできず、xcodebuildの-alltargetsオプションもworkspaceを使っている場合は使えません。 ```mf:Makefile all: xctool -workspace ${WORKSPACE} -scheme ${SCHEME1} build xctool -workspace ${WORKSPACE} -scheme ${SCHEME2} build xctool -workspace ${WORKSPACE} -scheme ${SCHEME3} build . . . ``` その上、毎回-workspaceなどの変わらないオプションを付けるのも煩雑です。そこで、Rakefileの登場です。rakeはRubyで記述されたMakefileで、Makefileと異なりより柔軟なタスクの記述ができます。たとえばXcodeCI.xcworkspaceのXcodeCIというスキームをDebug Configurationでビルドしたいとき ```zsh rake build:XcodeCI:Debug ``` のようにビルドできたらスマートだと思います。 はい、そこでこんなRakefileを書きました。(長すぎるのでgistに避難) [https://gist.github.com/keroxp/7707253](https://gist.github.com/keroxp/7707253) これをxcodeprojやwcworkspaceと同じディレクトリにいれて以下のようなRakefileをつくって ```ruby:Rakefile import "farm.rake" # デフォルトのタスクを記述 task :default => ["clean","build:all","test:all"] # 必要があればプロジェクトとワークスペースのパス # $PROJECT = "Hoge.xcodeproj" # $WORKSPACE = "Hoge.workspace" # デフォルトのビルドスキーム $PRIMARY_SCHEME = "XcodeCI" ``` ```zsh rake ``` と打つとあら不思議。  プロジェクト中のすべてのスキームをビルドしてテストを行ってくれます。 これはなにをやってるかというと、上記のfarm.rakeというRakefileのなかでxcodeproj, xcworkspaceが持っている情報を読み込んで、スキームとターゲット、コンフィギュレーションに応じて動的にrakeタスクを作っているんですね。なので、例えばXcodeCIとXcodeCI iPadというスキームがあるプロジェクトの場合 ```zsh rake info ``` と打つと  このような情報が表示されます。 また、 ```zsh rake --tasks ``` と打つと、現在実行可能なタスクの一覧が表示されます  これでコマンドラインでのビルドが簡単に記述できるようになりました! ※farm.rakeはcocoapods gemに依存しているので動かない人は ```zsh gem install cocoapods ``` をお願いします。 ##.travis.ymlを書く 最後にTravis CIが実行するタスクを記述しましょう。 ```yaml:.travis.yml language: objective-c before_install: - gem install cocoapods script: - rake ``` これだけです。そしてあとはgithubにpushするだけです。 # おわりに いかがだったでしょうか? 初日にもかかわらずあまりiOSとは関係の無いTIPSになってしまいましたが、明日以降素晴らしい記事がぞくぞくと上げられるので大丈夫でしょう:) お疲れさまでした! 今回使ったサンプルプロジェクトは[https://github.com/keroxp/XcodeCI](https://github.com/keroxp/XcodeCI)にアップしておいたのでよければ確認して下さい。 |
|
| 194位 |
|
|||
|
22:54:13 |
(Consensus Base Inc. 所属) |
|
# 結論
## いきなり結論 利用しているフレームワークの規約がないなら、 <a href="http://www.infiniteloop.co.jp/docs/psr/psr-2-coding-style-guide.html">PSR-2(日本語)</a> に従っておけば、間違いない! あとは、コマンドラインなり、エディタで自動整形する * [PHPコードをコマンドで自動整形! Condig Standards Fixer と PHP_CodeSniffer - Qiita](http://qiita.com/hshimo/items/e8374fe721492ac4658b "PHPコードをコマンドで自動整形! Condig Standards Fixer と PHP_CodeSniffer - Qiita") ## 日本語なら以下がお勧め! <a href="http://9ensan.com/blog/programming/php/php-psr-coding-standards/">PHPのコーディング規約 PSR-0、PSR-1、PSR-2、PSR-3とは | 9ensanのLifeHack</a> 以下、コーディング規約とツールまとめ # 目的 - 個人向け: PHPの開発をする場合、どのコーディング規約に従うべきか? をサクッと知りたい - チーム向け: チームでどれを使うか? を決めるための参考に - 教育: この規約でやって!と一言で教えるための参考URL # PHPコーディング規約の種類 PEARコーディング規約 や Zend Codig Starndard あたりが 有名かと思っていましたが、色々ありますよ! ## PSR-0, PSR-1, PSR-2, PSR-3 PHP-FIGによってまとめられている規約! 有名どころで入っていないのは、CodeIgniterくらい? <a href="http://www.php-fig.org/">PHP-FIG — PHP Framework Interop Group</a> 0-2まで日本語訳、3は英語です。 ## PSR-0: オートローディング規約 <a href="http://www.infiniteloop.co.jp/docs/psr/psr-0.html">PSR-0 (日本語)</a> ## PSR-1: 基本コーディング規約 <a href="http://www.infiniteloop.co.jp/docs/psr/psr-1-basic-coding-standard.html">PSR-1(日本語)</a> ## PSR-2: コーディング・スタイル・ガイド <a href="http://www.infiniteloop.co.jp/docs/psr/psr-2-coding-style-guide.html">PSR-2(日本語)</a> ## PSR-3: ロガー・インターフェイス <a href="http://www.php-fig.org/psr/3/">PSR-3 - Logger Interface</a> ## PEAR Manual :: 標準コーディング規約 http://pear.php.net/manual/ja/standards.php ## Zend Framework Zend Framework PHP 標準コーディング規約 - Programmer's Reference Guide - Zend Framework http://framework.zend.com/manual/1.12/ja/coding-standard.html ## Zend Framework 2.0 <a href="http://framework.zend.com/wiki/display/ZFDEV2/Coding+Standards">Coding Standards - Zend Framework 2.0 - Zend Framework Wiki</a> ## CakePHP コーディング規約 — CakePHP Cookbook v2.x documentation http://book.cakephp.org/2.0/ja/contributing/cakephp-coding-conventions.html ## Symfony2 コーディング規約 | Symfony2日本語ドキュメント http://docs.symfony.gr.jp/symfony2/contributing/code/standards.html ## CodeIgniter PHPコーディングスタイル : CodeIgniter ユーザガイド 日本語版 http://codeigniter.jp/user_guide_ja/general/styleguide.html ## Yii Framework 基本: 規約 | The Definitive Guide to Yii | Yii PHP Framework http://www.yiiframework.com/doc/guide/1.1/ja/basics.convention ## Laravel Coding Style https://laravel.com/docs/master/contributions#coding-style # コーディング規約用: コード整形ツール コーディング中にコマンドで! コミット時にフックしてもよし! 皆様のコーディングスタイルの自動統一化にお役に立ちます! ## 公式サイト The PHP Coding Standards Fixer for PSR-1 and PSR-2 http://cs.sensiolabs.org/ ## 日本語での説明 PHPを最新コーディング規約に合わせて修正してくれるツール「PHP Coding Standards Fixer」 | 9ensanのLifeHack http://9ensan.com/blog/programming/php/php-psr-coding-standards-fixer/ ## 意見が分かれそうなところ - SQLの書き方 ## 参考 - <a href="http://blog.tojiru.net/article/278307117.html">PSR-0はなぜ0(≒最重要)なのか - 泥のように</a> ## 検索用用語 PHP, Coding Standard, Coding Style, コーディング, 規約, ルール,スタイル |
|
| 195位 |
|
|||
|
10:09:37 |
(classmethod, Inc. 所属) |
|
# 注意 **この記事の内容は古くなってしまっています。** **内容をアップデートした記事は[こちら](http://dev.classmethod.jp/smartphone/remove-xcode8-related-unnecessary-files/)にあります。(2016/12/29 更新)** # はじめに 起動ディスクの空きスペースが10GBを下回っていたので、以下のページを参考にどのファイルがたくさんのディスク容量を使っているのかを調べてみました。 * [ハードディスクの空き容量が極端に少なくなる場合の対処方法](https://discussionsjapan.apple.com/docs/DOC-1081 "ハードディスクの空き容量が極端に少なくなる場合の対処方法") どうやらXcode関連のファイルがたくさんのディスク容量を使っているようだったので、以下の場所を確認してみました。 (実際にファイルを削除する場合は自己責任でお願いします。) # Archives アプリ申請時や配布用のipa作成時などにProduct -> Archiveを実行しますが、その時に作成されるデータが以下の場所にあります。XcodeのOrganizerのArchivesタブからも確認できます。 * ~/Library/Developer/Xcode/Archives デバッグ情報なども含まれているためipaファイルよりサイズが大きいようです。 保存し続ける必要がなければ削除します。XcodeのOrganizerのArchivesタブから削除することもできますし、finderで上記フォルダを開き、まとめて削除することもできます。 # DerivedData DerivedDataは以下の場所にあります。OrganizerのProjectsタブでも確認できます。 * ~/Library/Developer/Xcode/DerivedData DerivedDataはプロジェクトごとに作成され、プロジェクトのインデックスやビルド時の生成物、ログを含みます。これらは再びプロジェクトを開いた際に再生成されます。 XcodeのOrganizerのProjectsタブから削除できます。 finderで上記フォルダを開き、まとめて削除することもできます。 # Device Logs, Screenshots, Software Images Device Logs、Screenshots、Software Imagesは以下の場所に保存されています。私の環境では、Software ImagesフォルダにiOSのベータ版をインストールした際のipswファイルが10個ほど存在し、20GBくらいのサイズになっていました。スクリーンショットを撮った際の画像ファイルも数が多いと結構な容量になります。 | 項目 | 保存場所| |:-----------|:------------| | Device Logs| This | | Screenshots|Library/Application Support/Developer/Shared/Xcode/Screenshots| |Software Images|Library/MobileDevice/Software Images| # iOS DeviceSupport iOSデバイスを接続した際に、以下の場所にデータが生成されます。 デバイスとOSバージョンごとに生成されるようで、私の環境では合計サイズが14GBになっていました。接続したデバイスの機種とバージョンに該当するファルダがない場合は生成されるようです。 * ~/Library/Developer/Xcode/iOS DeviceSupport/ # iPhone Simulator Apps iOSシミュレーターにインストールしたアプリケーションは、以下のフォルダ内に保存されています。不要なアプリケーションを削除することでディスクの使用容量を削減できます。私の環境では200MB程度のサイズでした。 * ~/Library/Application Support/iPhone Simulator # 参考、関連ページ * [ハードディスクの空き容量が極端に少なくなる場合の対処方法](https://discussionsjapan.apple.com/docs/DOC-1081 "ハードディスクの空き容量が極端に少なくなる場合の対処方法") * [Xcode: 5 places to save some disk space](http://blog.favo.org/post/31649090293/xcode-5-places-to-save-some-disk-space "Xcode: 5 places to save some disk space") * [Is it safe to erase ~Library/Developer?](http://apple.stackexchange.com/questions/104810/is-it-safe-to-erase-library-developer "Is it safe to erase ~Library/Developer?") ## Apple Documents * [Devices Organizer Help](https://developer.apple.com/library/ios/recipes/xcode_help-devices_organizer/_index.html "Devices Organizer Help") * [Projects Organizer Help](https://developer.apple.com/library/ios/recipes/xcode_help-projects_organizer/_index.html "Projects Organizer Help") * [Archives Organizer Help](https://developer.apple.com/library/ios/recipes/xcode_help-archives_organizer/_index.html "Archives Organizer Help") |
|
| 196位 |
|
|||
|
05:16:32 |
|
|
v1.8.3から`git submodule deinit`が追加され、わずらわしかったサブモジュールの削除がほんの少しだけ楽になりました。
``` $ git submodule deinit path/to/submodule $ git rm path/to/submodule $ git config -f .gitmodules --remove-section submodule.path/to/submodule ``` ちなみにv1.8.5からは最後の行もいらなくなるそうです。 |
|
| 197位 |
|
|||
|
16:32:45 |
(LIFULL Co., Ltd. 所属) |
|
2012年頃に買ったMacBook Air。 Android Studioをインストールしようと思ったら、Javaがインストールされていない。 そこでJDKをインストールしたわけだが意外に面倒、というか複雑だったのでメモ。 OpenJDKについては調べてもいない。 # 現状の確認 ## Javaのバージョン /Applications/Utilities/Java Preferences.app を探したけど、Mac OSX10.8以降では存在しないらしい。 OSをアップデートした人は残ってるかも。 なので、コンソールから`java -version`で確認。 「インストールしますか?」と聞かれたら、まだインストールしない。 Android StudioなどJava製のアプリを起動してもAppStoreが「インストールしますか?」と聞いてくる。 そこでインストールしたとしても、何回でも聞いてくるのでインストールしない。 何かしらのJavaがインストールされている場合`/usr/libexec/java_home -V`でも確認できるはず。 例えばこんな感じ。 このコマンドなかなか出来るヤツ、という印象を受けた。 ```bash:java_home $ /usr/libexec/java_home -V Matching Java Virtual Machines (3): 1.7.0_51, x86_64: "Java SE 7" /Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home 1.6.0_65-b14-462, x86_64: "Java SE 6" /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home 1.6.0_65-b14-462, i386: "Java SE 6" /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home /Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home ``` ## パスについて MacのJavaは下記の場所にインストールされるらしい。 1. /System/Library/Java/JavaVirtualMachines/ 2. /Library/Java/JavaVirtualMachines/ 3. /Users/$USER/Library/Java/JavaVirtualMachines (1)にはAppleのJDKがインストールされる。 (2)にはOracleのJDKがインストールされる。 (3)はユーザ別にインストールした場合? http://blog.satooshi.jp/blog/2013/01/29/you-cannot-uninstall-apple-jdk6/ ## MacとJavaの関係 OracleでMac用のJDK6を探したけど見つからない。いったいどうなってんだ。 > Javaバージョン6以前については、Apple社が独自のバージョンのJavaを提供しています。 > http://www.java.com/ja/download/faq/java_mac.xml#java6 どうやら6まではAppleが提供してたらしい。 7からはAppleは提供していないので、Oracleからダウンロードできる。逆に。 > JDK 7がインストールされているシステムでApple Java 6を復元する場合は、まずJDK 7をアンインストールする必要があります。JDK 7のアンインストール手順を参照してください。 > http://www.java.com/ja/download/faq/java_mac.xml#restore 復元てなんですかね。 JDK7からインストールしてしまった場合、アンインストールしたほうがいいのかもしれない。 してないけど。 アンインストール方法は↓ http://docs.oracle.com/javase/7/docs/webnotes/install/mac/mac-jdk.html#uninstall # JDK6のインストール 前置きが長すぎる。JDK6からインストール。 Apple提供のものを下記のページからダウンロードしてインストール。 最新のものはAppleのサポートページから"java"で検索したほうがいいと思う。 Java for OS X 2013-005 http://support.apple.com/kb/DL1572?viewlocale=ja_JP /System/Library/Java/JavaVirtualMachines/1.6.0.jdk にインストールされているはず。 # JDK7以降のインストール こちらはOracleからダウンロードする。 "Accept License Agreement"をチェックしてダウンロード。 http://www.oracle.com/technetwork/java/javase/downloads/index.html こちらも特に何事も無くインストール完了。 /Library/Java/JavaVirtualMachines/ 以下にインストールされているはず。 ## JAVA_HOME環境変数とjava_homeコマンド 色々調べてみると内部は複雑な模様。 シンボリックリンク切ったり張ったりしてる人もいて嫌になる。 > We could keep our current PATH and it will be Apple java bootstrap system, living into /usr/bin/java who make use of JAVA_HOME and launch the proper JVM. Simple but efficient . > http://blog.hgomez.net/blog/2012/07/20/understanding-java-from-command-line-on-osx/ MacのjavaはJAVA_HOME環境変数を設定するだけで切り替えられるっぽい。 `java_home -v`でバージョンごとのパスを返してくれるので、それと合わせて使えばよさそう。 やはり`java_home`は出来る子だった。 ```bash:java $ echo $JAVA_HOME $ export JAVA_HOME=`/usr/libexec/java_home -v 1.8` $ echo $JAVA_HOME /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home $ java -version java version "1.8.0_25" Java(TM) SE Runtime Environment (build 1.8.0_25-b17) Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode) $ $ export JAVA_HOME=`/usr/libexec/java_home -v 1.6` $ echo $JAVA_HOME /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home $ java -version java version "1.6.0_65" Java(TM) SE Runtime Environment (build 1.6.0_65-b14-462-11M4609) Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-462, mixed mode) ``` |
|
| 198位 |
|
|||
|
20:08:48 |
(Syntax Sugar Inc. 所属) |
|
## この記事は Turbolinks v2.2.0 時点のものです
https://github.com/rails/turbolinks ## Turbolinks Classic is now deprecated Rails4系で使われていたTurbolinksは[turbolinks-classic](https://github.com/turbolinks/turbolinks-classic)と命名が変更されました。 Rails5ではVersion 5 として [turbolinks](https://github.com/turbolinks/turbolinks) のversion ~> 5.0 が使われます。動作としては似てますが内部の実装が大きく変わっているので注意してください。 ## Turbolinksとは * TurbolinksはRails4.0からデフォルトで導入されたgem * railsアプリケーションを **簡単に** pjaxっぽくすることが出来る。 * ajaxとhistoryAPI(popState, pushState)を利用して画面遷移 * Turbolinksで遷移する場合は、titleとbodyとcsrfトークンを変更する * csrfトークンは変更された場合のみ * js, cssの読み込みを初回時に行い次回以降の読み込み処理を省略することで高速化する。 ## よく言われるTurbolinksの問題 * `$(document).ready()`が発火しない * `$(window).load()`が発火しない * metaタグが更新されない ## 主なTurbolinksの動作は2つ #### fetchReplacement * `<a>`タグのリンクをクリックした時の遷移をajax化して画面遷移なしにページ遷移する。 * clickイベントの挙動を変えている #### fetchHistory * 戻るボタンなどを押した時のイベントを書き換えている * fetchReplacement時にpushStateでキャッシュしたページをpopStateで復元する。 ## Turbolinks対応ブラウザ 公式では > This includes Safari 6.0+ (but not Safari 5.1.x!), IE10, and latest Chromes and Firefoxes. と紹介されている。 * **主な判定はhistoryAPI(`pushState`, `replaceState`)をブラウザがサポートしているかどうか** * その他はソースを参照 * 後述するが、`page:change`の判定は別なのでTurbolinksは対応していないけど`page:change`が発火することがありえる。 ## 実はTurbolinksのソースは * js 約330行 * ruby 約80行 #### 1時間もあればだいたい仕様がつかめる。 ## Turbolinksが提供しているtrigger * 'page:before-change' * 'page:receive' * 'page:change' * 'page:update' * 'page:load' * 'page:restore' * 'page:expire' ## fetchReplacement ### `<a>`タグのリンクをクリックの挙動をajax化する 1. `<a>`タグをクリック 2. `'page:before-change'`。`'page:before-change'`を設定しているeventがfalseを返す場合、通常の画面遷移を行う。 3. fetchReplacement がcallされる 4. pushStateで現在のページをpageCashにキャッシュ 5. `'page:fetch'` 6. GETのajax request を送信 7. responceが返ってくる 8. `'page:receive'` 9. responceをチェック。statusが400~599でない, content-typeが正しいか, ヘッダが変わってないか 10. 9が正しくなければ `document.location.href = url` で遷移 11. body, title をごっそり変える 12. `'page:change'` 13. `'page:update'` 14. `'page:load'` ## fetchReplacement tips * fetchReplacementが成功した場合、既に前のページから読み込まれているjs, cssを読み込む必要がないのでその分高速。 * ajaxで画面遷移が行われるので`$(document).ready()`や`$(window).load()`が発火しない。 * headが変化してるかのチェックは`data-turbolinks-track`が付与されたnodeが前のページと内容が同じかをチェックする * レスポンスが正しくない場合は、サーバに2度リクエストする。redirectを挟む場合は4回リクエストが飛ぶ。 * サーバーがクロスドメインへのリダイレクトを返す場合、403を返す **※ここ重要** * consoleで`Turbolinks.visit('/hoge')`と実行することでfetchReplacementを実行できる。 * リンクをクリックした時の視覚的レスポンスがないため、ユーザーには不親切。`page:fetch`, `page:recieve`間で視覚的にローディングしている事を伝えた方がユーザーフレンドリー。 ## fetchReplacementが行われないパターン * 初めてそのサイトに訪れた時 * ページをリロードした時 * ignoreClickの条件に一致する時 * submit時はそもそも`<a>`タグではないので動作しない * その場合、従来の画面遷移が行われる。 ## ignore Click Turbolinksが動作しないリンク * crossOriginLink * anchoredLink * nonHtmlLink * noTurbolink * targetLink * nonStandardClick ## 主なignore Click * crossOriginLink * location.protocolとlocation.hostが現在のページと異なるページへのリンク * サブドメインが異なる場合、fetchReplacementは動作しない * nonHtmlLink * htmlで無いリンク * 画像などへのリンクの場合、fetchReplacementは動作しない * noTurbolink * 唯一明示的に開発者が設定できる * リンクから親を逆上って`data-no-turbolink` 存在するリンクは、fetchReplacementは動作しない * nonStandardClick * `shift`, `command`, `alt`, `control`キーなどが押されている場合は、動作しない * 別ウィンドを開く時など ## fetchHistory ### 戻るなどの遷移をpageCashからpopStateで復元する 1. pageCashに目的のページがある場合、popStateで復元する 2. 1で復元出来ない場合、`document.location.href = url` 3. `'page:change'` 4. `'page:update'` 5. `'page:restore'` ## fetchHistory tips * ブラウザのキャッシュから復元するので高速 * ブラウザの更新がないため、readyなどが発火しない * キャッシュに該当のページがない場合はブラウザによりサーバーにアクセスする。 * デフォルトでは10ページ分キャッシュする * consoleで`Turbolinks.pageCached();`を実行することでキャッシュするページ数を取得出来る。 ## 主なtriggerと`$(document).ready()` #### ``$(document).ready()`` * jQueryが提供 * どんなブラウザでも発火 * DOMが構成された後に発火 #### `'page:load'` * fetchReplacementが完了した時に発火 #### `'page:restore'` * fetchHistoryが完了に発火 #### `'page:change'` * どんな場合でもページを表示した後に発火 * `$(document).ready()` 相当 * `DOMContentLoaded` リスナーに追加されている * 発火タイミングは `DOMContentLoaded` + `fetchReplacement` + `fetchHistory` * **しかし、`DOMContentLoaded`はIE6,7など古いブラウザでは実装されていないので古いブラウザでも動作させたい処理を書くときは使用しない事。** ## triggerの発火タイミング | | landing, reload | fetchReplacement | fetchHistory | |-------------- |---------- |------------------ |-------------- | | ready | ○ | × | × | | page:load | × | ○ | × | | page:restore | × | × | ○ | | page:change | ○ | ○ | ○ | * fetchReplacement失敗時は再度レンダリングされ, landingの状態になる。 ## よく出てくるおまじない #### `$(document).on 'ready page:load', ->` これはjqueryが提供する`'ready'`とTurbolinksが提供する`'page:load'`の両方で定義すること。 * 非Turbolinks遷移時は`'ready'`で発火。 * Turbolinks遷移時は`'page:load'`で発火の両方をカバーするという意味。 * Turbolinksをサポートしていないブラウザでも`'ready'`が発火するので安心。 #### jquery-turbolinks gem * `'ready'`triggerに`page:load`を追加してくれる機能を提供。 ## Trigger tips * 通常、`page:change`でaddEventListenerしてはいけない。 * fetchHistoryの際にもイベントが追加されるのでページを戻った時に多重にEventを追加してしまう事があるため。 * 'ready page:load'を使用する。 * `page:change`はTurbolinksのサポート判定とは異なり、`document.addEventListener` と`document.createEvent`がブラウザでサポートされている事が判定基準。 * なのでTurbolinksは動かないけど`page:change`は発火する事がありえる。 * `page:change`は動くブラウザとそうでないブラウザがあるので注意が必要。 * 特にGoogleAnalyticsは`page:change`を使うと古いブラウザのデータが取れない。 ## Turbolinksと仲良くするために注意すること #### `<body>`タグにjsを書かない。 * `<body>`タグ内にeventを設定する処理(clickなど)をjsで書くとTurbolinksで遷移する度に設定され、何度も呼ばれる。 * どうしても書きたい時は`<script>` に `data-turbolinks-eval="false"`と書けば1度しか実行されない #### js, css, `<meta>`タグなど`<head>`をページごとに更新する必要がある場合は`data-turbolinks-track`を記述する * 読み込む必要があるjsやcssなどが読み込まれないことを防ぐため #### Rails側でクロスドメインへリダイレクトするリンクには`data-no-turbolink`を指定する * 無駄なリクエストを未然に防ぐ #### できるだけapplication.js, application.cssにまとめる * cssやjsが変わるとTurbolinksで遷移できず、再描画されるのでせっかくの旨味が減る。 * 無駄にリクエストが2度飛ぶのでTurbolinks使わない場合より遅くなることも。 * サブドメインが異なるとTurbolinksはそもそも動作しないため、サブドメインごとにファイルを分けるのはあり。 ## Turbolinksに向いているサイト * javascriptの記述量が少ない * Railsが生成したビューを返す事がメインであるサイト * 開発コスト上げてでもページレンダリングを高速化したい用途がある ## Turbolinksに向かないサイト * すでにJavascriptを多く記述してる * Javascriptのclientside frameworkをサイトで多く使用している(Backbone, Angular等) * ページレンダリングを高速化する必要がない規模のサイト * subdomainをサイトで多用している * ページごとにcss, jsを分けて記述している ## Turbolinksの良い点・悪い点 ### 良い点 * Turbolinksを使うとページのレンダリングが速くなる ([turbolinks_test](https://github.com/steveklabnik/turbolinks_test/tree/all_the_assets)) * 戻るボタンで画面遷移する場合、キャッシュから復元しサーバーにアクセスしないため超高速。 * うまく使いこなす事が出来ればリクエスト数を減らす事が出来る。 * Rails4.0がリリースされて約1年ちょっと。そろそろ情報を調べる事ができるようになってきた。 * ソースコード読むとそんなに難しくない ### 悪い点 * jsやブラウザの前提を覆す。 * readyが呼ばれない * headerが変化しない等 * フロントエンドエンジニアとデザイナーに負担を書けてしまう場合が多い * jsをTurbolinks依存したコードを書く必要がある * Turboliksで遷移出来ない場合、不要なアクセスが増える * 本来Turbolinksで遷移出来るはずのリンク(ignoreClickでない)がcss, jsが異なる理由で失敗する場合,2回リクエストが飛ぶので逆に遅くなる。 ## まとめ * Turbolinksでうまく遷移できるように構築しているとページのレンダリングが早くなる。 * 逆にTurbolinksでうまく遷移できるように構築していないとリクエストが多く発生し逆に遅くなる場合がある。 * 'page:change'は古いブラウザでは動かないので使用するのには注意する。 * jsを書く場合はTurbolinksの挙動を理解しておく必要がある。 * たぶんほとんどのwebアプリケーションでは使用する必要はないかもしれません。。 ### 開発コストがあがり苦労をする面もありますが、適切に使うとそれなりにメリットがあるので、使用用途をしっかり考え運用することをおすすめします。 ## 追記 [Turbolinksとプログレスバーをあわせると開発がかなり捗る nprogress-rails](http://qiita.com/saboyutaka/items/34d65201600ba00ea930) [Turbolinks 3こそRailsの未来](http://qiita.com/saboyutaka/items/2cd40f7942f8463dcfde) を書いたのでこちらもどうぞ。 |
|
| 199位 |
|
|||
|
23:47:45 |
|
|
これは[Git Advent Calendar / Jun. - Qiita](http://qiita.com/advent-calendar/git).16日目の記事です.
私はエイリアスが好きなのでgitまわりのエイリアスについて書いてみます. 間違ってたらすみません,指摘してくださると嬉しいです. # 設定方法 いろんなスコープにエイリアスを設定できます. 例として`git st`で`git status`を実行できるようにしてみましょう. 設定ファイルに書き込む方法と,`git config`コマンドで設定する方法があります. 設定ファイルに書く場合は以下のように`alias`セクションに`短縮形 = 展開形`の書式で書きます. ``` [alias] st = status ``` どの設定ファイルに書くかで適用される範囲が変わります. また,コマンドで設定する場合は,`git config [option] alias.短縮形 展開形`を実行します. - マシン全体に反映させる場合 `$(prefix)/etc/gitconfig`に書くか,以下のコマンドを実行します. ```sh % git config --system alias.st status ``` - ユーザ単位で反映させる場合 `~/.gitconfig`に書くか,以下のコマンドを実行します. ```sh % git config --global alias.st status ``` - リポジトリ単位で反映させる場合 `.git/config`に書くか,以下のコマンドを実行します. ```sh % git config alias.st status ``` ## 優先順位 マシンよりユーザ,ユーザよりリポジトリが優先されます. # ぺっくの使ってる設定例 ```:~/.gitconfig [alias] # いい感じのグラフでログを表示 graph = log --graph --date=short --decorate=short --pretty=format:'%Cgreen%h %Creset%cd %Cblue%cn %Cred%d %Creset%s' # 上の省略形 gr = log --graph --date=short --decorate=short --pretty=format:'%Cgreen%h %Creset%cd %Cblue%cn %Cred%d %Creset%s' st = status cm = commit # Untracked filesを表示せず,not stagedと,stagedだけの状態を出力する stt = status -uno # 行ごとの差分じゃなくて,単語レベルでの差分を色付きで表示する difff = diff --word-diff ``` さらに私の場合はシェルのエイリアスにもこんな感じで設定してgitすら省略しています. ```:~/.alias alias pull='git pull' alias push='git push' alias st='git status' alias stt='git status -uno' ``` # その他の便利そうな話 - 外部コマンド gitじゃない外部コマンドもエイリアスで実行できます.例えば`git hoge`で`echo hogehoge`が実行されるようにするには, ``` [alias] hoge = !echo hogehoge ``` のように`!`を展開形の前につけると,"git"を取って展開したコマンドを実行できます. - 内部コマンド あと,既に存在するコマンドを上書きするようなエイリアスは無視されるそうです.例えば ``` [alias] status = some other command ``` みたいなのはだめってことだと思います. - 引数の区切り 展開形の引数の区切りはスペースで,一般的なシェルでのクォートとかエスケープがサポートされてるそうです. # 参考 ```sh % man git-config % git help config ``` # 最後に 皆さんの使ってるエイリアスが知りたいのでコメントにいっぱい集まればいいなーなんて # 2015/07/23 追記 あれから数年が経ち、いろいろ設定が増えていました。 ```:~/.gitconfig # ファイル名のみの差分を表示する difff = diff --name-only # staged diff diffs = diff --cached dp = diff --no-prefix dsp = diff --cached --no-prefix co = checkout logg = log --graph --all --decorate log1 = log --pretty='format:%C(yellow)%h%Creset %C(magenta)%cd%Creset %s %Cgreen(%an)%Creset %Cred%d%Creset%C(black bold)%ar%Creset' --date=iso # lg = log --graph --pretty=format:'%Cred%h%Creset - %s%C(yellow)%d%Creset %Cgreen(%cr:%cd) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative lg = log --graph --pretty=format:'%Cred%h%Creset - %s%C(yellow)%d%Creset %Cgreen(%cr:%cd) %C(bold blue)<%an>%Creset' --abbrev-commit --date=iso lga = !"git lg --all" log0 = log --graph --all --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(bold white)― %an%C(reset)%C(bold yellow)%d%C(reset)' --abbrev-commit --date=relative log2 = log --graph --all --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(bold white)― %an%C(reset)' --abbrev-commit log3 = log --graph --date-order -C -M --pretty=format:"<%h> %ad [%an] %Cgreen%d%Creset %s" --all --date=short log4 = log --graph --pretty='format:%C(yellow)%h%Cblue%d%Creset %s %C(black bold)%an, %ar%Creset' sgraph = !"git log --oneline --graph" ancestor = !zsh -c 'diff -u <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | sed -ne \"s/^ //p\" | head -n 1' - unstage = reset HEAD ``` ```:~/.alias alias git-submodule-rm="git rm-submodule --no-commit" # 上のgitconfigで定義したもの alias gitu="git unstage" alias reb='git svn rebase' alias gl="git clone" # リモートからフェッチ(タグを更新し、削除済みブランチを削除)してpullする alias gu="git fetch --tags --prune&& git pull origin" ``` # 2015/12/09 追記 cherry-pickがめんどくさくなりました。 ``` cp = cherry-pick ``` # 2016/02/16 追記 git flow、git stash用のエイリアスを追加しました。 ``` ffs = flow feature start sp = stash pop ss = stash save ``` # 2016/08/23 追記 branch 長い。 ``` b = branch ``` # 2016/08/28 追記 現在のブランチ名を取得する。 使い方 `git push origin $(tb)` ```:~/.alias # this branch alias tb="git symbolic-ref --short HEAD|tr -d \"\\n\"" ``` # 2017/02/07 追記 Git LFSのステータスを確認する。 使い方 `git stl` `stl` `st`と組み合わせて、`st; stl` みたいに使い始めました。 ```:~/.alias alias stl='git lfs status' ``` ```:~/.gitconfig stl = lfs status ``` |
|
| 200位 |
|
|||
|
08:31:45 |
|
|
* [たくさんあるオープンソースライセンスのそれぞれの特徴のまとめ | コリス](http://coliss.com/articles/build-websites/operation/work/choose-a-license-by-github.html)
さんの記事がとても良かったので表形式にしました。 * [Google docs](https://docs.google.com/spreadsheets/d/1smp9rjunA2DeagL65gy2fqUC-7i-90gQZk2uJ81hE0M/edit#gid=0)にまとめられている方も居ます。 共通 (No License以外) ------------- * 許可: 商用利用、修正、配布 * 禁止: 責任免除 ライセンスの種類 ============= | ライセンス | Required(必須) | Permitted(許可) | Forbidden(禁止) | |----------|------|------|----| | [Apache v2](http://www.apache.org/licenses/LICENSE-2.0.html) | 著作権の表示、変更箇所の明示 | 商用利用、修正、配布、サブライセンス、特許許可 | トレードマークの使用、責任免除 | | [GPL v2](http://choosealicense.com/licenses/gpl-v2/) | 著作権の表示、変更箇所の明示、ソースの明示 | 商用利用、修正、配布、特許許可 | 責任免除、サブライセンス | | [MIT](http://choosealicense.com/licenses/mit/) | 著作権の表示 | 商用利用、修正、配布、サブライセンス | 責任免除 | | [Mozilla Public v 2.0](http://choosealicense.com/licenses/mozilla/) | ソースの明示、著作権の表示 | 商用利用、修正、配布、サブライセンス、特許許可 | 責任免除、トレードマークの使用 | | [LGPL v2.1](http://choosealicense.com/licenses/lgpl-v2.1/) | 著作権の表示、ライブラリの使用、ソースの明示 | 商用利用、修正、配布、サブライセンス、特許許可 | 責任免除 | | [The BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) | 著作権の表示 | 商用利用、修正、配布、サブライセンス | 責任免除、トレードマークの使用 | | [Artistic 2.0](http://opensource.org/licenses/Artistic-2.0) | 著作権の表示、変更箇所の明示、ソースの明示 | 商用利用、修正、配布、サブライセンス、個人利用 | 責任免除、トレードマークの使用 | | [GPL v3](http://choosealicense.com/licenses/gpl-v3/) | 著作権の表示、変更箇所の明示、ソースの明示 | 商用利用、修正、配布、特許許可 | 責任免除、サブライセンス | | [LGPL v3](http://choosealicense.com/licenses/lgpl-v3/) | 著作権の表示、ライブラリの使用、ソースの明示 | 商用利用、修正、配布、サブライセンス、特許許可 | 責任免除 | | [Affero GPL](http://choosealicense.com/licenses/agpl/) | 著作権の表示、変更箇所の明示、ソースの明示 | 商用利用、修正、配布 | 責任免除、サブライセンス | | [Public Domain](http://choosealicense.com/licenses/unlicense/) | - | 個人利用、商用利用、修正、配布、サブライセンス | 責任免除 | | [No License](http://choosealicense.com/licenses/no-license/) | 著作権の表示 | 個人利用、商用利用 | 修正、配布、サブライセンス | | [Eclipse Public v1.0](http://www.eclipse.org/legal/epl-v10.html) | ソースの明示、著作権の表示 | 商用利用、修正、配布、サブライセンス、特許許可 | 責任免除 | | [BSD 2-Clause](http://opensource.org/licenses/BSD-2-Clause) | 著作権の表示 | 商用利用、修正、配布、サブライセンス | 責任免除 | 備考:各項目の補足説明 ====================== Required(必須) ---------------- |項目|説明| |----|----| | Rename(リネーム)| 修正した場合は、ソフトウェアの名前を変えなくてはいけません。 | | License and copyright notice(著作権の表示)| ライセンスと著作権のコピーをコードに含めなくてはいけません。 | | Library Usage(ライブラリの使用) | ライブラリは非オープンソースのアプリケーションで使用されるかもしれません。 | | State Changes(変更箇所の明示) | コード内の重要な変更箇所を明示してください。 | | Disclose Source(ソースの明示) | ライブラリのソースを明らかにしてください。 | Permitted(許可) ------------------ |項目|説明| |----|----| | Sublicense(サブライセンス)| このソフトウェアはサブライセンスを与えてもよいです。 | | Modifications(修正)| このソフトウェアは修正してもよいです。 | | Commercial Use(商用利用)| このソフトウェアは商用目的のために使用してもよいです。 | | Private Use(個人利用)| このソフトウェアを配布しないで、個人的に修正・使用してよいです。 | | Distribution(配布)| このソフトウェアを配布してもよいです。 | | Patent Grant(特許許可)| このライセンスは個人または企業(コントリビュータ)をはじめ全ての利用者に特許権の特別な許可を提供します。 | Forbidden(禁止) ------------------ |項目|説明| |----|----| | Use Trademark(トレードマークの使用)| ロゴやトレードマークでの使用は禁止です。 | | Hold Liable(責任免除)| ソフトウェアは保証無しで提供されます。ソフトウェアによる損害賠償金などの法的責任は負わされません。 | | No Sublicense(サブライセンス禁止)| ライセンスに含まれない変更して、ソフトウェアにサブライセンスを与えてはいけません。 | | Modifications(修正なし)| このソフトウェアは修正してはいけません。 | | Distribution(配布の禁止)| このソフトウェアを配布してはいけません。 | |
|
| 201位 |
|
|||
|
04:09:01 |
(株式会社ずんシステム 所属) |
|
DI (Dependency Injection)ってのは日本語では依存性注入とも呼ばれ、大雑把に言うとAngularJSがコントローラなどに必要とされているコンポーネント(オブジェクト)をいい感じに渡してやる機能です。
ここでは特にAngularJSのDIがどのような仕組で動いてるか、そしてその独特なDIの実装にまつわるトラブルケースを説明します。 # AngularJSのコントローラの書き方 まずはAngularJSの中心的な機能であるコントローラの書き方には、簡単版と面倒版の複数の書き方があることを抑えておきましょう。 ## パターン1(グローバル関数パターン) サンプルとかでよく見るのは↓こういうグローバル関数の形のコントローラです。 ```html <div ng-app> <div ng-controller="HogeCtrl">(略)</div> </div> ``` ```javascript function HogeCtrl($scope) { //略 } ``` シンプルでとっつきやすいです。初心者にも優しくデモ向きですね。 AngularJSの素敵さ(不思議さ)がよく分かる形です。 ## パターン2(モジュール作って.controllerメソッドパターン) そしてもう一つは↓このようなキチンとしたモジュール定義の形です。 ```html <div ng-app="myApp"> <div ng-controller="HogeCtrl">(略)</div> </div> ``` ```javascript var myApp = angular.module('myApp', []); myApp.controller('HogeCtrl', ['$scope', function($scope) { //略 }]); ``` なんか急に複雑で、初心者お断りな雰囲気になりましたね…。 これからAngularJSやってみようかって人が最初にこれ見たら、その時点で半分は逃げ出しちゃいそうです。AngularJSの素敵さを説明するには本質以外の部分が多いのでデモには向かない形かもです。 でもAngularJSでちゃんとしたアプリを作ろうと思ったら覚えなきゃいけない形です。理由は続きを読めば分かります。 ## パターン3($inject Annotationパターン) 1にかなり近い形だけど2ほど面倒じゃなくて、でもAngularJS的には一応キチンとしてて1よりはベターな↓こんな形もあります。 ```html <div ng-app> <div ng-controller="HogeCtrl">(略)</div> </div> ``` ```javascript function HogeCtrl($scope) { //略 } HogeCtrl.$inject = ['$scope']; ``` 2つ目の形を覚えたくないならこの形も一応アリ、但しコントローラの機能しか使わないのであれば、だけど。フィルター定義とかしたくなったら結局は2つ目の形を覚える必要があるので、むしろこの形は一番覚える必要が無いとも言えます。 これの意味するところも最後まで読めば分かります。 ## Angular公式の推奨は2つ目の書き方(多分) 上記の3種類の書き方はどれも同じように動くけど、公式ドキュメントの[Understanding Controllers](http://docs.angularjs.org/guide/controller)のページでは、以下に引用するように上記2つ目の **.controllerメソッドを利用した形を推奨** する文章があります。 > **NOTE**: Many of the examples in the documentation show the creation of functions in the global scope. This is only for demonstration purposes - in a real application you should use the .controller method of your Angular module for your application as follows: ... ↓超訳 >ドキュメント上のサンプルではグローバル関数でコントローラ定義してるのが多いけど、それはあくまでデモの分かりやすさを優先してるからだよ。ちゃんとしたアプリを作るときは、アプリ用のモジュールを作ってその.controllerメソッドを使ってコンポーネント名を列挙した書き方をする **べき(should)** だ。 # AngularJSのDIの仕組み ## AngularJSの不思議 AngularJSのコントローラーの引数って`$scope`や`$route`や`$window`って名前が当たり前のように使われてますが、これってただの慣習的なものじゃないんですよね。試しに引数の変数名をscopeとかrとかに変えると動かなくなってしまいます。 それに、コントローラ関数の`$scope`とか`$route`という引数の順番を変えても動くことにも不思議を感じませんか?これ関数の引数になってるからグローバル変数でもありえないしどうなってんの、と。 その答えは実は、AngularJSのちょっと特殊(変態的)なDIの仕組みにあります。 ## 実装の秘密(変数名でDI) AngularJSの不思議の鍵はその実装方法にあります。 実はAngularJSはコントローラに指定された関数定義を **文字列としてパース** して、引数の **変数名** からコントローラ関数に渡すべきオブジェクトを決定するというかなり特殊(ぶっちゃけ変態的)なアプローチを取っているんです。だからこんなJavascript的には一見不思議なDIが可能なんですね。 このあたりのは`$injector`内の`annotate(fn)`という関数の[実装](https://github.com/angular/angular.js/blob/aa268560064e5875bd471da3f7d1ebc2f9e6b3b7/src/auto/injector.js#L64-L116)を見ると分かります。関数オブジェクトに対して`HogeCtrl.toString()`すると関数定義全体が文字列として取得できることを利用しているんですね。 # minifyにまつわるトラブル 勘の良い人ならもう気づいてると思いますが、AngularJSのDIが上述のような仕組みで動いている為、AngularJS用のソースコードはminify(文字数節約による圧縮)やobfuscate(難読化)等に弱いんです。 例えば、`function($scope){...}`が`function(s){...}`とかに変換されてしまうと、引数の変数名を見ても$scopeのオブジェクトを渡すべきってことが分からなくなってしまうのですね。 これを知らないとminifyしたとたんに動かなくなって、Angularがバグってやがる、とかminifierのバグだ、とか見当違いのデバッグ作業で時間を無駄にすることになったります。 公式ドキュメントのコメントとかにも「サンプル動かねーんだけど!」って声がよくあるのは、大抵このパターンだと思います。フレームワークで最初から勝手にminifyされるのに慣れちゃってるような人がよくハマる罠です。 ## 解決策(パターン2の書き方を推奨する理由) この問題の解決策として用意されているのが、コントローラの書き方パターン2の「.controllerメソッドを使うパターン」です。 minifyやobfuscateプログラムは文字列の値は弄りませんから、渡してほしいオブジェクト名を文字列として明示的に指示してやることで、AngularJSはわざわざfunctionオブジェクトをパースせずとも「1番目の引数として`$scope`を渡してやればいいんだな」ということが知ることが出来、結果としてアノテート対象の関数の引数名が何であろうと第1引数に`$scope`という名前のオブジェクトを受け取れるようになるわけです。 そんなわけで、導入としては便利だしとっつきやすいから1番目の書き方もできるけど、本格的に開発したりライブラリとして第3者に利用されることも考えたコードを書くなら、minifyが導入される可能性も考慮してパターン2の「モジュール作って.controllerメソッドを利用する」書き方を推奨してるのです。 ## 蛇足(パターン3の書き方でもよい理由) ちなみに、パターン1で関数を文字列パースして分かった引数名は最終的には関数オブジェクトの`$inject`というプロパティにキャッシュとして保存されて、以降Angularは$injectプロパティから要求されているコンポーネント名を取得するようになってます。 最初に紹介したパターン3の形はこの内部実装を利用した抜け道なわけですね。これは一応公式ドキュメントでも[$inject Annotation](http://docs.angularjs.org/guide/di)として紹介されている方法ですが、実装に依存してる感は拭いきれず気持ち悪いので個人的にはあんまし使わないです。 # コントローラに限った話ではない ここではコントローラを中心に説明しましたが、このDIの仕組みとminify問題は、全てのコンポーネント(フィルター、サービス、ディレクティブ、コンフィグ、etc)でも基本みんな同じです。それぞれ`.filter`とか`.service`とか`.factory`とか`.directive`とか`.config`とか…、要は[angular.Module](http://docs.angularjs.org/api/angular.Module)で定義されてるメソッドで関数を渡すべき場所ではみんな、関数単体を渡す代わりにコンポーネント名と関数を纏めた配列で渡すことができるし、そうすべきだよってことです。 ちなみにコントローラもコンポーネントのうちです。何かの折に名前で関数オブジェクトとして取得されて使われることもあります、テストとかね。 ## 例えば$routeProvider使った場合のコントローラの指定とかも ↓こんな感じに`.controller`で指定したコントローラ名を文字列で渡してやればminify対策が出来るようになってます。 ```js var myApp = angular.module('myApp', ['']) .controller('ListCtrl', ['$scope', function($scope) {/* 略 */}]) .controller('HogeCtrl', ['$scope', function($scope) {/* 略 */}]) .config(["$routeProvider", function($routeProvider) { $routeProvider. when('/', {controller:"ListCtrl", templateUrl:'list.html'}). when('/hoge', {controller:"HogeCtrl", templateUrl:'hoge.html'}); }]); ``` `$routeProvider.when(path, route)`の[ドキュメント](http://docs.angularjs.org/api/ng.$routeProvider)にも文字列or関数で指定すると書いてあります。 > route - {Object} > ・controller – {(string|function()=} – Controller fn that should be associated with newly created scope or the name of a registered controller if passed as a string. > # 参考URL - 公式系 - [AngularJS: Understanding Controllers](http://docs.angularjs.org/guide/controller) - [angular.js/src/auto/injector.js at master · angular/angular.js · GitHub](https://github.com/angular/angular.js/blob/master/src/auto/injector.js#L41-L71) - [AngularJS: Dependency Injection](http://docs.angularjs.org/guide/di) - [AngularJS: Module](http://docs.angularjs.org/api/angular.Module) - [AngularJS: $routeProvider](http://docs.angularjs.org/api/ng.$routeProvider) - 役に立つ情報 - [AngularJS style guide (日本語訳)](https://github.com/mgechev/angularjs-style-guide/blob/master/README-ja-jp.md) |
|
| 202位 |
|
|||
|
14:41:51 |
(千葉工業大学 所属) |
|
# ワンライナーWebサーバを集めてみた
クライアントサイドのJavaScriptをいじっていて,不意にローカルファイルでは実行できない領域に踏み込んでしまうことがあると思います.とりあえず私が踏み込んでしまったのはWebWorkersですが,他にもWebRTCや・・・(思い出し中)・・・(見つからなかった)・・・などが該当します. そんな時にいちいちApacheやnginxを立ち上げるのも面倒なので,ちょっと検索した結果を残しておきます. ## Python(SimpleHTTPServer)編 Pythonでは(バージョン2.4以降限定らしいですが)モジュールをスクリプトとして実行する"-m"オプションが追加されました.このオプションを使って,標準ライブラリの一つであるSimpleHTTPServerをワンライナーで実行します.起動例とアクセスログを示します.標準ポート番号は8000です. ```bash:8000番ポートで待ち受けする例 $ python -m SimpleHTTPServer Serving HTTP on 0.0.0.0 port 8000 ... 127.0.0.1 - - [09/Jan/2014 11:21:09] "GET / HTTP/1.1" 200 - ``` きちんとアクセスログが残るのが良いですね.ポート番号を変更したい場合は,末尾にポート番号を指定してください. ```bash:3000番ポートで待ち受けする例 $ python -m SimpleHTTPServer 3000 Serving HTTP on 0.0.0.0 port 3000 ... ``` 参考:[コマンド1つで今すぐWebサーバを起動させるためのワンライナー(Ruby or Python)](http://d.hatena.ne.jp/rx7/20090812/p1) ## Python3.x編 試していませんが,Python3.x系であれば以下のようにしても動作するそうです. ```bash:8000番ポートで待ち受けする例 $ python -m http.server 8000 ``` 参考:[ワンライナーでWebサーバ](http://tmkoike.hatenadiary.jp/entry/2013/04/19/002532) ## Ruby(WEBRick)編 Rubyにもwebrickというライブラリが標準添付されているので,これを使ってWebサーバを立てることができます.以下の例ではドキュメントルートとポート番号(8000)しか指定していませんが,その他の設定も行えます.標準ポート番号は80です.ポート番号を指定しない場合,管理者権限がないとエラーで止まるので注意してください. ```bash:8000番ポートで待ち受けする例 $ ruby -rwebrick -e 'WEBrick::HTTPServer.new(:DocumentRoot => "./", :Port => 8000).start' [2014-01-09 13:49:12] INFO WEBrick 1.3.1 [2014-01-09 13:49:12] INFO ruby 1.9.3 (2013-06-27) [x86_64-darwin12.4.0] [2014-01-09 13:49:12] INFO WEBrick::HTTPServer#start: pid=9479 port=8000 localhost - - [09/Jan/2014:13:49:15 JST] "GET / HTTP/1.1" 200 2377 - -> / ``` こちらもアクセスログが残るのが良いですね. ~~※停止する場合はkillコマンドに-KILLオプションを使ってください.~~ 先ほど試したら,ctrl+Cで停止できました. ```bash:昔の停止方法(今はctrl+Cで止まります) $ ps aux | grep ruby webrickのプロセス番号を調べて $ kill -KILL プロセス番号 ``` 参考:[コマンド1つで今すぐWebサーバを起動させるためのワンライナー(Ruby or Python)](http://d.hatena.ne.jp/rx7/20090812/p1) ## Ruby(WEBRick)でHTTPS編 久しぶりにWEBRickを調べてみたところ,簡単にHTTPSに対応できることが分かりました.以下の例では証明書を自動生成した上で,8000番ポートで待受をします.※HTTPS専用です.HTTPでは接続できません. ```bash:8000番ポートでHTTPSで待ち受けする例 $ ruby -rwebrick -rwebrick/https -e 'WEBrick::HTTPServer.new(:DocumentRoot => "./", :Port => 8000, :SSLEnable => true, :SSLCertName => [["CN", WEBrick::Utils::getservername]] ).start' [2016-06-01 14:10:36] INFO WEBrick 1.3.1 [2016-06-01 14:10:36] INFO ruby 2.1.4 (2014-10-27) [x86_64-darwin14.0] ................................................................................................................++++++ ....++++++ [2016-06-01 14:10:36] INFO Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: CN=sahara.local Validity Not Before: Jun 1 05:10:36 2016 GMT Not After : Jun 1 05:10:36 2017 GMT Subject: CN=sahara.local Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (1024 bit) Modulus: 00:b7:e4:37:9e:86:40:dd:cb:01:b2:a7:a8:53:db: 42:e8:81:0f:a9:23:63:d9:6c:6a:6a:13:0f:bc:66: ed:ac:04:6c:93:1d:82:7b:22:6f:11:fc:cd:b7:ab: a8:6b:fc:fc:60:e8:18:cc:52:95:b4:1a:aa:8c:1e: 01:cb:fa:14:0b:03:f0:3e:9e:d2:d4:5d:44:b2:83: e4:de:49:5c:37:7d:1a:2b:97:a7:82:e9:d9:cb:c0: fa:f5:5b:92:54:29:5c:e4:fe:c4:ed:a0:2f:3b:da: 83:09:e2:12:d8:01:84:9f:60:80:9c:5a:1b:12:70: be:97:e1:3c:34:b6:fd:80:29 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Key Usage: Key Encipherment X509v3 Subject Key Identifier: 00:0C:DD:F6:BD:66:22:72:82:DA:82:20:9C:54:E6:12:64:5E:90:15 X509v3 Extended Key Usage: TLS Web Server Authentication Netscape Comment: Generated by Ruby/OpenSSL X509v3 Authority Key Identifier: keyid:00:0C:DD:F6:BD:66:22:72:82:DA:82:20:9C:54:E6:12:64:5E:90:15 DirName:/CN=sahara.local serial:01 Signature Algorithm: sha1WithRSAEncryption 5b:ae:8f:06:7f:07:a2:ff:25:f1:4e:b6:04:c7:9f:fb:e0:aa: 54:3d:b8:ab:54:a5:cf:e0:94:52:d1:e5:03:0e:8f:73:13:6f: 34:f4:e7:88:93:9e:69:df:43:c2:33:5d:cc:48:4b:2a:84:48: a4:a2:7d:e8:e1:82:7e:3e:a4:23:34:c9:72:f3:5b:52:c7:41: 6a:99:29:16:70:ee:bf:bd:d5:38:92:9b:0a:0d:9a:70:6f:a8: c3:18:60:df:73:45:41:29:3d:b3:6a:59:23:fe:1f:14:e0:cf: a1:db:09:50:06:7c:a3:81:95:c0:78:29:3c:71:ce:ac:aa:20: 98:06 [2016-06-01 14:10:36] INFO WEBrick::HTTPServer#start: pid=7246 port=8000 localhost - - [01/Jun/2016:14:10:55 JST] "GET / HTTP/1.1" 200 4257 - -> / ``` Webブラウザで接続しようとすると「この接続ではプライバシーが保護されません」(Chromeの場合)や「安全な接続ではありません」(Firefoxの場合)などの注意書きが表示されます. Chromeの場合は「詳細設定」をクリックすると出てくる「localhostにアクセスする(安全ではありません)」をクリックすることで,ページを表示できます. Firefoxの場合はちょっと複雑で,「エラー内容」をクリックすると出てくる「例外を追加」をクリックし,ダイアログ内の「セキュリティ例外を承認」をクリックすることで,このサイトを例外扱いできます.しかしその後,HTTPSサーバを再起動してしまうと,証明書が更新されてしまい接続を拒否されてしまいます(エラーコード: SEC_ERROR_REUSED_ISSUER_AND_SERIAL).この場合,Firefoxに登録されている証明書を削除する必要があります.詳しくは参考2をご覧ください. 上記の方法では証明書を自動的に作成していますが,既存の証明書を利用することもできます.ワンライナーと呼ぶには長くなりすぎるので割愛しますが,詳しくは参考1をごらんください. 参考1:[WEBrickでSSLを有効にする](http://labs.appshelf.info/2011/06/01/256/) 参考2:[他と同じシリアル番号をもつサーバ証明書](https://support.mozilla.org/ja/kb/%E4%BB%96%E3%81%A8%E5%90%8C%E3%81%98%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%AB%E7%95%AA%E5%8F%B7%E3%82%92%E3%82%82%E3%81%A4%E3%82%B5%E3%83%BC%E3%83%90%E8%A8%BC%E6%98%8E%E6%9B%B8) ## Ruby(Sinatra)編 Rubyには,Sinatraフレームワークがあり,こちらを使ってワンライナーでWebサーバを立てることもできます.起動例とアクセスログを示します.標準ポート番号は4567です.Sinatraの書式に則れば複雑なURLも処理してくれますが,以下の例だとindex.htmlを無視して"Hello World"を返してしまいます. ```bash:4567番ポートで待ち受けする例 $ ruby -rsinatra -e 'set :public_folder, "./", get("/"){"Hello world"}' == Sinatra/1.4.2 has taken the stage on 4567 for development with backup from Thin >> Thin web server (v1.5.1 codename Straight Razor) >> Maximum connections set to 1024 >> Listening on localhost:4567, CTRL+C to stop 127.0.0.1 - - [09/Jan/2014 13:56:05] "GET / HTTP/1.1" 200 11 0.0025 ``` ポート番号を指定する場合は,以下のようにしてください. ```bash:8000番ポートで待ち受けする例 $ ruby -rsinatra -e 'set :port, 8000; set :public_folder, "./", get("/"){"Hello world"}' ``` 参考:[ワンライナーでウェブサーバを起動する方法](http://mojavy.com/blog/2012/07/18/one-line-webserver/) ※この方法ではlocalhostからしか接続できないようです.外部から接続したい場合は以下のようにproduction環境であることを明示してください. ```bash:他のホストからも受け入れる例 ruby -rsinatra -e 'set :port, 8000; set :public_folder, "./"; set :environment, :producntion; get("/"){"Hello world"}' ``` 参考:[Sinatraがデフォルトでは外部から繋がらなくなってたよ](http://qiita.com/u1_fukui/items/b86b21f6ed39f4c10d5d) ## PHP編 PHP 5.4.0からWebサーバの機能が組み込まれるようになりました(tadsan様,情報提供ありがとうございました).ドキュメントによると,「デモやテストに使ってね」とのことですが,本投稿の用途にはピッタリです.以下の例では8000番ポートを利用するように起動しています.ホスト名やポート番号は省略できません. ```bash:localhostからのみ受け付ける例 $ php -S localhost:8000 PHP 5.4.17 Development Server started at Thu Jan 9 17:35:44 2014 Listening on http://localhost:8000 Document root is /hoge Press Ctrl-C to quit. [Thu Jan 9 17:35:48 2014] ::1:59299 [404]: / - No such file or directory ``` Ruby(Sinatra)編と同様,このままではlocalhost以外から接続できません.他のホストから接続したい場合はホスト名として以下のように「0.0.0.0」を指定して起動してください. ```bash:他のホストからも受け入れる例 $ php -S 0.0.0.0:8000 ``` 参考:[ビルトインウェブサーバー](http://www.php.net/manual/ja/features.commandline.webserver.php) ##Ruby(unライブラリ)編 これまでノーチェックだったのですが,Rubyでも簡単にWebサーバを立てることができます( yoshi-taka様,情報提供ありがとうございました).ドキュメントによるとUnixの基本コマンドの代替となるユーティリティであるunライブラリの一部で,実際に起動するのはWEBRickのようです.以下の例ではカレントディレクトリをドキュメントルートとし,8000版ポートを利用するように起動しています.アクセスログはベースとなっているWEBRickと同じですね.ポート番号を省略すると80番ポートで起動しようとします.先に紹介したRuby(WEBRick編)と比べて「コマンドが短い」「ctrl+Cで終了できる」ことが利点です. ```bash:8000版で待ち受けする例 $ ruby -run -e httpd . -p 8000 [2014-03-01 11:03:36] INFO WEBrick 1.3.1 [2014-03-01 11:03:36] INFO ruby 2.0.0 (2013-06-27) [universal.x86_64-darwin13] [2014-03-01 11:03:36] INFO WEBrick::HTTPServer#start: pid=825 port=8000 localhost - - [01/Mar/2014:11:03:49 JST] "GET /test.html HTTP/1.1" 200 94937 - -> /test.html ``` 参考:[library un - Ruby](http://docs.ruby-lang.org/ja/2.1.0/library/un.html) 参考:[httpd (Object) - APIdock](http://apidock.com/ruby/Object/httpd) ##Python(CGIHTTPServer)編 Python界隈ではスタティックなWebページだけでは満足できずに,CGIすらワンライナーで動かすことのできるライブラリが存在するようです(mash76様,情報提供ありがとうございました).ライブラリの名前ですが,その名もズバリCGIHTTPServerとのことです.調べてみるとSimpleHTTPServerを継承して,さらにCGIに対応したとのことです. それでは事前準備としてCGIスクリプトを用意します.CGIスクリプトはカレントディレクトリのcgi-binに入れておいてください.以下の例ではpythonを使っていますが,他の言語でも実行可能です. ```bash:CGIの準備 $ mkdir cgi-bin $ cd cgi-bin $ cat > index.py #!/usr/bin/python # -*- coding: utf-8 -*- ''' 文字列(hello world.)を表示する ''' print "Content-Type: text/plain" print "hello world." ^D (←コントロール+Dを押してください) $ chmod 755 index.py $ cd .. $ tree . └── cgi-bin └── index.py ``` それでは起動してみます.以下の例ではカレントディレクトリをドキュメントルートとして起動します.なお,ポート番号を省略すると8000番ポートを利用します.なお,ctrl+Cで修了できますが,終了時にエラーメッセージが出てくるので心の準備をしておいてください. ```bash:標準(8000番)ポートで待ち受けする例 $ python -m CGIHTTPServer Serving HTTP on 0.0.0.0 port 8000 ... 127.0.0.1 - - [02/Dec/2015 12:53:38] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [02/Dec/2015 12:53:39] code 404, message File not found 127.0.0.1 - - [02/Dec/2015 12:53:39] "GET /favicon.ico HTTP/1.1" 404 - 127.0.0.1 - - [02/Dec/2015 12:53:47] "GET /cgi-bin/index.py HTTP/1.1" 200 - ``` ポート番号を指定する場合は以下のようにしてください. ```bash:9000番ポートで待ち受けする例 $python -m CGIHTTPServer 9000 ``` ##Perl編 PerlでもワンライナーWebサーバがあるとの情報を頂きました(xtetuji様,情報提供ありがとうございました).個人的にPerlをほとんど使っていなかったので,インストール方法から記述したいと思います. ```bash:cpanの設定とPlackのインストール $ cpan (結構長いです.インストール先など,いくつか質問されるので適切に入力する.私は全て標準的な設定でよかったのでEnterを押しました.最後に起動スクリプトに環境変数を設定して良いか聞かれます.) $ sudo cpan Task::Plack (こちらも結構長いです.必要なモジュールなどをダウンロードします.待たされた後,標準的なインストールで良いか聞かれるので適切に答えてください.私はYを選択しました.また,最初にsudo無しで実行したらPermission Deniedと表示されたのでsudoを付けています.) ``` それでは5000番ポートで待ち受けするように実行してみましょう.ちょっとコマンドラインが長いですが,ドキュメントルートも容易に変更できます. ```bash:PerlによるワンライナーWebサーバの起動 $ plackup -MPlack::App::Directory -e 'Plack::App::Directory->new({root=>$ENV{PWD}})->to_app' HTTP::Server::PSGI: Accepting connections at http://0:5000/ 127.0.0.1 - - [02/Dec/2015:14:01:01 +0900] "GET / HTTP/1.1" 200 31580 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36" 127.0.0.1 - - [02/Dec/2015:14:01:02 +0900] "GET /favicon.ico HTTP/1.1" 404 9 "http://localhost:5000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36" ``` 参考:[Plack入門:Plackでウェブサーバを起動してhello worldするまで](http://dqn.sakusakutto.jp/2011/08/plackplackhello_world.html) ## node.js(http-server)編 ###その1. http-serverをグローバルインストール node.jsにもワンライナーWebサーバがあるようです.ということでhttp-serverを紹介します.node.jsのインストールが済んでいるものとして説明します.まずはhttp-serverのインストールからです. ```bash:グローバルインストールの例 $ npm install -g http-server ``` ドキュメントルートは,publicディレクトリが有ればそちらに,publicが無ければカレントディレクトリになります.今,下記のようなディレクトリ構成であるものとします. ```text:ディレクトリ構成の例 . └── public └── index.html ``` 上記の「.」に相当するディレクトリから,標準ポート(8080番)を利用するように起動します. ```bash:8080番ポートで待ち受けする例 $ http-server Starting up http-server, serving ./public Available on: http:127.0.0.1:8080 http:192.168.0.1:8080 (←localhost以外でも待ち受けします) Hit CTRL-C to stop the server [Wed, 02 Dec 2015 04:10:35 GMT] "GET /" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36" ``` ポート番号を変更するには```-p```オプションを利用します. ```bash:8000番ポートで待ち受けする例 $ http-server -p 8000 Starting up http-server, serving ./public Available on: http:127.0.0.1:8000 http:192/168/0.1:8000 Hit CTRL-C to stop the server ``` また,http-serverには起動と同時にWebブラウザを開くためのオプションが```-o```が有ります.以下のように実行すると,自動的にWebブラウザが開きます.(すでにWebブラウザが起動している場合,新しいタブで該当ページが開きます:chromeの場合) ```bash:ブラウザを同時に開く例 $ http-server -p 8000 -o Starting up http-server, serving ./public Available on: http:127.0.0.1:8000 http:172.16.11.3:8000 Hit CTRL-C to stop the server [Wed, 02 Dec 2015 05:10:59 GMT] "GET /" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36" [Wed, 02 Dec 2015 05:11:00 GMT] "GET /favicon.ico" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36" [Wed, 02 Dec 2015 05:11:00 GMT] "GET /favicon.ico" Error (404): "Not found" ``` 参考:[Node.jsのhttp-serverっていうコマンドラインのウェブサーバーが便利](https://firegoby.jp/archives/5706) ###その2. http-serverをローカルインストール 上の例ではhttp-serverをグローバルインストールしましたが,場合によってはローカルインストールで済ませたい場合があると思います.そのような人のために,ローカルインストールについても説明したいと思います.ローカルインストールでは,モジュールがカレントディレクトリのnode_modules以下にインストールされるので,環境を汚染しません.それではさっそくインストールしてみましょう. ```bash:http-serverをローカルインストール $ npm install http-server http-server@0.8.5 node_modules/http-server ├── opener@1.4.1 ├── corser@2.0.0 ├── colors@1.0.3 ├── http-proxy@1.12.0 (eventemitter3@1.1.1, requires-port@0.0.1) ├── optimist@0.6.1 (wordwrap@0.0.3, minimist@0.0.10) ├── ecstatic@0.7.6 (url-join@0.0.1, he@0.5.0, mime@1.3.4, minimist@1.2.0) ├── union@0.4.4 (qs@2.3.3) └── portfinder@0.4.0 (async@0.9.0, mkdirp@0.5.1) $ ``` 続けてWebサーバの起動です.8000番ポートを利用するようにしています. ```bash:http-serverの起動 $ ./node_modules/.bin/http-server -p 8000 Starting up http-server, serving ./ Available on: http:127.0.0.1:8000 http:192.168.0.1:8000 Hit CTRL-C to stop the server ``` コマンドが長いという人は,```npm bin```を併用してください(kaminaly様,ありがとうございました).なお,```npm bin```の利点として,サブディレクトリに潜っていてもきちんと実行できます.以下の例では,あえてカレントディレクトリをpublicにした上で実行しています. ```bash:npmコマンドを併用したhttp-serverの起動 $ cd public (←publicにcdしていても・・・) $ $(npm bin)/http-server -p 8000 (←実行可能.※本来はこの行のみで実行) Starting up http-server, serving ./ Available on: http:127.0.0.1:8000 http:192.168.0.1:8000 Hit CTRL-C to stop the server ``` ###その3. package.jsonを利用してローカルインストール 起動コマンドが長いという人は,以下の様なファイルを作成して,npmを活用してみてください.ポート番号を変更したい場合は6行目の-pの後ろの数値を変更してください. ```json:package.json { "name": "web", "version": "1.0.0", "description": "シンプルなWebサーバ", "scripts": { "start": "http-server -p 8000" }, "author": "suda", "license": "ISC", "dependencies": { "http-server": "^0.8.5" } } ``` npmを利用してhttp-serverをインストールします.package.jsonの置いてあるディレクトリ内で以下のコマンドを叩いてください. ```bash:package.jsonに従ってモジュールをインストール $ npm install npm WARN package.json web@1.0.0 No repository field. npm WARN package.json web@1.0.0 No README data http-server@0.8.5 node_modules/http-server ├── opener@1.4.1 ├── corser@2.0.0 ├── colors@1.0.3 ├── http-proxy@1.12.0 (eventemitter3@1.1.1, requires-port@0.0.1) ├── optimist@0.6.1 (wordwrap@0.0.3, minimist@0.0.10) ├── union@0.4.4 (qs@2.3.3) ├── portfinder@0.4.0 (async@0.9.0, mkdirp@0.5.1) └── ecstatic@0.7.6 (url-join@0.0.1, he@0.5.0, mime@1.3.4, minimist@1.2.0) $ ``` 続けてnpmを利用してWebサーバを起動します.package.jsonの"script"の"start"の項目が実行されます.なお,自動的に~~./node_modules/<パッケージ名>/binにパスが通った状態で~~```./node_modules/.bin```にパスが通った状態で実行されます(kaminaly様,ありがとうございました). ``` $ npm start > web@1.0.0 start /home/suda > http-server -p 8000 Starting up http-server, serving ./public Available on: http:127.0.0.1:8000 http:192.168.0.1:8000 Hit CTRL-C to stop the server ``` package.jsonを作ってnpmで管理するのはいささか大げさな気がしますが,環境を破壊せずに使える上,初心者にとっては覚えることが少ないので良いという利点もあります. ## node.js(serve)編 http-serverに似たモジュールとしてserveがあることを教えていただきました(kaminaly様,ありがとうございました),ということでさっそく試してみようと思います.ここではグローバルインストールのみ説明していますが,ローカルインストールやpackage.jsonによるローカルインストールも可能です. ```bash:グローバルインストールの例 $ npm install -g serve ``` 続けて,カレントディレクトリをドキュメントルートとして,標準ポート(3000番)を利用するように起動します. ```bash:3000番ポートで待ち受けする例 $ serve serving /home/suda on port 3000 GET / 200 13ms - 132 ``` ポート番号を変更するには```-p```オプションを利用します. ```bash:8000番ポートで待ち受けする例 $ serve -p 8000 serving /home/suda on port 8000 ``` 参考:[serve](https://www.npmjs.com/package/serve) ## ncコマンド編 ncコマンドはTCPやUDPを利用してコマンドラインからデータを送受信するツールです.このコマンドを使うことで,簡易的なWebサーバを立てることが可能です(と言いつつtukiyo3様のコメントを見て初めて知りました.ご指摘ありがとうございました). まずは返信する文字列をワンライナー内で指定する場合の実行方法です.返信する文字列を変更したい場合は\n\nの後ろを変更してください.以下の例では"hello"を返します. ```bash:8000番ポートで待ち受けする例 $ while :; do { echo -e 'HTTP/1.1 200 OK\n\nhello'; } | nc -l 8000; done ``` ファイルの内容を返信したい場合は,以下のように実行してください.以下の例ではindex.htmlの内容を返信します. ```bash:返信するファイルを指定する場合 $ while :; do { echo -e 'HTTP/1.1 200 OK\n\n'; cat index.html; } | nc -l 8000; done ``` 参考1によると,Ctrl+Cでは停止できないとのことでしたが,手元のMacOS Xのbash上で実行したところ,停止できました(何故かzsh上で実行した場合は停止できませんでした).確実に停止するようにしたい場合は同記事をご覧ください. 参考1:[nc コマンドで簡易HTTPサーバ](http://blog.livedoor.jp/sonots/archives/34703829.html) 参考2:[すぐに簡易Webサーバを起動したいとき](http://dev.classmethod.jp/etc/webserver/) |
|
| 203位 |
|
|||
|
13:38:32 |
|
|
## direnvとは?
ディレクトリ毎に環境変数を定義して、そのディレクトリがカレントになった時だけ環境変数を有効にしてくれるツール。開発中のアプリ毎に環境変数を変えたい時に重宝する。 リポジトリはこちら https://github.com/zimbatm/direnv direnvは今話題のgoで作成されているみたいですね。 ### インストール方法 ``` git clone http://github.com/zimbatm/direnv cd direnv sudo make install ``` Mac上でHomebrewを利用しているのであれば、```brew install direnv``` でいいです。 #### shellにhookを追加する ##### zsh ```shell-session:~/.zshrc export EDITOR=お使いのエディタ eval "$(direnv hook zsh)" ``` ### 使い方 環境変数を設定したいディレクトリにcdし、 ```shell-session:zsh direnv edit . ``` すると、カレントディレクトリに.envrcが作成され、設定した環境変数が追記されます。 例えばカレントの"bin"をPATHに追加したいのであれば、下記のように環境変数を宣言しましょう。 ```shell-session:.envrc export PATH=bin:$PATH ``` または、ショートカットで指定できます。 ```shell-session:.envrc PATH_add bin ``` 自動的に環境変数が読み込まれます。 #### エラーが発生する場合 ``direnv: error .envrc is blocked. Run `direnv allow` to approve its content.`` というエラーが発生する場合は(エラーメッセージにある通り)以下のコマンドを実行してください。 ``` direnv allow ``` ## 注意 このファイルをリポジトリに追加し、githubにpushしてしまうと環境変数に設定したパスワード等が漏れてしまいます。`.gitignore`に`.envrc`を追加することをオススメします。 |
|
| 204位 |
|
|||
|
13:21:33 |
(HiganWorks LLC. 所属) |
|
<!-- permanent -->
`Infrastracture as code`流行の副産物として、もうOpsはある程度Rubyでライブラリを書けるようになりました。 折角Rubyでライブラリを書いたなら、安全&ラクに配布するためGemパッケージにしましょう、出来る人には今更でしょうが知らない人は真似してみてね。 ちなみに意外と誤解されてる点、`gemにする=Rubygems.orgで公開する`、ではありません、してもOKというだけで。 ## 目標 gemファイルを置いて、gemコマンドで自分のライブラリをサーバに導入する。 こんなかんじで。 `gem install -l my_libs-0.0.1.gem` じゃあやってみましょう。 ## Gemの雛形をつくろう Gemの作り方は色々あるようですが、私はもっぱらbundlerです。 `bundle gem`で必要なファイル群を作成します、便利ですね。 ```shell:Shell $ bundle gem my_libs create my_libs/Gemfile create my_libs/Rakefile create my_libs/LICENSE.txt create my_libs/README.md create my_libs/.gitignore create my_libs/my_libs.gemspec create my_libs/lib/my_libs.rb create my_libs/lib/my_libs/version.rb ``` 作られたファイルで、触るのはこれだけ。 |ファイル名|役割| |---|---| |my_libs.gemspec|gemの説明と、依存する他のrubygemsがあればそれを記述します。設定ファイルのコメントくらいに捉えていればOK。| |lib/my_libs/version.rb|このgemバージョンを書きます、zoneファイルのシリアルよろしく何かする度適当に増やしていきましょう。| |lib/my_libs.rb|自前の関数をここに書きましょう。| メインは`lib/my_libs.rb`です。 ## 概要をgemspecに 初めにこのGemが何するものぞを記述する必要があります、特に難しいことではありません。 `my_libs.gemspec`の初期状態はこのようになっています。 ```ruby:my_libs.gemspec(initial) # coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'my_libs/version' Gem::Specification.new do |spec| spec.name = "my_libs" spec.version = MyLibs::VERSION spec.authors = ["sawanoboly"] spec.email = ["sawanoboriyu@higanworks.com"] spec.description = %q{TODO: Write a gem description} spec.summary = %q{TODO: Write a gem summary} spec.homepage = "" spec.license = "MIT" spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency "rake" end ``` 親切なことに**TODO**がありますね、`my_libs.gemspec`この2行を編集します。 ```ruby:my_libs.gemspec spec.description = %q{TODO: Write a gem description} spec.summary = %q{TODO: Write a gem summary} ``` `description`と`summary`は意味的にそのままですが、今回は特に区別しません。短めにgemの説明を書いておきましょう。 ```ruby:my_libs.gemspec spec.description = %q{ruby sample libs for me} spec.summary = %q{ruby sample libs for me} ``` ### 依存するRubyGems 自前ライブラリ内で他のgemを使いたいことがある場合は`my_libs.gemspec`に追加しておきます。 RubyGems.orgで公開されている`twitter`が必要なら適当な箇所に1行この様に書けばOK、`gem install`実行時に警告を出してくれます。 ```ruby:my_libs.gemspec spec.add_dependency 'twitter' ``` 今回は特に何も追加せずに先へいきましょう。 ## my_libを読み込んでみる この時点ですでに他のgemのように機能します、中身はカラですが読み込んでみましょう。 Rubyの対話型インタプリタ`Irb`を起動します。 ```shell:Irb $ bundle exec irb > require 'my_libs' => true > MyLibs MyLibs > MyLibs::VERSION => "0.0.1" ``` ## ライブラリを書こう では`my_libs/lib/my_libs.rb`に自前ライブラリを書いて行きましょう、初期状態は下記です。 ```ruby:my_libs/lib/my_libs.rb require "my_libs/version" module MyLibs # Your code goes here... end ``` こちらもわかりやすく**`# Your code goes here…`**となっています。 サンプルとして外から呼べる`hello`とinclude/extend用の`hello2`メソッドを実装します。 ```ruby:my_libs/lib/my_libs.rb require "my_libs/version" module MyLibs def self.hello 'hello gem!' end def hello2 'hello gem! 2' end end ``` モジュールを記述したので、先程の要領で読み込んで使っていきましょう。 ## ライブラリのメソッドを実行しよう ```shell:Irb $ bundle exec irb > require 'my_libs' => true > MyLibs.methods(false) => [:hello] > MyLibs.hello => "hello gem!" > include MyLibs => Object > self.methods.include?(:hello2) => true > hello2 => "hello gem! 2" ``` `require 'my_libs'`から後なら`MyLibsモジュール`が使えるようになっていますね。 これを`gem install`で使えるようにするために、gemパッケージにします。 ## Gemをビルドしよう `bundle gem`で作った雛形にはgemパッケージ用のタスクが含まれています。 ```shell:Shell $ rake -vT rake build # Build my_libs-0.0.1.gem into the pkg directory. rake install # Build and install my_libs-0.0.1.gem into system gems. rake release # Create tag v0.0.1 and build and push my_libs-0.0.1.gem to Rubygems ``` このうち`rake build`がgemファイルを作るタスクです、早速実行してみます。 ```shell:Shell $ rake build my_libs 0.0.1 built to pkg/my_libs-0.0.1.gem. $ ls pkg/ my_libs-0.0.1.gem ``` `pkg/`以下にgemファイルができました。`my_libs-0.0.1.gem`とバージョン情報が付与されてますね。 試しにバージョンを更新してみましょう、ライブラリは特に変更せず。 ```ruby:my_libs/lib/my_libs/version.rb module MyLibs VERSION = "0.0.2" end ``` 再び`rake build` ```shell:Shell $ rake build my_libs 0.0.2 built to pkg/my_libs-0.0.2.gem. $ ls pkg/ my_libs-0.0.1.gem my_libs-0.0.2.gem ``` 目論見通り`my_libs-0.0.2.gem`となりました。 これでGemファイルが作れるようになったので、配布したいサーバに適当な手段で持っていきましょう。 ## 作ったGemをインストールしよう ローカルのgemファイルをインストールするには、`gem install`時に`-l`オプションでファイル名を指定します。 ※ 依存にRubyGemsで公開されているGemがある時は、`-b` オプションにしましょう。 ```shell:Shell $ gem install -l pkg/my_libs-0.0.2.gem -V Installing gem my_libs-0.0.2 PATH_TO_RUBY/gems/my_libs-0.0.2/.gitignore PATH_TO_RUBY/gems/my_libs-0.0.2/Gemfile PATH_TO_RUBY/gems/my_libs-0.0.2/LICENSE.txt PATH_TO_RUBY/gems/my_libs-0.0.2/README.md PATH_TO_RUBY/gems/my_libs-0.0.2/Rakefile PATH_TO_RUBY/gems/my_libs-0.0.2/lib/my_libs.rb PATH_TO_RUBY/gems/my_libs-0.0.2/lib/my_libs/version.rb PATH_TO_RUBY/gems/my_libs-0.0.2/my_libs.gemspec Successfully installed my_libs-0.0.2 1 gem installed ``` 一旦インストールしてしまえばあとは通常のgemsと扱いは変わりません。 ```shell:Shell $ gem list | grep my_libs my_libs (0.0.2) ``` ついでに実行テストをしてみます。 ```shell:Shell $ ruby -e "require 'my_libs'; puts MyLibs.hello" hello gem! ``` できましたね。 ## 最後に 一つ注意点として、作成するmodule,classの一番親に来る名前は他と被らないようにしましょう。 これはOKですが、 ```ruby:sample.rb module MyOriginModule class File # hogehogehoge end end ``` この様にド頭に一般名称などを持ってくると他のモジュールとミックスされて、**色々と他人がヒドイ目に遭う**のでお気をつけて。 ```ruby:sample.rb class File # hogehogehoge end ``` |
|
| 205位 |
|
|||
|
05:40:05 |
|
|
この記事は、2013年12月20日に書きました。 ・2014年5月15日にzshの設定にpreztoの事も名前だけ紹介しました。 ・2014年5月15日に[.zshrc読み込むたびにPATHが増えていく場合の対処法](#2-5)を追加しました。 #■まえがき ##自分なりのzshを入れるメリット。 + 入力補完が優れてる ←゚+。゚(・∀・)゚。+゚イイ!! + 非標準コマンド(brew、gitとか)の入力補完 ←゚+。゚(・∀・)゚。+゚イイ!! + カラーリングがbashより見やすい ←゚+。゚(・∀・)゚。+゚イイ!! + bashより処理が高速 + 同時に立ち上げてるターミナル間でのhistory共有 + [時間のかかる処理が終わったら通知](http://qiita.com/takc923/items/75d67a08edfbaa5fd304 "時間のかかる処理が終わったら通知") + gitのカレントブランチの表示 ←゚+。゚(・∀・)゚。+゚イイ!! + 履歴機能が強力(改行も記録される) ←゚+。゚(・∀・)゚。+゚イイ!! 2014年1月号のソフトウェアデザイン誌にシェル特集がありオススメです。 ※尚、私の環境はMacOSX 10.9.1です ※日本語入力が勝手に変換されるの問題は私は起きませんでした(古いPCだけ?) ※インストール前にbrew infoを叩くの推奨(内容を知って導入したいですよね) #■brew update[蛇足] 本題とは関係ありませんが、 最新のzshインストール前にbrew udpateを行いました。 その際に出たエラーの解決を下記に投稿しました。 興味があればドゾ☆(ゝω・)v [brew updateでエラーでたった[Homebrew]](http://qiita.com/harapeko_wktk/items/f4f44ddb5d3912e15ea2 "brew updateでエラーでたった[Homebrew]") #■インストール プリインスコされてるzsh(4.3.11)ではなく、最新のを入れようじゃなイカ? ```Bash $ brew install zsh …省略… /usr/local/Cellar/zsh/5.0.3: 1063 files, 8.8M, built in 5.7 minutes ``` インストールはこれだけで終わり! イイネ♪d(‘∀’o) #■ログインシェルをzshにする ##インストールされてるシェルとパスの確認! ```Bash:ターミナル $ cat /etc/shells /bin/bash /bin/csh /bin/ksh /bin/sh /bin/tcsh /bin/zsh ``` ##シェルのパスを追加! homebrewでインストールしたものは、 /usr/localにシンボリックリンクが貼られるんでしたね。 今回インストールした「/usr/local/bin/zsh」のパスを追加しましょう 実際はceller(貯蔵庫)に入っています。 シンボリックリンクのパスの切り替えでバージョン管理も実現しているんですね。 ```Bash:ターミナル $ vi /etc/shells /bin/bash /bin/csh /bin/ksh /bin/sh /bin/tcsh /bin/zsh /usr/local/bin/zsh ``` ##デフォルトのシェルを変更! ```Bash:ターミナル $ chsh -s /usr/local/bin/zsh # パスワードを入れれば反映 ```` *参考:*[ログインシェルの変更 | UNIXコマンド コマンドの機能と使用方法](http://www.k-tanaka.net/unix/chsh.php "ログインシェルの変更 | UNIXコマンド コマンドの機能と使用方法") ###チラシの裏 ログインシェル=ユーザ認証が行われた後、デフォルトで起動されるデフォルトのシェル #■設定 ホームディレクトリに.zshrcを作成。 設定ファイルみたいなものです。 誰かが作った設定を手軽に導入できる「oh-my-zsh」っていうプラグインもあるようですが、好みのthemeが無かったので止めました。 本記事では出てきませんがzshの設定としてprezto良さそうです。紹介だけしておきます。私もそのうち使ってみたいです。 [oh-my-zshを使っていて重いと感じている方、preztoはいかが?](http://qiita.com/kei_q/items/814ec412b3c28f580927 "oh-my-zshを使っていて重いと感じている方、preztoはいかが?") ひとまず、 [少し凝った zshrc](https://gist.github.com/mollifier/4979906 "少し凝った zshrc") を拝借させて頂きました。 これを使ってオレオレにしていきましょう。 ```Bash:ターミナル $ vi ~/.zshrc ``` ```i``` を押して入力モード。 [少し凝った zshrc](https://gist.github.com/mollifier/4979906 "少し凝った zshrc") からコピペする。 下記コマンドのどちらかで反映させて下さい。 ```Bash:ターミナル source ~/.zshrc //設定ファイルの読み直す exec $SHELL //シェル再起動する ←オヌヌメ ``` 反映終了゚.+:。(・ω・)b゚.+:。グッ ##.zshrc読み込むたびにPATHが増えていく場合の対処法 結論として、 `source ~/.zshrc`で読み込むと現在PATHに追加する記述になってる時、2回目以降の読み込みで増える。 なので、 `exec $SHELL` または、 `exec $SHELL -l`を使うのがよさそうです。 ``` source ~/.zshrc //設定ファイルの読み直す exec $SHELL //シェル再起動する exec $SHELL -l //設定ファイルにあるPATHだけ上書きする ``` ```PATHがどんどん増える理由 .zshrcを読み込むたびにPATHがどんどこ増えていく場合には、 .zshrcの中に次のような記述がないかを疑ってください。 `PATH=$HOME/.cabal/bin:$PATH` このような記述があるとPATHの中身は読み込むたびに 「追加+現在」 「追加+「追加+現在」」 のように増えていきます。 設定変更がある度に読み込んで、意図しないものがどんどこ増えていきます。 現在のPATH確認は、シェルから`echo $PATH`です。 これを元に重複、不要なPATHを削除した上で、下記のように`PATH=整理したPATH`で読みこめば直ると思います。 (※要注意:下記のPATHは私のものなので、コピペするとぶっ壊れます:(;゙゚'ω゚'): ぶっ壊れたら、さっき確認した「自分のPATH」を読み込んであげる事で直ります) `PATH=/bin:/usr/bin:/usr/local/bin:/usr/bin:/bin/:Users/harapeko/.nodebrew/current/bin:/Users/harapeko/.cabal/bin:` ``` #■zsh-completionsインスコ 非標準コマンドを補完できるようになる素敵なプラグイン 入れない手はない。 ##インスコ ```Bash:ターミナル $ brew install zsh-completions ```` インスコおしまい ##設定 .zshrc内の「autoload -Uz compinit」より前に次を追記 brew infoに書いてある。 ``` #for zsh-completions fpath=(/usr/local/share/zsh-completions $fpath) ``` zsh補完情報の再構築を行う brew infoに書いてある。 ```Bash:ターミナル $ rm -f ~/.zcompdump; compinit ``` なんか聞かれたら[y]でcontinueおk 起動の度にこの質問聞かれるので、 .zshrcの中身を修正。 ```compinit -u```とすればおk! ```# 補完機能を有効にする autoload -Uz compinit compinit -u ``` 反映の呪文 ```Bash:ターミナル $ source ~/.zshrc ``` 終わったら、ターミナルを再起動。 ```vagrant [Tab]```とかした時にサブコマンドのリストがずらっと並ぶ gitのカレントブランチも表示されている。 ┬|´ω`*)。oO(ヤッタ♪) #■幸せになれた? すんごく幸せです//// 少し早いですが2014年もよろしくお願いします┏○))ペコ #■参考サイト + [初めて Mac で zsh を使う人のためのチュートリアル](http://mollifier.hatenablog.com/entry/2013/02/22/025415 "初めて Mac で zsh を使う人のためのチュートリアル") + [少し凝った zshrc](https://gist.github.com/mollifier/4979906 "少し凝った zshrc") + [iTerm2 + zshで時間のかかる処理が終わったらGrowlに通知したりアラートダイアログ出したり音出したりする方法](http://qiita.com/takc923/items/75d67a08edfbaa5fd304 "iTerm2 + zshで時間のかかる処理が終わったらGrowlに通知したりアラートダイアログ出したり音出したりする方法") + [ログインシェルをzshに換えた | 黄昏てなんかいられない](http://kronus9.sblo.jp/article/70293394.html "ログインシェルをzshに換えた | 黄昏てなんかいられない") + [zshに引越しだ!!for Mac](http://www.absolute-keitarou.net/blog/?p=452 "zshに引越しだ!!for Mac") + [oh-my-zshを使っていて重いと感じている方、preztoはいかが?](http://qiita.com/kei_q/items/814ec412b3c28f580927) |
|
| 206位 |
|
|||
|
20:17:39 |
|
|
あんまり知識ないけどがんばる
`SQL`にも存在する様だけど, `MySQL`でしか経験がないから`MySQL`で書くよ # 対象 以下に該当する方が対象です。 ある程度, SQLクエリが読める ある程度, DBの知識がある # 背景 資料を探せど探せど, (個人的に)良いドキュメントが見つからなかったので 『じゃあ書いちゃおう』と思いました。 ※悪い資料だらけってわけじゃないけど, 物足りないとか惜しい記事ばかりだったので。 # ストアドプロシージャって? DB上での一連処理に, 名前をつけて関数のように, 呼び出して使用できるもの。 DB上で動作を完結させちゃうから, 開発言語に依存しないよ! `Ruby` とか `PHP` だとか `Perl` でも `Python` だろうと CALL できれば結果は同じになるはずだよ! # 権限まわり ## 作成 ```mysql CREATE ROUTINE ``` ## 変更 ```mysql ALTER ROUTINE ``` ## 実行 ```mysql EXECUTE ``` # さんぷる ## ①超初級 手始めに簡単なものから。 * 現在時間を表示 ```mysql CREATE PROCEDURE sample01() SELECT NOW(); ``` ```shell-session:実行結果 mysql> CALL sample01; +---------------------+ | NOW() | +---------------------+ | 2014-03-01 00:00:00 | +---------------------+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec) ``` ## ②引数 関数として使うから入力( 引数 )も欲しいよね。 * 入力 ( `x` ) に `+1` して結果を表示 ```mysql CREATE PROCEDURE sample02( IN x INT ) SELECT x + 1; ``` ```shell-session:実行結果 mysql> CALL sample02( 2 ); +-------+ | x + 1 | +-------+ | 3 | +-------+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec) ``` ### ※引数が増えたら... `IN` 句を増やすだけだよ。 ```mysql CREATE PROCEDURE sample02_sub( IN x INT, IN y INT ) SELECT x + y; ``` ## ③戻り値( 返り値 ) 出力( 戻り値 )も欲しいよね。 * 出力 ( `3` ) を `@x` に保存。 ```mysql CREATE PROCEDURE sample03( OUT x INT ) SET x = 3; ``` ```shell-session:実行結果 mysql> CALL sample03( @x ); Query OK, 0 rows affected (0.00 sec) mysql> SELECT @x; +------+ | @x | +------+ | 3 | +------+ 1 row in set (0.00 sec) ``` ### ※戻り値を増やしたい... `IN` と同じく, `OUT` 句を増やすだけだよ。 # 設定周り ## ①一覧の表示 一覧を見たい時ってあるよね。 * 定義済プロシージャの一覧を表示 ```mysql SHOW PROCEDURE STATUS; ``` ```shell-session:実行結果 mysql> SHOW PROCEDURE STATUS; +------+-----------------+-----------+----------------+---------- | Db | Name | Type | Definer | Modified +------+-----------------+-----------+----------------+---------- | test | sample01 | PROCEDURE | root@localhost | 2014-03-0 | test | sample02 | PROCEDURE | root@localhost | 2014-03-0 | test | sample02_sub | PROCEDURE | root@localhost | 2014-03-0 | test | sample03 | PROCEDURE | root@localhost | 2014-03-0 +------+-----------------+-----------+----------------+---------- 4 rows in set (0.00 sec) ``` ## ②確認 あのプロシージャの中身って, どうしてたっけ?ってとき * 定義済プロシージャの詳細を表示 ```mysql SHOW CREATE PROCEDURE sample01; ``` ```shell-session:実行結果 mysql> SHOW CREATE PROCEDURE sample01; +-----------+----------+----------------------------------------- | Procedure | sql_mode | Create Procedure +-----------+----------+----------------------------------------- | sample01 | | CREATE DEFINER=`root`@`localhost` PROCED +-----------+----------+----------------------------------------- 1 row in set (0.00 sec) ``` ## ③削除 このプロシージャ, もういらないよね。 * 定義済プロシージャを削除 ```mysql DROP PROCEDURE sample01; ``` ```shell-session:実行結果 mysql> DROP PROCEDURE sample01; Query OK, 0 rows affected (0.00 sec) mysql> SHOW CREATE PROCEDURE sample01; ERROR 1305 (42000): PROCEDURE sample01 does not exist ``` # おうよう ## ①複数定義 セミコロン( `;` )で定義が完了しちゃうから, 複数のクエリが発行できないよね。 そんな時は `DELIMITER` を使って, 区切り文字を一時的に変更! ( 変更した区切り文字は, 戻してあげないと変更されたままだよ ) `BEGIN` ~ `END` 内に, 複数行のプロシージャを記述しよう。 * `1` と `2` を表示するプロシージャ ```mysql DELIMITER // CREATE PROCEDURE sample01() BEGIN SELECT 1; SELECT 2; END // DELIMITER ; ``` ```shell-session:実行結果 mysql> CALL sample01; +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec) +---+ | 2 | +---+ | 2 | +---+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec) ``` ## ②分岐 入力値で分岐とかしたいね。 基本的な `IF (条件) THEN` ~ `ELSEIF (条件) THEN` ~ `ELSE` ~ `END IF` 文だよ。 * 入力値 ( `x` ) によって条件分岐した結果を表示 ```mysql DELIMITER // CREATE PROCEDURE sample02( IN x INT ) BEGIN IF x = 1 THEN SELECT "input is 1"; ELSEIF x = 2 THEN SELECT "input is 2"; ELSE SELECT "input is else"; END IF; END // DELIMITER ; ``` ```shell-session:実行結果 mysql> CALL sample02( 2 ); +------------+ | input is 2 | +------------+ | input is 2 | +------------+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec) ``` ## ③SELECT ~ INTO ~ 計算結果とか欲しいよね。 `SELECT` ~ `INTO` ~ 文で, `INTO` の後ろに指定した変数に, `SELECT` 結果をセットできるよ! `SELECT ~ INTO ~ FROM ~ WHERE ~ ` みたいに, 通常の `SELECT` 文と同じ動きをするよ。 * 入力値 ( `x` , `y` ) の 和 と 差 を 変数へ出力 ```mysql DELIMITER // CREATE PROCEDURE sample03( IN x INT, IN y INT, OUT sum INT, OUT sub INT ) BEGIN SELECT x + y INTO @sum; SELECT x - y INTO @sub; SET sum = @sum; SET sub = @sub; END // DELIMITER ; ``` ```shell-session:実行結果 mysql> CALL sample03( 10, 1, @x, @y ); Query OK, 1 row affected (0.00 sec) mysql> SELECT @x; +------+ | @x | +------+ | 11 | +------+ 1 row in set (0.00 sec) mysql> SELECT @y; +------+ | @y | +------+ | 9 | +------+ 1 row in set (0.00 sec) ``` ## ④DECLARE, CURSOR, FETCH レコード単位で処理したい時ってあるよね! `DECLARE` で 変数宣言 するよ。 `CURSOR` 型 っていう, レコード用変数定義を `DECLARE ~ CURSOR` ってふうに宣言できるよ! 続けて `FOR SELECT ~` で, `SELECT` 結果を定義するよ! 読み出しは, `FETCH ~ CURSOR FOR ~` って記述すると, 各カラムに代入できるよ。 ただし, カーソルは `FETCH` する前に `OPEN` してあげる必要があって, 処理が終わったら `CLOSE` してあげよう! * 処理テーブル ( `test_tbl` ) から, 指定したID( `in_id` ), 文字列( `in_str` )を探索 * 指定ID ( `in_id` ) か 指定文字列( `in_str` )が見つかれば, 若いID と, その文字列を返す * 見つからなければ, `0` と `空文字( '' )` を返す ```shell-session:処理テーブル mysql> select * from test_tbl; +----+-----+ | id | str | +----+-----+ | 1 | aa | | 2 | bb | | 3 | cc | | 4 | dd | +----+-----+ 4 rows in set (0.00 sec) ``` ```mysql DELIMITER // CREATE PROCEDURE sample04 ( IN in_id INT, IN in_str VARCHAR(10), OUT out_id INT, OUT out_str VARCHAR(10) ) BEGIN # 変数宣言! DECLARE myId INT; # id カラムの格納用だよ DECLARE myStr VARCHAR( 10 ); # str カラムの格納用だよ DECLARE myCur CURSOR FOR SELECT id, str FROM test_tbl ; # test_tbl の SELECT 結果を格納する カーソルだよ # ループ用に変数宣言! SET @pos = 0; # 何レコード目か SELECT COUNT(*) INTO @total FROM test_tbl; # 全部で何レコードか SET out_id = 0; # 入力ID ( in_id )が見つからなかった時のための宣言 SET out_str = ''; # 入力文字列( in_str )が見つからなかった時のための宣言 # カーソル開けごま! OPEN myCur; WHILE @total > @pos DO # 1レコード読みだして, 各変数へ代入 FETCH myCur INTO myId, myStr; # 入力ID か 入力文字列 が見つかったら, 出力値として設定 IF myId = in_id OR myStr = in_str THEN SET out_id = myId; SET out_str = myStr; SET @pos = @total; # WHILE を抜け出すために設定! END IF; # 見つからなかったら, @posが増えるね! SET @pos = @pos +1; END WHILE; # カーソルありがとう! CLOSE myCur; END // DELIMITER ; ``` ```shell-session:実行結果:入力値見つかったVer. mysql> CALL sample04( 4, 'cc', @id, @str ); Query OK, 0 rows affected (0.01 sec) mysql> SELECT @id; +------+ | @id | +------+ | 3 | +------+ 1 row in set (0.00 sec) mysql> SELECT @str; +------+ | @str | +------+ | cc | +------+ 1 row in set (0.00 sec) ``` ```shell-session:実行結果:入力値なかったよVer. mysql> CALL sample04( 0, 'ee', @id, @str ); Query OK, 0 rows affected (0.00 sec) mysql> SELECT @id; +------+ | @id | +------+ | 0 | +------+ 1 row in set (0.00 sec) mysql> SELECT @str; +------+ | @str | +------+ | | +------+ 1 row in set (0.00 sec) ``` ## ⑤例外処理 エラーなんて出してなんぼだよね!← MySQL上にも例外処理の考えがあるよ! `DECLARE ~ HANDLER FOR ~` って宣言を使うよ! `FOR` 以降の条件をハンドラがキャッチして, `DECLARE` ~ `HANDLER` 間に記述した ハンドラタイプ で, 処理を実行しちゃいます! ハンドラタイプには次の2つがるよ。 | ハンドラタイプ | 処理内容 | |:-----|:-----| | CONTINUE | 実行制御を元の場所に戻す | | EXIT | プログラムを終了 | キャッチするハンドラは, 主に次の3パターンじゃないかな?と思ってる! | ハンドラ | 処理内容 | |:-----|:-----| | SQLWARNING | '01'で始まるSQLSTATE | | NOT FOUND | '02'で始まるSQLSTATE | | SQLEXCEPTION | '02', '01' 以外で始まるSQLSTATE | SQLSTATEについては『[ここ](http://msdn.microsoft.com/ja-jp/library/ms714687.aspx "Microsoft")』とか『[ここ](http://dev.mysql.com/doc/refman/4.1/ja/error-returns.html "MySQLリファレンス")』とか『[ここ](http://software.fujitsu.com/jp/manual/manualfiles/M060051/J2X01637/02Z200/sqlbgab/sqlbg092.html "富士通")』を参考にするといいかも。 ④で使用した, 処理テーブル( `test_tbl` )を使って例示するね! ### ●サンプルA * 処理テーブル ( `test_tbl` ) から, 指定したID( `in_id` )を探索 * 指定ID ( `in_id` )が見つかれば, そのIDを返す * 見つからなければ, `-2`を返す ```mysql DROP PROCEDURE sample05a; DELIMITER // CREATE PROCEDURE sample05a ( IN in_id INT, OUT num INT ) BEGIN # ハンドラの宣言! DECLARE EXIT HANDLER FOR NOT FOUND SET num = -2; # SELECT 結果が見つからなかった( NOT FOUND )場合 # 『 SET num = -2 』を実行して, 処理を終了( EXIT ) する # という宣言 # @num に SELECT 結果を代入 SELECT id INTO @num FROM test_tbl WHERE id = in_id; # in_idが見つかれば, 見つかったIDが, # 見つからなかった場合は -2 が 返ります。 SET num = @num; END // DELIMITER ; ``` ```shell-session:実行結果:入力値見つかったVer. mysql> CALL sample05a( 4, @x ); Query OK, 1 row affected (0.00 sec) mysql> SELECT @x; +------+ | @x | +------+ | 4 | +------+ 1 row in set (0.00 sec) ``` ```shell-session:実行結果:入力値なかったよVer. mysql> CALL sample05a( 0, @x ); Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> SELECT @x; +------+ | @x | +------+ | -2 | +------+ 1 row in set (0.00 sec) ``` ### ●サンプルB * 処理テーブル ( `test_tbl` ) から, 指定したID( `in_id` )を探索 * 指定ID ( `in_id` )が見つかれば, そのIDを `-1` へ更新し, `-1` を返す * 見つからなければ, `0` を返す ※前提条件として, `test_tbl` の `id` カラムは `UNSIGNED INT` とします ```mysql DELIMITER // CREATE PROCEDURE sample05b ( IN in_id INT, OUT num INT ) BEGIN # ハンドラの宣言! DECLARE EXIT HANDLER FOR SQLWARNING SET num = -1; # 警告 が 発生( SQLWARNING )した場合 # 『 SET num = -1 』を実行して, 処理を終了( EXIT ) する # という宣言 # 警告が発生する前に戻り値を宣言しておきます SET num = 0; # 指定ID ( in_id ) の id を -1 へ変更するというクエリ UPDATE test_tbl SET id = -1 WHERE id = in_id; END // DELIMITER ; ``` ```shell-session:実行結果:入力値見つかったVer. mysql> select * from test_tbl; +----+-----+ | id | str | +----+-----+ | 1 | aa | | 2 | bb | | 3 | cc | | 4 | dd | +----+-----+ 4 rows in set (0.00 sec) mysql> CALL sample05b( 1, @x ); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> SELECT @x; +------+ | @x | +------+ | -1 | +------+ 1 row in set (0.00 sec) mysql> select * from test_tbl; +----+-----+ | id | str | +----+-----+ | 0 | aa | | 2 | bb | | 3 | cc | | 4 | dd | +----+-----+ 4 rows in set (0.00 sec) ``` ```shell-session:実行結果:入力値なかったよVer. mysql> select * from test_tbl; +----+-----+ | id | str | +----+-----+ | 1 | aa | | 2 | bb | | 3 | cc | | 4 | dd | +----+-----+ 4 rows in set (0.00 sec) mysql> CALL sample05b( 6, @x ); Query OK, 0 rows affected (0.00 sec) mysql> SELECT @x; +------+ | @x | +------+ | 0 | +------+ 1 row in set (0.00 sec) mysql> select * from test_tbl; +----+-----+ | id | str | +----+-----+ | 1 | aa | | 2 | bb | | 3 | cc | | 4 | dd | +----+-----+ 4 rows in set (0.00 sec) ``` ### ●サンプルC SQLSTATE を直に指定する方法もあるよ! `DECLARE ~ HANDLER FOR SQLSTATE 'xxxxx'` って感じで宣言してね! * 処理テーブル ( `test_tbl` ) に, 指定したID( `in_id` )と文字列 `**` を新規登録 * 指定ID ( `in_id` )が重複していれば `-3` を返す * 登録に成功すれば, `0` を返す ```mysql DELIMITER // CREATE PROCEDURE sample05c ( IN in_id INT, OUT num INT ) BEGIN # ハンドラの宣言! DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET num = -3; # 重複キーエラー が 発生( SQLSTATE '23000' )した場合 # 『 SET num = -3 』を実行して, 処理を続行( CONTINUE ) する # という宣言 # 警告が発生する前に戻り値を宣言しておきます SET num = 0; # 指定ID ( in_id ) の レコード を 追加するクエリ INSERT INTO test_tbl VALUES ( in_id, '**' ); END // DELIMITER ; ``` ```shell-session:実行結果:重複しちゃったVer. mysql> select * from test_tbl; +----+-----+ | id | str | +----+-----+ | 1 | aa | | 2 | bb | | 3 | cc | | 4 | dd | +----+-----+ 4 rows in set (0.00 sec) mysql> CALL sample05c( 3, @x ); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> SELECT @x; +------+ | @x | +------+ | -3 | +------+ 1 row in set (0.00 sec) mysql> select * from test_tbl; +----+-----+ | id | str | +----+-----+ | 1 | aa | | 2 | bb | | 3 | cc | | 4 | dd | +----+-----+ 4 rows in set (0.00 sec) ``` ```shell-session:実行結果:登録に成功Ver. mysql> select * from test_tbl; +----+-----+ | id | str | +----+-----+ | 1 | aa | | 2 | bb | | 3 | cc | | 4 | dd | +----+-----+ 4 rows in set (0.00 sec) mysql> CALL sample05c( 6, @x ); Query OK, 0 rows affected (0.00 sec) mysql> SELECT @x; +------+ | @x | +------+ | 0 | +------+ 1 row in set (0.00 sec) mmysql> select * from test_tbl; +----+-----+ | id | str | +----+-----+ | 1 | aa | | 2 | bb | | 3 | cc | | 4 | dd | | 6 | ** | +----+-----+ 5 rows in set (0.00 sec) ``` ## ⑥宣言順序 変数, カーソル, ハンドラの宣言を同時に行う際は注意が必要だよ! 変数, カーソル, ハンドラの順に宣言しないとエラーになるから気をつけてね! ```shell-session:宣言に失敗ver. mysql> DELIMITER // mysql> CREATE PROCEDURE sample06 ( OUT num INT ) -> BEGIN -> -> #カーソル宣言! -> DECLARE myCur CURSOR FOR SELECT * FROM test_tbl ; -> -> # ハンドラの宣言! -> DECLARE EXIT HANDLER FOR SQLWARNING SET num = -1; -> -> # 変数宣言! -> DECLARE myId INT; -> -> # 戻り値を宣言 -> SET num = 0; -> END -> // ERROR 1337 (42000): Variable or condition declaration after cursor or handler declaration ``` ```shell-session:宣言に成功ver. mysql> DELIMITER // mysql> CREATE PROCEDURE sample06 ( OUT num INT ) -> BEGIN -> -> # 変数宣言! -> DECLARE myId INT; -> -> #カーソル宣言! -> DECLARE myCur CURSOR FOR SELECT * FROM test_tbl ; -> -> # ハンドラの宣言! -> DECLARE EXIT HANDLER FOR SQLWARNING SET num = -1; -> -> # 戻り値を宣言 -> SET num = 0; -> END -> // Query OK, 0 rows affected (0.00 sec) ``` 以上です! 何かありましたらコメントください。 |
|
| 207位 |
|
|||
|
15:47:31 |
|
|
Railsアプリで検索機能を実装するケースは非常に多いと思います。
簡単な検索であればwhereとLIKEを使って書けますし、やや複雑なものも[everywhere](https://github.com/amatsuda/everywhere)が便利ですが、ここではもっと複雑な条件の組み合わせを実装する時に便利な[ransack](https://github.com/ernie/ransack)を紹介します。 ## 基本 searchメソッドで条件を指定し、resultメソッドで結果を返します。 ```Ruby Item.search(:name_cont => 'ほげ').result ``` resutはActiveRecord::Relationを返すので、SQLは普通のActiveRecord同様遅延評価されますし、さらにwhereを繋げたり、kaminariでページングしたりすることもできます。また、to_sqlで発行されるSQLを確認することもできます。 もう少し詳しく書くと、searchはModelまたはActiveRecord::RelationをレシーバにしてRansack:Searchを返し、Ransack:search#resultはActiveRecord::Relationを返します。 ## searchの書き方 searchメソッドは一種のDSLになっています。 属性をpredicate(述語)で繋いだ文字列のシンボルをキーに、検索対象を値にして書いていきます。 前述の例だと「name属性に'ほげ'という文字列を含む(countain)」対象を検索します。以下のようなSQLが発行されます。 ```Ruby Item.search(:name_cont => 'ほげ').result.to_sql # => "SELECT `items`.* FROM `items` WHERE `items`.`name` LIKE '%ほげ%')" ``` 以下のようなpredicateがあります。 ### eq 等しいものにマッチします。 ```Ruby Item.search(:name_eq => 'ほげ').result.to_sql # => "SELECT `items`.* FROM `items` WHERE `items`.`name` = 'ほげ')" ``` 単体だとwhereで十分なので意味がありませんが、複数の条件を動的に組み合わせる際に便利です。 not_eqで「等しくないもの」になります。 ### lt ある値より小さいものにマッチします。 ```Ruby Item.search(:price_lt => 1000).result.to_sql # => "SELECT `items`.* FROM `items` WHERE `items`.`price` < 1000)" ``` lteqだと「以下」になります。 ### gt ある値より大きいものにマッチします。 ```Ruby Item.search(:price_gt => 1000).result.to_sql # => "SELECT `items`.* FROM `items` WHERE `items`.`price` > 1000)" ``` gteqだと「以上」になります。 ### in SQLのinです。含まれるものにマッチします。 ```Ruby Item.search(:category_id_in => [5,10,15,20]).result.to_sql # => "SELECT `items`.* FROM `items` WHERE `items`.`category_id` IN (5,10,15,20))" ``` not_inで「含まれないもの」になります。 ### cont 前述したように「文字列が含まれるもの」にマッチします。 not_contで「文字列が含まれないもの」になります。 ### start 「特定の文字列が先頭のもの」にマッチします。 ```Ruby Item.search(:name_start => 'ほげ').result.to_sql # => "SELECT `items`.* FROM `items` WHERE `items`.`name` LIKE 'ほげ%')" ``` endで「特定の文字列が末尾のもの」になります。 predicateはこの他にもあるので、[wiki](https://github.com/ernie/ransack/wiki/Basic-Searching)を参考にするといいでしょう。 ## 組み合わせ 複数の属性を組み合わせることができます。 その場合、andかorで繋ぎます。 ```Ruby Item.search(:name_and_description_cont => 'ほげ').result.to_sql # => "SELECT `items`.* FROM `items` WHERE (((`items`.`name` LIKE '%ほげ%') AND (`items`.`description` LIKE '%ほげ%')))" Item.search(:name_or_description_cont => 'ほげ').result.to_sql # => "SELECT `items`.* FROM `items` WHERE (((`items`.`name` LIKE '%ほげ%') OR (`items`.`description` LIKE '%ほげ%')))" ``` ## 複数条件 条件を組み合わせることができます。 末尾に_allを付けると全ての条件にマッチ、_anyだといずれかにマッチするものを検索します。 ```Ruby Item.search(:name_cont_all => ['ほげ', 'ひげ']).result.to_sql # => "SELECT `items`.* FROM `items` WHERE (((`items`.`name` LIKE '%ほげ%') AND (`items`.`name` LIKE '%ひげ%')))" Item.search(:name_cont_any => ['ほげ', 'ひげ']).result.to_sql # => "SELECT `items`.* FROM `items` WHERE (((`items`.`name` LIKE '%ほげ%') OR (`items`.`name` LIKE '%ほげ%')))" ``` ## 関連モデルの検索 関連先を条件に含めることができます。 ```Ruby Item.search(:comments_body_cont => 'ほげ').result.to_sql # => "SELECT `items`.* FROM `items` LEFT OUTER JOIN `item_comments` ON `item_comments`.`item_id` = `items`.`id` WHERE `item_comments`.`body` LIKE '%ほげ%'" ``` ## 注意点 存在しないカラムを指定すると例外を返さず無視されます。 ```Ruby Item.search(:unknownattribute_cont => 'ほげ').result.to_sql # => "SELECT `items`.* FROM `items`" ``` 動的に組み立てる際には便利ですが、実装せずにテストが通ってしまったりするのでテストコードにマッチ・アンマッチ双方のテストを書くなど工夫しましょう。 |
|
| 208位 |
|
|||
|
17:50:38 |
(pixiv Inc. 所属) |
|
こんにちはこんにちは
私は日々大量のサーバーで作業をする必要があるので tmux が欠かせません そんな中最高便利な記事が先日公開されました [Tmuxでウィンドウをインタラクティブに移動する - Qiita [キータ]](http://qiita.com/t_cyrill/items/c7e3e2187d0fce3b9856) しかしこの記事が全く話題になっていません おそらく理解されていないのだと思います ということで私がもう少し詳しく説明したいと思います 先程の記事と同様に * [ssh-configにはパターンが使えるので便利 - Qiita [キータ]](http://qiita.com/t_cyrill/items/437091856f6d9f6dae1f) * [tmuxで色んなホストにsshする時に便利な.ssh/config - Qiita [キータ]](http://qiita.com/catatsuy/items/631d1d5d0b357082ba74) の合計 3 記事を組み合わせて初めて達成できる最高のソリューションを紹介します ## tmux のウィンドウの名前 tmux で大量のウィンドウを立ち上げて ssh しているとどのウィンドウがどこのホストにいるのか分からなくなります そこで先程紹介した 2 記事です * [ssh-configにはパターンが使えるので便利 - Qiita [キータ]](http://qiita.com/t_cyrill/items/437091856f6d9f6dae1f) * [tmuxで色んなホストにsshする時に便利な.ssh/config - Qiita [キータ]](http://qiita.com/catatsuy/items/631d1d5d0b357082ba74) 例えばサーバーが a0, a1, b0, c0 のようにアルファベット一文字+数字 1 桁のような命名規則であった場合は `.ssh/config` を以下のようにします ```:ssh-config Host a? b? c? PermitLocalCommand yes LocalCommand tmux rename-window %n ``` このようにすると ssh した際に tmux のウィンドウの名前がサーバーのホスト名に変わります `Host *` のようにすると `git pull` など全く関係ないところで動くのでパターンを使って確実にサーバーのホストにだけ当てるのがポイントです ## select-window で確実に指定ホストに移動する tmux のウィンドウの名前にホスト名を表示できるようにしても,結局指定ホストに移動するには `choose-window` などを使って選ぶしかありません `choose-window` はデフォルトで `bind-key w` で見れますが,とはいえ量が多いと大変手間です ここで `select-window` の登場です [Tmuxでウィンドウをインタラクティブに移動する - Qiita [キータ]](http://qiita.com/t_cyrill/items/c7e3e2187d0fce3b9856) この記事の通り,適当なキーバインドで `select-window` を使えるようにします ```.tmux.conf unbind-key s bind-key s command-prompt "select-window -t '%%'" ``` 元から `bind-key s` に割り当てられているものを調べたい場合は以下の記事のように `list-keys` を使います [tmux のキーバインドを調べる方法 - Qiita [キータ]](http://qiita.com/catatsuy/items/5c80ff1f15bb226640eb) このように設定することで __ウィンドウの名前を直接指定してそのウィンドウに移動ができます__ 実例を見せます  サーバー a0, a1, b0, c1 にログインしている状況で現在 b0 で作業しています そこで `bind-key s[Enter] a0` と入力するだけで a0 に移動することが出来ます 最初に紹介した手法を使えば ssh するだけでウィンドウの名前を確実にホスト名に変えておいてくれます また頭文字数文字を打つだけでも名前が一つに限定できるならそのウィンドウに移動することが出来ます `bind-key ,` を使えばウィンドウの名前は変えられるのですぐに飛びたい特殊なウィンドウにはユニークな名前を付けるのも良いと思います 今回は 4 つだけでしたが,これが画面に収まる量を超えるとこれがないと作業が極めて困難になります 一つ一つの威力は小さいですが,紹介した 3 つ全てを組み合わせるとかなり強力な味方になってくれると思いますので皆さんも是非試してみてください! 最高便利!! |
|
| 209位 |
|
|||
|
20:56:44 |
|
|
QiitaではGoogleアナリティクスのトラッキングコードを埋め込むことができます。
それにより、自分の投稿や、マイページ、ストックページなどがどの程度見られているかを把握することができるようになります。 この記事では、Googleアナリティクスでトラッキングコードを取得して、それをQiitaに登録し、確認するまでの手順を解説します。 # 1. (事前準備)Googleアナリティクスのアカウントを持っていない場合は作る  [Googleアナリティクス](http://www.google.com/analytics/) へアクセスし、画面右上のリンクからアカウントを作ってください。  そして、Googleアナリティクスの **[お申込み]** をクリックします。 # 2. 新しいアカウントを作る ログインしたら、アカウント作成ページが表示されるので入力していきます。  **トラッキングの対象**は、 **「ウェブサイト」** に設定して、アカウント名はお好きな名前を入力してください。  続いて画面をおりていくとウェブサイトの名前とURLを入力する欄があるのでそれぞれ **「Qiita」** **「 [http://qiita.com](http://qiita.com) 」**と入力します。タイムゾーンには **「日本」**を選択します。業種は適当に選んでください。 最後に画面最下部にある **[トラッキングIDの取得]** をクリックします。  画面に **トラッキングID** ( `UA-XXXXX-Y` というやつです)が表示されれば完了です。続いてQiitaにこのトラッキングIDを登録します。 # 3. 新しく作ったトラッキングIDをQiitaへ登録する [Googleアナリティクス設定](https://qiita.com/settings/analytics) へアクセスして下さい。画面の下部にアナリティクスコードを設定するフォームがあるので、そこに先ほど作ったプロパティIDを登録して保存します。 # 4. 正しく設定できたか確認する  QiitaでアナリティクスのIDを登録できたら試しにマイページを表示してみましょう。 **[マイページ]**へは画面右上のリンクから辿ることができます。 そして、画面を表示したらGoogleアナリティクスで先ほど作ったプロパティの **[リアルタイム] > [サマリー]** を見てみてください。  ブラウザをリロードしてみて、画像のように棒がニョキッと現れたら正しく設定できています。 # Googleアナリティクスでエラーが表示される場合  Googleアナリティクスの **[プロパティの設定]** から **デフォルトのURL** を自分のユーザーページ(例 http://qiita.com/Qiita )に変更して保存してください。 |
|
| 210位 |
|
|||
|
23:24:45 |
(Increments inc. 所属) |
|
- [jkbr/httpie : Github](https://github.com/jkbr/httpie) : HTTPie is a CLI, cURL-like tool for humans
curl(<del>see urlと発音するらしい</del>[本家のFAQ](http://curl.haxx.se/docs/faq.html#What_is_cURL)によると開発陣は *kurl* と発音してるらしいです)はプログラムから使うには便利だけど、オプションがわかりにくい。 httpieはより直感的なcurl代替コマンド。よほどcurlに思い入れがない限りhttpieをおすすめする。 ##インストール ```sh easy_install httpie # OR pip install -U httpie ``` ##使用例  奥がcurlで、手前がhttpieを使った場合。見れば分かるようにhttpieは自動で色付けをしてくれるし、コマンドもかなり直感的だ。内部的にはPythonのrequestsというモジュールを使っている。Python組み込みのHTTPクライアントに辟易している人はそちらも調べてみるととても幸せになれる。 - [kennethreitz/requests : Github](https://github.com/kennethreitz/requests) : Python HTTP Requests for Humans™ |
|
| 211位 |
|
|||
|
09:59:57 |
(IMJ Corp. 所属) |
|
# Jadeの記法について(あまりまとまっていない)
- node.js ver.0.10.24 - npm ver.1.3.21 - Jade ver 1.1.4 ## タグの記述と構造化 タグ名を書いて、インデントで構造を表す。 ```jade:in div ul li い li ろ li は ``` ```html:out <div> <ul> <li>い</li> <li>ろ</li> <li>は</li> </ul> </div> ``` インデントには半角スペースかタブが使用できる。が、両方を使うとコンパイルできずにエラーになる。 --- `:`を使用するとインデントせずに、同じ行に再度タグを記述できる。 ```jade:in footer: p: small Qiita ``` ```html:out <footer> <p><small>Qiita</small></p> </footer> ``` --- テキストを複数行記述する場合、`|`か`.`を使用する。 ```jade:in p | aaa | bbb | ccc p. ddd eee fff ``` ```html:out <p> aaa bbb ccc </p> <p> ddd eee fff </p> ``` --- 直にHTMLを書くこともできる。 ```jade:in footer p <a href="//qiita.com/">Qiita</a> ``` ```html:out <footer> <p><a href="//qiita.com/">Qiita</a></p> </footer> ``` ### 属性 丸括弧で囲う。 ```jade:in a(href="//google.com/", target="_blank") google.com ``` ```html:out <a href="//google.com/" target="_blank">google.com</a> ``` --- 属性を改行して書くこともでき、コンマも省略できる。 ```jade:in a(href="//google.com/", target="_blank") google.com a( href="//google.com/" target="_blank") google.com ``` ```html:out <a href="//google.com/" target="_blank">google.com</a><a href="//google.com/" target="_blank">google.com</a> ``` ### classとid classを付加するにはタグ名の後に`.`を、idを付加するにはタグ名の後に`#`を書き、それの名前を書く。 ```jade:in h1#title.class1.class2 title ``` ```html:out <h1 id="title" class="class1 class2">title</h1> ``` --- タグ名を省略して記述した場合は、`div`タグにclassもしくはidが付与されて出力される。 ```jade:in #frame .inline ``` ```html:out <div id="frame"> <div class="inline"></div> </div> ``` --- class属性のみ、配列をそのまま受け取れる。 ```jade - var classes = ['class1', 'class2', 'class3']; div(class=classes) div(data-class=classes) ``` ```html:out <div class="class1 class2 class3"></div> <div data-class='["class1","class2","class3"]'></div> ``` --- `false`や`null`、`undefined`もしくは定義されていない変数が属性に指定された場合、出力されない。 ```jade:in a(href="//google.com/", title=false) google a(href="//google.com/", title=null) google a(href="//google.com/", title=undefined) google a(href="//google.com/", title=unknown) google a(href="//google.com/", title=(typeof unknown !== 'undefined')) google ``` ```html:out <a href="//google.com/">google</a> <a href="//google.com/">google</a> <a href="//google.com/">google</a> <a href="//google.com/">google</a> <a href="//google.com/">google</a> ``` ### 単独タグ 独自の単独タグを記述する場合は、タグ名の最後に`/`を書く。 ```jade:in special:tag(option="1")/ special:tag/(option="1") ``` ```html:out <special:tag option="1"/> <special:tag option="1"/> ``` どちらも同じ出力になるが、vim-jadeだと後者は属性が強調表示されなくなるため前者の方が良いか? `br`や`hr`などは自動で単独タグ扱いされるため、`/`を書く必要はない。 - [jade/lib/self-closing.js](https://github.com/visionmedia/jade/blob/96a6b0a9057534fc02c86f1c84e1cdd1a54602c2/lib/self-closing.js) ## JavaScriptでの制御 `-`で直接JavaScriptを書くことが出来る。 ```jade:in - for (var i = 0; i < 3; i++) { li list - } ``` ```html:out <li>list</li> <li>list</li> <li>list</li> ``` ```jade:in - var objs = []; - objs.push({name: "a", value: "1"}); - objs.push({name: "b", value: "2"}); - objs.push({name: "c", value: "3"}); - for (var i = 0; i < objs.length; i++) { p #{objs[i].name}: #{objs[i].value} - } ``` ```html:in <p>a: 1</p> <p>b: 2</p> <p>c: 3</p> ``` ## DOCTYPE宣言 `doctype xxx`と記述する。 ```jade:in doctype doctype html doctype xml doctype strict ``` ```html:out <!DOCTYPE html> <!DOCTYPE html> <?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> ``` - [jade/lib/doctype.js](https://github.com/visionmedia/jade/blob/96a6b0a9057534fc02c86f1c84e1cdd1a54602c2/lib/doctypes.js) 以前使用できた、`!!! 5`や`doctype 5`はコンパイルできずにエラーになる。 ## コメント コメントは`//`で書く。 ```jade:in // コメントだよ // コメント コメント コメント ``` ```html:out <!-- コメントだよ--> <!-- コメント コメント コメント --> ``` --- HTMLに出力しないコメントを記述する場合は`//-`を使用する。 ```jade:in //- 出力されない div ``` ```html:out <div></div> ``` --- 条件付きコメントの機能は削除された? ```jade:in //if lt IE 8 script(src="http://ajax.aspnetcdn.com/ajax/modernizr/modernizr-2.7.1.js") ``` ```html:out <!--if lt IE 8script(src="http://ajax.aspnetcdn.com/ajax/modernizr/modernizr-2.7.1.js") --> ``` - [Conditional Comments](https://github.com/visionmedia/jade/issues/1345) 直接書けということか。 ```jade:in <!--[if lt IE 8]> script(src="http://ajax.aspnetcdn.com/ajax/modernizr/modernizr-2.7.1.js") <![endif]--> ``` ```html:out <!--[if lt IE 8]> <script src="http://ajax.aspnetcdn.com/ajax/modernizr/modernizr-2.7.1.js"></script><![endif]--> ``` ## 変数展開 タグ名のあとに`=`もしくは`!=`でそのまま変数を展開できる。`!=`の場合はエスケープされない。 ```jade:in - var text = "<span>Qiita</span>"; p= text p!= text ``` ```html:out <p><span>Qiita</span></p> <p><span>Qiita</span></p> ``` --- 文字列中に展開したい場合は`#{}`もしくは`!{}`を使用すると展開できる。`!{}`の場合はエスケープされない。 ```jade:in - var text = "<span>Qiita</span>"; p #{text}:Team p !{text}:Team ``` ```html:out <p><span>Qiita</span>:Team</p> <p><span>Qiita</span>:Team</p> ``` そのままJavaScriptの式が書けるので変数展開、というよりかは式展開か。 --- 式がそのまま書けるので、展開せずとも`+`でそのまま連結するなどの方法もある。 ```jade:in - var domain = 'google.com' a(href="//#{domain}/") google a(href="//" + domain + "/") google ``` ```html:out <a href="//google.com/">google</a> <a href="//google.com/">google</a> ``` ## フィルタ `:`につづいて`markdown`などと記述すると、そのブロックではJade以外の記法で記述することが出来る。 Markdownフィルタを使用する場合は`marked`などのモジュールがインストールされている必要がある。 ```jade:in body :markdown # h1 Markdown filter footer ``` ```html:out <body><h1 id="h1">h1</h1> <p>Markdown filter</p> <footer></footer> </body> ``` ## インクルード `include ファイル名`で他のファイルを挿入することができる。拡張子を省略した場合は`ファイル名.jade`と見なしてそのファイルを読み込みに行く。 ```jade:_template.jade p before ``` ```jade:in include _block p _template p after ``` ```html:out <p>before</p> <p>_template</p> <p>after</p> ``` --- HTMLをそのまま挿入することもできる。 ```html:_block.html <p>_block</p> ``` ```jade:in p before include _block.html p after ``` ```html:out <p>before</p> <p>_block</p> <p>after</p> ``` --- `yield`と書いておくと間にコンテンツを挿入することができる。 ```jade:_block.jade yield p 1 ``` ```jade:in include _block p 0 ``` ```html:out <p>0</p> <p>1</p> ``` --- データのバインド(?)もできる。 ```jade:twitter.jade p= twitter.name p= twitter.description ``` ```jade:in - var twitterData = [{name: "sasaplus1", description: "JavaScript, node.js, Bash, Delphi"}]; each twitter in twitterData include twitter ``` ```html:out <p>sasaplus1</p> <p>JavaScript, node.js, Bash, Delphi</p> ``` ### フィルタ付きインクルード `include`の後に`:フィルタ名`を記述すると、ファイルをフィルタで変換したものを挿入できる。 この場合も適用したいフィルタのモジュールがインストールされている必要がある。 ```markdown:text.txt # h1 ## h2 ``` ```jade:in p before include:markdown text.txt p after ``` ```html:out <p>before</p> <h1>h1</h1> <h2>h2</h2> <p>after</p> ``` --- 拡張子の自動判別は無くなった? ```markdown:text.md # h1 ``` ```jade:in p before include text.md p after ``` ```html:out <p>before</p> # h1 <p>after</p> ``` --- ## ブロック インクルードされるファイルに`block ブロック名`を記述し、インクルードする側で`include`でなく`extends`で読み込んだあと同じブロック名で異なる内容を書くと、ブロックの内容を上書きできる。 ```jade:_block.jade header ヘッダ block content p コンテンツ footer フッタ ``` ```jade:in extends _block block content #content Qiitaだよー ``` ```html:out <header>ヘッダ</header> <div id="content">Qiitaだよー</div> <footer>フッタ</footer> ``` ### append/prepend `block`でなく`prepend`とすると指定したブロックの前に挿入でき、`append`とすると指定したブロックの後に挿入できる。 ```jade:_block.jade header ヘッダ block content p コンテンツ footer フッタ ``` ```jade:in extends _block prepend content #prepend 前に挿入される append content #append 後ろに挿入される ``` ```html:out <header>ヘッダ</header> <div id="prepend">前に挿入される</div> <p>コンテンツ</p> <div id="append">後ろに挿入される</div> <footer>フッタ</footer> ``` ## mixin `mixin`で関数のような形でブロックを生成できる。 ```jade:in mixin title-block(title, date) h1= title p= date || new Date mixin title-block("Hello, World!", "Tue Jan 14 2014 00:00:00 GMT+0900 (JST)") mixin title-block("Hello, World!") ``` ```html:out <h1>Hello, World!</h1> <p>Tue Jan 14 2014 00:00:00 GMT+0900 (JST)</p> <h1>Hello, World!</h1> <p>Tue Jan 14 2014 00:12:39 GMT+0900 (JST)</p> ``` --- 展開する際は`mixin`の代わりに`+`が使用できる。 ```jade:in mixin title-block(title) h1= title +title-block("Hello, World!") ``` ```html:out <h1>Hello, World!</h1> ``` --- `attributes`でclass/idや属性を受け取ることができる。(あまり良くわかっていない) `+`を使用した場合と`mixin`を使用した場合で挙動が異なるのだが…… ```jade:in mixin title-block div(id=attributes.id, class=attributes.class) p Qiita +title-block#special-id.class1.class2.class3 ``` ```html:out <div id="special-id" class="class1 class2 class3"> <p>Qiita</p> </div> ``` --- ```jade:in mixin title-block div(id=attributes.id, class=attributes.class) p Qiita mixin title-block#special-id.class1.class2.class3 ``` ```html:out <div> <p>Qiita</p> </div> <div id="special-id" class="class1 class2 class3"></div> ``` --- `attributes`と引数との違いがあまりよくわかっていない。どういう風に使い分けるのか。 ```jade:in mixin content(text) div(id=attributes.id, class=attributes.class) a(href=attributes.href, title=attributes.title)= text +content("google")#frame-google.frame(title="Google", href="//google.com/") ``` ```html:out <div id="frame-google" class="frame"><a href="//google.com/" title="Google">google</a></div> ``` --- `mixin`内では`arguments`も使用できる。 ```jade:in mixin title-block ul - for (var i = 0; i < arguments.length; i++) { li= arguments[i] - } mixin title-block(1) mixin title-block(1, 2) mixin title-block(1, 2, 3) ``` ```html:out <ul> <li>1</li> </ul> <ul> <li>1</li> <li>2</li> </ul> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> ``` ## if/unless `if`もしくは`unless`や`else`で条件分岐ができる。 ```jade:in - var flag = true; if flag p trueです else p falseです ``` ```html:in <p>trueです</p> ``` ```jade:in - var flag = true; unless flag p trueです else p falseです ``` ```html:out <p>falseです</p> ``` --- `else if`も使用できる。 ```jade:in - var flag = 1 if flag === 0 p 0です else if flag === 1 p 1です else p 0でも1でもありません ``` ```html:out <p>1です</p> ``` ## case-when `case`でも条件分岐ができる。条件は`when`の後に記述し、どれにも当てはまらない場合は`default`を書く。 ```jade:in - var flag = 1 case flag when 0 p 0です when 1 p 1です default p 0でも1でもありません ``` ``` <p>1です</p> ``` --- `when 値`のあとに`:`を記述することで、一行で書くことができる。また、条件の値は`,`で複数の値を指定できる。 ```jade:in - var flag = 2 case flag when 0: p 0です when 1, 2: p 1か2です when 3: p 3です ``` ```html:out <p>1か2です</p> ``` ## while `while`で繰り返しを記述できる。 ```jade:in - var i = 3 while i-- p= i ``` ``` <p>2</p> <p>1</p> <p>0</p> ``` ## for-in, each-in `for-in`または`each-in`でも繰り返しが記述できる。`for`でも`each`でも動作は同じ。 ループでは変数の値と、変数がオブジェクトの場合はキーを、変数が配列の場合はインデックスが取得できる。キー、もしくはインデックスが必要ない場合は変数名の記述を省略できる。 ```jade:in ul for val, index in ['a', 'b', 'c'] li #{index}: #{val} ul each val, key in {key1: 'a', key2: 'b', key3: 'c'} li #{key}: #{val} ``` ```html:out <ul> <li>0: a</li> <li>1: b</li> <li>2: c</li> </ul> <ul> <li>key1: a</li> <li>key2: b</li> <li>key3: c</li> </ul> ``` ## 参考 - http://jade-lang.com/ - https://github.com/visionmedia/jade/blob/96a6b0a9057534fc02c86f1c84e1cdd1a54602c2/jade.md - https://github.com/visionmedia/jade/blob/96a6b0a9057534fc02c86f1c84e1cdd1a54602c2/jade-language.md - http://nextdeveloper.hatenablog.com/entry/2013/12/10/180457 - http://blog.craftgear.net/4f501e97c1347ec934000001/title/10%E5%88%86%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8Bjade%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3 - http://blog.kazupon.jp/post/19007475354/jade-template-inheritance - http://blog.kazupon.jp/post/38877362879/jade-mixin-tips - http://rtshaaaa.hateblo.jp/entry/2013/12/20/133054 - https://gist.github.com/kazupon/4379815 |
|
| 212位 |
|
|||
|
15:28:44 |
(pixiv Inc. 所属) |
|
dstatは便利なオプションがたくさんあるのでみんなオレオレdstatをつくろう。
## 最後まで読むのがめんどくさい人向け ### なんか重くなってるっぽい ``` $ sudo dstat -ta --top-cpu ``` ### なんかIOが刺さってるっぽいとき ``` $ sudo dstat -ta --top-io-adv --top-bio-adv ``` ### とりあえず全部みたい ``` $ sudo dstat -af ``` ### vmstatが好きだ ``` $ dstat -v ``` ### 詳しく知りたい ``` $ man dstat ``` ## よく使うオプション 下に基本的なオプションを並べてみた。複数使うときは`dstat -tcdn`みたいな感じで指定する。ロングオプションも使えて`dstat --time --cpu --disk`みたいにできるけど長いので使ったことない。 | | 説明 | |--:|:--| | -a | よく使うオプション詰め合わせ<br>`-cdngy`と等価でオプションをつけないと勝手にこれになる。 | | -t | 時間を表示する。<br>どのタイミングかわからなくなることがあるのでいつもつけている。| | -T | epoch timeを表示する<br>epoch timeで出ているログと併せてみたいときとかに便利。 | | -c | CPU使用率を表示する<br>基本。 | | -d | Disk IOを表示する<br>基本。 | | -g | ページIN/OUTを表示する<br>-sと併せてつかったりする。 | | -i | 割り込みを表示する<br>あまり使ったことないけど、特定番号の割り込みを監視したいときには便利かな。 | | -l | ロードアベレージを表示する<br>ロードの数値だけで負荷を評価することがあまりないので使わない。 | | -m | メモリの使用量を表示する<br>すごい勢いでfreeが減っていくときにとか。 | | -n | ネットワークIOを表示する<br>基本。単位はB/s。 | | -p | プロセス数を表示する<br>forkしてプロセスが増減するときに監視するときとかに使えるかも。 | | -s | スワップのused/freeを表示する<br>swapが特定タイミングで増えてしまうときとかに使う。 | | -y | 割り込み回数とコンテキストスイッチの回数を表示する<br>あまり使ったことない…… | ## CPUコア、ディスク、ネットワークインターフェース毎に見る 最近のWebサーバーだと、(仮想化マシンだと普通だけど)シングルコアCPUは逆に珍しいし、ディスクもHDDとSSDが載ってたり、eth0とeth1があったり、コアそれぞれのstatとかインターフェースごとのstatをみたいことは割とよくある。 そういうときには、`-C`、`-D`、`-I`、`-N`といったオプションを使う。それぞれ`-c`、`-d`、`-i`、`-n`オプションに対応していて、カンマ区切りで指定する。また合計値は`total`を使って見られる。 例えば、4コアのマシンでCPUコアそれぞれ毎に見たいときは次のようになる。 ``` $ dstat -c -C 0,1,2,3,total # CPUコア0,1,2,3と合計を表示 ``` eth1だけを見たい時とかは次のようになる。 ``` $ dstat -n -N eth1 # eth1のstatだけを表示 ``` インターフェースが複数あるんだよ、全部指定するのめんどくさいよ、って時は`-f`オプションが便利。ただし`total`の値は見られないので、`total`を見たい時はめんどくさいけどいっこいっこ指定する必要がある。 ``` $ dstat -af # とりあえず全部みたい ``` ## ログをファイルに保存する dstat中のログを保存するには`--output`オプションを使う。すると各値がCSV形式で保存される。 ``` $ dstat --output dstat.csv ```` 保存されるログは次のような書式だ。 ``` $ cat dstat.csv "Dstat 0.7.2 CSV output" "Author:","Dag Wieers <dag@wieers.com>",,,,"URL:","http://dag.wieers.com/home-made/dstat/" "Host:","debian7",,,,"User:","harukasan" "Cmdline:","dstat --output dstat.csv",,,,"Date:","31 Oct 2013 15:18:21 JST" "total cpu usage",,,,,,"dsk/total",,"net/total",,"paging",,"system", "usr","sys","idl","wai","hiq","siq","read","writ","recv","send","in","out","int","csw" 2.291,0.227,97.373,0.004,0.001,0.105,37.173,12668.059,0.0,0.0,0.0,0.0,505.675,275.612 0.500,0.0,99.500,0.0,0.0,0.0,0.0,69632.0,134756.0,71311.0,0.0,0.0,228.0,218.0 1.250,0.250,98.500,0.0,0.0,0.0,0.0,0.0,243280.0,99172.0,0.0,0.0,204.0,156.0 0.750,0.0,98.750,0.0,0.0,0.500,0.0,0.0,793178.0,47389.0,0.0,0.0,365.0,148.0 3.250,0.0,96.500,0.0,0.0,0.250,0.0,0.0,2018204.0,36349.0,0.0,0.0,784.0,300.0 ``` ## 便利なプラグインを活用する dstatはプラグインを組み込むことができ、標準でもいくつかプラグインが入っている(と思う)。これらを使うと様々な情報をdstatにまとめて表示できる。dstatはPythonで書かれているので、Pythonで簡単にプラグインを書くこともできる。 ### CPU負荷の原因を探る ロードがすごく上がってるサーバとかでuserがCPU使いまくってる場合にtopでCPU使ってるプロセスを見たりするが、それをdstatでできるのが`--top-cpu`である。このプラグインでは、最もCPUを使ってるプロセス名とその値を表示してくれる。 ``` $ sudo dstat -ta --top-cpu ``` Apache等同じプロセス名のプロセスが複数ある時はPIDも知りたい。そのようなときは`--top-cpu-adv`が便利だ。`--top-cpu-adv`はPIDに加えてread、writeの値も表示する。 ``` $ sudo dstat -ta --top-cpu-adv ``` ### IOの原因を探る IOが刺さってるようなサーバで詳しく見たい時には`--top-io`、`--top-bio`が便利だ。 ``` $ sudo dstat -ta --top-io --top-bio ``` `--top-io`は最もIOが多いプロセスを表示し、`--top-bio`は最もブロックIOが多いプロセスを表示する。また、一般ユーザでは他ユーザのプロセスなどを取得できないので、`sudo`をつけて実行するとよい。もちろんPIDを知りたい時は`--top-cpu-adv`と同じように`--top-io-adv`、`--top-bio-adv`もある。 ``` $ sudo dstat -ta --top-io-adv --top-bio-adv ``` ### どんなプラグインが入っているのか調べる どのようなプラグインが使えるかしりたい場合は、`dstat -V`を使う。Debian7.1では次のようになった。 ``` harukasan@debian7:~$ dstat -V Dstat 0.7.2 Written by Dag Wieers <dag@wieers.com> Homepage at http://dag.wieers.com/home-made/dstat/ Platform posix/linux2 Kernel 3.2.0-4-amd64 Python 2.7.3 (default, Jan 2 2013, 13:56:14) [GCC 4.7.2] Terminal type: screen (color support) Terminal size: 43 lines, 224 columns Processors: 4 Pagesize: 4096 Clock ticks per secs: 100 internal: aio, cpu, cpu24, disk, disk24, disk24old, epoch, fs, int, int24, io, ipc, load, lock, mem, net, page, page24, proc, raw, socket, swap, swapold, sys, tcp, time, udp, unix, vm /usr/share/dstat: battery, battery-remain, cpufreq, dbus, disk-tps, disk-util, dstat, dstat-cpu, dstat-ctxt, dstat-mem, fan, freespace, gpfs, gpfs-ops, helloworld, innodb-buffer, innodb-io, innodb-ops, lustre, memcache-hits, mysql-io, mysql-keys, mysql5-cmds, mysql5-io, mysql5-keys, net-packets, nfs3, nfs3-ops, nfsd3, nfsd3-ops, ntp, postfix, power, proc-count, qmail, rpc, rpcd, sendmail, snooze, squid, test, thermal, top-bio, top-bio-adv, top-childwait, top-cpu, top-cpu-adv, top-cputime, top-cputime-avg, top-int, top-io, top-io-adv, top-latency, top-latency-avg, top-mem, top-oom, utmp, vm-memctl, vmk-hba, vmk-int, vmk-nic, vz-cpu, vz-io, vz-ubc, wifi ``` このようなかんじで`/usr/share/dstat`にあるプラグインが表示された。使ったことないプラグインでも便利そうなやつが結構あるのでいつか試してみたい。 ## dstatは便利 こんな感じでdstatはとても便利なのでみんな使おう。 |
|
| 213位 |
|
|||
|
14:20:24 |
(bexide 所属) |
|
```vim:
:w !sudo tee % ``` |
|
| 214位 |
|
|||
|
15:38:14 |
|
|
SSL/HTTPSの仕組みをざっくり理解しながら、 オレオレHTTPSの稼働まで。 ##0.そもそもSSLって何? ###概要 SSLサーバー証明書とはウェブサイトの所有者の情報、送信情報の暗号化に必要な鍵、発行者の署名データを持った電子証明書です。 SSLサーバーには主に二つの役割があります。 - 証明書に表示されたドメインの所有者であることの証明 - ブラウザとウェブサーバー間でのSSL暗号化通信の実現 一般的には、第三者サービスがWHOISと企業実在情報を照会して証明書を発行します。証明書を発行する人を認証局といいます。 よし、SSLサーバー証明書をつくればいいんだな。 ###オレオレSSLとは "俺自身が認証局になることだ…" 社会的信用はないSSLなので運用には注意してください。 ###その前に、暗号化ってなんですか? ```ruby:sample_cipher.rb # encoding: utf-8 require 'OpenSSL' def encrypt_data(data, password, salt) #暗号化方式を選ぶ cipher = OpenSSL::Cipher::Cipher.new("AES-256-CBC") cipher.encrypt cipher.pkcs5_keyivgen(password, salt) cipher.update(data) + cipher.final end def decode_data(data, password, salt) cipher = OpenSSL::Cipher::Cipher.new("AES-256-CBC") cipher.decrypt cipher.pkcs5_keyivgen(password, salt) cipher.update(data) + cipher.final end password = "foobar" # pass_phrase salt = "8-octets" # 8-octet string data = "Hello, World" # このデータを暗号化するとします # 暗号化 encrypted_data = encrypt_data(data, password, salt) p encrypted_data # => "\xDEW\xFD\x8A\xB6\x83\xF1\xC1\x96\x15\x81\x02\xE3\x05\x879" # 復号化 p decode_data( encrypted_data, password, salt ) # => "Hello, World" ``` パスフレーズとソルトを使って暗号化・復号化しています。 ####塩? ソルトって何という疑問が浮かぶと思います。 平たくいえば、パスフレーズをランダム化するために使います。 例えばいまパスワードは"foobar"ですが、データ流出事故が起きたとします。 - DBのpasswordの項目に"foobar"と入っている場合 言わずもがなパスワードも流出します - DBのpasswordの項目に"foobar"を暗号化したもの"x83\xF1\xC1\x96\x15\x81\"が入っている場合 "x83\xF1\xC1\x96\x15\x81\"が出るまでpass_phraseを入れ続けて一致すれば、元のpasswordが"foobar"が判明する - DBのpasswordの項目はその時々にソルトを使って暗号化される場合 "x83\xF1\xC1\x96\x15\x81\"に一致するパスフレーズが見つかったとしても"foobar"は判明しない ソルトがわかっていたら?となるので、"foobar"をパスワードとしているユーザーが複数いたらそれぞれにソルトを変える 故にソルトは一意に生成されます そしてソルトの長さは上の例では8-octet stringでしたが、暗号化方式によってまちまちです。 とりあえずこれだけでも秘密のトークアプリケーションがつくれますね。 ####RSA? 暗号化と証明書を同時につくれるナイスな方式がRSAです。 誰が暗号化するのか、誰が復号化するのかを考えると、 1. クライアントが送る情報はクライアント側で暗号化して、 2. 送信された情報をホスト側で受信してデコードします。 このときクライアントが勝手に暗号化するとホスト側でデコードができませんので、先にホストからクライアントに暗号化のルールを伝えます。 0. ホストからクライアントに暗号化のルールを伝える 1. クライアントが送る情報はクライアント側で暗号化して、 2. 送信された情報をホスト側で受信してデコードします。 そのあたりを注目してRSAの動作を確認します。 ```ruby:sample_rsa.rb # encoding: utf-8 require 'openssl' include OpenSSL::PKey ### 1.SSL証明書を発行 # RSAを鍵長2048bitで作成します rsa = RSA.generate(2048) # 公開鍵を取得します public_key = rsa.public_key.to_s # 暗号鍵を取得します 暗号化にパスワードを入力します ここでは'password'にしてます private_key = rsa.export(OpenSSL::Cipher::Cipher.new('aes256'),'password') ### 2.クライアント側の処理を想定 # 公開鍵をもらいます # 発行された公開鍵でRSAを作成します pub = RSA.new(public_key) # 送信する情報の暗号化 enc_data = pub.public_encrypt("Personal Information") p enc_data #=> 暗号化されたデータ よくわからない文字列 ### 3.ホスト側の処理を想定 # 送信された情報を受信した! # 暗号鍵でRSAを作成します 暗号化されているのでパスワードも必要です private = RSA.new(private_key,'password') # 送信された情報を復号化します p private.private_decrypt(enc_data) #=> 'Personal Information' 復号化できました ``` できました。 前段が長くなりましたが、いよいよ本番です。 ##1.DES3という暗号化方式でRSA暗号鍵を"server.key"という名前で生成 暗号鍵のパスフレーズを設定します ``` $ [sudo] openssl genrsa -des3 -out server.key 1024 Enter pass phrase for server.key: Verifying - Enter pass phrase for server.key: # genrsa Generation of RSA Private Key. Superceded by genpkey. # 暗号化方式一覧 # Cipher commands (see the `enc' command for more details) # aes-128-cbc aes-128-ecb aes-192-cbc # aes-192-ecb # aes-256-cbc aes-256-ecb base64 # bf # bf-cbc bf-cfb bf-ecb bf-ofb # camellia-128-cbc camellia-128-ecb camellia-192-cbc camellia-192-ecb # camellia-256-cbc camellia-256-ecb cast cast-cbc # cast5-cbc cast5-cfb cast5-ecb cast5-ofb # des des-cbc des-cfb des-ecb # des-ede des-ede-cbc des-ede-cfb des-ede-ofb # des-ede3 des-ede3-cbc des-ede3-cfb des-ede3-ofb # des-ofb des3 desx rc2 # rc2-40-cbc rc2-64-cbc rc2-cbc rc2-cfb # rc2-ecb rc2-ofb rc4 rc4-40 # seed seed-cbc seed-cfb seed-ecb # seed-ofb zlib ``` ##2.RSA暗号鍵から"server.csr"という名前で証明書を発行 証明書に必要な情報をいろいろ聞かれます。 ``` $ [sudo] openssl req -new -key server.key -out server.csr Enter pass phrase for server.key: [暗号鍵のパスワード] Country Name (2 letter code) [XX]: State or Province Name (full name) []: Locality Name (eg, city) [Default City]: Organization Name (eg, company) [Default Company Ltd]: Organizational Unit Name (eg, section) []: Common Name (eg, your name or your server's hostname) []: Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: ``` ##3.毎回のパスワード入力を避けるため暗号鍵を復号化 ``` $ [sudo] cp server.key server.key.org $ [sudo] openssl rsa -in server.key.org -out server.key Enter pass phrase for server.key.org: writing RSA key # openssl rsa [-in filename] : ファイルをRSA復号 # # -in filename # 入力する証明書要求のファイル名(filename) # デフォルトは、標準出力 # -out filename # 出力する証明書要求のファイル名(filename) # デフォルトは、標準出力 ``` ##4.SSL証明書の作成 とりあえず30日の期限にします。 ``` $ openssl x509 -req -days 30 -in server.csr -signkey server.key -out server.crt # 形式 # openssl req [-new] [-in filename] [-out filename] # [-key filename] # [-x509] [-days n] # 機能 # 証明書の署名要求(CSR)の作成 # オプション # -in filename # 入力する証明書要求のファイル名(filename) # デフォルトは、標準出力 # -out filename # 出力する証明書要求のファイル名(filename) # デフォルトは、標準出力 # -signkey filename # 入力する秘密鍵のファイル名(filename) # -days n # X.509形式の証明書の有効期限をn日とする ``` 以上でオレオレSSL証明書が発行できました。 ##(5.nginxで動作させてみる) nginxのconfに書いてみる ``` server { : listen 443 default ssl; ssl on; ssl_certificate /path/to/server.crt; ssl_certificate_key /path/to/server.key; : } ``` サーバーを再起動させてアクセスすると、  うまくいきました。 |
|
| 215位 |
|
|||
|
22:16:25 |
|
|
## 目次
0. なぜ bundler? 1. ruby を入れておく 2. rubygems を入れておく 3. bundler を入れておく 4. Gemfile を作る 5. bundle install で gem をインストールする 6. bundle exec で使う 7. まとめ 8. bundle update で更新する 9. bundle clean で古い gem を削除する ## 0. なぜ bundler? 普通に `gem install veewee` でインストールしようとすると、依存関係の解決で出来るだけ最新を入れようとするだけで、うまく試行錯誤してくれず、うまく解決出来ない状態になると `Gem::DependencyError` で諦めてしまいます。 たとえば今最新の veewee をインストールしようとすると以下のように失敗します。 ```:実行例 % gem install veewee ERROR: While executing gem ... (Gem::DependencyError) Unable to resolve dependencies: gherkin requires json (>= 1.7.6) ``` このような場合でも `bundler` を使うと一部のバージョンを下げるなど、解決出来る組み合わせがあれば、うまく解決してくれます。 (`bundler` を使った場合の例は後で出てきます。) ## 1. ruby を入れておく まず ruby をインストールしておきます。 Mac などでは OS に標準で入っている ruby をそのまま使っても大丈夫だと思います。 ### 元に戻すには? インストールに使った方法によるので一概には言えません。 ## 2. rubygems を入れておく 1.8.7 なら rubygems を入れておきます。 これも OS などに標準で入っていれば、それを使えば大丈夫です。 ### 元に戻すには? ruby 自体をまるごと入れ直すのが楽だと思います。 ## 3. bundler を入れておく `gem install bundler` で bundler を入れておきます。 この後、使用するコマンドは `bundle` ですが、 gem の名前としては `bundler` と `r` が付いているのに注意してください。 ### 元に戻すには? `gem uninstall bundler` でインストール前に戻せます。 ### Tips: システムをよごしたくない場合 `GEM_HOME` 環境変数を設定しているとそこにインストール出来るので、システムをよごさずに試すことが出来ます。 `bundle` コマンドは `$GEM_HOME/bin` に入るので `PATH` も通しておく必要があります。 ```:実行例 export GEM_HOME=/tmp/gem export PATH=$GEM_HOME/bin:$PATH ``` ## 4. Gemfile を作る ここからは vagrant + veewee を使うディレクトリを作って、その中で作業をしていきます。 説明のための例なので `/tmp/` 以下に作成していますが、普通は `$HOME` 以下に作ると良いと思います。 1. `bundle init` でカレントディレクトリに `Gemfile` のひな形を作成します。 2. エディタなどで `Gemfile` に使いたい gem を追加します。 ```:実行例 % mkdir /tmp/veewee % cd /tmp/veewee % bundle init Writing new Gemfile to /private/tmp/veewee/Gemfile % cat Gemfile # A sample Gemfile source "https://rubygems.org" # gem "rails" % echo 'gem "vagrant"' >> Gemfile % echo 'gem "veewee"' >> Gemfile % cat Gemfile # A sample Gemfile source "https://rubygems.org" # gem "rails" gem "vagrant" gem "veewee" ``` ### 元に戻すには? 作業用に作ったディレクトリを削除するだけです。 この後も作業ディレクトリの中に閉じ込めている限りは同じです。 ## 5. bundle install で gem をインストールする `bundle install` で gem をインストールします。 * `--path` オプションを使うと、指定した場所に gem がインストールされるので、不要になったときにまとめて削除出来て便利です。 * `bundle install` に指定したオプションの情報が `.bundle/config` に保存されていて、この後の `bundle` の実行時に使われます。 * `Gemfile.lock` にどのバージョンを要求されてどのバージョンを使う (インストールした) のかなどの情報が保存されています。 * 今後のために `git` などでバージョン管理するなら `.bundle/` や `vendor/bundle` は `.gitignore` で無視して、 `Gemfile` と `Gemfile.lock` をバージョン管理するのがおすすめです。 ```:実行例 % bundle install --path vendor/bundle % bundle install --path vendor/bundle Fetching gem metadata from https://rubygems.org/........ Fetching gem metadata from https://rubygems.org/.. Resolving dependencies... Installing rake (10.0.3) Installing libxml-ruby (2.6.0) Installing CFPropertyList (2.0.17) Installing Platform (0.4.0) Installing ansi (1.3.0) Installing archive-tar-minitar (0.5.2) Installing builder (3.2.0) Installing ffi (1.4.0) Installing childprocess (0.3.8) Installing diff-lcs (1.2.1) Installing json (1.5.5) Installing gherkin (2.11.5) Installing cucumber (1.2.1) Installing erubis (2.7.0) Installing excon (0.19.3) Installing fission (0.4.0) Installing formatador (0.2.4) Installing mime-types (1.21) Installing multi_json (1.6.1) Installing net-ssh (2.2.2) Installing net-scp (1.0.4) Installing nokogiri (1.5.6) Installing ruby-hmac (0.4.0) Installing fog (1.9.0) Installing posix-spawn (0.3.6) Installing grit (2.5.0) Installing highline (1.6.15) Installing i18n (0.6.4) Installing log4r (1.1.10) Installing open4 (1.3.0) Installing popen4 (0.1.2) Installing progressbar (0.20.0) Installing rspec-core (2.13.0) Installing rspec-expectations (2.13.0) Installing rspec-mocks (2.13.0) Installing rspec (2.13.0) Installing ruby-vnc (1.0.1) Installing thor (0.17.0) Installing vagrant (1.0.6) Installing veewee (0.3.7) Using bundler (1.3.0) Your bundle is complete! It was installed into ./vendor/bundle ``` ### 元に戻すには? 作業ディレクトリを削除するだけです。 `bundle install` の時に `--path` を指定し忘れて ruby のシステム側に gem がインストールされてしまった場合は `Installing veewee (0.3.7)` なら `gem uninstall -v 0.3.7 veewee` のように個別に頑張ってアンインストールしてください。 Debian の apt の autoremove のような便利な機能はありません。 ## 6. bundle exec で使う `vagrant` や `veewee` などのコマンドの前に `bundle exec` を付けて実行します。 この方法ならサブディレクトリを作成して、その中でも同様の方法で実行出来ます。 `vagrant` は Vagrant Project ごとのサブディレクトリを作って使うと良いと思います。 ```:実行例 % bundle exec vagrant Usage: vagrant [-v] [-h] command [<args>] -v, --version Print the version and exit. -h, --help Print this help. Available subcommands: basebox box destroy gem halt init package provision reload resume ssh ssh-config status suspend up For help on any individual command run `vagrant COMMAND -h` % bundle exec veewee Tasks: veewee add_share # Adds a Share to the Guest veewee fusion # Subcommand for Vmware fusion veewee help [TASK] # Describe available tasks or one specific task veewee kvm # Subcommand for KVM veewee parallels # Subcommand for Parallels veewee vbox # Subcommand for VirtualBox veewee version # Prints the Veewee version information ``` ### Tips: bundle exec を省略するには? `bundle install` の時に `--binstubs` というオプションを使うと省略出来るようになりますが、 `vagrant` のようにサブディレクトリでも同様に使いたい場合には向いていないと思ったため、詳しい説明は省略します。 ## 7. まとめ `bundler` を使って `gem install` より賢く gem をインストールする方法、インストールした gem を簡単に削除出来る方法を紹介しました。 以上で使うまでは出来るようになったと思います。 続いてインストール後に使うコマンドのうち知っておくと良さそうなものをいくつか紹介しておきます。 ## 8. bundle update で更新する `bundle update` で `Gemfile` の記述を満たす最新の gem に更新してくれます。 以下では上の例でインストールされたのと同じバージョンの json だけインストールしてみて、それを最新にアップデートしています。 例では、まず `gem "json", "1.5.5"` で古いバージョンを指定してインストールしています。 新しいバージョンがうまく動かないなどの理由で古いバージョンが使いたい時に必要になるかもしれません。 ```:実行例 % mkdir x % cd x % bundle init Writing new Gemfile to /tmp/x/Gemfile % echo 'gem "json", "1.5.5"' >> Gemfile % cat Gemfile # A sample Gemfile source "https://rubygems.org" # gem "rails" gem "json", "1.5.5" % bundle install --path vendor/bundle Fetching gem metadata from https://rubygems.org/.. Resolving dependencies... Installing json (1.5.5) with native extensions Using bundler (1.2.4) Your bundle is complete! It was installed into ./vendor/bundle % echo .bundle > .gitignore % echo vendor/bundle >> .gitignore % git init Initialized empty Git repository in /tmp/x/.git/ % git add -A % git commit -m "Initial commit" [master (root-commit) db09b6e] Initial commit 3 files changed, 17 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 Gemfile.lock % editor Gemfile % cat Gemfile source "https://rubygems.org" gem "json" % bundle update Fetching gem metadata from https://rubygems.org/.. Resolving dependencies... Installing json (1.7.7) with native extensions Using bundler (1.2.4) Your bundle is updated! Use `bundle show [gemname]` to see where a bundled gem is installed. diff --git a/Gemfile b/Gemfile index 3b67429..b439d83 100644 --- a/Gemfile +++ b/Gemfile @@ -2,4 +2,4 @@ source "https://rubygems.org" # gem "rails" -gem "json", "1.5.5" +gem "json" diff --git a/Gemfile.lock b/Gemfile.lock index 1acd703..02f7a20 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,10 +1,10 @@ GEM remote: https://rubygems.org/ specs: - json (1.5.5) + json (1.7.7) PLATFORMS ruby DEPENDENCIES - json (= 1.5.5) + json ``` ## 9. bundle clean で古い gem を削除する `bundle update` や `Gemfile` の変更などで使われなくなった gem は `bundle clean` で削除出来ます。 さきほどの json の例の後だと次のようになります。 ```:実行例 % bundle clean Removing json (1.5.5) ``` |
|
| 216位 |
|
|||
|
03:41:31 |
(株式会社オープンストリーム 所属) |
|
#速さは正義!
やはり「最速」という言葉は胸を熱くするものがあります。 使うWebフレームワークのレスポンスが速ければ速いほど、ユーザービリティはよくなる上に、必要なサーバー台数も減らせるなど、いいことづくめです。 ただ、レスポンスの速さだけを追い求めて機能を削ったものは、開発の速度を遅くします。 Web開発に通常必要な機能は全て用意した上で、なお速い、そんな都合のいいフレームワークが求められます。 そこで、最近知った「revel」という「最速」にして「フルスタック」のWebフレームワークについて、調べたことを記載していきます。  今回は、 - 速さの計測方法解説 - revelの概要 - Go言語の概要 - revelのインストール・実行までの手順 などについて記載します。 **※ ここで言っている「最速」とは、一秒間に何レスポンス返せるか、というような、ベンチマーク的な意味です。** **※ ここで言っている「フルスタックWebフレームワーク」とは、テンプレート、データベース補助、ルーティングなど、Web開発に必要になるメジャーな機能を一通り全部そろえたフレームワーク、という意味です。** #速さの計測方法 「revel」が最速だとする根拠ですが、TechEmpowerというWeb開発会社が公開しているベンチマークをもとに判断しました。 何種類か計測結果があるのですが、私が主に注目したのは以下の2つのテストです。 どちらも、EC2の m1.large を使っています。 ##テスト1:Jsonシリアライズ オブジェクトをJSONにシリアライズして {"message":"Hello, World!"} とレスポンスを行う一連の流れを、一秒間に何回出来るか計測しています。 http://www.techempower.com/benchmarks/#section=data-r8&hw=ec2&test=json&c=1 ##テスト2:DB値の複数行取得とJsonシリアライズ 複数行検索されるSELECTクエリをを実行してデータを取得し、JSONにシリアライズしてレスポンスを行う一連の流れを、一秒間に何回出来るか計測しています。 http://www.techempower.com/benchmarks/#section=data-r8&hw=ec2&test=query&c=1 ##詳しい計測条件 もっと詳しい条件は以下に記載されています。 http://www.techempower.com/benchmarks/#section=code&hw=ec2&test=json&c=1 計測したフレームワークについては下記に一覧があります。 http://www.techempower.com/benchmarks/#section=environment&hw=i7&test=json また、計測に使ったソースコードはGithubに公開されています。 https://github.com/TechEmpower/FrameworkBenchmarks #計測結果解説 どちらも実はrevelではなく、「gemini」というWebサーバーが1位になってますが、これは計測を行ったTeckEmpowerさんの社内フレームワークで、公開もされてないらしいので、ノーカンです。 実質、今回紹介する「revel」が現時点の「最速」フルスタックWebフレームワークだと言えるでしょう。 その速さは凄まじく、例えばJSONシリアライズのほうだと、最大で秒間21920回(!?)のレスポンスをさばけ、 他のフレームワークと比べると、例えば - Ruby on Railsと比べると約25倍 - CakePHPだと80倍、 - コンパイル言語で出来たSpring Frameworkと比べても4倍 という、なかなか衝撃の結果になっています。 #revelはどんなフレームワークか revelは、Go言語で作られたWebフレームワークです。開発もGo言語で行います。 ↓本家 http://revel.github.io/ ホットリロードを備えているため、コンパイルのストレスなく開発でき、ルーティング、バリデーション、テンプレートエンジン、DB処理、テスト支援などの他、様々な機能を備えています。 サーバーや、プロジェクトの作成機能なども含めて全てセットになっており、 ``` revel new myapp revel run myapp ``` などとするとすぐひな形が作られ、動きます。 ちなみにJava、Scala界隈で最近人気のPlay Frameworkを参考にして作られており、機能も大体同じみたいです。 #Go言語とは Google製の比較的最近できたプログラム言語です。 ↓本家 http://golang.org/ - 非常にシンプルな言語で覚えやすく - コンパイルも早く - C言語並みの速度を持ち - 同期処理が書きやすい などの特徴があります。 速さと開発のしやすさの両立を目指してできた言語みたいですね。 色々とくせはありますが、確かにシンプルで覚えやすい言語な気はします。 まだ勉強したてですが。 ただ、開発にはEclipseのプラグインなどもありますが、まだまだ整備はされていない印象は受けます。 #Go言語のインストール Macにインストールしました。 ``` brew update brew install go brew install mercurial ↑go言語のライブラリで mercurialというのを使っているらしいのでいれる ``` vi .bash_profile でエディタを開き ``` export GOROOT=`go env GOROOT` export GOPATH=$HOME/go export PATH=$PATH:$GOROOT/bin:$GOPATH/bin ``` を記載してパスを通す。 保存して再読み込み。 ``` source .bash_profile ``` #revelのインストール ``` go get github.com/robfig/revel ↑revel本体のインストール go get github.com/robfig/revel/revel ↑revelコマンドラインツールのインストール? ``` #revelのサンプルアプリ実行 設定したGOPATH(~/go)に、revelのサンプルアプリがダウンロードされているはずなので、 実行してみます。 ``` cd $GOPATH/src revel run github.com/robfig/revel/samples/booking ``` これで http://localhost:9000/ にアクセスして 「revel framework booking demo」 などと表示されればOK。 #revelのアプリ新規作成 どうにも、アプリのソースは $GOPATH/src 配下にしなければいけないらしいです。 ソースがかぶってしまわないように、適切なディレクトリを切る必要があります。 とりあえずこんな感じで作成して起動してみます。 ``` cd $GOPATH/src revel new github.com/myname/test/myapp revel run github.com/myname/test/myapp ``` http://localhost:9000/ にアクセスして 「It Works」 などと表示されればOK。 |
|
| 217位 |
|
|||
|
19:28:51 |
|
|
ssh接続エラー(ワーニング)になり接続できないことがある。 * エラー原因のknown_hostsの設定削除する方法 * 手軽にエラーを無視する方法 * エラーとならないようにサーバ側を設定する方法 について記載する。 **ただし、これらの方法がセキュリティ的によいのかは各自判断が必要。** ssh接続先サーバがOSを再インストールしたとか、接続先サーバがDHCPでアドレスが変わるとか、接続先サーバがホスト名を付け替えたとか、そういったときに次のエラー(ワーニング)が発生して接続ができない。 ```shell:接続エラー $ ssh remote_host @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that the RSA host key has just been changed. The fingerprint for the RSA key sent by the remote host is 29:24:c2:69:a3:b0:dc:4d:23:fc:9d:85:9f:ea:01:9b. Please contact your system administrator. Add correct host key in /home/grgrjnjn/.ssh/known_hosts to get rid of this message. Offending key in /home/grgrjnjn/.ssh/known_hosts:3 RSA host key for remote_host has changed and you have requested strict checking. Host key verification failed. ``` なぜ? ----- SSHでは、安全な接続を行うために接続先サーバの情報(RSA公開鍵のフィンガープリント)を、クライアントは保存する。SSH接続時には、以前保存したこの情報と、いままさに接続しようとしているサーバの情報が一致しているかを確認する。こうすることで、ユーザ(クライアント)が知らない間に、別のサーバへ接続してしまうことを防ぐ。よりセキュアになるってわけだ。 エラー原因のknown_hostsの設定削除する方法 ------------------------------------ 保存している接続先サーバの情報(フィンガープリント)を削除してしまえば、新規接続となるためエラーを回避できる。そのコマンドは、`ssh-keygen -R hostname`で行うのが正解。.oldを付けてバックアップも自動で作られる。 ```shell:正解 $ ssh-keygen -R remote_host_name /home/grgrjnjn/.ssh/known_hosts updated. Original contents retained as /home/grgrjnjn/.ssh/known_hosts.old ``` 実態は`~/.ssh/known_hosts`ファイルなので、直接編集しても良い。エラーとなったサーバ名(またはIPアドレス)から始まる行を1行削除するだけ。 sedを使ってもよいだろう。 ```shell:sedを使ったやり方 $ sed -i '/remote_host_name/d' ~/.ssh/known_hosts ``` 単に、viで編集してもダメではない。 手軽にエラーを無視する方法 ---------------------- `WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!`が発生しても無視して接続するのは、接続時に`-o 'StrictHostKeyChecking no'`オプションを使う方法と、このオプションを設定ファイルに記載して常に有効にする方法がある。 ```shell:エラーを無視する接続方法 $ ssh -o 'StrictHostKeyChecking no' remote_host_name @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: POSSIBLE DNS SPOOFING DETECTED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ The RSA host key for remote_host_name has changed, and the key for the corresponding IP address 10.211.55.28 is unchanged. This could either mean that DNS SPOOFING is happening or the IP address for the host and its host key have changed at the same time. Offending key for IP in /home/grgrjnjn/.ssh/known_hosts:1 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that the RSA host key has just been changed. The fingerprint for the RSA key sent by the remote host is 29:24:c2:69:a3:b0:dc:4d:23:fc:9d:85:9f:ea:01:9b. Please contact your system administrator. Add correct host key in /home/grgrjnjn/.ssh/known_hosts to get rid of this message. Offending key in /home/grgrjnjn/.ssh/known_hosts:3 Password authentication is disabled to avoid man-in-the-middle attacks. Keyboard-interactive authentication is disabled to avoid man-in-the-middle attacks. Last login: Fri Feb 28 15:27:36 2014 from 10.211.55.27 ``` 上記のコマンド実行結果をよく見るとわかるように、この方法を使うと、エラーとともに、次のような初回接続時の`(yes/no)?`の対話的なやりとりも省略できる。 ```shell:この対話処理も省略される $ ssh remote_host_name The authenticity of host 'remote_host_name (10.211.55.28)' can't be established. RSA key fingerprint is 29:24:c2:69:a3:b0:dc:4d:23:fc:9d:85:9f:ea:01:9b. Are you sure you want to continue connecting (yes/no)? ``` 常にこのオプションを有効にしたいなら設定ファイルに`StrictHostKeyChecking no`を記載する。システム全体に適用したいなら、`/etc/ssh/ssh_config`に、ユーザ毎に設定したいなら`~/.ssh/config`に、次のように記載する。 ```shell:~/.ssh/config StrictHostKeyChecking no ``` `~/.ssh/config`がなければ新規に作成する。その場合、パーミッションに注意。このファイルのパーミッションは600でないと、SSH接続時にエラーとなる。 ```shell:~/.ssh/configのパーミッション設定 $ chmod 600 ~/.ssh/config ``` この設定がされていると、次のように警告は出るがエラーで止まることも、対話的な応答もすることなく、接続される。 ```shell:StrictHostKeyCheckingが設定されている場合の挙動 $ ssh remote_host_name Warning: Permanently added 'remote_host_name' (RSA) to the list of known hosts. Last login: Fri Feb 28 15:32:12 2014 from 10.211.55.27 ``` この動作をさせるなら、言うまでもないが、セキュリティ的なリスクについては十分に検討してもらいたい。 エラーとならないようにサーバ側を設定する方法 ------------------------------------ 接続先サーバ側のRSA公開鍵のフィンガープリントが変わってしまうことが原因なら、変わらないようにするという手もある。フィンガープリントの元ネタは`/etc/ssh/ssh_host_key.pub`などがこれにあたる。 これらをバックアップしておいてOS再インストール後に戻してやるとか、複数サーバで共有するとか、してやればいい。 ```shell:RHEL、CentOSの場合 /etc/ssh/moduli /etc/ssh/ssh_config /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_key /etc/ssh/ssh_host_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/sshd_config ``` バックアップから戻すときの参考に、私のCentOS6.5の環境ではパーミッションは以下の通りになっている。(ここのシンタックスハイライトうまくいってないなぁ...) ```shell:CentOS6.5のパーミッション $ ls -ld /etc/ssh/ drwxr-xr-x. 2 root root 4096 2月 11 21:06 2014 /etc/ssh/ $ ls -l /etc/ssh/ 合計 156 -rw-------. 1 root root 125811 11月 23 07:40 2013 moduli -rw-r--r--. 1 root root 2047 11月 23 07:40 2013 ssh_config -rw-------. 1 root root 668 2月 11 19:21 2014 ssh_host_dsa_key -rw-r--r--. 1 root root 590 2月 11 19:21 2014 ssh_host_dsa_key.pub -rw-------. 1 root root 963 2月 11 19:21 2014 ssh_host_key -rw-r--r--. 1 root root 627 2月 11 19:21 2014 ssh_host_key.pub -rw-------. 1 root root 1675 2月 11 19:21 2014 ssh_host_rsa_key -rw-r--r--. 1 root root 382 2月 11 19:21 2014 ssh_host_rsa_key.pub -rw-------. 1 root root 3879 2月 11 21:06 2014 sshd_config ``` 以上 |
|
| 218位 |
|
|||
|
22:51:46 |
|
|
※こちら内容がかなり古くなっていたのでMac OS El CapitanでPostgreSQLをインストールした記事を書きました。
[Homebrewを使ったPostgreSQLのインストール(Mac OS El Capitan)](http://tstomoki.com/programming/posgre_inst) heroku用にPostgreSQLを使ったアプリを作りたかったので確認用にPostgreSQLをインストールします。 ##HomebrewでPostgreSQLのインストール >brew install postgresql ##データベースの初期化 (文字コードはUTF-8) >initdb /usr/local/var/postgres -E utf8 PostgreSQLサーバの起動 >postgres -D /usr/local/var/postgres データベース一覧が取得出来ればインストール成功 >psql -l Mac OS Lion だとこんなエラーが出ますが、LionにはデフォルトでPostgreSQLが既にバンドルインストールされてるらしいですね。 >psql: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/pgsql_socket/.s.PGSQL.5432"? 修正用バッチをファイルをcurlでインストールし適用。 >curl -o fixBrewLionPostgresql.sh http://nextmarvel.net/blog/downloads/fixBrewLionPostgres.sh ファイル権限の変更 >chmod 777 fixBrewLionPostgresql.sh バッチシェルの実行 >./fixBrewLionPostgresql.sh これでもう一度 >psql -l をすればデータベース一覧が取得できるはずです。 ##環境変数の設定 環境変数PGDATAの設定。bashなら~/.bashrc、今回は~/.zshrcに以下を記述 ```~/.zshrc export PGDATA=/usr/local/var/postgres ``` 変更を反映 (Terminalソフトの再起動でも可) > source ~/.zshrc PostgreSQLサーバの起動 >pg_ctl -l /usr/local/var/postgres/server.log start PostgreSQLサーバの終了 >pg_ctl -D /usr/local/var/postgres stop -s -m fast ##PostgreSQLの自動起動設定 自動起動リストにPostgreSQLの追加 >cp /usr/local/Cellar/postgresql/9.3.2/homebrew.mxcl.postgresql.plist ~/Library/LaunchAgents/ 自動起動リストの設定ファイルの読み込み >launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist ##コマンドライン上でのPostgreSQLの起動とコマンド ###起動 >psql 'database名' ###終了(プロンプト上) >\q ###データベース作成 >createdb 'database名' ###データベース一覧表示 >psql -l |
|
| 219位 |
|
|||
|
21:22:00 |
(bexide 所属) |
|
vimでクラスの宣言元にジャンプするために必要なtagsファイル、それを作るのがctagsコマンド。 プロジェクトのルートディレクトリから ``` ctags -R ``` をすればルートディレクトリにtagsファイルが作られていい感じにジャンプしてくれるのですが、tagが宣言されていないクラスがあったり(ctagsのバージョンを上げたら解決)したのでこの際とばかりにちゃんと使うために設定を調べてみた。 ##ctagsのバージョンを上げる まず普段開発をしている環境がcentOS5系で、ctagsのバージョンが5.6だったのでとりあえず上げる。 ``` sh: mkdir -p $HOME/local/bin mkdir -p $HOME/local/src cd $HOME/local/src/ wget http://prdownloads.sourceforge.net/ctags/ctags-5.8.tar.gz tar zxf ctags-5.8.tar.gz cd ctags-5.8 ./configure --prefix=$HOME/local make sudo make install ``` これで自分のローカル環境にctagsの5.8が入った。`$HOME/local/bin`にパスを張っておく。 5.6ではtags内に宣言されなかった関数も5.8にしたら宣言されていた。 ## .ctagsファイルを作る ctagsのmanを見ると`$HOME/.ctags`にctagsコマンドの引数を記述しておけるので作成しておく。 とりあえず中身はこんな感じにしてみた。 ``` bash:.ctags --append=yes --recurse=yes --langmap=PHP:+.inc --php-kinds=cfd ``` `--append=yes` tagsファイルがあれば追加する `--recurse=yes` 再起的に処理する(-Rと同じ) `--langmap=PHP:+.inc` 言語に対する対応する拡張子の追加(この場合は.incファイルをPHPとしてtagsを作る) `--php-kinds=cfd` 言語に対してタグの作成するものを指定(この場合はphpの場合はクラス、関数、定数のタグのみの作成) ## 設定項目の確認 拡張子追加やタグの作成基準を確認することが出来る。 ```sh:対応言語と対応拡張子の表示 ctags --list-maps … PHP *.php *.php3 *.phtml *.inc … ``` 記述した.incが入っている事がわかる ```sh:作成するタグの表示 $ ctags --list-kinds=php c classes i interfaces [off] d constant definitions f functions v variables [off] v variables [off] j javascript functions [off] j javascript functions [off] j javascript functions [off] ``` `[off]`が付いているもの以外のタグが作成される。 `variables`や`javascript functions`が複数表示されているのが若干の謎だが。 ## tagsの作成 tagsファイルを自分のHOMEディレクトリ以下に`php.tags`として作成する。 標準のプロジェクトルートディレクトリに作っても良いが、PEAR等のファイルが入っている別ディレクトリのtagsを一緒にしておく時に個人的にやりやすいという理由でHOMEディレクトリに作成しているので変えても良い。 ```sh:PHPのみの場合 ctags --languages=php -f ~/php.tags `pwd` ``` `-f`指定で作成されるパスと名前を変更、この時に`pwd`指定でタグのパスを絶対パスに変えないと自分のHOMEディレクトリに置いてもtagsが機能しない。 PEARディレクトリに移動して同じコマンドを打てばtagが追加される。 .vimrcに下記を追加 ```.vimrc " 拡張子で読み込みタグ変更 au BufNewFile,BufRead *.php set tags+=$HOME/php.tags ``` これでtagsが作成された。 ## vimを開いてる時にtagsの追加 コードをもりもり書いていると新しく宣言されたclass等のtagを作成しなければならない。いちいちvimを終了してctagsコマンドを打つのも面倒なのでプラグインを入れる。 例えば[vim-tags](https://github.com/szw/vim-tags)とか neobundle.vim使っているのなら以下で入る。 ```.vimrc NeoBundle 'szw/vim-tags' ``` .vimrcに以下を記述 ```.vimrc " vim-tags au BufNewFile,BufRead *.php let g:vim_tags_project_tags_command = "ctags --languages=php -f ~/php.tags `pwd` 2>/dev/null &" ``` これでコードをもりもり書いている時にたまに ``` :TagsGenerate ``` を打ってやればtagsが更新される。 ちなみにtagジャンプの時に複数タグがあっても第1候補に飛んでしまって気づかない事があるのでキーバインドを少し変えている ```.vimrc " tagsジャンプの時に複数ある時は一覧表示 nnoremap <C-]> g<C-]> ``` これで快適。 参考: http://www.asahi-net.or.jp/~wv7y-kmr/memo/vim_php.html#ctags http://blog.naberon.jp/post/2010/09/27/ctags_setting/?utm_source=2010-09-27&utm_medium=old_blog&utm_campaign=MoveSite http://labs.timedia.co.jp/2010/12/codereading-with-ctags-on-vim.html http://hp.vector.co.jp/authors/VA025040/ctags/help_j.html |
|
| 220位 |
|
|||
|
07:48:03 |
(サークルアラウンド株式会社 所属) |
|
なんか作業内容が妙になってしまった時に
「ええーいリモートが合ってるんだからアイツに合わせたいんだよ!」 とイライラしたら下記。 ```bash: git fetch origin git reset --hard origin/master ``` 以下参考。 http://stackoverflow.com/questions/1628088/how-to-reset-my-local-repository-to-be-just-like-the-remote-repository-head |
|
| 221位 |
|
|||
|
02:02:01 |
|
|
[bundler](https://github.com/bundler/bundler)周りを調べていた時に、bundle installを爆速にする方法を発見したので記事を書きました。
## Bundlerで並列処理 なんと最新版のBundler(v1.4.0.pre.1)から **並列処理** が出来る様になりました! 準備はこれだけ。最新のBundlerをインストールします。 ```zsh: gem install bundler --pre ``` これで下記のコマンドで並列処理された`bundle install`が実行できます。 ```zsh bundle install --jobs=4 ``` もしくは。 ```zsh bundle install -j4 ``` 滅茶苦茶高速化されて、ライフチェンジング :tada: v1.5.0からは`bundle config --global jobs 4`とするとデフォルトとして設定できます(http://bundler.io/v1.5/whats_new.html) [bundle installのドキュメントはこちら(GitHub)](https://github.com/bundler/bundler/blob/master/man/bundle-install.ronn#L92-L93) ## 注意! @take からコメントがあり > ちなみに pre1 だと後方互換性が保たれてないので pre2 を入れることをオススメします。 > > ref: https://github.com/bundler/bundler/issues/2600 とのこと。ご注意ください! |
|
| 222位 |
|
|||
|
18:11:59 |
(e2esound.com 所属) |
|
## テンプレートエンジンSlimを使ってスマートにHTMLを作成する 昨日うちの事務所で開催した[agatsuma.survive\#02][atnd]の中で[Slim][slimlang]の説明が思ったよりも好評だったので改めてまとめてみます。 このエントリでは __Slimを使ってHTMLを作成する__ ことだけを目的としているため、動的にSlimでゴニョゴニョするような話は出て来ません。HTMLの代替にSlimを使う、といった主旨の内容です。プログラムの話も出て来ませんが、残念ながらコマンドラインを少し使います。 ### Slim とは? - 拡張子は __.slim__ - view の構文を本質的な部品まで減らすことを目指したテンプレート言語 - Ruby製のテンプレートエンジン - 高速, 軽量 - インデント重要 #### 用途 - Railsプロジェクトのテンプレート - Sinatra(Padorino)のテンプレート - HTMLの代替(個人的には) #### SlimでHTMLを作成するメリット - タイプ数が減る - HTML に比べ見通しが良い - 実はそんなに難しくない ### Slim環境の用意 SlimはRubyのgemとして配布されています。Rubyが入れてあれば、コマンドラインからgemをインストールするだけです。 `gem install slim` もしRubyが入っていない場合には、頑張って入れてください。Mac/LinuxであればRVMやrbenvといった便利なツールがあります。 gemのインストールが完了したら次のコマンドを実行しましょう。 `slimrb --help` slimrbコマンドのオプション一覧が表示されれば問題ありません。 先述のコマンドのとおり、Slimで使うコマンドは`slimrb`です。SlimをHTMLに変換するには次のコマンドを叩きましょう。 `slimrb -p path/to/sample.slim > path/to/converted_sample.html` `-p`はslimrbコマンドのオプションで読みやすいHTML(改行有り)を出力してくれます。 ### Slimの書き方 まず、次のHTMLとSlimを比較してみましょう。 ```html:sample.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Sample File</title> </head> <body class="sample"> <div id="contents"> <h1>Sample File</h1> <img alt="yterajima" src="http://www.e2esound.com/images/yterajima.jpg" /> <p>テキストテキストテキスト</p> <p>テキストテキストテキスト テキストテキストテキスト</p> </div> </body> </html> ``` ```slim:sample.slim doctype html html head meta charset="utf-8" title Sample File body.sample #contents h1 Sample File img src="http://www.e2esound.com/images/yterajima.jpg" alt="yterajima" p テキストテキストテキスト p | テキストテキストテキスト テキストテキストテキスト ``` Slimはインデントを用いてHTMLの入れ子を表現します。このインデントの深さは自由に決めることができます。今回はRubyらしく2スペースで書いています。 #### Slimの簡単な覚え方 1. 通常のHTMLから __<, >, />__ を取り除く 2. doctype 3. テキストと | (パイプ)の扱い 4. コメント #### 1. 通常のHTMLから<, >, />を取り除く これは読んでそのままです。例に上げたsample.slimの10行目、imgタグを見てください。 ```html <img alt="yterajima" src="http://www.e2esound.com/images/yterajima.jpg" /> <!-- Slimの場合 img src="http://www.e2esound.com/images/yterajima.jpg" alt="yterajima" --> ``` 何が変わったかと言えば, <と/>が無くなっただけです。他のタグでも同様に書き直すとSlimとしてパースすることができます。 また、idとclassを表すショートカットとして、CSSの表記のように __#__ と __.__ が使用できます。デフォルトではこのショートカットだけで表記した場合、div id="", div class=""になります。 #### 2. doctype sample.slimの1行目、`doctype html`はHTML文書のドキュメントタイプを表しています。この表記方法についてはSlimのREADME([日本語版][slim])にまとめてあるので参照してください。XML宣言を記述することも可能です。 #### 3. テキストと | (パイプ)の扱い HTMLのタグの中に書く通常のテキストの扱いには若干のルールがあります。 まず、簡単なものはタグの後にスペースを置いて表記する方法です。sample.slimであればh1タグの 後や最初のpタグの後に続く書き方です。 ```slim h1 Sample File p テキストテキストテキスト ``` スペース後のテキストがそのままHTMLタグの中身になります。少々ややこしいのは複数行のテキストを書く場合です。その場合には __|__ を使います。 ```slim p | テキストテキストテキスト テキストテキストテキスト p | テキストテキストテキスト テキストテキストテキスト ``` |以降にある文字列、インデントされた文字列はすべてテキストとして扱われます。 #### 4. コメント Slimのコメントには変換後出力されるコメントとされないコメントの2種類があります。 ```slim / このコメントは変換後表示されない /! このコメントは変換後HTMLコメントになる ``` ### まとめ HTMLの作成のみを目的とするなら、この程度のことを覚えれば実用できるかなと思います。その他の機能の詳細やRubyコードの扱いについては[日本語版README][slim]を参照してください。 ### その他 - Sass/Scss, Less等のCSSプリプロセッサと組み合わせる場合には[Middleman][]を使うと便利です。 - syntax対応してるエディタが少ないのが辛いところ [atnd]:http://atnd.org/events/37127 [slimlang]:http://slim-lang.com/ [slim]:https://github.com/slim-template/slim/blob/master/README.jp.md [middleman]:http://middleman-guides.e2esound.com/ |
|
| 223位 |
|
|||
|
14:40:22 |
(株式会社ハートレイルズ 所属) |
|
scaffold を制するものは rails を制す、という言い伝えがありますが、 `rails g scaffold User` などとすると routes.rb に突如現れるのが、 resources というやつです。
いまここに、Group と User という2つのモデルがあり、 Group が `has_many :users` で、 User が `belongs_to :group` だとしましょう。よくある関係ですね。 このような場合に、resources を ```ruby:config/routes.rb resources :group do resources :user end ``` こんな風に nest すると、新規に User を作る時の URL が /groups/:group_id/users/new となり、自然な形で group_id を渡すことができます。 ところが、このように routes を定義すると、いざ User が生成された後にその User を show するには /groups/:group_id/users/:id などとしなければならなくなってしまいます。 これは冗長ですね。 User の id が group に寄らず unique であるなら、 /users/:id で参照出来て然るべきです。 そこで、shallow です。 ```ruby:config/routes.rb resources :group, shallow: true do resources :user end ``` こうすると、なんと、 ```sh # rake routes group_user_index GET /group/:group_id/user(.:format) user#index POST /group/:group_id/user(.:format) user#create new_group_user GET /group/:group_id/user/new(.:format) user#new edit_user GET /user/:id/edit(.:format) user#edit user GET /user/:id(.:format) user#show PUT /user/:id(.:format) user#update DELETE /user/:id(.:format) user#destroy group_index GET /group(.:format) group#index POST /group(.:format) group#create new_group GET /group/new(.:format) group#new edit_group GET /group/:id/edit(.:format) group#edit group GET /group/:id(.:format) group#show PUT /group/:id(.:format) group#update DELETE /group/:id(.:format) group#destroy ``` **な、なんて美しいんだ。。!** 何が起きているかというと、user_id を指定しない action である index, new, create の3つは group_id を必要とし、それ以外の action では user_id のみを指定すればよい、ということに、たったの "shallow: true" だけでなってしまったのです。 もちろん、以下のように action を拡張した場合 ```ruby:config/routes.rb resources :group, shallow: true do resources :user do get :search, on: :collection post :follow, on: :member end end ``` には、 ```sh # rake routes (抜粋) search_group_user_index GET /group/:group_id/user/search(.:format) user#search follow_user POST /user/:id/follow(.:format) user#follow ``` ご覧の通り、 on: :collection で定義した :search の方にのみ、:group_id が必須になっています。 そうですそうです、そうして欲しかったんですよ! さらに深く resources を nest した場合にも、 shallow:true は1つだけで大丈夫。 ```ruby:config/routes.rb resources :group, shallow:true do resources :user do resources :entry end end ``` この場合、entries#new は /user/:id/entries/new となり、 entries#show は /entries/:id となります。 最後に、おそらく多くの方が form でつまずくと思うので、その時はこれを読みましょう: <a href="http://stackoverflow.com/a/9944554/683157" target="_blank">http://stackoverflow.com/a/9944554/683157</a> Author: [kuboon](https://plus.google.com/112722233959990837559?rel=author) |
|
| 224位 |
|
|||
|
00:04:18 |
(株式会社nanapi 所属) |
|
こんにちは!12/11担当の [@koogawa](https://twitter.com/koogawa) です。どうぞよろしくお願いします。 さて、iPhoneには「加速度センサー」や「電子コンパス」など、いろいろなセンサーが搭載されていることはご存知だと思います。そこで今回は、iPhoneで検知できる様々な情報とその実装方法を、10個ピックアップしてまとめてました。メジャーなものからマニアックなものまで色々揃えましたので、何か一つでもお役に立てたら嬉しいです。 では、いってみましょう! # 光・音声系 ## 1. 輝度センサー iPhoneの画面輝度(明るさ)が取得・設定できます。 画面輝度=周りの明るさなので、輝度が高ければユーザが明るい場所にいると判断することもできそうです。ただ、中には画面輝度を固定に設定しているユーザもいるので、一概にこの基準が当てはまるとは限りません。 ### 実装方法 画面輝度は次のように取得できます。 ```objc CGFloat brightness = [UIScreen mainScreen].brightness; ``` 0.0〜1.0 の値が返ってきます。数値が増えるほど明るくなります。 ### 詳しい記事 * [【Tips】iOSで輝度センサーを使う](http://blog.koogawa.com/entry/2013/11/17/111304) ### サンプルコード * https://github.com/koogawa/iSensor/blob/master/iSensor/BrightnessViewController.m ## 2. 近接センサー 通話中、画面に顔が近付くと自動的に画面をロックしてくれるアレです。もちろん顔じゃなくても反応します。 ### 実装方法 まずは近接センサーをオンにします。 ```objc [UIDevice currentDevice].proximityMonitoringEnabled = YES; ``` 次に、近接センサーを監視します。 近接センサーが反応すると、`UIDeviceProximityStateDidChangeNotification` が飛んでくるので、それを監視します。 ```objc [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(proximitySensorStateDidChange:) name:UIDeviceProximityStateDidChangeNotification object:nil]; ``` ### 詳しい記事 * [【Tips】iOSで近接センサーを使う](http://blog.koogawa.com/entry/2013/11/17/133500) ### サンプルコード * https://github.com/koogawa/iSensor/blob/master/iSensor/ProximityViewController.m ## 3. シェイクジェスチャー iPhoneを振ったときの動作を検知します。 ### 実装方法 シェイクジェスチャーを検知するためにはファーストレスポンダにならないといけないので、`canBecomeFirstResonder` をオーバライドして `YES` を返します。 ```objc - (BOOL)canBecomeFirstResponder { return YES; } ``` この状態でiPhoneを振ると、次のメソッドが呼ばれるようになります。 ```objc // シェイク開始 - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) { NSLog(@"Motion began"); } } // シェイク完了 - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (event.type == UIEventTypeMotion && event.subtype == UIEventSubtypeMotionShake) { NSLog(@"Motion ended"); } } ``` このメソッドを利用して、シェイクジェスチャーを検知します。 ### 詳しい記事 * [【Tips】iOSでシェイクジェスチャーを検知する](http://blog.koogawa.com/entry/2013/11/20/222242) ### サンプルコード * https://github.com/koogawa/iSensor/blob/master/iSensor/ShakeViewController.m ## 4. マイクの音 Core Audioを使用して、マイクから音を検知します。 ### 実装方法 記録するデータフォーマットを決めます。 ```objc AudioStreamBasicDescription dataFormat; dataFormat.mSampleRate = 44100.0f; dataFormat.mFormatID = kAudioFormatLinearPCM; dataFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; dataFormat.mBytesPerPacket = 2; dataFormat.mFramesPerPacket = 1; dataFormat.mBytesPerFrame = 2; dataFormat.mChannelsPerFrame = 1; dataFormat.mBitsPerChannel = 16; dataFormat.mReserved = 0; ``` 音の監視を開始します。 ```objc AudioQueueNewInput(&dataFormat, AudioInputCallback, (__bridge void *)(self), CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &_queue); AudioQueueStart(_queue, NULL); ``` マイクのレベルを取得できるように、レベルメータを有効化しておきます。 ```objc UInt32 enabledLevelMeter = true; AudioQueueSetProperty(_queue, kAudioQueueProperty_EnableLevelMetering, &enabledLevelMeter, sizeof(UInt32)); ``` 一定時間ごとにレベルを取得して、画面に表示します。 ```objc - (void)detectVolume:(NSTimer *)timer { // レベルを取得 AudioQueueLevelMeterState levelMeter; UInt32 levelMeterSize = sizeof(AudioQueueLevelMeterState); AudioQueueGetProperty(_queue, kAudioQueueProperty_CurrentLevelMeterDB, &levelMeter, &levelMeterSize); // 最大レベル、平均レベルを表示 self.peakTextField.text = [NSString stringWithFormat:@"%.2f", levelMeter.mPeakPower]; self.averageTextField.text = [NSString stringWithFormat:@"%.2f", levelMeter.mAveragePower]; } ``` 通常、`mPeakPower`、`mAveragePower` には負の値がセットされており、音量に比例して値も大きくなっていきます。最大値は 0 になります。 ### 詳しい記事 * [【Tips】iOSでマイクの音を検知する](http://blog.koogawa.com/entry/2013/11/24/121807) ### サンプルコード * https://github.com/koogawa/iSensor/blob/master/iSensor/AudioViewController.m # 位置情報系 ## 5. 緯度・経度 GPSを使って、現在地の緯度・経度を取得します。 ### 実装方法 現在地の取得を開始します。 ```objc if ([CLLocationManager locationServicesEnabled]) { // インスタンスを生成 _locationManager = [[CLLocationManager alloc] init]; // デリゲートを設定 _locationManager.delegate = self; // 位置情報の取得開始 [_locationManager startUpdatingLocation]; } ``` この状態で位置情報が更新されると、次のメソッドが呼ばれます。 ```objc // 位置情報が更新されるたびに呼ばれる - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { self.latTextField.text = [NSString stringWithFormat:@"%.2f", newLocation.coordinate.latitude]; self.lonTextField.text = [NSString stringWithFormat:@"%.2f", newLocation.coordinate.longitude]; } ``` 引数 `newLocation` から現在地を取得することができます。 ### 詳しい記事 * [iOSで緯度・経度を取得する方法メモ](http://blog.koogawa.com/entry/2013/11/23/190909) ### サンプルコード * https://github.com/koogawa/iSensor/blob/master/iSensor/LocationViewController.m ## 6. 電子コンパス 磁力センサーを利用して、iPhoneの向きを計測することができます。 ### 実装方法 取得を開始します。 ```objc if ([CLLocationManager headingAvailable]) { // インスタンスを生成 _locationManager = [[CLLocationManager alloc] init]; // デリゲートを設定 _locationManager.delegate = self; // 何度動いたら更新するか(デフォルトは1度) _locationManager.headingFilter = kCLHeadingFilterNone; // デバイスの度の向きを北とするか(デフォルトは画面上部) _locationManager.headingOrientation = CLDeviceOrientationPortrait; // ヘディングイベントの開始 [_locationManager startUpdatingHeading]; } ``` この状態でiPhoneの向きが変わると、次のメソッドが呼ばれます。 ```objc // デバイスの方位が変わるたびに呼ばれる - (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading { CLLocationDirection heading = newHeading.magneticHeading; self.textField.text = [NSString stringWithFormat:@"%.2f", heading]; } ``` `magneticHeading` には 0.0 から 359.9 の値が入ります。北の方向がちょうど 0.0 になり、それから東、南、西の順に値が増えていき、北に戻ると再び 0.0 に戻ります。 | 方角 | 値 | |:-----------|------------:| | 北 | 0.0 | | 東 | 90.0 | | 南 | 180.0 | | 西 | 270.0 | ### 詳しい記事 * [【Tips】iOSで電子コンパスを使う](http://blog.koogawa.com/entry/2013/11/22/155025) ### サンプルコード * https://github.com/koogawa/iSensor/blob/master/iSensor/HeadingViewController.m ## 7. 標高 GPSを使って、現在地の標高を測定できます。 ### 実装方法 現在地情報の取得を開始します。 ```objc if ([CLLocationManager locationServicesEnabled]) { // インスタンスを生成 _locationManager = [[CLLocationManager alloc] init]; // デリゲートを設定 _locationManager.delegate = self; // 位置情報の取得開始 [_locationManager startUpdatingLocation]; } ``` この状態でiPhoneの位置情報が更新されると、次のメソッドが呼ばれます。 ```objc // 位置情報が更新されるたびに呼ばれる - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { self.textField.text = [NSString stringWithFormat:@"%.2f m", newLocation.altitude]; } ``` 引数 `newLocation` の `altitude` プロパティで標高が取得できます。単位はメートルです。 ### 詳しい記事 * [【Tips】iOSで標高を測定する](http://blog.koogawa.com/entry/2013/11/23/112832) ### サンプルコード * https://github.com/koogawa/iSensor/blob/master/iSensor/AltitudeViewController.m # 移動・動作系 ## 8. 加速度センサー 加速度センサーを利用することで、例えば iPhone の傾きによって画面上のボールが転がる、などと言った動きのあるアプリを作ることができます。 ### 実装方法 インスタンスを生成します。 ```objc _motionManager = [[CMMotionManager alloc] init]; ``` 適切な更新間隔を設定をして加速度センサーを有効化します。 ```objc if (_motionManager.accelerometerAvailable) { // センサーの更新間隔の指定 _motionManager.accelerometerUpdateInterval = 1 / 10; // 10Hz // ハンドラを指定 CMAccelerometerHandler handler = ^(CMAccelerometerData *data, NSError *error) { // 画面に表示 self.xLabel.text = [NSString stringWithFormat:@"x %f", data.acceleration.x]; self.yLabel.text = [NSString stringWithFormat:@"y %f", data.acceleration.y]; self.zLabel.text = [NSString stringWithFormat:@"z %f", data.acceleration.z]; }; // 加速度の取得開始 [_motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:handler]; } ``` `startAccelerometerUpdatesToQueue`メソッドを実行すると、指定した更新間隔でハンドラの内容が実行されます。 ### 詳しい記事 * [【Tips】iOSで加速度センサーを使用する](http://blog.koogawa.com/entry/2013/11/26/022710) ### サンプルコード * https://github.com/koogawa/iSensor/blob/master/iSensor/AccelerometerViewController.m ## 9. 歩数・進行状況 CoreMotionを使って、歩数カウントを取得できます。(現時点ではiPhone 5Sのみ対応) ### 実装方法 `CMStepCounter ` インスタンスを生成します。 ```objc _stepCounter = [[CMStepCounter alloc] init]; ``` 歩数のカウントを開始します。 ```objc [_stepCounter startStepCountingUpdatesToQueue:[NSOperationQueue mainQueue] updateOn:1 withHandler:^(NSInteger numberOfSteps, NSDate *timestamp, NSError *error) { // 歩数が更新されるたびに呼ばれる }]; ``` * 第1引数:handler を呼び出す queue を指定 * 第2引数:何歩歩いたら handler を呼び出すか(実際にはこの指定通りには動かなかった) * 第3引数:歩数更新時に呼び出す handler 第3引数で指定する Blocks には、計測を始めてからの累計歩数、更新時のタイムスタンプ、エラー情報が返ってきます。 また、歩数以外にもユーザの歩行情報(歩いているのか、走っているか等)も取得できます。詳しくは、下記の記事を参照してください。 ### 詳しい記事 * [【Tips】iOSで歩数をカウントする](http://blog.koogawa.com/entry/2013/11/28/013437) ### サンプルコード * https://github.com/koogawa/iSensor/blob/master/iSensor/MotionActivityViewController.m # その他 ## 10. 顔検出 Core Image を使って、顔を検出することができます。 ### 実装方法 インスタンスを生成します。 ```objc NSDictionary *options = [NSDictionary dictionaryWithObject:CIDetectorAccuracyHigh forKey:CIDetectorAccuracy]; CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:options]; ``` `detectorOfType` には検出タイプを指定します。今回は顔検出なので、`CIDetectorTypeFace` を指定します。((今のところ CIDetectorTypeFace しか設定できないようです)) `options` には検出精度 `CIDetectorAccuracy` 等を指定できます。 次に、顔検出を実行します。 ```objc CIImage *ciImage = [[CIImage alloc] initWithCGImage:originalImage.CGImage]; NSDictionary *imageOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1] forKey:CIDetectorImageOrientation]; NSArray *array = [detector featuresInImage:ciImage options:imageOptions]; ``` `featuresInImage` には、顔検出したい画像を `CIImage` クラスで指定します。 `options` には画像の向きなどを指定します。カメラの画像を利用する場合には、デバイスの方向を正しく指定しないと顔検出ができないので注意してください。 正常に顔が検出されると、検出結果が `CIFaceFeature` オブジェクトで返ってきます。このオブジェクトには、検出された顔の範囲や、目と口の位置などが含まれます。 * bounds - 顔の範囲 * leftEyePosition - 左目の位置 * mouthPosition - 口の位置 * rightEyePosition - 右目の位置 ここで注意したいのは、`CoreImage` は、<b>左下の座標が (0,0) となる</b>点です。よって、検出元画像の上に部品を載せたい場合は、座標をUIKitの座標系に変換する必要があります。詳しくは、下記の記事を参照してください。 ### 詳しい記事 * [【Tips】iOSの顔検出機能を使ってみる](http://blog.koogawa.com/entry/2013/11/30/154347) ### サンプルコード * https://github.com/koogawa/iSensor/blob/master/iSensor/FaceDetectionViewController.m # さいごに 輝度センサーから顔検知までざっと書いてみましたが、いかがでしたでしょうか。すべての項目を詳しく書いていると、相当な量になってしまうため、各項目は簡単な説明に留めました。もっと詳しく知りたい場合はぜひ「詳しい記事」も参照してみてください。 なお、今回紹介した10項目のサンプルコードは、ひとつのアプリにまとめてGithubにアップしてあります。すぐにビルドして使えるようにしてあるので、ぜひダウンロードしてみてくださいね。 [https://github.com/koogawa/iSensor](https://github.com/koogawa/iSensor) この記事が皆様のお役に立てれば光栄ですm(_ _)m |
|
| 225位 |
|
|||
|
14:09:48 |
|
|
Ruby on RailsでDev環境は使ったことあるけど、test・prod環境を考慮した環境構築をしたことがない人にお勧めの内容です。 ##サーバー構成図  ##サーバーの役割 ###リバースプロキシサーバー(ホスト名:rp01) * ロードバランサ機能を使ってWEBサーバ二台に処理を振り分け、アクセスを1台のサーバーに集中させない * WEBサーバーを外部から隠せることでセキュリティ面の向上 ###WEBサーバー(web01、web02) * webサーバーを2台用意することでアクセスが1つのサーバーに集中しないため、レスポンスを早くできる ###マスターDBサーバー(db01m) * DB内容をもう1台のDBサーバー(スレーブ)へリアルタイムにコピーし、障害でマスターが停止したときはスレーブに切り替える ###スレーブDBサーバー(db01s) * 読み込み専用のサーバー。書き込みをしない分レスポンスが早くなる * マスターの内容を常にコピーする(レプリケーションという) ###開発環境(dev01) * 開発はこの環境だけで行い、完成物をWEBサーバーにアップロードする(デプロイという) ###DNS(rp01) * 各サーバーのホスト名を管理するDNS。リバースプロキシと同じサーバー内で動作させる ##準備 * VPS等でサーバーを5台借りておく(rp01、web01、web02、db01m、db01s) * リバースプロキシはグローバルIP必須 * 開発環境(手持ちにMacやUnix環境がないならvpsで借りる) ##サーバー仕様 ###WEBサーバー・リバースプロキシ * Apache/2.2.15 (Unix) ###データベース * MySQL Server 5.1.71 ###DNS * mydns 1.2.8.31 ###Ruby * ruby-2.0.0-p353 ###Ruby on Rails * Rails 4.0.2 ##全サーバーに共通するセットアップ ###ログインユーザーのセキュリティ設定編 初期インストールされたパッケージをアップデート `$ sudo yum update` ログインユーザー(deploy01)の作成 ``` $ useradd deploy01 $ passwd deploy01 ``` deploy01をsudo権限が使えるグループに所属させる ``` $ usermod –G wheel deploy01 ``` wheelグループ以外はsudo権限を与えない設定。 下記行のコメントを外す。 ```zsh:/etc/pam.d/su #auth required pam_wheel.so use_uid ``` wheelグループのみrootユーザーにログイン可能にする。 下記行を追加 ```vim:/etc/login.defs SU_WHEEL_ONLY yes ``` wheelグループのユーザーのみ sudoコマンドを実行許可。 下記コマンドを実行する。 ``` $ visudo ``` すると、設定ファイルが開くので下記行のコメントを外す ``` # %wheel ALL=(ALL) ALL ``` rootユーザーでのsshログインを禁止する。 下記行をコメント化する ```vim:/etc/ssh/sshd_config PermitRootLogin no ``` SSHのポートを狙った攻撃を回避するため、ポート番号を変更する。 Portの後ろに変更したいポート番号を入れる ```vim:/etc/ssh/sshd_config Port 3243 ``` SSHの変更を有効にするため、SSHを再起動するコマンドを実行 `$ /etc/init.d/sshd restart` ###ファイアウォール編 下記コマンドを実行するとファイアウォールの設定情報を確認できる ``` $ /sbin/iptables -L --line-number Chain INPUT (policy ACCEPT) num target prot opt source destination Chain FORWARD (policy ACCEPT) num target prot opt source destination Chain OUTPUT (policy ACCEPT) num target prot opt source destination ``` 上記と同じ表示の場合、すべての通信を許可しているためセキュリティ上問題がある。 各サーバーごとにファイアウォールを設定する。 #### リバースプロキシ(rp01)のファイアウォール設定 外部パケットから入ってくるパケットのうち http、https、ssh(ポートは3824)、DNSのみ許可する。 ```vim:/etc/sysconfig/iptables *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT ## allow well-known port ## -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT ## allow port(SSH,http,https,DNS) ## -A INPUT -m state --state NEW -m tcp -p tcp --dport 3824 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT -A INPUT -m state --state NEW -m udp -p udp --dport 53 -j ACCEPT COMMIT ``` #### WEBサーバー(web01、web02)の設定 ※IPアドレスをホスト名に置き換えてます。 ```vim:/etc/sysconfig/iptables *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT #------------------------------------------------------ # ping、ループバックアドレスを許可 #------------------------------------------------------ -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT #------------------------------------------------------ # RPのみ、webサーバーへの通信を許可 # 80は本番環境(prod)のサイト表示 # 8000はテスト環境(test)のサイト表示 #------------------------------------------------------ -A INPUT -s rp01 -p tcp --dport 80 -j ACCEPT -A INPUT -s rp01 -p tcp --dport 8000 -j ACCEPT #------------------------------------------------------ # SSHを許可 #------------------------------------------------------ -A INPUT -m state --state NEW -m tcp -p tcp --dport 3843 -j ACCEPT COMMIT ``` #### DBサーバー(db01m)の設定 ※IPアドレスをホスト名に置き換えてます。 ``` *filter #------------------------------------------------------ # メンテナンス可能リスト # web01 # web02 # db01s #------------------------------------------------------ #------------------------------------------------------ # ポリシー:全ての送受信パケットを破棄。必要な用途に応じて開けていく。 #------------------------------------------------------ :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT DROP [0:0] #------------------------------------------------------ # pingとループバックアドレス(自分自身のIP)からの送受信を許可 #------------------------------------------------------ -A INPUT -i lo -j ACCEPT -A OUTPUT -o lo -j ACCEPT -A INPUT -p icmp -j ACCEPT -A OUTPUT -p icmp -j ACCEPT #------------------------------------------------------ # 内部からのDNS送信を許可 #------------------------------------------------------ -A OUTPUT -m state --state NEW -m udp -p udp --dport 53 -j ACCEPT #------------------------------------------------------ # 内部からのhttp送信を許可(yum等) #------------------------------------------------------ -A OUTPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT #------------------------------------------------------ # メンテナンスのみSSH接続を許可 #------------------------------------------------------ -A INPUT -s web01 -p tcp --dport 3843 -j ACCEPT -A OUTPUT -d web01 -p tcp --sport 3843 -j ACCEPT -A INPUT -s web02 -p tcp --dport 3843 -j ACCEPT -A OUTPUT -d web02 -p tcp --sport 3843 -j ACCEPT -A INPUT -s db01s -p tcp --dport 3843 -j ACCEPT -A OUTPUT -d db01s -p tcp --sport 3843 -j ACCEPT #------------------------------------------------------ # メンテナンスのみMySQL接続を許可 #------------------------------------------------------ -A INPUT -s web01 -p tcp --dport 3306 -j ACCEPT -A OUTPUT -d web01 -p tcp --sport 3306 -j ACCEPT -A INPUT -s web02 -p tcp --dport 3306 -j ACCEPT -A OUTPUT -d web02 -p tcp --sport 3306 -j ACCEPT -A INPUT -s db01s -p tcp --dport 3306 -j ACCEPT -A OUTPUT -d db01s -p tcp --sport 3306 -j ACCEPT #------------------------------------------------------ # 接続済みパケットの送受信を許可 #------------------------------------------------------ -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT COMMIT ``` #### DBサーバー(db01s)の設定 ※IPアドレスをホスト名に置き換えてます。 ``` *filter #------------------------------------------------------ # メンテナンス可能リスト # web01 # web02 # db01m #------------------------------------------------------ #------------------------------------------------------ # ポリシー:全ての送受信パケットを破棄。必要な用途に応じて開けていく。 #------------------------------------------------------ :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT DROP [0:0] #------------------------------------------------------ # pingとループバックアドレス(自分自身のIP)からの送受信を許可 #------------------------------------------------------ -A INPUT -i lo -j ACCEPT -A OUTPUT -o lo -j ACCEPT -A INPUT -p icmp -j ACCEPT -A OUTPUT -p icmp -j ACCEPT #------------------------------------------------------ # 内部からのDNS送信を許可 #------------------------------------------------------ -A OUTPUT -m state --state NEW -m udp -p udp --dport 53 -j ACCEPT #------------------------------------------------------ # 内部からのhttp送信を許可(yum等) #------------------------------------------------------ -A OUTPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT #------------------------------------------------------ # メンテナンスのみSSH接続を許可 #------------------------------------------------------ -A INPUT -s web01 -p tcp --dport 3843 -j ACCEPT -A OUTPUT -d web01 -p tcp --sport 3843 -j ACCEPT -A INPUT -s web02 -p tcp --dport 3843 -j ACCEPT -A OUTPUT -d web02 -p tcp --sport 3843 -j ACCEPT -A INPUT -s db01m -p tcp --dport 3843 -j ACCEPT -A OUTPUT -d db01m -p tcp --sport 3843 -j ACCEPT #------------------------------------------------------ # メンテナンスのみMySQL接続を許可 #------------------------------------------------------ -A INPUT -s web01 -p tcp --dport 3306 -j ACCEPT -A OUTPUT -d web01 -p tcp --sport 3306 -j ACCEPT -A INPUT -s web02 -p tcp --dport 3306 -j ACCEPT -A OUTPUT -d web02 -p tcp --sport 3306 -j ACCEPT -A INPUT -s db01m -p tcp --dport 3306 -j ACCEPT -A OUTPUT -d db01m -p tcp --sport 3306 -j ACCEPT #------------------------------------------------------ # 接続済みパケットの送受信を許可 #------------------------------------------------------ -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT COMMIT ``` すべての設定を書き終えたらiptablesを再起動してファイアウォールを有効化 ``` $ service iptables restart ``` ファイアウォールの設定は以上。 開発環境のファイアウォールは任意で設定してください。 ## MySQL(データベースサーバー)のインストール ### Mysqlのパッケージについて mysqlのパッケージには大きく分けて3種類ある * mysql:client用 * mysql-server:server用 * mysql-devel:アプリケーションとの連携用 そのため、用途によってはインストールするパッケージを絞ることが可能 terminalからmysqlに接続する * mysql railsやmydns等のアプリケーションからmysqlサーバーに接続 * mysql、mysql-devel データベース運用 * mysql-server 今回はサーバーごとにインストールするパッケージを変える ### MySQLのインストール rp01(DNSサーバー用として使う) `$ yum install mysql mysql-devel mysql-server` web01、web02(rails用にインストール) `$ yum install mysql mysql-devel` db01m、db01s(mysqlデータベース用) `$ yum install mysql mysql-server` ### mysqlの設定 以下をサーバー5台全てに設定する mysqlの設定ファイルをバックアップする `$ sudo cp /etc/my.cnf /etc/my.cnf.org` MySQLの設定ファイルをバックアップ `$ sudo cp /etc/my.cnf /etc/my.cnf.org` MySQLの設定ファイルを下記のように設定 ```vim:/etc/my.cnf [mysqld] character-set-server = utf8 skip-character-set-client-handshake datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock user=mysql symbolic-links=0 innodb_buffer_pool_size = 512MB # サーバのメモリ70〜80% innodb_log_file_size = 100MB # 100-500MB程度 innodb_flush_log_at_trx_commit = 2 [mysqld_safe] log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid ``` rootのパスワード設定 ``` $ sudo mysqladmin -u root password 'パスワード' ``` mysql-serverをインストールしている rp01とdb01m、db02mのみMySQLの自動起動をONにする ``` $ sudo service mysqld start $ sudo chkconfig mysqld on ``` MySQLにログインし、不要なユーザーとデータベースを削除する ``` $ mysql -u root -p **** mysql > use mysql; mysql > select user,password, host from user; +------+-------------------------------------------+-------------------------+ | user | password | host | +------+-------------------------------------------+-------------------------+ | root | *hogehogehogehogehogehogehogehogehogehoge | localhost | | root | | hogehogehogehogeh.ne.jp | | root | | 127.0.0.1 | | | | localhost | | | | hogehogehogehogeh.ne.jp | +------+-------------------------------------------+-------------------------+ mysql > DELETE FROM user WHERE user = '' OR ( user = 'root' AND host != 'localhost' ); Query OK, 4 rows affected (0.00 sec) mysql > select user,password, host from user; +------+-------------------------------------------+-----------+ | user | password | host | +------+-------------------------------------------+-----------+ | root | *hogehogehogehogehogehogehogehogehogehoge | localhost | +------+-------------------------------------------+-----------+ 1 row in set (0.00 sec) ``` 続いて、不要なデータベースを削除 ``` mysql > show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | test | +--------------------+ 3 rows in set (0.00 sec) mysql> DROP DATABASE test; Query OK, 0 rows affected (0.00 sec) ``` 文字セットの確認 ``` show variables like 'character_set%'; # ↓ のようになっていれば成功 +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | utf8 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | utf8 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+ 8 rows in set (0.00 sec) ``` ### rails用のデータベースを作成 ``` mysql > create database app; mysql > create database app_test; mysql > create database app_dev; ``` ## mydns(DNSサーバー)のインストール ### mydnsとは? MySQLのデータベースを使って、ホスト名の管理ができる。 ホストを追加するときはinsert文を1つ打つだけで設定可能なので管理が楽。 ### インストールについて MyDNSのインストールは任意。 SSH接続やiptables、apache設定でipを使わずホスト名を使いたい人は 設定してください。(この記事は全てホスト名になっています) ### インストール方法 インストールはrp01で行います。 mydnsをダウンロードするためのリポジトリを追加 ``` rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm ``` mydnsパッケージのインストール ``` $ yum install mydns $ yum install mydns-mysql ``` mysqlにログインし、mydns用データベースを作成 ``` mysql > create database mydns; ``` ターミナルから下記コマンドを実行 `$ mydns --create-tables | mysql -u root -p mydns` mysqlにログインし、mydns用のテーブルができているか確認 ``` mysql > use mydns; Database changed mysql> show tables; +-----------------+ | Tables_in_mydns | +-----------------+ | rr | | soa | +-----------------+ ``` mydns用のユーザーを作成する。 ※データベースmydnsに対して、全ての権限を持ったユーザー(全てのホストからのアクセス許可) ``` GRANT ALL ON mydns.* TO 'mydns'@'%' IDENTIFIED BY 'mydns'; Query OK, 0 rows affected (0.01 sec) ``` DNSゾーンとレコードの作成 ※ネットワークをxxx.localにする ※xxx.xxx.xxx.xxxはIPアドレスです ``` mysql> insert into soa (origin, ns) values ('local.', 'ns.local'); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> insert into rr (zone, name, type, data) values(1, 'web01', 'A', ‘xxx.xxx.xxx.xxx’); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> insert into rr (zone, name, type, data) values(1, 'web02', 'A', 'xxx.xxx.xxx.xxx’); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> insert into rr (zone, name, type, data) values(1, 'db01m', 'A', 'xxx.xxx.xxx.xxx’); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> insert into rr (zone, name, type, data) values(1, 'db01s', 'A', 'xxx.xxx.xxx.xxx’); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> insert into rr (zone, name, type, data) values(1, ‘rp01, 'A', 'xxx.xxx.xxx.xxx’); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> insert into rr (zone, name, type, data) values(1, ‘dev01’, 'A', 'xxx.xxx.xxx.xxx’); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> select * from rr; +----+------+-------+------------------+-----+-------+------+ | id | zone | name | data | aux | ttl | type | +----+------+-------+------------------+-----+-------+------+ | 1 | 1 | web01 | xxx.xxx.xxx.xxx | 0 | 86400 | A | | 2 | 1 | web02 | xxx.xxx.xxx.xxx | 0 | 86400 | A | | 3 | 1 | db01m | xxx.xxx.xxx.xxx | 0 | 86400 | A | | 4 | 1 | db01s | xxx.xxx.xxx.xxx | 0 | 86400 | A | | 5 | 1 | rp01 | xxx.xxx.xxx.xxx | 0 | 86400 | A | | 6 | 1 | dev01 | xxx.xxx.xxx.xxx | 0 | 86400 | A | +----+------+-------+------------------+-----+-------+------+ mysql > select * from soa; +----+--------+----------+------+--------+---------+-------+--------+---------+-------+ | id | origin | ns | mbox | serial | refresh | retry | expire | minimum | ttl | +----+--------+----------+------+--------+---------+-------+--------+---------+-------+ | 1 | local. | ns.local | | 1 | 28800 | 7200 | 604800 | 86400 | 86400 | +----+--------+----------+------+--------+---------+-------+--------+---------+-------+ 1 row in set (0.00 sec) ``` ###DNSの設定とmydnsの起動 ホスト名で通信したいすべてのサーバーで下記を設定 xxx.xxx.xxx.xxxはrp01のグローバルIPアドレス ```vim:/etc/resolv.conf search local nameserver xxx.xxx.xxx.xxx ``` mydnsを起動 ``` $ service mydns start ``` 設定したホスト名でpingが通るか確認 ``` ping web01 ``` ##レプリケーションの設定 ### レプリケーションとは データベースの内容を別のデータベースにリアルタイムで同期させる。 コピ元をマスター、コピー先をスレーブという。 ### レプリケーションの設定方法 マスターのdb01mのmy.cnfを下記のように設定 ```vim:/etc/my.cnf [mysqld] log-bin=mysql-bin server-id=1001 ``` スレーブのdb01sにマスターに接続するmysqlユーザーを作成するため、 db01sのmysqlにログインして、下記を実行 ※xxx.xxx.xxx.xxxはmasterのipアドレス ``` GRANT REPLICATION SLAVE ON *.* TO 'repl’@‘xxx.xxx.xxx.xxx’ IDENTIFIED BY 'slavepass'; ``` スレーブのmy.cnfファイル内で以下の通り設定 ```vim:/etc/my.cnf [mysqld] server-id=1002 ``` マスター側のpositionを確認 ``` mysql> SHOW MASTER STATUS; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000002 | 379 | | | +------------------+----------+--------------+------------------+ 1 row in set (0.00 sec) ``` それぞれの意味 File : バイナリログ名 Position : ファイル内のオフセット(現在位置) Positionとファイル名をメモしておき、マスターのデータベースのテーブルをロックしておく ``` mysql > FLUSH TABLES WITH READ LOCK ``` マスターのデータベースのバックアップを取る `$ mysqldump -u root -p --all-databases --lock-all-tables > /tmp/dbdump.db` スレーブ側でSCPコマンドを使って、マスターのdbファイルを取得 `$ scp -P sshのポート番号 deploy01@db01m :/tmp/dbdump.db /tmp/` スレーブのデータベースにマスターの内容を反映 `# mysql -u root -p /tmp/dbdump.db ` スレーブ側でマスター情報を登録 xxx.xxx.xxx.xxxにはマスターのipアドレスを入れる ``` mysql > CHANGE MASTER TO MASTER_HOST=‘xxx.xxx.xxx.xxx’, MASTER_USER='repl', MASTER_PASSWORD='XXXXXX', MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=379; ``` レプリケーション開始するため、スレーブ側で下記コマンドを実行 ``` mysql > START SLAVE; ``` レプリケーションが開始されているか確認 ``` show slave status; mysql> show slave status \G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: xxx.xxx.xxx.xxx Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000002 Read_Master_Log_Pos: 450 Relay_Log_File: mysqld-relay-bin.000003 Relay_Log_Pos: 251 Relay_Master_Log_File: mysql-bin.000002 Slave_IO_Running: Yes Slave_SQL_Running: Yes ``` 下記を確認できればDBの同期が取れている ``` Slave_IO_Running: Yes Slave_SQL_Running: Yes ``` ## Rubyのインストール 作業はweb01とweb02で行う rootユーザーでインストールすると、root権限が必要なときがあるため deploy01ユーザーにインストールを行う。(シングルインストール) ### rvmを使ったRubyのインストール rvmはRubyのバージョン管理を容易に行える。 コマンド1つでバージョンを切り替えたり、バージョンごとのインストールが可能 必要なパッケージのインストール ``` $ sudo yum -y install curl curl-devel gcc gcc-c++ httpd-devel readline-devel tk-devel make zlib-devel libffi-devel ``` rvmをインストール ``` $ bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer ) $ echo "source $HOME/.rvm/scripts/rvm" >> ~/.bash_profile $ source ~/.bash_profile ``` Rubyのver 2.0.0をインストール ``` $ rvm install 2.0.0 ``` RVMの設定確認 ``` $ rvm list =* ruby-2.0.0-p247 [ x86_64 ] # ``` ### railsの導入 railsのインストール(RDocのインストール部分で結構時間かかる) ``` $ gem install rails ``` RDocが不要な場合は下記でインストールする ``` $ gem install rails –no-ri –no-rdoc ``` passangerのインストールを行う ``` $ gem install passenger $ passenger-install-apache2-module ``` インストール完了後、下記のような表示が出るのでコピー ``` LoadModule passenger_module /usr/lib64/ruby/gems/1.8/gems/passenger-4.0.19/buildout/apache2/mod_passenger.so PassengerRoot /usr/lib64/ruby/gems/1.8/gems/passenger-4.0.19 PassengerDefaultRuby /usr/bin/ruby ``` passenger.confに上記設定とオプションを記入 ```vim:/etc/httpd/conf.d/passnger.conf # Passengerの基本設定 LoadModule passenger_module /usr/local/rvm/gems/ruby-2.0.0-p247@global/gems/passenger-4.0.18/buildout/apache2/mod_passenger.so PassengerRoot /usr/local/rvm/gems/ruby-2.0.0-p247@global/gems/passenger-4.0.18 PassengerDefaultRuby /usr/local/rvm/wrappers/ruby-2.0.0-p247@global/ruby # Passengerが追加するHTTPヘッダを削除するための設定 Header always unset "X-Powered-By" Header always unset "X-Rack-Cache" Header always unset "X-Content-Digest" Header always unset "X-Runtime" # 必要に応じてPassengerのチューニングのための設定を追加 PassengerMaxPoolSize 20 PassengerMaxInstancesPerApp 4 PassengerPoolIdleTime 3600 PassengerHighPerformance on PassengerStatThrottleRate 10 RailsSpawnMethod smart RailsAppSpawnerIdleTime 86400 PassengerMaxPreloaderIdleTime 0 ``` ### デプロイ用のディレクトリを作成 test環境とprod環境の2つを用意する ``` $ sudo mkdir /var/www/deploy01/ $ sudo chown deploy01:deploy01 /var/www/deploy01/ $ chmod 775 /var/www/deploy01/ $ mkdir /var/www/deploy01/prod/ $ mkdir /var/www/deploy01/test/ ``` ### 開発環境側の設定 開発環境側(dev01)はappというアプリが出来ている状況とします。 database.ymlにslaveとmasterのdb情報を設定する ```ruby:config/database.yml development: adapter: mysql2 encoding: utf8 database: app_dev pool: 5 username: root password: xxxx host: localhost test_slave_database: adapter: mysql2 encoding: utf8 database: app_test pool: 5 username: root password: xxx host: db01s test: adapter: mysql2 encoding: utf8 database: app_test pool: 5 username: root password: xxx host: db01m production_slave_database: adapter: mysql2 encoding: utf8 database: app pool: 5 username: root password: xxx host: db01s production: adapter: mysql2 encoding: utf8 database: app pool: 5 username: root password: xxx host: db01m ``` Gemfileに下記パッケージを記載する ```ruby:Gemlife gem 'multi_db' ``` #### デプロイツールのインストール web01とweb02にgitをインストール ``` $ sudo yum install git ``` appアプリ配下で下記を実行 ``` $ gem install capistrano ``` インストールが完了したらcapifyコマンドを実行 ``` capify . > [add] writing './Capfile' > [add] writing './config/deploy.rb' > [done] capified! ``` test環境とprod環境用のCapistrano設定ファイルを作成 ``` $ mkdir config/deploy $ touch config/deploy/test.rb $ touch config/deploy/production.rb ``` 共通のデプロイ情報をdeploy.rbに記入 ipアドレスをホスト名に変えてます。 ```ruby:app/config/deploy.rb # -*- coding: utf-8 -*- set :default_stage, "production" # 複数環境にデプロイできるようにする require "capistrano/ext/multistage" # capistranoの出力をカラーに require 'capistrano_colors' # cap deploy時に自動で bundle installを実行 require "bundler/capistrano" # RVM require "rvm/capistrano" set :rvm_ruby_string, ‘2.0.0-p353’ set :rvm_type, :user # gitリポジトリの設定 #### gitリポジトリをセット #### set :repository, “gitのリポジトリ” set :scm, :git set :deploy_via, :remote_cache # SSH set :use_sudo, false set :default_run_options, :pty => true ssh_options[:port] = 22 ssh_options[:forward_agent] = true set :normalize_asset_timestamps, false # 過去のデプロイしたフォルダを履歴として保持する数 set :keep_releases, 5 # デプロイ先のサーバの設定 server “web01:3843", :app, :web, :db, :primary => true server “web02:3843", :app, :web, :db, :primary => true # bundle install条件 set :bundle_flags, "--no-deployment --without test development" # SSH set :user, "deploy01" set :user_group, "deploy01" set :password, “SSHのログインパスワード” # RVM set :rvm_path, '/home/deploy01/.rvm/' set :rvm_bin_path, “app/bin" set :rvm_lib_path, “app/lib" # assets:precompile namespace :assets do task :precompile, :roles => :web do run "cd #{current_path} && RAILS_ENV=#{rails_env} bundle exec rake assets:precompile" end end # メンテナンスモード namespace :maintenance do desc "Maintenance start" task :on, :roles => :web do on_rollback { run "rm #{current_path}/tmp/maintenance.yml" } page = File.read("config/maintenance.yml") put page, "#{current_path}/tmp/maintenance.yml", :mode => 0644 end desc "Maintenance stop" task :off, :roles => :web do run "rm #{release_path}/tmp/maintenance.yml" end end namespace :deploy do # Passengerの実行ユーザー/Groupをセット task :set_file_process_owner do sudo "chown -R #{user}:#{user_group} #{deploy_to}" end #### sitemap_generatorを使ってない場合は削除 #### desc "sitemapの更新" task :refresh_sitemaps do run "cd #{latest_release} && RAILS_ENV=#{rails_env} bundle exec rake sitemap:refresh" end # 本番サーバでPassenger以外を使っている場合は適宜変更して下さい。 desc "Passenger用に起動/停止タスクを変更" task :restart, :roles => :web do run "touch #{current_path}/tmp/restart.txt" end # page_cacheを使用していない場合は不要 desc "キャッシュhtmlの削除(deploy中に不完全なhtmlを生成しないための対策)" task :remove_caches, :roles => :web do run "rm -rf #{current_path}/public/index.html" end #### Wheneverを使っていない場合は削除 desc "wheneverのアップデート" task :whenever_update do run "cd #{latest_release} && RAILS_ENV=#{rails_env} bundle exec whenever --update-crontab #{application} -f config/schedule_#{rails_env}.rb" end end before :deploy, "deploy:set_file_process_owner" ``` prod用の設定 ```ruby:app/config/deploy/production.rb #### デプロイ先のフォルダを設定 #### set :deploy_to, "/var/www/deploy01/prod/app” set :rails_env, "production" # deploy ========================== after :deploy, "maintenance:on" after :deploy, "assets:precompile" after :deploy, "deploy:migrate" after :deploy, "deploy:restart" after :deploy, "deploy:remove_caches" after :deploy, "maintenance:off" after :deploy, "deploy:cleanup" ``` test用の設定 ```ruby:app/config/deploy/test.rb #### デプロイ先のフォルダを設定 #### set :deploy_to, "/var/www/deploy01/test/app” set :rails_env, “test” # deploy ========================== after :deploy, "assets:precompile" after :deploy, "deploy:migrate" after :deploy, "deploy:restart" after :deploy, "deploy:remove_caches" after :deploy, "deploy:cleanup" ``` メンテナンス画面表示用にconfig/maintenance.ymlを追加。 ``` --- reason: メンテナンス中です allowed_paths: - ^/help - ^/contact_us allowed_ips: - 127.0.0.1 - 192.168.0.0/24 ``` リポジトリへのパスフレーズ入力を省略するため、ssh-agentの追加をする ※鍵にはgitリポジトリの秘密鍵を指定 ``` # ssh-agent zsh # ssh-agent ~/.ssh/id_rsa ``` デプロイができるかのテスト(初回のみ) ``` cap deploy:check ``` デプロイコマンド一覧 ``` セットアップ(初回に実行) $ cap production deploy:setup $ cap test deploy:setup デプロイを実行 $ cap production deploy:cold $ cap test deploy:cold 以前の状態に戻す $ cap production deploy:rollback $ cap test deploy:rollback 以前の状態のソースコードを削除 $ cap production deploy:cleanup $ cap test deploy:cleanup ``` ### Apacheの設定 deployが完了したらweb01とweb02のhttpd.confにrailsアプリの設定を行う xxx.xxx.xxx.xxxにはweb01ならweb01のIPを記入 ```vim:/etc/httpd/conf/httpd.conf # prod <VirtualHost *:80> ServerName xxx.xxx.xxx.xxx DocumentRoot /var/www/deploy01/prod/app/current/public/ ErrorLog /var/log/httpd/prod_app_error.log CustomLog /var/log/httpd/prod_app_access.log combined <Directory "/var/www/deploy01/prod/app/current/public"> Options -MultiViews AllowOverride All Order allow,deny Allow from all </Directory> </VirtualHost> # test <VirtualHost *:8000> RailsEnv test ServerName xxx.xxx.xxx.xxx DocumentRoot /var/www/deploy01/test/app/current/public/ ErrorLog /var/log/httpd/test_app_error.log CustomLog /var/log/httpd/test_app_access.log combined <Directory "/var/www/deploy01/test/app/current/public"> Options -MultiViews AllowOverride All Order allow,deny Allow from all </Directory> </VirtualHost> ``` serverNameでvirtualhostを分ける設定をONにするため 下記行のコメントを外す ```vim:/etc/httpd/conf/httpd.conf #NameVirtualHost *:80 ``` web01とweb02のapacheを再起動 ``` $ sudo service httpd restart ``` iptablesを停止して、testとprod環境にアクセスできるか確認 ``` http://xxx.xxx.xxx.xxx:80/ http://xxx.xxx.xxx.xxx:8000/ ``` testとprod環境が動いていることを確認できたらiptablesを再スタートする ``` $ sudo service iptables start ``` ## リバースプロキシの設定 rp01のhttp.confファイルに下記モジュールを設定 ```vim:/etc/httpd/conf/httpd.conf LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_balancer_module modules/mod_proxy_balancer.so LoadModule proxy_ftp_module modules/mod_proxy_ftp.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule proxy_ajp_module modules/mod_proxy_ajp.so LoadModule proxy_connect_module modules/mod_proxy_connect.so ``` さらに、rp01のhttp.confにロードバランサの処理を追加 ※ipアドレスをホスト名に置き換えてます ``` #prod環境へのアクセスを「xxx.com」 #test環境へのアクセスを「test.xxx.com」のサブドメインにした場合の設定 #prod設定 <VirtualHost *:80> ServerName xxx.com:80 ProxyPass /balancer-manager ! <Location /balancer-manager> SetHandler balancer-manager </Location> ProxyPass / balancer://rp01/ ProxyPassReverse / balancer://rp01/ <Proxy balancer://rp01/> BalancerMember http://web01:80 loadfactor=5 BalancerMember http://web02:80 loadfactor=5 </Proxy> </VirtualHost> #test設定 <VirtualHost *:80> ServerName test.xxx.com:80 ProxyPass /balancer-manager ! <Location /balancer-manager> SetHandler balancer-manager </Location> ProxyPass / balancer://test-rp01/ ProxyPassReverse / balancer://test-rp01/ <Proxy balancer://test-rp01/> BalancerMember http://web01:8000 loadfactor=5 BalancerMember http://web02:8000 loadfactor=5 </Proxy> </VirtualHost> ``` loadfactorの数字を大きくすると、使用頻度が大きくなります。 web01・web02のapacheを交互に停止して、xxx.comにアクセスできたらロードバランサの設定が完了している。 ## 余談 DTIのVPSを借りれば、月420円 * 5台 = 2100円で実現可能です。 DTIの場合、aaa.bbb.ccc.1とaaa.bbb.ccc.2のように、グローバルIPの4桁目以外が同じの場合は セキュリティ上、通信が出来ないようです(T_T) |
|
| 226位 |
|
|||
|
17:28:32 |
(Wantedly, Inc. 所属) |
|
何個もCommitがあるような一つのPull Requestを全てRevertしたいようなときに使えます。 ## そもそもRevertとは あるコミットを打ち消すような、全く逆のコミットを作ることです。 追加した部分を削除して、削除した部分を追加して、変更した部分を変更前の状態にするコミットを作成します。 取り消したいコミットがあるのだけれど、既にリモートにコミットしてしまって、`git reset`, `git rebase -i`, `git reflog`などを使っての取り消しが不可能なときに使います。 ## 通常のRevert 普通のcommitなら、revertは ``` $ git revert f60f24d ``` てな感じで、revertしたいcommit(今回は`f60f24d`)を指定すれば、実現出来ます。 ## Merge Commitの場合 ただ、もし`f60f24d`がMerge Commitだった場合 ``` $ git revert f60f24d fatal: Commit f60f24d34845fba4e038b3e165f74973b3a19580 is a merge but no -m option was given. ``` というエラーメッセージが出ます。 これを解決するには ``` $ git revert -m 1 f60f24d ``` という風に`-m 1`と付けるだけです。 ## 念のため確認 念のため、ちゃんと目的のものがrevertされているか確認しましょう。 ``` $ git show ``` あれ、思っている方と違う方がRevertコミットされている!だめじゃん!!と思ったら、 ``` $ git reset --hard HEAD~ ``` で元の状態に戻して、 ``` $ git revert -m 2 f60f24d ``` という風に`-m 2`に変えて試してみましょう。 ## ちょっと解説 ちなみにこの`-m`は`--mainline`の略で`parent-number`を指定します。 この`parent-number`には基本的に1か2を指定すれば良く、`git show`をした時に ``` $ git show f60f24d commit f60f24d34845fba4e038b3e165f74973b3a19580 Merge: 049d32b ebbcb6a ... ``` と出たとしたら、1は`049d32b`, 2は`ebbcb6a`を指します。 ## 参考 - http://git-scm.com/docs/git-revert - http://schacon.github.com/git/howto/revert-a-faulty-merge.txt |
|
| 227位 |
|
|||
|
16:17:23 |
|
|
以前は問題なく動いていたはずの機能が、最新版では動かなくなっている・・・。こんなときは、「どのコミットが問題を混入させてしまったのだろうか?」を知りたくなるでしょう。 これを手助けするのが [git bisect](http://git-scm.com/docs/git-bisect) コマンドです。git bisect コマンドは、二分探索によって問題箇所を特定します。 事前準備 ======== 最初に大事なことがひとつあります。それは、__「問題がない(good)状態と問題がある(bad)状態を、確実に判定できるようにする」__ことです。 当然のことではありますが、ここがあやふやだと、二分探索をしても問題箇所をうまく特定できません。 可能なら、「テストスクリプトを1つ実行するだけで判定」できるようにしたほうが良いです。このとき、テストスクリプトは、git リポジトリからチェックアウトした作業ツリーに対して実行できるようにします(例えばソースからのビルド処理もテストスクリプトに含めます)。また、テストスクリプトは、good なら 0 を、bad なら 1~127(ただし 125 は除く)を返すようにします。 テストスクリプトが用意できない場合は、手動でテストを行うこともできます。 git bisect の実行方法 ===================== good と bad の指定 ------------------ まず、問題がない(good)時点と問題がある(bad)時点を指定します。 $ git bisect start <bad-commit> <good-commit> たとえば、V1.0 タグの時点では good で、最新(HEAD)では bad である、とすると次のように指定します。 $ git bisect start HEAD V1.0 これを実行すると、bad と good の間にある適当な中間バージョンがチェックアウトされます。 テストスクリプトの指定 ---------------------- 次に、テストスクリプトを指定します(テストスクリプトが用意できない場合は、次の「手動でテストする」に進んでください)。 $ git bisect run <テストスクリプトのファイル名> これを実行すると、git が自動的に「テスト実行」と「中間バージョンのチェックアウト」を繰り返して、問題が混入した時点を見つけてくれます。処理が終わるまで特にすることはありませんので、コーヒーでも飲んで心を落ち着けながら待ちましょう。 無事終了すれば、問題が混入した時点のバージョンがチェックアウトされています。コミット内容を調べて問題を解決しましょう。 手動でテストする ---------------- テストスクリプトが用意できない場合、手動でテストを行います。 テストの結果、問題がない(good)なら以下を実行します。 $ git bisect good テストの結果、問題がある(bad)なら以下を実行します。 $ git bisect bad 上記のどちらかを実行すると、git が自動的に bad と good の間にある中間バージョンをチェックアウトしてくれます。これに対して、ふたたび手動でテストを行います。 手動の「テスト実行」と git による「中間バージョンのチェックアウト」を繰り返すと、問題が混入した時点が特定できます。コミット内容を調べて問題を解決しましょう。 その他のコマンド ================ 現在チェックアウトしているコミットを確認する -------------------------------------------- git bisect がチェックアウトしているコミットを見るには、次のコマンドが便利です。 $ git bisect view デフォルトだと gitk で表示します。オプション -p をつけると、パッチ形式でコンソールに出力します。 二分探索の過程を確認する ------------------------ git bisect のログを表示します。 $ git bisect log もとの状態に戻す ---------------- git bisect を実行する前の状態に戻すには、以下を実行します。 $ git bisect reset まとめ ====== git bisect を使う機会はそんなに多くはないと思いますが、こういうものがあるということを覚えておくと、いざというときに助かります。実際に使ってみると、きっと感動しますよ。 |
|
| 228位 |
|
|||
|
13:56:38 |
(フリーランス 所属) |
|
今更感があるというか、自分も分かってるつもりでRails以外でも触るMySQL DBの扱いで少し困ったので再整理しておく。
ちなみにバージョンは以下の場合である。 - Rails 4以降 - MySQL 5.1以降 RailsにおけるTimeZone関連の設定は以下のとおり。 - config.time_zone (各地の都市名等を指定する) - config.active_record.default_timezone (:utc or :local) 考えなければいけない時間は概ね以下のとおり。 - システムのタイムゾーンと、システム時間 - Railsのタイムゾーンと、Time.nowとTime.zone.now(Time.current) - データベースのタイムゾーンとデータベースに記録されている時間。 おさらい、Time.zone.nowはActiveSupport::TimeWithZoneクラスのインスタンス。ActiveRecordでdatetime型のカラムにアクセスした場合もこれになる。 ActiveRecordのdefault_timezoneのデフォルト値は:utc。 既に良く分からなくなってきた……。 ややこしいので、とりあえずデータベースは自分が良く使っているMySQLのみを考える。基本的にはMySQLに関する話だと思って欲しい。 ## 時間の対応関係 JSTは`+9:00`、CSTは`-6:00` | 概要 | System TZ | System Time | Rails TZ | ActiveRecord TZ | Time.now | Time.current | DB TZ | DB Time | AR Instance Time | | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | | 何も考えずにRailsを使う場合のデフォルト | JST | 2:00 | UTC | :utc | 2:00 JST | 17:00 UTC (TWZ) | SYSTEM(JST) | 17:00 | 17:00 UTC (TWZ) | | time_zone Tokyo | JST | 2:00 | Tokyo(JST) | :utc | 2:00 JST | 2:00 JST (TWZ) | SYSTEM(JST) | 17:00 | 2:00 JST (TWZ) | | active_record.default_timezone :local | JST | 2:00 | Tokyo(JST) | :local | 2:00 JST | 2:00 JST (TWZ) | SYSTEM(JST) | 2:00 | 2:00 JST (TWZ) | | time_zone なし, active_record.default_timezone :local | JST | 2:00 | UTC | :local | 2:00 JST | 17:00 UTC (TWZ) | SYSTEM(JST) | 2:00 | 17:00 UTC (TWZ) | | time_zone Central Time | JST | 2:00 | CST | :utc | 2:00 JST | 11:00 CST (TWZ) | SYSTEM(JST) | 17:00 | 11:00 CST (TWZ) | | time_zone Central Time, active_record.default_timezone :local | JST | 2:00 | CST | :local | 2:00 JST | 11:00 CST (TWZ) | SYSTEM(JST) | 2:00 | 11:00 CST (TWZ) | ## 整理 まず、ここまでで一旦整理する。 Time.nowはRubyの組み込みなのでシステムのタイムゾーンしか見ない。OSの時間と常に一致する。Time.localの出力結果もOSのタイムゾーンと一致する。 TimeWithZoneクラスは`config.time_zone`に左右される。 Ruby組み込みのメソッドで取得したUTCの時間を基準に、設定されているタイムゾーンの時間に変換する。 ActiveRecordのインスタンスに対してアクセサを利用して時間をやり取りする場合はTimeWithZoneで行われる。 仮にTimeクラスを渡しても代入時にTimeWithZoneに変換される。 `config.active_record.default_timezone`の設定はDBを読み書きする際に、DBに記録されている時間を`Time.utc`で読むか`Time.local`で読むかを設定する。 `:utc`の場合DBに記録されている時間はUTC扱いで、この時DBサーバのタイムゾーン設定は考慮しない。 ActiveRecordのインスタンスが持っているTimeWithZoneの値をUTCに変換し、その時刻をDBに書き込む。 `:local`の場合は、DBに記録されている時間はシステムのタイムゾーンとして扱う。 ActiveRecordのインスタンスが持っているTimeWithZoneの値をシステムのタイムゾーンに変換し、その時刻をDBに書き込む。 ## まとめ `config.time_zone`は、Railsのアプリケーション上で表示したい時間の基準となるタイムゾーンでOSとは独立している。しかし基本的にはOSのタイムゾーンと合わせておくのが安全だと思う。 もし、マシンが海外にあってOSの時計はUTCだが、メインターゲットが日本だったりする場合は、日本のタイムゾーンにしたくなるかもしれない。 そうすると、ActiveRecordやActiveSupportに関する時間は、デフォルトで日本時間で表示される。 しかし、Time.now等を混在させると`strftime`等で整形した場合に時間がズレてしまう。 徹底してTimeWithZoneを使うなら大丈夫かもしれないが、OSのタイムゾーンに合わせておいて、適宜タイムゾーンを変換する方が分かりやすいように思う。 まあ、シリコンバレーに行って、特定の国向けのサービスを作るとかでない限りは、あんまり考えることは無いかもしれない。 `config.active_record.default_timezone`は、DBのタイムゾーンと一致させておくべきだ。 DBのタイムゾーンがUTCなら`:utc`にDBのタイムゾーンがSYSTEMなら`:local`にしておく。 ## 問題点 表を見れば分かるのだが、初っ端のデフォルトからDBに記録される値がズレている。 これは、普通日本で使うマシンの時計はJSTで、MySQLのタイムゾーンのデフォルトがSYSTEMでJSTになるからだ。 しかも、MySQLのDATETIME型はタイムゾーン情報を持てないし、ActiveRecordはMySQLのタイムゾーン設定を考慮しない。 そのため、UTCじゃないマシンで何も考えずにデフォルトの設定でRailsを利用するとDBに記録される時間がズレることになる。 JSTになっているDBのタイムゾーンに合わせて正しい時間で書き込むためには、`config.active_record.default_timezone`を`:local`にしておく必要がある。 しかし、残念なことに`config.active_record.default_timezone`の設定はかなり地味だったりする。 デフォルトで生成されるコンフィグファイルの雛形には影も形も出てこない。 Rails Guidesにはちゃんと説明があるのだが、ちゃんと読んでないと知らないままだったりする。 自分も知ってはいたものの、設定するの忘れてたり、どっちが正しかったんだっけ?となることがしばしばある。 これは、Railsから出ない限りは余り問題にならないから、なおのこと気付きにくい。 なので、これが`:utc`でMySQLがSYSTEMになったまま運用されているRailsアプリが一杯あると思う。 Railsから出ないでDBに触っていればOKなのだが、Rails外で時間を触るとこれはバグの温床になる。 まず、MySQLの内部で時間を取得する場合。例えばMySQLの`NOW()`関数を使うとOSの時間と一緒の時間が取れるのだが、カラムに記録されている時間は9時間前の時間になっている。もしこの値を使おうとすると時間が噛み合わなくなる。しかも、MySQLが取得する時間そのものにはタイムゾーン情報は無い。なので、これを使って比較すると日付の境目当たりでバグって死ぬことが良くある。 RailsでTime.currentを取得して、ActiveRecordのクエリ経由で時間を渡せばちゃんと変換されてクエリに渡るので、正しく比較できる。 更に、MySQLのタイムゾーンを考慮する別システムからDBにアクセスする場合。特にJDBCからMySQLに接続する場合等、オプションがカオスで訳が分からないことになっていて、デフォルトでどういう挙動をするか良く分からない。もしMySQLサーバのタイムゾーンを考慮して時間を変換してクエリを実行する動作をする場合、やはり記録されている時間から9時間ズレてしまう。 というわけで、新しくRailsのシステムを開発する場合は、DBのタイムゾーン設定と`config.active_record.default_timezone`を合わせるように注意した方が良い。 もし、その設定に気付かず(忘れて)運用を開始してしまった場合は、MySQLのタイムゾーンの設定をUTCにした方が良いかもしれない。 他のシステムで触っていなければ、これでカラムの値とタイムゾーンが一致する。 しかし、ここにもまだ問題があって、もしTIMESTAMP型が混在してたらタイムゾーンを変えると困ったことになる。 TIMESTAMP型は書き込む時にサーバのタイムゾーン設定を確認してUTCに変換してからデータを保存する。取得する時はUTCからサーバのタイムゾーンに変換してから表示する。 なので、記録時と異なるタイムゾーンに変更すると取得した時の時間がズレてしまう。 (ALTER TABLEで型を変えたらどうなるかまでは調べてません……。) 既に時間がごっちゃに記録されていたら、まあ諦めてどっちかに合わせるしか無いかな……。 そもそも、こんなことで色々悩むのも、Railsのデフォルト設定値とMySQLのデフォルト設定値が噛み合ってない上にMySQLの時間カラムはタイムゾーン情報持ってないし、RailsはMySQLのタイムゾーンを無視するし、JDBCはタイムゾーン考慮して勝手に時間変換するしで、何が何の設定で何から何に時間を変換してるのかがさっぱり分からんからだ。 もう時差とかタイムゾーンの無い世界に行きたい……。 Postgresとかはカラム自体にタイムゾーン込みでデータ保持できるのかな。とすると、MySQLが無駄にややこしいだけ、という話にもなるが……。orz |
|
| 229位 |
|
|||
|
13:18:07 |
(株式会社Subot 所属) |
|
Androidアプリ配布プラットフォームのDeployGateが、ついにiOSアプリ配布対応されました!
早速使ってみたのでその流れをメモしておきます。 <br/> まずは[DeployGateのページ](https://deploygate.com/beta_program)でアカウント作成。 Githubアカウントでアカウント作成できるのがcoolでgeekですね。  <br /> ログインするとダッシュボード画面が表示されるので、左下の「アップロード」ボタンからおもむろにipaファイルをアップロードします。 アップロードボタンはAndroid版と共通なのですね。  <br /> すると、登録したメールアドレスにメールが届くので、開発端末でメールを開きます。 メールの中にリンクがあるので、このリンクをsafariで開くと、アプリのインストール画面が開きます。 この時、端末とアプリの配布管理のための構成プロファイルがインストールされていないのでインストールされます。 構成プロファイルインストール後、このリンクもしくは構成プロファイルがインストールされるときに作成されるiPhone上のDeployGateアプリから、先ほどアップロードしたipaファイルがインストールできるようになります。 ||  |  | <br/> 簡単ですね! <br/> 後は共同開発者を招待し、ガンガン配布しまくりましょう。 [シンプルなコマンドラインツールも提供されており](https://deploygate.com/docs)、Jenkinsから使うのも簡単そうです。 ```bash #dgateコマンドインストール $curl https://deploygate.com/install.sh | /bin/sh #ipaのアップロード $ dgate push k_kinukawa dokusyokanri.ipa ``` <br/> 多分ハマるところはipaファイルの作成だと思います。 これはGoogle検索すれば色々出てくるのでそちらを参照してください。 ポイントとしては、 - AdHocのprovisioning profileを作成し、Xcodeでアーカイブする際にそれを指定しておくこと - 作成されたアーカイブをオーガナイザーから「Distribute > Save for Enterprise or Ad Hoc Deployment」でipaファイル作成する ってところでしょうか。   <br/> 個人的にDeployGateのiOS版は待ち望んでいたので、とても嬉しいです。 個人プロジェクトや次に仕事で立ち上げるプロジェクトでは積極的に使っていこうと思います。 2014/2/27追記 makeコマンド一発でビルド > アーカイブ > deploygateで自動配信 までできたので、[別エントリーに書きました](http://qiita.com/k_kinukawa/items/42c240a9e9e8b6cb4923)。 合わせて読みたい。 |
|
| 230位 |
|
|||
|
17:22:48 |
|
|
git を https 経由で使う場合、pull や push のたびに毎回パスワードを聞かれてしまいます。
これを改善するには [git-credential](http://git-scm.com/docs/gitcredentials) を使うと良いです。 git-credential は git 1.7.9 以降で使用可能です。 なお、古いやり方としては .netrc を使う方法もありますが、パスワードを平文でファイルに保存するので、やらないほうがいいと思います。 # 使用可能な管理方式 git-credential では、以下のような方法でユーザ名とパスワードを管理できます。 * `git-credential-store` : ファイルに保存します。ただし、パスワードが平文が保存されます。 * `git-credential-cache` : 常駐プロセスに記憶させます。 * `git-credential-osxkeychain` : Mac OS X のパスワード管理を使います。 * `Git-Credential-Manager-for-Windows` : Windows のパスワード管理を使います。 * `git-credential-gnomekering` : GNOME のパスワード管理を使います。 なお、git 本家の contrib に、Windows 用の wincred というものが入っているようです。試していませんが・・・。 # cache cache 方式は標準で付属しています。 次のコマンドで cache 方式が使用できます。 ```sh git config --global credential.helper cache ``` 初回パスワード入力時に git-credential-cache--daemon プロセスが常駐して、タイムアウト時間が過ぎるまでパスワードを記憶します。 タイムアウトは、デフォルトでは 900 秒(15分)です。変更するには、次のようにタイムアウト時間(例では 3600 秒)を指定します。 ```sh git config --global credential.helper 'cache --timeout=3600' ``` # osxkeychain osxkeychain は git の contrib として付属しています。MacPorts や Homebrew でインストールすると有効になっています。 次のコマンドで osxkeychain が使用できます。 ```sh git config --global credential.helper osxkeychain ``` 初回パスワード入力時に OS X キーチェーンに登録され、以後はパスワード入力が不要になります。 # windows 別途インストールが必要です。 https://github.com/Microsoft/Git-Credential-Manager-for-Windows 手順通りインストールを行えば良いです。 初回パスワード入力時に Internet Explorer などと同じ場所にアクセストークンが保存され、以後はパスワード入力が不要になります。 # gnomekeyring gnomekeyring は別途インストールが必要です。 https://github.com/shugo/git-credential-gnomekeyring 手順通りビルド・インストールを行えば良いです。 初回パスワード入力時に GNOME キーリングに登録され、以後はパスワード入力が不要になります。 # 最後に 実は同様のことが github のヘルプにほとんど書いてありました。 https://help.github.com/articles/set-up-git |
|
| 231位 |
|
|||
|
21:23:57 |
|
|
前案件でBEMチックな書き方をして、あまりのクラス名の長さに
キィェエエエ!!ってなった後で、 CodeGridさんでSMACSSについてのステキ記事を見つけたのでメモ。 ※以下、CodeGridさんの記事に書いてることを、自分が後で見るようにプチ要約したものです。 本記事をまだ読んだことが無い人はここよりも、本記事の方をじっくり読むのをお勧めします。 [SMACSSによるCSSの設計 - ベースとレイアウト | CodeGrid](https://app.codegrid.net/entry/smacss-1) # CSSカテゴライズ 5つにカテゴライズして、それに準じた名前を付ける 1.ベース 2.レイアウト 3.モジュール 4.状態(ステート) 5.テーマ #1.ベースルール サイト全体で要素そのもののデフォルトスタイル ``` body a:link table ... ``` 1パターンしかないからといって、ここでデザインを指定しない。 CSSのリセットは使用を避けて、なるべくNormalize.css を使う。 #2.レイアウトルール ページのエリア分け ``` .l-main .l-sub ``` プレフィックスで`l-`をつけて一目でわかるように。 IDが絶対だめってわけじゃないけど、 ID要素は重複利用できなかったり、スタイルの上書きがむずかしくなるから、なるべくクラスを使う CSSでは位置や配置の指定をする。 (フォントや色はモジュールや状態の方で指定する) ##子孫セレクタを使って分岐 ```css .l-main { width: 70%; } .l-fixed .l-main { width: 600px; } ``` `.l-fixed`のついてるのはpx指定、そうでなければ%指定 #3.モジュールルール 再利用可能なパーツ ``` ・ ロゴ ・ナビゲーション ・タブ などページを構成するパーツすべて ``` レイアウトパーツの中に入れて、 どこに置いても再利用できるように独立させておく。 ##レイアウトとモジュールの合わせ技 ```html <div class="l-container-12"> <div class="l-grid-06"> <div class="box">box1</div> </div> <div class="l-grid-06"> <div class="box">box2</div> </div> </div> ``` * レイアウト .l-grid-01~nという風に幅が違うクラスを量産する これで、レイアウトの調整が無限大! * モジュール .boxは汎用的なの ##モジュールの命名規則 親モジュールの名前をプレフィックスでつける ```html <div class="item"> <p class="item-text"></p> </div> ``` ##クラス名をつけるタイミング モジュール全部にクラス付ける必要はない。 * li、a ...クラス名をつけなくてもいいよ * div、span ...クラス名つける liは階層が深くなる場合>を使う。 見出しはあとでh2→h3に変わったり、 階層が変わる場合もあるから、クラス名付いたdivで囲む方がいいっぽい。 ##親要素での使い分けはしない 同じ要素だけど置く場所が違うものは、 親要素によっての指定はしない。 ```html <div class="l-main"> <div class="search">~</div> </div> <div class="l-sub"> <div class="search">~</div> </div> ``` こうじゃなくて ```html <div class="l-main"> <div class="search">~</div> </div> <div class="l-sub"> <div class="search search-vertical">~</div> </div> ``` こう。 `.search-vertical`に指定することで、どこに移動してもスタイルを維持できる。 #4.状態ルール 特定の状態によってスタイルを上書きする 状態の切りかえはJavascript * 結果がtrueの場合のとき プレフィックス is- * 表示/非表示 .is-hidden {} .is-error {} .is-active {} 例 .is-dialog-hidden .is-tab-active !importantはなるべく避ける。 状態ルールはモジュール名を付けてあげた方が競合しなくてよさそう。 #5.テーマルール ##テーマ管理用のCSS ```css:main.css .box { border: 1px solid; } ``` ```css:theme.css .box { background-color: #eee; border-color: #ccc; } ``` 色に関わる部分をテーマで管理する ##プレフィックス theme- ```css:theme.css .theme-border { border-color: #ccc; } .theme-background { background-color: #eee; } ``` # SMACSS のゴール * HTMLとコンテントのセマンティックな価値を向上すること * 特定の HTML 構造への依存を低減すること # 参考サイト * [SMACSSによるCSSの設計 - ベースとレイアウト | CodeGrid](https://app.codegrid.net/entry/smacss-1) * [SMACSS 読んだ - CHROMA](http://chroma.hatenablog.com/entry/2013/07/22/120818) *** 「自分以外の誰が見てもわかる」+「CSSを編集することがあまりない」 そんな更新しやすい組み方ができたらいーなーと思う今日この頃。 |
|
| 232位 |
|
|||
|
21:19:23 |
|
|
ここ1月ほど、google spreadsheetを用いた案件管理シートを効率化するために、google app scriptを使って処理の自動化を行いました。その中で、google spreadsheetに対してgoogle app scriptを書く際の基本的な処理コードを、気をつけるポイント・高速化のコツと共にまとめます。
# 基本の操作 ## シートの取得 シートのデータを取得する場合、最初はシートを取得するのが基本かと思います。 ```js //シートの名前で呼ぶ場合 var sheet = SpreadsheetApp.getActive().getSheetByName('シート1'); //今開いているシートを呼ぶ場合 var sheet = SpreadsheetApp.getActiveSheet(); ``` SpreadSheetクラスとSheetクラスがありますが、SpreadSheetがシートファイル全体(エクセルでいうところの.xlsxファイル)を指し、Sheetクラスがシートファイル内の各シートを指します。 ### 2回以上呼ぶならメモ化で高速化 複雑な処理を書き始めると、上記コードを何度も呼ぶことになりますが、2回以上呼ぶようであれば、メモ化を行うことでAPIへのアクセス回数を減らしましょう。 google spreadsheetのスクリプトで高速化の基本は、APIアクセスを減らすことです。 ```js // 特定のシートのメモ化を行う関数 function getMainSheet() { if (getMainSheet.memoSheet) { return getMainSheet.memoSheet; } getMainSheet.memoSheet = SpreadsheetApp.getActive().getSheetByName('メインシート'); return getMainSheet.memoSheet; } // 最初に呼ばれる関数 function firstCalledFunc() { var sheet = getMainSheet(); //APIを叩いてシートを取得 // some code .... } // 次に呼ばれる関数 function secondCalledFunc() { var sheet = getMainSheet(); //メモ化されたシートを返す // some code .... } ``` ## データの取得 データを取得する場合は、取得したいデータの範囲をRangeオブジェクトを用いて指定したり、全てのデータを取得したりできます。 ```js //シート全体のデータを取得する場合 var data = sheet.getDataRange().getValues(); // dataにはシートのデータが2次元配列で入る //特定の行(1行目)のデータを取得する場合 var rowIndex = 1; var colStartIndex = 1; var rowNum = 1; var data = sheet.getRange(rowIndex, colStartIndex, rowNum, sheet.getLastColumn()).getValues(); //または、以下も使える // sheet.getSheetValues(rowIndex, colStartIndex, rowNum, sheet.getLastColumn()); ``` ### シート全体のデータ探索処理は、JS側でチェックするほうが速い 案件管理などの場合、完了しているタスクに対して色を塗る等の処理が必要となります。このような、シートの各行の値に応じて何か処理をしたい場合は、各行のデータをgetRangeで取得し各行の状態に応じて色を塗ったりしてもよいですが、APIを多く叩くことになるため処理時間が長くなります。 先にシート全体のデータを取得し、js側で各行のデータをループさせ、処理をする行の位置をマークした後、マークした行群に対して色を塗るAPIを叩く処理のほうがよいでしょう。 ```js var statusColumnIndex = 2; //ステータスを保持する列のインデックス var sheet = SpreadsheetApp.getActiveSheet(); var data = sheet.getDataRange().getValues(); var finishedRowIndexes = []; //ステータスが完了済みの行数を保持する for (var i = 0; i < data.length; i++) { if (data[i][statusColumnIndex] === '完了') { finishedRowIndexes.push(i + 1); } } for (var i = 0; i < finishedRowIndexes.length; i++) { // 完了済みの行に色を塗る sheet.getRange(finishedRowIndexes[i], 1, 1, sheet.getLastColumn()).setBackgroundColor('#eeeeee'); } ``` ## 色塗り シートを編集していると、色を塗る処理なども使われたりするかと思います。RangeクラスのsetBackgroundColorでrangeで指定された範囲のセルを塗れます。 ```js var sheet = SpreadsheetApp.getActiveSheet(); //1行目だけに色を塗る sheet.getRange(1, 1, 1, sheet.getLastColumn()).setBackgroundColor('#eeeeee'); //2行目から5行目まで色を塗る sheet.getRange(2, 1, 3, sheet.getLastColumn()).setBackgroundColor('#eeeeee'); ``` 同じ色が近接セルにあるならまとめて塗ると、APIを叩く回数を減らせます。 ただし、1行飛ばし、みたいなRange指定はできません。 また、色塗りに関連して、罫線の太さを設定できるかと思ったのですが、太い罫線は現時点では引けないようです。 ## シート間の行移動(コピー)・同期 今回、案件管理のやり方として、以下のようなシート配置で行いました。 1. マスターシートに案件の全情報を置く。 2. 案件は、マスターシートに1案件が1行として保存されており、案件ID, 担当者、ステータスがある。 3. 各ユーザーのシートにユーザーが担当する案件を、マスターシートからコピーする。 このような方法にしたため、マスターシートから、各ユーザーのシートに案件行をコピーする必要がありました。この場合は空白行を追加した後、その領域にデータをsetValuesすることで実現できます。 ```js var userSheet = SpreadsheetApp.getActive().getSheetByName('担当:ryan5500'); var copyData = [[] ...]; //ユーザーシートにコピーするデータ // 2行目の前に空白行を挿入 userSheet.insertRowsBefore(2, copyData.length); // 挿入した空白行領域にデータをコピー var range = userSheet.getRange(2, 1, copyData.length, userSheet.getLastColumn()); range.setValues(copyData); ``` ### シート間の情報の同期 各ユーザーのシートで、例えば案件のステータスを変更したことを、マスターシートにも反映したい、という場合があります。その場合は、セル編集時のトリガで呼び出される関数onEditの引数で、シートと変更したセルの位置を受けとれるので、それに応じて処理をするような形で実現できます。 ```js function onEdit(event) { var idColumnIndex = 0; //シート内で案件IDがセットされている列のインデックス var editedRowIndex = event.range.getRow(); //編集された行のインデックス var sheetName = event.source.getSheetName(); //編集されたシートの名前 //編集されたシートから、編集された行の案件データを取得する var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName); var editedRowData = sheet.getSheetValues(editedRowIndex, 1, 1, sheet.getLastColumn()); //マスターシートのデータを取得する var masterSheet = SpreadsheetApp.getActive().getSheetByName('マスターシート'); var masterSheetData = masterSheet.getSheetValues(); for (var i = 0; i < masterSheetData.length; i++) { //マスターシートにおける、編集された案件の行を案件IDで探す if (masterSheetData[i][idColumnIndex] === editedRowData[0][idColumnIndex]) { //マスターシートのその行に対して編集された行の案件データを貼付ける var range = masterSheet.getRange(i + 1, 1, 1, masterSheet.getLastColumn()); range.setValues(editedRowData); break; } } } ``` ※ マスターシートでの変更もユーザーシートに反映されるべきであるため、このサンプルは必ずしも正しくありません。 ## カスタムメニュー Spreadsheetのgoogle app scriptでは、シートのメニューUI内にカスタムメニューを配置できます。 私の場合は、案件の追加などをマスターシートに対して処理した後に、各ユーザーのシートに案件を分配する処理を実行できるリンクにしています。 このような、ユーザーの特定の行動の後に実施したいタスクについては、カスタムメニューに入れると良いと思います。 ```js // シートが表示された際に呼び出される関数 function onOpen() { var ss = SpreadsheetApp.getActive(); var items = [{name: '案件を各担当者シートにコピー', functionName: 'customMenuMigrateCaseToUserSheet'}]; ss.addMenu('案件管理メニュー', items); } // カスタムメニューの「案件を各担当者シートにコピー」をクリックされると呼ばれる関数 function customMenuMigrateCaseToUserSheet() { Logger.log('here'); } ``` 基本の操作は以上です。次に、避けて通れないエディタやデバッグなどの開発スタイルについてです。 # 開発スタイルについて ## スクリプトエディタ spreadsheetに対応づくような、container-basedと言われるgasの場合、ソースコードのexport/import機能が(現時点では)使えません。そのため、お気に入りのエディタで編集したコードは、コピーした上でspreadsheetのスクリプトエディタを開き、貼付けることになります。 ただし、gasのスクリプトエディタは、基本的には2スペースインデントです。もとのコードが4スペースインデントだと、貼付けたあとちょっと手直しする場合に残念な気持ちになります。なので、お気に入りのエディタで開発される場合も、2スペースインデントをお勧めします。 ## デバッグ onOpen等のトリガのコールバック関数のデバッグは、引数を取らないため、再生ボタンを選べば簡単に実行できます。 しかし、onEdit等の、コールバック関数に渡す引数に、特定の値を渡すようなトリガの場合、デバッグ機能を有効活用できません。onEdit等のトリガのコールバック関数内は、できるだけサブの関数に分けて処理を進めるのがよいかと思います。 # 終わりに いかがでしたでしょうか? 1月ほどしか触っていないので、間違いのご指摘、もっとこうした方が速い!というご意見歓迎です! よろしくお願いします。 |
|
| 233位 |
|
|||
|
21:31:36 |
(メドピア 所属) |
|
## Karmaって何?
簡単に言うとnode.jsで作成された、テスト実行環境 間違ってたら(m´・ω・`)m ゴメン… ## Jasmineって何? Karmaはあくまでの実行環境なので テストの実処理を行うライブラリが必要 それがこれ 他にもmocha + (chai or expect)とかあるけど とりあえず、今回はJasmine ## 能書きはいいから、実際の作業をはよ 以下の2つは、インストール済みである事。 * node * npm ### ではでは 作業用ディレクトリ作成&移動 ```bash $ mkdir -p karma/spec $ mkdir -p karma/src $ cd karma ``` グローバル領域にkarmaをインストール ※- gがないと動かない事が多いため、修正しました。 ```bash $ npm install -g karma ``` karma初期化 ```bash $ karma init Which testing framework do you want to use ? Press tab to list possible options. Enter to move to the next question. > jasmine Do you want to use Require.js ? This will add Require.js adapter into files. Press tab to list possible options. Enter to move to the next question. > no Do you want to capture a browser automatically ? Press tab to list possible options. Enter empty string to move to the next question. > Chrome > Which files do you want to test ? You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js". Enter empty string to move to the next question. > Any files you want to exclude ? You can use glob patterns, eg. "**/*.swp". Enter empty string to move to the next question. > Do you want Testacular to watch all the files and run the tests on change ? Press tab to list possible options. > yes ``` karma.conf.jsってファイルが出来ました。 修正します。 ```bash $ vim karma.conf.js ``` ```karma.conf.js files: [ 'spec/*.js' ], ``` 簡単なテストケースを作成します。 ```bash $ vim spec/HelloSpec.js ``` ```spec/HelloSpec.js /** * http://pivotal.github.io/jasmine/ */ describe("Hello Test", function() { it("test", function() { var a = 'test1';//actual テストする値 var e = 'test';//expect 期待値 expect(a).toEqual(e); }); }); ``` 別ターミナルで次のコマンドを実行 ```bash $ cd work/karma $ karma start karma.conf.js ```` するとbrowserが立ち上がり、ターミナルにテスト結果が!! ```bash $ karma start karma.conf.js INFO [karma]: Karma server started at http://localhost:9876/ INFO [launcher]: Starting browser Chrome INFO [Chrome 27.0 (Mac)]: Connected on socket id FqwLPzInsYRW6baEegdg Chrome 27.0 (Mac) Hello Test test FAILED Expected 'test1' to equal 'test'. Error: Expected 'test1' to equal 'test'. at null.<anonymous> (work/karma/spec/HelloSpec.js:9:15) Chrome 27.0 (Mac): Executed 1 of 1 (1 FAILED) (0.107 secs / 0.02 secs) ``` テスト駆動開発は、まずはerrorを起こすことから始まります。 では、次はテストを成功させてみましょう。 karmaのいいところは、テストファイルを修正して、保存すれば テストが実行されることです。 #監視最高!!! では、修正 ```spec/HelloSpec.js /** * http://pivotal.github.io/jasmine/ */ describe("Hello Test", function() { it("test", function() { var a = 'test';//actual テストする値 var e = 'test';//expect 期待値 expect(a).toEqual(e); }); }); ``` 今回は、var aの値を変更しました。 これで、a = eとなります。 そして、karmaを実行したターミナルを見ると ```bash INFO [watcher]: Changed file "/work/karma/spec/HelloSpec.js". Chrome 27.0 (Mac): Executed 1 of 1 SUCCESS (0.293 secs / 0.011 secs) ``` #SUCCESS Javascriptでのテストをもっと流行らせたいです。J # 参考 Karma 公式ページ http://karma-runner.github.io/0.8/index.html Jasmine コマンド一覧 http://pivotal.github.io/jasmine/ |
|
| 234位 |
|
|||
|
23:56:17 |
|
|
最近ホットな Vim Plugin を3つ紹介します。どれもカッコいいプラグインですので、気に入ったら是非使ってみてください。 ##vim-over ###vim-overの概要 [vim-over](https://github.com/osyo-manga/vim-over)は、EmacsのEvilのような一括置換ができるプラグインです。 どういうことかというと、置換する対象文字列がハイライトされ、さらに、その横に変更予定の文字列を表示するという、見た目がすごくカッコいい置換なのです。 Vimでは、`:%s/foo/bar/g`としても、ハイライトされませんし、変更予定の文字列も表示されません。 たしかに、1つずつ確認するには、`:%s/foo/bar/gc`でできますが、どうも味気なく、地味なのです。 しかし、`vim-over`を使うと、そんな地味さも吹っ飛びます。以下の画像を見てください。画像では、一括置換を実行していますが、視覚的に分かりやすいし、カッコいい置換になっています。 `%s/command/testpaste` ###vim-overのインストール プラグイン管理をしている場合は、以下のようにします。プラグイン管理というのは、具体的には、[Vundle](https://github.com/gmarik/vundle)や[NeoBundle](https://github.com/Shougo/neobundle.vim)を使った方法を言います。 ```~/.vimrc NeoBundle 'osyo-manga/vim-over' ``` `:so $MYVIMRC | NeoBundleInstall` プラグイン管理していない場合は、プラグインフォルダに当該プラグインをダウンロードします。 ``` $ mkdir -p ~/.vim/plugin/ $ cd !$ && git clone https://github.com/osyo-manga/vim-over.git ``` ###vim-overの使い方 `vim-over`の使い方は簡単です。基本的には、コマンドが設定されていますので、そちらで起動します。 ``` :OverCommandLine ``` キーマップで起動したい方は、`~/.vimrc`設定ファイルに以下を追記します。 ```~/.vimrc "" over.vim {{{ " over.vimの起動 nnoremap <silent> <Leader>m :OverCommandLine<CR> " カーソル下の単語をハイライト付きで置換 nnoremap sub :OverCommandLine<CR>%s/<C-r><C-w>//g<Left><Left> " コピーした文字列をハイライト付きで置換 nnoremap subp y:OverCommandLine<CR>%s!<C-r>=substitute(@0, '!', '\\!', 'g')<CR>!!gI<Left><Left><Left> " }}} ``` これで、`,`+`m`を押すと、`vim-over`を起動できるようになりました。ちなみに、`,`は`<Leader>`を意味します。キーボードによって異なりますので注意が必要です。 追記です。ノーマルモードで`sub`を押すと、カーソル下の単語をハイライト付きで置換できます。また、`subp`を押すと、コピーした文字列をハイライト付きで置換できます。 コマンドラインスペースでの操作方法は以下のとおりになっています。 |キー |効果 | |-----------|------------| | < Up > or < C-p > |1つ前のコマンド履歴を挿入 | |< Down > or < C-n > |1つ後のコマンド履歴を挿入 | | < C-r >< C-f >| カーソル下のファイル名を挿入| |< C-r >< C-w >|カーソル下の word を挿入| |< C-r >< C-r >|カーソル下の WORD を挿入| |< C-r >{0-9a-z"%#:-=.*}|番号/名前付きレジスタの挿入| 初期の`vim-over`は、割と見た目重視だったような気がするのですが、最近、本気で便利になってきました。 また、コマンドラインのキーマッピングする機能を追加されています。 ```~/.vimrc " OverCommandLineNoremap {lhs} {rhs} " {lhs} を {rhs} のキーとして割り当てる " ここで設定する事ができるキーは1文字、 " もしくは修飾キー+1文字のみ割り当てることができます OverCommandLineNoremap <A-b> <C-b> OverCommandLineNoremap <A-f> <C-f> ``` ##yankround.vim ###yankround.vimの概要 副作用の少ない[YankRing.vim](https://github.com/vim-scripts/YankRing.vim)を目指して作られたプラグインです。 `YankRing.vim`は他のプラグインや設定と干渉してしまう弊害があるので、好みが分かれるプラグインでした。 しかし、その機能が魅力的故に、我慢して使っている人も多かったみたいです。 そこで登場したのが、[yankround.vim](https://github.com/LeafCage/yankround.vim)です。 `YankRing.vim`とほぼ同じようなことが実現できて、かつ、弊害が少ないです。 ###yankround.vimのインストール ```~/.vimrc NeoBundle 'LeafCage/yankround.vim' NeoBundle 'kien/ctrlp.vim' ``` `:so $MYVIMRC | NeoBundleInstall` プラグイン管理していない場合は、プラグインフォルダに当該プラグインをダウンロードします。 ``` $ mkdir -p ~/.vim/plugin/ $ cd !$ && git clone https://github.com/LeafCage/yankround.vim.git ``` ###yankround.vimの使い方 ```~/.vimrc " yankround.vim {{{ "" キーマップ nmap p <Plug>(yankround-p) nmap P <Plug>(yankround-P) nmap <C-p> <Plug>(yankround-prev) nmap <C-n> <Plug>(yankround-next) "" 履歴取得数 let g:yankround_max_history = 50 ""履歴一覧(kien/ctrlp.vim) nnoremap <silent>g<C-p> :<C-u>CtrlPYankRound<CR> " }}} ``` 基本的には、ペースト直後に`C-n`を押して、履歴を辿れます。 そして、`,`+`g`+`C-p`を押して、履歴一覧を[ctrlp.vim](https://github.com/kien/ctrlp.vim)で表示し、選択することが出来ます。 ##airsave.vim ###airsave.vimの概要 [airsave.vim](https://github.com/syui/airsave.vim)は、簡単には、オートセーブのプラグインです。 現在行が変更されると、ノーマルモードに移行した時、ファイルが自動で保存されるようになります。 ###airsave.vimのインストール ```~/.vimrc NeoBundle 'syui/airsave.vim' ``` `:so $MYVIMRC | NeoBundleInstall` プラグイン管理していない場合は、プラグインフォルダに当該プラグインをダウンロードします。 ``` $ mkdir -p ~/.vim/plugin/ $ cd !$ && git clone https://github.com/syui/airsave.vim.git ``` ###wauto.vimの使い方 ```~/.vimrc nmap <Leader>s <Plug>(AutoWriteStart) nmap <Leader>ss <Plug>(AutoWriteStop) ``` キーでオートセーブの有効無効を切り替えます。 |キー|効果| |---|---| |< Leader >+s|オートセーブを有効にする| |< Leader >+ss|オートセーブを無効にする| 設定でオートセーブの有効無効を設定します。 ```~/.vimrc let g:auto_write = 1 ``` |
|
| 235位 |
|
|||
|
04:18:29 |
|
|
Rails Advent Calendar 11 日目です。
Railsの定番paginatorと言えば[kaminari](https://github.com/amatsuda/kaminari)です。Railsで開発をしたことがあれば、少なくとも名前くらいは聞いたことがあるでしょう。 大変使いやすく、ほぼゼロコンフィグでページネーションできますが、ドキュメントがシンプルなこともあって、ちょっと凝ったことをしようとすると情報を探すのに苦労します。 と言うわけで「kaminariを使って三日目」くらいの人向けに書いてみます。 ## Why kaminari? まずは基本的なところをおさらいします。 ActiveRecordオブジェクトをkaminariでpaginateするには、以下のようなコードを書きます。(Gemfileは設定済みとします) ```ruby Item.page ``` Item.all.pageとしては**いけません**。 これは以下のようなSQLを発行します。 ```ruby puts Item.page.to_sql #=> SELECT `items`.* FROM `items` LIMIT 25 OFFSET 0 puts Item.page(3).to_sql #=> SELECT `items`.* FROM `items` LIMIT 25 OFFSET 50 ``` 引数に整数を渡すとOFFSETが加算されていきます。 pageメソッドはActiveRecord::Relationオブジェクトを受け取ってActiveRecord::Relationオブジェクトを返すため、whereメソッドなどで絞り込んだ結果に対してpageメソッドを実行したり、pageメソッドで得られた結果に対してさらに操作したりできます。 また、一度に全てのレコードを取得しないので実行速度やメモリの点で有利です。 ## 基本 ページネーションするには以下のようにします。 ```ruby:items_controller.rb def index @items = Item.page(params[:page]) end ``` ```erb:index.html.erb <%= paginate @items %> ``` とりあえずこれだけで目的は達せます。 ## 1ページの件数を変えたい コントローラで都度変更するなら以下のようにします。 ```ruby:items_controller.rb def index @items = Item.page(params[:page]).per(10) end ``` デフォルトを変更するなら、モデルに以下の一行を追加します。 ```ruby:item.rb paginates_per 10 ``` デフォルトは25です。 ## 見た目を変えたい kaminari組み込みのデザインは十分クールだと思いますが、デザインを合わせたいなどということもあるでしょう。その場合は以下のようにします。 まず、以下のコマンドを実行します。 ``` rails g kaminari:views default ``` これでapp/views/kaminari以下にテンプレートが書き出されるので、それを変更します。 テンプレートの先頭に利用可能なローカル変数の一覧が出ているので、それを参考にクラスなどを設定していけば好みのデザインにできるでしょう。 なお、Twitter Bootstrapを使う場合、kaminariデフォルトのままだとbootstrapのクラスとぶつかって正しく表示されません。 手で直しても良いですが、便利なスニペットがあります。 [twitter-bootstrap-kaminari-views](https://github.com/gabetax/twitter-bootstrap-kaminari-views) これをcloneして上書きすればbootstrapできちんと表示されます。 [hamlバージョン](https://github.com/konung/twitter-bootstrap-kaminari-views)もあります。 ## よく使うAPI ```ruby @item.total_count #=> レコード総数 @item.offset_value #=> オフセット @item.num_pages #=> 総ページ数 @item.per_page #=> 1ページごとのレコード数 @item.current_page #=> 現在のページ @item.first_page? #=> 最初のページならtrue @item.last_page? #=> 最後のページならtrue ``` ### 例 ```erb:page_info.html.erb ページ:<%= @item.current_page %> / <%= @item.num_pages %> <%= @item.total_count %>件中<%= @item.offset_value %> 〜<%= @item.offset_value + @item.length %>件を表示しています。 ``` ## Ajax Ajaxでページネーションするには、ビューを以下のようにします。 ```erb:index.html.erb <%= paginate @items, :remote => true %> ``` これだけでxhrでリクエストが行われますが、実際に書き換えを行う部分は自分で用意してやる必要があります。以下は一例です。 ```ruby:items_controller.rb def index @items = Item.page params[:page] respond_to do |format| format.html format.js end end ``` ```javascript:index.js.erb $("#items").html("<%= j(render :partial => 'items') %>"); ``` ```erb:index.html.erb <div id="items"> <%= render :partial => "items" %> </div> <%= paginate @items %> ``` ```erb:_items.html.erb <% @items.each do |item| %> 以下略 ``` ## その他 ActiveRecordの例のみを紹介しましたが、kaminariはMongoidやDataMapperにも対応しています。また、Arrayオブジェクトをページネーションすることも可能です。 ドキュメントは整備されているとは言えませんが、ソースは読みやすくまたコーディングの参考にもなるので、興味があったら是非読んでみることをお勧めします。 |
|
| 236位 |
|
|||
|
18:25:07 |
(freee 所属) |
|
PowerPoint2003から2013になってちょっと戸惑いを隠せない。。。 ありきたりなスライドテンプレートもなんか嫌だし。。。 と思っていたところ、markdownでWebスライドが作れることを発見しました。 GithubでWebページを公開する方法を発見、SublimeTextも入れたので、ちょっとWebスライドを作ってみます。 完成サンプル [http://budougumi0617.github.io/reveal.js-myMaster/](http://budougumi0617.github.io/reveal.js-myMaster/)  #作り方 Reveal.jsというJavaScriptライブラリを利用します。 * GitHubから[reveal.js](https://github.com/hakimel/reveal.js/)のプロジェクトzipをGET * index.htmlをテキストエディタで開く。 * `<div class="slides">`タグ内の`<section>`内容を削除 * 後述のオプション付き`<section>`を記載する * `<section>`内にMarkdownでゴリゴリ書く これだけ!! --- #Markdown用のsection定義。 以下の`<section>`定義でmarkdownを使用してスライドが書けます。 もちろん、通常の`<section>`定義で`HTML`を利用してスライドを作成することも可能です。 `---`を入れることで右に次のページを作成。 `--`を入れることで下に次のページを作成。 ```html <section data-markdown data-separator="\n---\n$" data-vertical="\n--\n"> <script type="text/template"> ここにmarkdownを記載していく。 </script> </section> ``` たとえコードタグ内でも、markdown記載中に`</script>`がでるとバグる点だけ気をつけてください。 Markdown記述部分を別ファイルにしておけばindex.htmlは使いまわすことも可能です。 ```html <section data-markdown="./md/firstpage.md" data-separator="\n---\n$" data-vertical="\n--\n"> <script type="text/template"> </script> </section> ``` --- #編集後はGithubで公開 あとはGithubで新規のリポジトリ作成。 clone,init後にzipの中身を全てコピーして、gh-pagesブランチにpushすればGithub上にスライドが公開できます。 #今回コミットはSourceTreeで行なったので、コマンドログは割愛。 --- #その他 `<iframe>`で埋め込みしたいときなどはおとなしくHTML使う必要があります。 その他Reveal.jsの詳細な使い方はGitHubの[README.md](https://github.com/hakimel/reveal.js/)を参照してください。 ちなみにブラウザでスライド確認中は、`esc`か`o`でスライドを一覧表示にできるのですが、 ViChrome使っていると競合して動かないので注意。 `Ctrl + esc`でViChromeをエマージェンシーモードにしてから`o`で一覧表示ができます。 --- #参考 今回は以下の情報を参考にさせていただきました! 本家のデモ [http://lab.hakim.se/reveal-js/#/](http://lab.hakim.se/reveal-js/#/) なんかかっこいいプレゼンテーションテンプレートを探しているならreveal.js使ってみろって [http://qiita.com/ryurock/items/9c6de36b9d6a716e7992](http://qiita.com/ryurock/items/9c6de36b9d6a716e7992) reveal.js+Markdown [http://qiita.com/siguremon/items/c717eca388070215712c](http://qiita.com/siguremon/items/c717eca388070215712c) reveal.jsで格好いいプレゼンを作ってみた [http://d.hatena.ne.jp/zebevogue/20121110/1352521622](http://d.hatena.ne.jp/zebevogue/20121110/1352521622) |
|
| 237位 |
|
|||
|
15:57:17 |
(レジュプレス株式会社 所属) |
|
Googleが公開していた[Google HTML/CSS Style Guide](http://google-styleguide.googlecode.com/svn/trunk/htmlcssguide.xml?showone=Optional_Tags#Optional_Tags)によると、よく使われる下のようなHTMLのお決まりの記法は非推奨らしい。
```html:not_recommended.html <!DOCTYPE html> <html> <head> <title>Spending money, spending bytes</title> </head> <body> <p>Sic.</p> </body> </html> ``` これはファイル容量のために下のように書くべき。 ```html:recommended.html <!DOCTYPE html> <title>Saving money, saving bytes</title> <p>Qed. ``` つまり`html, head, body`などの使い慣れたお決まりのタグは今更書くまでもないということ。 参考: どのようなタグが省略可能か http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#syntax-tag-omission P.S. [STORYS.JP](http://storys.jp)、 [coincheck](https://coincheck.jp) などのサービスを運営、開発しています。興味のある方はぜひ [和田](http://qiita.com/wadako111)まで連絡を! |
|
| 238位 |
|
|||
|
01:04:11 |
(Consensus Base Inc. 所属) |
|
以下に移行しました<br><a href="http://block-chain.jp/tech/what-is-blockchain/" title="わかりやすいブロックチェーン(blockchain)とは何か? の説明">わかりやすいブロックチェーン(blockchain)とは何か? の説明</a>
|
|
| 239位 |
|
|||
|
20:33:22 |
|
|
## はじめに
Angular JS で複数のコントローラ間でモデル(状態や値)を共有する方法として、次の 3 種類を解説します。 * モデルを共有するサービスを使用する (Shared Service)。 * 親コントローラのスコープを子コントローラで共有する (Parent Scope Sharing)。 * イベントを利用する (Pub/Sub)。 ## Shared Service 複数のコントローラ間で共有するモデルをサービスとして作成し、そのサービスを複数のコントローラで参照します。 実装例を示します。 ```html:app.html <!DOCTYPE HTML> <html ng-app="AngularJsStudy"> <head> <title>Shared State Service - AngularJS Study</title> <meta charset="utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width" /> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" /> <link rel="stylesheet" href="app.css" /> </head> <body> <h4>Shared State Service</h4> <div class="container ng-cloak" ng-controller="ShareControllerA"> <h5>This is ShareControllerA</h5> <input type="text" class="form-control" ng-model="data.text"> </div> <div class="container ng-cloak" ng-controller="ShareControllerB"> <h5>This is ShareControllerB</h5> <input type="text" class="form-control" ng-model="data.text"> </div> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> <script src="app.js"></script> </body> </html> ``` ```javascript:app.js (function() { var app = angular.module("AngularJsStudy", []); app.factory("SharedStateService", function() { return { text: 'SharedStateService' }; }); app.controller("ShareControllerA", function($scope, SharedStateService) { $scope.data = SharedStateService; }); app.controller("ShareControllerB", function($scope, SharedStateService) { $scope.data = SharedStateService; }); }()); ``` `ShareControllerA` と `ShareControllerB` では、スコープ `$scope` の `data` に `SharedStateService` をバインドしています。 こうすることで、AngularJS の一番の特長であるビューとモデルの双方向バインドを効果的に利用できます。 ## Parent Scope Sharing AngularJS ではコントローラをネストすることができます。 コントローラをネストした場合、子要素のコントローラは、親要素のコントローラのスコープを参照することができます。 実装例を示します。 ```html:app.html <!DOCTYPE HTML> <html ng-app="AngularJsStudy"> <head> <title>Parent Scope Sharing - AngularJS Study</title> <meta charset="utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width" /> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" /> <link rel="stylesheet" href="app.css" /> </head> <body> <h4>Parent Scope Sharing</h4> <div class="container ng-cloak" ng-controller="MasterController"> <h5>Master Controller</h5> <input type="text" class="form-control" ng-model="text"> <hr/> <div class="container" ng-controller="ChildControllerA"> <h5>Child Controller A</h5> <input type="text" class="form-control" ng-model="$parent.text"> </div> <hr/> <div class="container" ng-controller="ChildControllerB"> <h5>Child Controller B</h5> <input type="text" class="form-control" ng-model="$parent.text"> </div> </div> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> <script src="app.js"></script> </body> </html> ``` ```javascript:app.js (function() { var app = angular.module("AngularJsStudy", []); app.controller("MasterController", function($scope) { $scope.text = "Shared Text"; }); app.controller("ChildControllerA", function($scope) { }); app.controller("ChildControllerB", function($scope) { }); }()); ``` `Shared Service` と同様の方法で、こちらの方が直感的かもしれませんが、 AngularJS のお勧めではビューに無関係なロジックはサービスにとあるので、 AngularJS らしく実装したいなら `Shared Service` の方でしょう。 ## Pub/Sub プロパティが変更されたら、`$scope` の `$emit()`, `$broadcast()` メソッドを利用してイベントを送信し、 `$scope` の `$on` メソッドでイベントを受信する方法です。 実装例を示します。 ```html:app.html <!DOCTYPE HTML> <html ng-app="AngularJsStudy"> <head> <title>Pub/Sub - Share State</title> <meta charset="utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width" /> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" /> <link rel="stylesheet" href="app.css" /> </head> <body> <h4>Pub/Sub</h4> <div class="container" ng-controller="Controller"> <h5>Controller 1</h5> <input type="text" class="form-control" ng-model="text"> <button type="button" class="btn btn-default" ng-click="setText()">設定</button> </div> <hr/> <div class="container" ng-controller="Controller"> <h5>Controller 2</h5> <input type="text" class="form-control" ng-model="text"> <button type="button" class="btn btn-default" ng-click="setText()">設定</button> </div> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> <script src="app.js"></script> </body> </html> ``` ```javascript:app.js (function() { var app = angular.module("AngularJsStudy", []); app.factory("SharedService", ["$rootScope", function($rootScope) { var text = "Shared Text"; return { text: { get: function() { return text; }, set: function(t) { console.log("[enter] text.set"); text = t; $rootScope.$broadcast('changedText'); console.log("[leave] text.set"); } } }; }]); app.controller("Controller", function($scope, SharedService) { $scope.text = SharedService.text.get(); $scope.setText = function() { console.log("[enter] setText"); SharedService.text.set($scope.text); console.log("[leave] setText"); }; $scope.$on('changedText', function() { console.log("[enter] changedText"); $scope.text = SharedService.text.get(); console.log("[leave] changedText"); }); }); }()); ``` 実装例ではすべてのコントローラにイベントが届くように `$rootScope` の `$broadcast()` メソッドを利用してイベントを送信しています。 ## 参考 * [Sharing Data, State and Models on AngularJS: Alternatives, comparison and my solution](http://www.blogeek.com.ar/2013/05/01/sharing-data-state-on-angularjs-alternatives-comparison-and-my-solution/) 参考サイトによると directive を用いてモデルを共有する方法もあるようです。 |
|
| 240位 |
|
|||
|
03:13:42 |
|
|
はじめまして。ひつじです。
[TechBooster](http://techbooster.org/)というウェブサイトでAndroidなどの記事をかいてます。Qiitaには初めて投稿します。 [vvakame](http://qiita.com/vvakame)さんの[技術的な文章を書くための1歩、2歩、3歩](http://qiita.com/vvakame/items/d657baf26cf83ac98bd0) を見る人の補助的な気持ちで0歩目を紹介します。 記事の構成方法については、vvakameさんの記事で触れられてることもあり、あまり触れません。 読みやすい文章のための心得をまとめました。 0歩というぐらいなので普段から気を付けている(そして完璧には、こなせていない)内容をまとめています。ただ内容については私自身が気を付けていることですので、もっと皆さんにあった良い方法があるかもしれません。参考になれば幸いです。 # 文章を書くのに大切なことって? * 言いたいことを1つに絞る 1つの文章にたくさんの主張を入れると読む人は混乱してしまいます。ですので、なるべく1つのコンテキスト(章、節、項で主張の大小はあります)では、言いたいことを1つ主張するようにしてください。 # 文章に興味を持ってもらうには? * まとめ(アブスト)を最初にかく 読者はあなたの主張を読みたがっています。数あるウェブサイト、雑誌、書籍のなかから偶然にも興味をもって読んでくれています。しかし、最後まで読んでみて期待した内容でなかった時の残念感は計り知れません。 最初にまとめをのせることは読者と記事のマッチングをとるためでもあります。まとめでは主張の魅力を最大限伝えてください。まとめが良ければよいほど最後まで読んでもらいやすくなります。 # 文章を理解してもらうには? ここでは文法的な、または文章として読みやすくする工夫を紹介します。ちょっとしたことなのですが、すべてを守ろうとおもうと意外に難しいものです。以下に3点のポイントに絞って、まとめました。 * 文章は短く、簡素に書く 長い文章は、主語、述語の対応が取りにくく(筆者の国語力もありますが)主張があいまいになりがちです。経験的に技術文章で3行を超える文章(句点「。」で区切られていない長文)は読みづらく感じます。 まれに長くても面白い文章を書く人がいるのですが、一般的なノウハウというより国語的な才能の場合があります。技術的な文章であれば修飾的な表現は割り切って簡潔に伝えましょう。 * 初出の用語/概念は説明する 書いていると用語解説を忘れることがあります。「文章を書く」という作業は言い換えるならば筆者の主張や考えをまとめる、という行為です。 そうして出てきた単語は、筆者の中では既知のものとなっており説明不要と思い込んでしまうことがままあります。読者にとっては初めて出てきた単語は未知の物体Xであり、理解の妨げになることを十分留意して下さい。 * 表現を統一する ささいなことですが読みやすさに最も影響します。国語的なことではありますが筆者が主張を文章に落とし込んだあとは、その文章があなたの主張、意見そのものとして読まれます。 読みやすさを気にしてしすぎる、ということはありません(もちろん著者の書くモチベーションや勢いも大事なので最後にチェックするといいでしょう)。 # 表現チェックリスト ここで紹介する内容は~しなければならない!といった強制的なものでありません。特に雑誌や書籍では出版社ごとに異なる方針のものもあります。ですので、読みやすさの参考程度にしてください。たとえばウェブの記事であれば多少ライトな表現のほうが読みやすく感じます。 * 表現を統一する: 著者、書き手、筆者、執筆者=著者でまとめる * 用語を統一する: onCreate()/onCreateメソッド/Activity#onCreate() * 開きを統一する: ~出来る/~できる、~する事/~すること など漢字の開きを合わせる * 指示語を減らす: あれ、それ、これ、前述の~、など。何を指しているか明確か?確認する * 記号は意識する: ()や「」、『』の使い分けを統一する * ~と思います:~です。で改めることを薦めます。事実、現象であれば断定系で構いません ## さらに一歩踏み込んで 細かい話になるので例を使って確認してみましょう。もっとも表現方法は千差万別ですので著者の書いた文章中で統一されていれば問題ありません。文章の品位を上げる作業といえます。 **これは作例です(テストです。)** という文章があったとします。私の場合は以下のように順次考えて、なおしていきます。 **これは作例です(テストです)。** 句点「。」の位置を文章の最後に変えました。 **これは作例です(テストです)。** 「()」が全角と半角だったので全角にあわせました。 **これは作例(テスト)です。** 「です」が重複していることに気づいたので片方を消しました。 これら細かい推敲は、書きながら(ほぼ勝手に)脳内で行われている作業です。 実際、執筆を複数回に分けていたり長時間にわたって書いていたりすると経験上、マチマチになってしまいます。 # 推敲する * 書き終わったら自分で読む 書き終わったら、いったん気持ちをリセットして読者視点で文章を読んでみてください。なおしたいところが出てきたら修正してまた読む。気にならなくなるまで繰り返します。一見すると単純ですが、自分が書いた文章は自分が一番よく理解しているので、意外に難しい作業です。 # 最後に 技術的な文章を書くための0歩として「読みやすい文章のための心得」を紹介しました。 0歩にしては細かいところから考え方まで幅広かったので結構めんどくさいですね。 もちろん最初からすべてを完璧にできる人はいません。私自身も試行錯誤の連続です。あなたが自分なりの書き方、表現方法を考えるきっかけになったのであれば、この記事の目的は達成されたといえます。 何度もくりかえしになりますが読者にとって大事なのは筆者の主張です。 Qiitaのような場所では、みんなが技術文書を読みたがっています。今読んだことを活かす為にも、さっそくひとつ書いてみませんか? それでは良い執筆ライフを:) |
|
| 241位 |
|
|||
|
13:07:22 |
(株式会社キュリオシティソフトウェア 所属) |
|
iOSについては環境の進化が速く、書籍の内容が古くなっていることがほとんどなのですが[ Android開発を始めたばかりの僕が読みたかった本](http://takiguchi0817.github.io/blog/2013/09/28/android-books/)が面白かったので自分も似たようなことを書いておきます。 ## 詳解 Objective-C 2.0 <a href="http://www.amazon.co.jp/gp/product/4797368276/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4797368276&linkCode=as2&tag=curiosityamazon-22">  </a> Objective-Cのメモリ管理の仕組みであるリファレンスカウンタについて理解するために購入。現在発売中の第3版ではARCについても記載されているので最新のものを買うのが良いです。 Objective-Cの特徴であるカテゴリやプロトコル、KVOについても詳細に記載されているので何度も読み返すことになると思います。 <a href="http://www.amazon.co.jp/gp/product/4797368276/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4797368276&linkCode=as2&tag=curiosityamazon-22">Amazonへのリンク: 詳解 Objective-C 2.0 第3版</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=curiosityamazon-22&l=as2&o=9&a=4797368276" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> ## iOS開発におけるパターンによるオートマティズム <a href="http://www.amazon.co.jp/gp/product/4861007348/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4861007348&linkCode=as2&tag=curiosityamazon-22">  </a> マイナビで[iPhoneアプリ開発の連載](http://news.mynavi.jp/author/0000204/)を持たれていた[HMDTさん](http://hmdt.jp/)の書かれた本です。 UIやメモリ管理、実際のモデル設計など盛りだくさんの内容で、メモリ管理の話題ではP94のsetterの実装についての内容が自分にとっては勉強になりました。UITableViewなどで使われるdataSourceやdelegateはそもそもなぜそのような手法なのか、など他の本では取り扱わない事柄に触れていて珍しい一冊です。 ただ、本書に書かれている内容は、設計手法やコードの記述に癖が強く、複数人の開発でこの流儀をそのまま持ってこられるのは反発を招くことがあります。たとえばプロトコルの文法を使わずNSObjectへのカテゴリ追加でまかなうという非形式プロトコルを使っている点については、個人で把握できる範囲内の規模の開発だけなら良いですが、そうでない場合そのまま真似してしまうのはデメリットのほうが多いでしょう。 また、2011年の発売でARC以前のコードなので、今は惜しい感じになってます。 <a href="http://www.amazon.co.jp/gp/product/4861007348/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4861007348&linkCode=as2&tag=curiosityamazon-22">Amazonへのリンク: iOS開発におけるパターンによるオートマティズム</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=curiosityamazon-22&l=as2&o=9&a=4861007348" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> ## iPhoneプログラミングUIKit詳解リファレンス <a href="http://www.amazon.co.jp/gp/product/4897978440/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4897978440&linkCode=as2&tag=curiosityamazon-22">  </a> UIKitについて書かれたかなり詳しい本で、iPhoneアプリ開発の現場では机の上に最もよく見かけた本です。リファレンスとしてあの部品の使い方ってどうやるんだろうという時に役に立ちました。例えばピッカーの使い方はネットで調べるより本で読んだほうがまとまっていて良いですね。 ただ、2010年ぐらいの本なのでiOS7用に書きなおして貰えると最高なんですが… <a href="http://www.amazon.co.jp/gp/product/4897978440/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4897978440&linkCode=as2&tag=curiosityamazon-22">Amazonへのリンク: iPhoneプログラミングUIKit詳解リファレンス</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=curiosityamazon-22&l=as2&o=9&a=4897978440" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> ## iPhoneアプリ設計の極意 ―思わずタップしたくなるアプリのデザイン <a href="http://www.amazon.co.jp/gp/product/4873115027/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4873115027&linkCode=as2&tag=curiosityamazon-22">  </a> iOSアプリはUIの魅力がその良さの9割を決めてしまうらしいのですが、洗練されたUIとは何か、他のアプリはどのようにしてUIを決めているのかという事が書かれています。 デザインという大枠だけではなく、ウインドウを閉じるボタン、アプリ内に表示する用語、チュートリアルなどについて心理学、文化、人間工学の視点から語られていて興味深く読めます。 <a href="http://www.amazon.co.jp/gp/product/4873115027/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4873115027&linkCode=as2&tag=curiosityamazon-22">Amazonへのリンク: iPhoneアプリ設計の極意 ―思わずタップしたくなるアプリのデザイン</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=curiosityamazon-22&l=as2&o=9&a=4873115027" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> ## インターフェースデザインの実践教室 <a href="http://www.amazon.co.jp/gp/product/4873116082/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4873116082&linkCode=as2&tag=curiosityamazon-22">  </a> 心理学などの観点から使いやすいUIについてさらに知りたくなったら、インターフェースデザインについての専門書を読むのがお勧めです。 最近発売されたインターフェースデザインの実践教室では、iOSにかぎらず使いやすいUIで優れたUXを提供するための「リサーチ」「デザイン」「インプリメンテーション」というフェーズで分かりやすく書かれています。 <a href="http://www.amazon.co.jp/gp/product/4873116082/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4873116082&linkCode=as2&tag=curiosityamazon-22">Amazonへのリンク: インタフェースデザインの実践教室 ―優れたユーザビリティを実現するアイデアとテクニック</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=curiosityamazon-22&l=as2&o=9&a=4873116082" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> # 高速な動作をさせたくなったり、無駄のないコードを書きたくなったら ここから先は作成したアプリケーションをフラストレーションなく動作させたくなったり、さらに深く知りたくなった人用の書籍についてです。 ## iOS SDK Hacks ―プロが教えるiPhoneアプリ開発テクニック <a href="http://www.amazon.co.jp/gp/product/4873114721/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4873114721&linkCode=as2&tag=curiosityamazon-22">  </a> Method SwizzlingやrespondesToSelectorメソッドをフックするコードなどHackと呼ぶにふさわしい内容が書かれています。iOSアプリ開発者の机の上には結構な確率で置いてある本ですね。 <a href="http://www.amazon.co.jp/gp/product/4873114721/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4873114721&linkCode=as2&tag=curiosityamazon-22">Amazonへのリンク: iOS SDK Hacks ―プロが教えるiPhoneアプリ開発テクニック</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=curiosityamazon-22&l=as2&o=9&a=4873114721" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> ## エキスパートObjective-Cプログラミング -iOS/OS Xのメモリ管理とマルチスレッド <a href="http://www.amazon.co.jp/gp/product/4844331094/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4844331094&linkCode=as2&tag=curiosityamazon-22">  </a> Objective-Cへ新たに追加されたARCやBlocks、GCDについて詳しく書かれています。序盤のメモリ管理についての項目が例がありObjective-C初心者寄りなので、始めたばかりの人はこの本の序盤をまず読むのが良いかもしれません。 GCDについては同期/非同期、キュー、dispatch系の関数の細かな使い分けが詳細に書かれている点や、所々にあるコラムが言語の歴史的経緯について記載されているところがとても良いです。BlocksについてはBlocks自身の実装について書かれているので、Blocksを使う上での制限を理解することに役立ちます。 ただ、文章のわかり辛い点を図が効果的に説明していない事があって惜しいところです。例えば強い参照、弱い参照は文字でわかり辛いのを補足するのであれば、図中の文字で強い/弱いを記載するのではなく、図自身で強弱を示して比較するようにしたほうがいいんじゃないかなと思いました(また、図が必要以上に大きい気がしてこなれていない感があるところが気になります)。 [達人出版会](http://tatsu-zine.com/)で電子書籍版も出ていますが、私は電子書籍版も買っていて、自宅のソファでは紙の本で、事務所では電子書籍で読むような使い方をしています。電子書籍版は更新されているようで内容が増えていて素晴らしいです。 <a href="http://www.amazon.co.jp/gp/product/4844331094/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4844331094&linkCode=as2&tag=curiosityamazon-22">Amazonへのリンク: エキスパートObjective-Cプログラミング -iOS/OS Xのメモリ管理とマルチスレッド-</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=curiosityamazon-22&l=as2&o=9&a=4844331094" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> # 番外: プログラミングについてさらに詳しく知りたくなったら ## エキスパートCプログラミング―知られざるCの深層 <a href="http://www.amazon.co.jp/gp/product/4756116396/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4756116396&linkCode=as2&tag=curiosityamazon-22">  </a> Objective-Cでプログラミングしていても、根本はC言語であり開発時にはポインタ、スタティックリンク、ヒープやセグメントなどといった用語を目にすることも多いと思います。 本書はそういった内容を歴史的経緯から説明していて、かなり軽い口調で書かれているので読みやすく、あれってそういう意味だったのかと思うことが多いでしょう。 また、ライブラリのリンクについてもページを割いているので、Xcodeが出すライブラリに関するビルドエラーについてもなんとなくわかってくるのではないかと思います。 初版は1996年に発行されたものなので、最近ではほとんど売ってないため本屋で見つけたら即買いの一冊です。この本にかぎらず、古い良書は技術書も揃う大型書店よりも大型電気店の技術書コーナーなどにひっそりとあったりしますので確認してみると良いでしょう。 <a href="http://www.amazon.co.jp/gp/product/4756116396/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4756116396&linkCode=as2&tag=curiosityamazon-22">Amazonへのリンク: エキスパートCプログラミング―知られざるCの深層 (Ascii books)</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=curiosityamazon-22&l=as2&o=9&a=4756116396" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> # まとめ ARCやBlocksなどの比較的新しい技術によって、本屋に並んでいるiOS関連書籍は選ぶのが難しいのですが、今でも読み返す本を選んでみました。 また、iOS7の登場でUIKitに関する書籍は定番がなくなっているので、新たな定番本が出てくるのが待ち遠しいところです。著者の皆さんには頑張っていい本を作っていただけることを期待してます。 追記:他にも最近はこういう本がいいよというのがありましたらコメントやメールで教えて頂ければと思います! |
|
| 242位 |
|
|||
|
20:59:51 |
|
|
JavaScriptでグリッド表示を行ってくれるライブラリは色々あるが、その中でも一番しっくりきたSlickGridをご紹介。
※2016/3現在、SlickGridは更新が停止しています(2014/3/5より)。その代り、6pacという方が主要なバグフィックス(パッチ)を取り込んだ[alternative master](https://github.com/6pac/SlickGrid/wiki)を運用してくれているので、こちらの方を使うとよいです([オフィシャルからの公認](https://github.com/mleibman/SlickGrid/commit/bf4705a96c40fea088216034def4d0256a335e65)も出ています)。 JavaScriptのグリッド系ライブラリは下表のようにいろいろある。 | ライブラリ名 | 概要 | |:-----------|:------------|: | [SlickGrid](https://github.com/mleibman/SlickGrid/wiki/Examples) | 今回お勧め。表示速度・編集機能に優れる | | [DataTable](https://datatables.net/examples/) |初回の表示速度が若干遅いが、一旦表示したら早い| | [jqGrid](http://trirand.com/blog/jqgrid/jqgrid.html) | 使ったことがないが、Exampleが豊富。ただ、オプションが書きにくい印象| | [dhtmlGrid](http://dhtmlx.com/docs/products/dhtmlxGrid/) | 多機能でExampleも豊富。Excelにインポートできるのは圧巻。ただ、商用だと有料| | [FlexGrid](http://flexigrid.info/) | 使ったことなし。あんまり更新されてないような・・・| 正直、表示だけするならどれも高機能で大差ないと思う。 ただ、多くのグリッド系ライブラリは、高機能な代わりに遅い&書きにくかったり、編集機能はあるものの表示→編集モードへの切り替えがもっさりしていたり保存機能が書きにくかったりする。 その中で高速な表示、Excelかのような編集機能を備えているのがSlickGridだ。 「Excelみたいにできないの?」とはよく言われることでその言葉にそんな簡単じゃねーんだよ、とイラッとしたことがある人も多いかと思うが、SlickGridを使うことでお互いストレスフリーな関係を築けるだろう。 では、SlickGridのパワーが実感できる例を紹介したい。 ## 1.パフォーマンス このExampleで表示しているデータは、なんど500,000件である。他のグリッドが数万件表示可能!と言っているそばで、ケタが違う。何せExcel(2003)の限界行より多いんだからビックリだ。 http://mleibman.github.io/SlickGrid/examples/example-optimizing-dataview.html ## 2.編集機能 セル内編集が本当にスムーズである。下記のExampleで、Auto-edit ON のボタンを押してもらえればそのスムーズさを体感してもらえると思う。 http://mleibman.github.io/SlickGrid/examples/example3-editing.html 私は編集可能なグリッドライブラリを無償・有料関係なく探し回ったが、これほどスムーズなのは他になかった。しかも、Editorは自分で簡単に拡張可能だ。 ## 3.値表示の自由度 例えば10を10%としてメーターで表示したり、0/1の数字をチェックマークで表示したりと、各列に応じて値をどう表示するかを自由にカスタマイズできる。多くのGridが行の色ぐらいしかいじれない中で、セル内の値をDOMレベルで編集できる機能は表現力の面で非常に大きい。 http://mleibman.github.io/SlickGrid/examples/example2-formatters.html ## その他の機能 他に独特なのは、以下のExampleだ。 ・非同期の描画処理。表示が重たい処理(行内グラフなど)などは、非同期で処理できる。 http://mleibman.github.io/SlickGrid/examples/example10-async-post-render.html ・Undo機能まで実装可能。 http://mleibman.github.io/SlickGrid/examples/example3b-editing-with-undo.html もちろん、セル/行のイベントハンドリングやソート機能など、普通のグリッドライブラリにあるようなものはきちんとそろっている。 ぜひ使ってみていただきたい一品である(なお前提ライブラリとしてjQuery/jQuery.event.dragが必要。jQuery UIはcssがいるがjsは読み込まなくても動かせる)。  |
|
| 243位 |
|
|||
|
16:14:51 |
|
|
# 概要
実行時間の長いコマンドが終わったら、Mac の Notification Center (Growl にするのも可能) で通知がくる。Terminal.app と iTerm 2 に対応している。  通知の様子。30秒を超えるコマンドが通知されている。  30秒を超えないコマンドでも、失敗したら通知される。 どちらもターミナルがバックグラウンドのときのみ通知される。長いコマンドを投げてから twitter とか見てたらいつのまにか時間が経ってしまうような人に便利。 # 設定 ## 1. 準備 - Notification Center に通知する場合 [alloy/terminal-notifier](https://github.com/alloy/terminal-notifier) をインストールする。コマンドから Notification Center に通知をするためのアプリ。 [Releases](https://github.com/alloy/terminal-notifier/releases) から .app をインストールしてもよいし、[Download](https://github.com/alloy/terminal-notifier#download) にあるように RubyGems もしくは homebrew で入れてもよい。 - Growl に通知する場合 [Growl - Downloads](http://growl.info/downloads) から GrowlNotify を入れる。Growl に通知するのは使ってないので割愛... ## 2. インストール [marzocchi/zsh-notify](https://github.com/marzocchi/zsh-notify) を zsh で使えるようにする。 ダウンロードして適宜配置し、`.zshrc` に以下を追記する。 ```zsh source ~/.zsh.d/zsh-notify/notify.plugin.zsh ``` デフォルトでは terminal-notifier を .app でインストールした場合を想定しているので、インストールする方法によって修正する。`$ which terminal-notifier` の実行結果を貼り付ければいいんじゃないかな。 ```zsh export SYS_NOTIFIER="/Users/kei/.rbenv/shims/terminal-notifier" ``` ## 3. 設定 デフォルトでは閾値が30秒になっているので、各自よしなに。 ```zsh export NOTIFY_COMMAND_COMPLETE_TIMEOUT=10 ``` |
|
| 244位 |
|
|||
|
23:00:51 |
|
|
##結論:Highchart ccchart(canvasChart)と悩みましたが、Highchartを選んだ理由は、下記の通りです。 1.円グラフや棒グラフを組み合わせた複合的なグラフのサンプルを載せている 2.複合グラフのアニメーションがかっこいい 商用ライセンスなので、無料にこだわる方は、ccchart(canvasChart)がおすすめです。 ##Highchart http://www.highcharts.com/ >表現力、完成度の点でいちばんだと思います。商用なのでお金さえあればこれがイチオシ。 ##ccchart(canvasChart) http://jsgt.org/c/ >私のイチオシは canvasChart です ライセンスは PUBLIC DOMAIN! ##raphael.js http://raphaeljs.com/ >qiitaのプロフィールの円グラフで使われているjQueryプラグイン 美しめなグラフを作成することができる。 raphael.jsのプラグインのelycharts.js http://elycharts.com/docs ##Google Chart Tools https://developers.google.com/chart/ >Google Chart Tools も良いかと思いますが、 3D棒グラフが出来たかどうかが謎です。 >僕が自分で使おうと決めたのはこれ。デモを見ただけだと貧弱に見えるのですが、グラフの種類とオプションなど柔軟性があり、アニメーションやドロップシャドウなどもできて、実はHighChart並の表現力と柔軟性があります。オープンソースなので、気軽に使えますし、プログラミングしがいがあります。 ##SenchaTouch Chats http://extjs.co.jp/products/touch/ >これも表現力、種類など十分です。ただ円グラフの凡例がちょっと独特だったので、見送りました。 Flot http://code.google.com/p/flot/ Flotr http://solutoire.com/flotr/ jqPlot http://www.jqplot.com jQuery Sparkline http://omnipotent.net/jquery.sparkline/ jQuery-Visualize https://github.com/filamentgroup/jQuery-Visualize MilkChart http://mootools.net/forge/p/milkchart moochart http://moochart.coneri.se/ MooWheel http://www.labs.unwieldy.net/moowheel/ PlotKit http://www.liquidx.net/plotkit/ table2chart http://mootools.net/forge/p/table2chart TufteGraph http://xaviershay.github.com/tufte-graph/ YUI Charts http://yuilibrary.com/yui/docs/charts/ 上記は、html5のユーザーグループへの投稿に対する返信をまとめたものです https://groups.google.com/forum/?fromgroups#!topic/html5-developers-jp/dmkGbLJ6CAo |
|
| 245位 |
|
|||
|
01:48:07 |
|
|
## やりたいこと
常時起動させたいスクリプトなど、デーモンプロセスとして動かしたいことがあると思います。 しかし例えば/etc/init.dのスクリプトなどを自分で書くとなると・・・ * PIDファイルの管理をいちいち書くのが面倒でミスりやすい。 * ステータスの取得とかも同様。 * そもそもそんなローレベルのツールを使わなくてもよい。もっとユーザレベルに近いもので充分。 * 一般ユーザで起動停止くらいさせたい。 つまりは、大体同じ様になるはずの **「プロセス管理スクリプト書くのが面倒」** なのと、 サービスとして登録してしまうと **「基本はrootで起動停止管理」** になるので、 この2つを避けたいということになります。 systemdを使うとスクリプトを書く必要はなくなりますが、 * rootでなければ起動停止ができない、という扱いにしたくない。 * stdout/errに色々吐きまくって直したくない。 * **システムのサービス扱いではなくカジュアル起動停止させたい。** といった場合にも需要があるかと思います。 ## deamonize tool この手の問題にはdaemon化のツールを使うことになります。 daemontoolsとかforeverとか。 ここではpythonで実装されている「supervisor」を使ってみます。安定性もよいので。 * **2〜3年ほど3.X系バージョンで運用しましたがsupervisordそのものが落ちたことはありません。** ## supervisor * プロセス管理/デーモン化のツール。 * コンフィグちょっと書くだけで簡単にデーモンプロセスの生成/管理が可能。 * 本家のドキュメントはこちら * http://supervisord.org/ ## 試しに * 私はメトリクスのグラフ化にGrowthForecastとHRForecastを使用させてもらってますので、試しにこれらをデーモン化してみます。 * GrowthForecast http://kazeburo.github.io/GrowthForecast/ * HRForecast http://blog.nomadscafe.jp/2013/02/hrforecast--.html * この時メインで試したOSはCentOS6.4です。 ## インストール 方法は大きく2通り。 1. EPELリポジトリからインストール 2. pythonパッケージマネージャ(pip/easy_install)を使う。 1の方法ではバージョンが古いことがあります。 最新バージョンを使いたい場合には2の方法を使います。 * 特にバージョン2.X系を使うよりは様々な機能が追加された3.X系を使うべきでしょう。 * 古いバージョンだと例えばstopasgroup(stopの際にchild含むプロセスグループをkillする)が使えなかったりします。 #### 1. EPELからインストールする場合 * supervisord本体及び、supervisordの管理スクリプトが/etc/init.dに入り、serviceコマンドで管理できます。 ```bash yum install supervisor ``` * (CentOSではEPELリポジトリはデフォルトでは有効化されていないので、登録してない場合は事前にこちらを。) ```bash rpm --import http://apt.sw.be/RPM-GPG-KEY.dag.txt rpm -ivh http://apt.sw.be/redhat/el6/en/x86_64/rpmforge/RPMS/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm ``` * CentOS7系でも同じような手順を踏むと思われます。その場合supervisord本体の管理をsystemdで行えるようになるでしょう。 #### 2. pip/easy_installを使う。 pip/easy_installなどのpythonパッケージマネージャからインストールすると、 EPELリポジトリからのインストールとは異なりデフォルトではsupervisord自体のサービス管理は含まれません。 そちらは手動でやっておくと良いでしょう。 * supervisordのインストール ```bash sudo easy_install supervisor # または sudo pip install supervisor ``` そもそもpythonパッケージマネージャを入れていない場合、 easy_installを使うには`sudo yum install python-setuptools`を、 pipを使うには、easy_installを入れた後`sudo easy_install pip`を実行しておきましょう。 * デフォルトコンフィグファイル生成 EPELリポジトリインストールでは自動的に/etc/supervisord.confに配置されますが、これを手動生成します。 ```bash sudo echo_supervisord_conf > /etc/supervisord.conf ``` * includeコンフィグ用のディレクトリ作成 EPELリポジトリインストールでは自動的に/etc/supervisord.dが生成されていますので、これを作っておきます。 ```bash sudo mkdir /etc/supervisord.d ``` * supervisordログファイル supervisord本体が吐くログファイル周りの調整を行います。 デフォルトのコンフィグでは/tmp/supervisord.logなので、 /etc/supervisord.confを修正し、/var/log/supervisor/supervisord.logにします。 ```ini [supervisord] # ログディレクトリ ;logfile=/tmp/supervisord.log logfile=/var/log/supervisor/supervisord.log ``` ログ出力先のディレクトリを作成し、ついでにlog rotationの設定も行っておきます。 ```bash sudo mkdir /etc/supervisord.d sudo sh -c "echo '/var/log/supervisor/*.log { missingok weekly notifempty nocompress }' > /etc/logrotate.d/supervisor" ``` * pid, includeの設定 こちらも/etc/supervisord.confを修正しておきます。 pidファイルは/var/run/以下に生成するようにし、 /etc/supervisord.d/以下の設定ファイルをincludeできるようにします。 ```ini [supervisord] # ...中略 # pidファイル ;pidfile=/tmp/supervisord.pid pidfile=/var/run/supervisord.pid # ...中略 # includeセクションがコメントアウトされているので、コメントインして下記の用に修正。 [include] files = supervisord.d/*.ini ``` * supervisord本体のシステムサービス登録(CentOS6系) initスクリプトは作成してくれた方がいらっしゃるようで、ありがたく利用させてもらいましょう。 https://github.com/Supervisor/initscripts 何種類かのディストロに対応していますが、今回は下記を使います。 https://github.com/Supervisor/initscripts/blob/master/redhat-init-equeffelec ```bash sudo curl -o /etc/rc.d/init.d/supervisord https://raw.githubusercontent.com/Supervisor/initscripts/master/redhat-init-equeffelec sudo chmod 755 /etc/rc.d/init.d/supervisord sudo chkconfig --add supervisord ``` * supervisord本体のシステムサービス登録(CentOS7系) CentOS7ではsystemdに登録することになります。 systemdのunitファイルは下記を参考にして作ります。 https://github.com/zokeber/supervisor-systemd/blob/master/etc/systemd/system/supervisord.service 下記の内容で/etc/systemd/system/supervisord.serviceに格納します。 ```text [Unit] Description=Supervisor process control system for UNIX Documentation=http://supervisord.org After=network.target [Service] ExecStart=/usr/bin/supervisord -n -c /etc/supervisord.conf ExecStop=/usr/bin/supervisorctl $OPTIONS shutdown ExecReload=/usr/bin/supervisorctl $OPTIONS reload KillMode=process Restart=on-failure RestartSec=50s [Install] WantedBy=multi-user.target ``` 作成後は、 `systemctl list-unit-files --type=service` でsystemdへの管理登録を確認し、試しに起動してみます。 ``` systemctl start supervisord systemctl status supervisord systemctl stop supervisord ``` 問題なければ自動起動に登録しておきます。 ``` systemctl enable supervisord.service ``` ## supervisordのコンフィグ * /etc/supervisord.confを編集して、以下をコメントインします。 ```ini [inet_http_server] port=127.0.0.1:9001 [supervisorctl] serverurl=unix:///var/tmp/supervisor.sock serverurl=http://127.0.0.1:9001 ``` * webの管理コンソールを開放し、同時にroot以外のユーザからのクライアントコマンド許可を行っています。 ## プロセス管理登録 自分がインストールしたsupervisorはversion 3系で、2系とはコンフィグが若干異なるようです。 特にログ出力の辺りで。(2系は古いので3系使いましょう) 以下、ジョブの標準出力ログ格納ディレクトリを`/var/log/supervisor/jobs`にしています。そのまま使う場合は予め作成しておきます。また、ローテーションの設定も以下は適当です。 * GrowthForecast登録の例 ```ini [program:GrowthForecast] command=/usr/local/bin/growthforecast.pl --enable-float-number --data-dir /data/gf ; 起動コマンド user=monadmin ; 起動ユーザ autorestart=true ; プロセスダウン時に自動再起動 stdout_logfile=/var/log/supervisor/jobs/GrowthForecast-supervisord.log ; 標準出力ログ stdout_logfile_maxbytes=1MB stdout_logfile_backups=5 stdout_capture_maxbytes=1MB redirect_stderr=true ; エラー出力を標準出力にリダイレクト ``` これを/etc/supervisord.d/GrowthForecast.iniにでも作成しておきます。 * HRForecast登録の例。 * /opt/HRForecastディレクトリにインストールしてあります。 ```ini [program:HRForecast] command=perl hrforecast.pl --config config.pl ; direcotryからの相対パスでOK。 user=monadmin directory=/opt/HRForecast ; インストールディレクトリがある場合にはこちらに。 autorestart=true stdout_logfile=/var/log/supervisor/jobs/HRForecast-supervisord.log stdout_logfile_maxbytes=1MB stdout_logfile_backups=5 stdout_capture_maxbytes=1MB redirect_stderr=true ``` こちらも同じく/etc/supervisord.d/HRForecast.iniにでも作成しておきます。 ## 起動確認 ```bash sudo service supervisor start ``` /var/log/supervisor以下のログをチェックしておくといいでしょう。 今回の設定なら、設定したプロセス(GrowthForecastやHRForecast)のstdout/errも出てきてます。 起動後は、root以外からクライアントコマンドのsupervisorctlを叩いてみます。 単独で叩けば、コマンドラインの管理インタフェースに入れます。 で、成功していれば、以下の様に見えます。 ``` [monadmin@myhost]% supervisorctl GrowthForecast RUNNING pid 24955, uptime 0:12:00 HRForecast RUNNING pid 24956, uptime 0:12:00 supervisor> ``` CLIコンソール上からは、start|stop|restart [プロセス名]、 statusで管理プロセスの一覧が出てきます。 CLIコンソールに入らなくても、supervisorctl {start|stop|restart|status} [プロセス名] などが可能です。 rootにならなくてもできるので、 ユーザレベルから管理できるデーモンが出来上がりました。 ## 失敗していると ``` [monadmin@myhost]% supervisorctl GrowthForecast FATAL can't find command 'growthforecast.pl' HRForecast RUNNING pid 8136, uptime 0:00:29 supervisor> ``` 起動失敗時にはステータスがFATALになります。 PATHの通ってないコマンドを書いちゃったのがこれでした。 ## やってないこと&その他 * セキュリティ関連を普通は考えます。userやpasswordを設定しましょう。 * このままだと一般ユーザ全てから出来てしまうので。 * psコマンドでプロセスツリーを見てみると、rootのsupervisor配下に登録プロセスがぶら下がってるのが解ります。 * ps auxwwfとかで見てみましょう。 * webコンソールも持っています。http://127.0.0.1:9001で見えます。 * 登録したプロセスが異常終了した場合にメールで通知させる。 * 下記の通りやってみました。 * [Supervisorのプロセス異常終了をメールで通知](http://qiita.com/yushin/items/6c698f084b92c7868b69) |
|
| 246位 |
|
|||
|
18:14:13 |
(私立リリアン女学園高等部 所属)
|
|
[JavaScriptのthisの覚え方](http://qiita.com/items/74005adacc0e8e2a3cab) や [JavaScriptの「this」は「4つ」だけ!](http://qiita.com/items/9935ce476a17d6258e27)の授業でやったから、this はみんなばっちりだよな。じゃあ今から抜き打ちテストするぞー。まだ読んでないひとは先に上の記事を読んどくといいと思うけど、腕に自信のある人はすぐに回答を始めても構わないぞ。赤点とった奴は、今日の放課後[補習](http://qiita.com/items/d249a2f2f13532748324)だからなー。
**【注意】 問題 18 ~20 について、[カンマ演算子](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Comma_Operator) を知らないから解けなかった、という人が結構いるみたいです。本問はカンマ演算子の知識を問うものではなく、あくまで this の振る舞いについての理解を試すものなので、本来の題意を損なわないように当該の問題は改題しました。改題後も正答とその根拠は変わりません。** | 得点 | 評価 | |:-----------|:---------------------------------------| | 0 ~ 5 | テスト中に寝るんじゃない | | 6 ~ 11 | 鉛筆転がしのほうがマシ | | 12 ~ 15 | 平凡な一般市民 | | 16 ~ 19 | 10 人に一人の逸材 | | 20 ~ 21 | this の世界的権威 | | 22 | あなたが神か | # 問 以下の JavaScript プログラムをウェブブラウザ上で実行したとき、`true` が出力されるものには ○ 、そうでないものには × をつけよ。(各1点) ## 1 ```js console.log(this === window); ``` [答え](http://jsfiddle.net/7xFnS/) ## 2 ```js 'use strict'; console.log(this === window); ``` [答え](http://jsfiddle.net/V2h8K/1/) ## 3 ```js function hoge(){ console.log(this === window); } hoge(); ``` [答え](http://jsfiddle.net/mEneJ/1/) ## 4 ```js var piyo = { hoge: function(){ console.log(this === window); } }; piyo.hoge(); ``` [答え](http://jsfiddle.net/rPtQ2/) ## 5 ```js function Hoge(){ console.log(this === window); } new Hoge(); ``` [答え](http://jsfiddle.net/6Lyaw/1/) ## 6 ```js var hoge = { Hoge: function(){ console.log(this === window); } }; new hoge.Hoge(); ``` [答え](http://jsfiddle.net/Xfq2a/1/) ## 7 ```js console.log((function(){ return this; })() === window); ``` [答え](http://jsfiddle.net/hdeFK/1/) ## 8 ```js 'use strict'; console.log((function(){ return this; })() === window); ``` [答え](http://jsfiddle.net/aSWMj/1/) ## 9 ```js function hoge(){ console.log(this === window); } hoge.apply(window); ``` [答え](http://jsfiddle.net/4DemV/1/) ## 10 ```js function hoge(){ console.log(this === window); } hoge.apply(42); ``` [答え](http://jsfiddle.net/8bGRk/1/) ## 11 ```js function hoge(){ console.log(this === window); } hoge.apply(); ``` [答え](http://jsfiddle.net/yZqGK/1/) ## 12 ```js 'use strict'; function hoge(){ console.log(this === window); } hoge.apply(); ``` [答え](http://jsfiddle.net/Af5F5/1/) ## 13 ```js function hoge(){ console.log(this === window); } hoge.apply(undefined, []); ``` [答え](http://jsfiddle.net/vGCW8/1/) ## 14 ```js console.log(eval('this') === window); ``` [答え](http://jsfiddle.net/sMCfJ/) ## 15 ```js 'use strict'; console.log(eval('this') === window); ``` [答え](http://jsfiddle.net/aH5He/) ## 16 ```js (function(){ console.log(eval('this') === window); })(); `````` [答え](http://jsfiddle.net/53yN7/) ## 17 ```js 'use strict'; (function(){ console.log(eval('this') === window); })(); ``` [答え](http://jsfiddle.net/ydUAS/) ## 18 ```js var _eval = eval; console.log(_eval('this') === window); ``` [答え](http://jsfiddle.net/H74Ns/1/) ( [改題前の問題と答え](http://jsfiddle.net/H74Ns/) ) ## 19 ```js 'use strict'; var _eval = eval; console.log(_eval('this') === window); ``` [答え](http://jsfiddle.net/xzEk8/1/) ( [改題前の問題と答え](http://jsfiddle.net/xzEk8/) ) ## 20 ```js 'use strict'; (function(){ var _eval = eval; console.log(_eval('this') === window); })(); ``` [答え](http://jsfiddle.net/AukpQ/1/) ( [改題前の問題と答え](http://jsfiddle.net/AukpQ/) ) ## 21 ```js console.log(new Function('return this')() === window); ``` [答え](http://jsfiddle.net/ByBRj/) ## 22 ```js 'use strict'; console.log(new Function('return this')() === window); ``` [答え](http://jsfiddle.net/nnnfD/) |
|
| 247位 |
|
|||
|
23:08:05 |
(Increments inc. 所属) |
|
Stack Overflowに面白い質問があったので紹介する
- [javascript - Why does Google prepend while(1); to their JSON responses? - Stack Overflow](http://stackoverflow.com/questions/2669690/why-does-google-prepend-while1-to-their-json-responses) ## 質問 Googleのサービス内で使われるJSONの先頭に `while(1);` てついているのは何故? 例えばGoogle Calendarではカレンダーを切り替えるときに以下のような内容のデータがサーバから返される。 ```javascript:json while(1);[['u',[['smsSentFlag','false'],['hideInvitations','false'],['remindOnRespondedEventsOnly','true'],['hideInvitations_remindOnRespondedEventsOnly','false_true'],['Calendar ID stripped for privacy','false'],['smsVerifiedFlag','true']]]] ``` これ以外にもGoogleのサービスでは `&&&START&&&` とか `while(1); &&&START&&&` てのが先頭に入ってたりするんだけど、これは一体何? ## 解答 これはクロスサイト・リクエスト・フォージェリ対策。 例えばGoogleが `gmail.com/json?action=inbox` というURLを持ってて、そこにユーザのCOOKIEでアクセスすると最新50件のメールが取れるとする。悪意のあるサイトからはsame-origin policyでAjaxではアクセスできないが、scriptタグのsrc属性に直接入れることでJSONを取得することはできてしまう。そして [ArrayコンストラクタやそのアクセッサメソッドをオーバライドすることでJSONの値にアクセスできてしまう。](http://ejohn.org/blog/re-securing-json/) 先頭に `while(1);` とかシンタックスエラーになる文字列があればこれを抑制できる。Ajaxではレスポンスを文字列として扱うことができるけれどscriptタグでは即実行されるため、これら余分な箇所を取り除くことができないからだ。 ### 余談 - 最近のWebブラウザでは組み込み型のコンストラクタの再定義はできないようになっている。 - FacebookのJSONの先頭には `for(;;);` がついているらしい。こっちの方が1文字短い。 - 古いブラウザのセキュリティホールに対応するためのバッドノウハウなので、相当セキュリティに気を使わなければならないようなページでなければ必要ない。 - 「CSRF対策」というよりは「JSON hijacking対策」という方が適切。 - より正確に言うならAjaxでアクセスできないのはSame-Origin Policyと[Cross-Origin Resource Sharing](http://www.w3.org/TR/cors/)が設定されていないため。 ### 合わせて読みたい - [ GoogleのJSON(モドキ)の先頭にwhile(1); がつく理由 - 葉っぱ日記](http://d.hatena.ne.jp/hasegawayosuke/20130206/p1) |
|
| 248位 |
|
|||
|
00:05:13 |
(dwango 所属) |
|
## 忙しい人のための
設定ファイルに記述する. `.profile`, `.bashrc`, `.zshrc`などなんでもいいですが,環境変数`JAVA_HOME`を`java_home`コマンドを使って設定します. ``` export JAVA_HOME=`/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home -v "1.6"` PATH=${JAVA_HOME}/bin:${PATH} ``` 以下詳細. ## はじめに 先日,Java8で遊んでみようと思って,OSX(10.7)にインストールしました. ```java -version```を打つと ``` java version "1.8.0-ea" Java(TM) SE Runtime Environment (build 1.8.0-ea-b121) Java HotSpot(TM) 64-Bit Server VM (build 25.0-b63, mixed mode) ``` がでて,ちゃんと1.8になってるなよしよし. と思って生活してたんだけども,思わぬところで事故ったので,1.6に戻す事にしました. でも,いじっているうちに良くわからなくなったので,ログとしてやったことをまとめておこうと思います. ## Javaのバージョンを切り替える2つの方法 OSXでJavaのバージョンを切り替える方法は2つ(しか知らない)あります. 1. `JAVA_HOME`を切り替える 2. **非推奨**: `/System/Library/Frameworks/JavaVM.framework/Versions`の下のCurrentJDKを切り替える ## javaコマンドの実体 まず,1.8をインストールした後,`which java`を打つと,`/usr/bin/java`が返ってきます. 実は,シンボリックリンクなので`ls -l /usr/bin/java`でリンク先を見てみると, ``` java -> /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java ``` という結果が返ってきます. javaコマンドだけではなく,javacなどのコマンドも`/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands`下にあるものを指してます. ## MacOSXにおけるjavaのバージョンについて Mac OSXのJavaのバージョンは**一応**,`/System/Library/Frameworks/JavaVM.framework/Versions`で管理(?)されています(本当か?). ここの,ディレクトリをみてみると, ``` $ ls -l /System/Library/Frameworks/JavaVM.framework/Versions total 40K lrwxr-xr-x 1 root wheel 10 8 27 12:53 1.4 -> CurrentJDK/ lrwxr-xr-x 1 root wheel 10 8 27 12:53 1.4.2 -> CurrentJDK/ lrwxr-xr-x 1 root wheel 10 8 27 12:53 1.5 -> CurrentJDK/ lrwxr-xr-x 1 root wheel 10 8 27 12:53 1.5.0 -> CurrentJDK/ lrwxr-xr-x 1 root wheel 10 8 27 12:53 1.6 -> CurrentJDK/ lrwxr-xr-x 1 root wheel 10 8 27 12:53 1.6.0 -> CurrentJDK/ drwxr-xr-x 9 root wheel 306 1 27 18:57 A/ lrwxr-xr-x 1 root wheel 1 1 28 12:39 Current -> A/ lrwxr-xr-x 1 root wheel 59 8 27 12:53 CurrentJDK -> /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/ ``` となっています. ん?1.4も1.5も1.6も`CurrentJDK`を指してます. `CurrentJDK`は,`/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/`を指しています. **つまり,バージョン1.4も1.5も,実は1.6だったということ.** ## 各Javaバージョンの実体 OSXへJavaを提供する提供元は2箇所あります.AppleとOracleです. Appleが提供しているJavaは,CurrentJDKの指している`/System/Library/Java/JavaVirtualMachines`に格納されています. また,Oracleが提供しているパッケージを使って1.7や1.8をインストールした場合は, `/Library/Java/JavaVirtualMachines`になります. **JDKの置き場所は2箇所ある.** ## JDKのバージョン切り替え ### 1. `JAVA_HOME`を切り替える OSXのJavaは次のような項目で環境が決定されるそうです. 1. Java Preferencesの優先順位をみて決定する 2. 環境変数にJAVA_HOMEが設定してある場合はこちらを優先する Java Preferencesは,2012年10月16日にのJavaのセキュリティアップデート(Java for OS X 2012-006)で削除されました. これ以降(?),OSXにJavaに提供するのはAppleからOracleに変わったようです. どちらにせよ,JAVA_HOMEを設定して上書きしてしまえばバージョンは変更できそうです. このバージョンのパスを返す便利なコマンドが実は存在します. #### java_home コマンド このコマンドは,指定したJavaバージョンのhomeがどこにあるかを返すコマンドです. 配置されている場所は,`/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands`です. 実行するとこんな感じになります. ``` $ ./java_home -v "1.4" Unable to find any JVMs matching version "1.4". /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home $ ./java_home -v "1.5" Unable to find any JVMs matching version "1.5". /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home $ ./java_home -v "1.6" /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home $ ./java_home -v "1.7" Unable to find any JVMs matching version "1.7". /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home $ ./java_home -v "1.8" /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home ``` 1.6と1.8しかインストールしてないので,1.4,1.5と1.7は最新である1.8を指します. 1.6だけはインストールされているので,ちゃんと1.6のパスが返ってきます. #### 設定ファイルに記述する .profile, .bashrc, .zshrcなどなんでもいいですが,どれかで,環境変数JAVA_HOMEに設定します. さきほどの`java_home`コマンドを使います. ``` export JAVA_HOME=`/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home -v "1.6"` PATH=${JAVA_HOME}/bin:${PATH} ``` 以降,1.6の部分を,1.8に変えると簡単にバージョンを切り替えることができます. --- ### 2. **非推奨?**: `/System/Library/Frameworks/JavaVM.framework/Versions`の下のCurrentJDKを切り替える この方法は,OSX標準の構造を色々といじるので,いつ,どこで,どんな事件が起こるかわかりません. そのため,個人的には非推奨です. アンチパターン?として,一応紹介しておきます. まず,`/System/Library/Frameworks/JavaVM.framework/Versions`にある,1.6以前のバージョンを整理します. ``` 1.4 -> CurrentJDK/ 1.4.2 -> CurrentJDK/ 1.5 -> CurrentJDK/ 1.5.0 -> CurrentJDK/ 1.6 -> CurrentJDK/ 1.6.0 -> CurrentJDK/ CurrentJDK -> /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/ ``` CurrentJDKをバシバシ切り替えたいので,1.4と1.5の向き先を1.6にします. (上書きしようと思ったけれども,-f,Fつけても上書きされなかった..) ``` sudo rm 1.4; sudo ln -sf 1.4.2 1.4 sudo rm 1.4.2; sudo ln -sf 1.5 1.4.2 sudo rm 1.5; sudo ln -s 1.5.0 1.5 sudo rm 1.5.0; sudo ln -s 1.6 1.5.0 sudo rm 1.6; sudo ln -s 1.6.0 1.6 sudo rm 1.6.0; sudo ln -s /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents 1.6.0 sudo rm CurrentJDK; sudo ln -s 1.6 CurrentJDK ``` 次に,1.8を入れたので1.8用のリンクを追加します. ``` ln -s /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents 1.8.0 ln -s 1.8.0 1.8 ``` すると,こういう構成になります. ``` $ ls -l total 40K lrwxr-xr-x 1 root wheel 5 1 28 16:39 1.4 -> 1.4.2/ lrwxr-xr-x 1 root wheel 3 1 28 16:40 1.4.2 -> 1.5/ lrwxr-xr-x 1 root wheel 5 1 28 16:45 1.5 -> 1.5.0/ lrwxr-xr-x 1 root wheel 3 1 28 16:45 1.5.0 -> 1.6/ lrwxr-xr-x 1 root wheel 5 1 28 16:46 1.6 -> 1.6.0/ lrwxr-xr-x 1 root wheel 59 1 28 16:47 1.6.0 -> /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/ lrwxr-xr-x 1 root wheel 5 1 28 15:27 1.8 -> 1.8.0/ lrwxr-xr-x 1 root wheel 55 1 28 15:26 1.8.0 -> /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/ drwxr-xr-x 9 root wheel 306 1 27 18:57 A/ lrwxr-xr-x 1 root wheel 1 1 28 12:39 Current -> A/ lrwxr-xr-x 1 root wheel 3 1 28 16:50 CurrentJDK -> 1.6/ ``` CurrentJDKの向き先を1.8や1.6にすると切り替えられるようになりました. #### javaコマンド この状態で`java -version`コマンドを実行すると,まだ1.8のjavaが実行されてます. Current -> A下のCommand下にあるjavaコマンドを実行しています(Aってなんだ...). Aの中身は, ``` $ ls -l A total 40K lrwxr-xr-x 1 root wheel 3 1 27 18:57 1.6 -> 1.6 drwxr-xr-x 44 root wheel 1.5K 8 27 12:53 Commands/ drwxr-xr-x 4 root wheel 136 11 18 2011 Frameworks/ drwxr-xr-x 14 root wheel 476 3 29 2013 Headers/ -rwxr-xr-x 1 root wheel 102K 8 27 12:53 JavaVM* drwxr-xr-x 42 root wheel 1.4K 8 27 12:53 Resources/ drwxr-xr-x 3 root wheel 102 8 27 12:53 _CodeSignature/ ``` のようになっています. **1.6のシンボリックリンクは無限ループになってます.** おそらく,1.8をインストールすると, `/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/bin`にあるコマンドが, `/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands`にコピーされているような気がします. なので,これもシンボリックリンクで参照してあげます. ``` sudo mv Commands Commands.back sudo ln -s /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home/bin Commands ``` ## 参考文献 - [How to Revert to Java 1.6 on Mac OS X 10.7.5](http://stackoverflow.com/questions/13594864/how-to-revert-to-java-1-6-on-mac-os-x-10-7-5 ) - [[OS X] [ENV] [JAVA] JAVA SE 7 と JAVA SE 6 との共存](http://hirooka.pro/?p=102) - [OSXのJava環境切替えのしくみ](http://www.matsuaz.com/matsumotojs/2009/11/17/1258392513883.html) - [「JavaはOracleから直接入手を」、Apple経由のプラグインに削除措置](http://www.itmedia.co.jp/enterprise/articles/1210/19/news043.html) - [[Mac][Java] Macで複数のバージョンのJDKを管理する](http://blog.olivinecafe.info/post/59358925622/mac-java-mac-jdk) - [はてなブログで「Markdown記法一覧」を書いてみるテスト](http://mametanuki.hateblo.jp/entry/2012/09/22/MarkdownList) |
|
| 249位 |
|
|||
|
00:19:47 |
(株式会社ソニックムーブ 所属) |
|
## 概要 2.0になってから更にシンプルに記述できるようになっています。 下記が必須要件となっていますので導入前に確認してください。 * Xcode 5以上 * iOS 6.0以上 大きな変更点として[ドキュメント](http://cocoadocs.org/docsets/AFNetworking/2.0.0/Classes/AFHTTPSessionManager.html)にあるようにiOS7以降のサポートのみでよければ`AFHTTPSessionManager`のサブクラスを作成して実装することが推奨されています。 iOS6やそれ以前のバージョンをサポートする場合の選択肢として`AFHTTPRequestOperationManager`が用意されています。 ここでは主に`AFHTTPRequestOperationManager`の使い方について触れておきます。 ## 使い方 インストールもCocoaPodsで簡単に導入できますので、下記Podfileをプロジェクトのルート直下に作成してインストールしましょう。 ``` platform :ios, '7.0' pod "AFNetworking", "~> 2.0" ``` インストールできたら後はこの記述のみでJSONにパースするとこまでやってくれます。 ```objc AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager manager]; [manager GET:@"http://hoge/sample.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"response: %@", responseObject); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"Error: %@", error); }]; ``` POSTでパラメータを設定する場合 ```objc AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager manager]; NSDictionary* param = @{@"param1" : @"value1", @"param2" : @"value2"}; [manager POST:@"http://hoge/sample.json" parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"response: %@", responseObject); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"Error: %@", error); }]; ``` 結果が通常のレスポンスの場合 ```objc AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager manager]; manager.responseSerializer = [AFHTTPResponseSerializer serializer]; ``` として`AFHTTPResponseSerializer`をセットしておきましょう。 でないとJSONのパースエラーになります。 レスポンスがXMLの場合少し面倒です。 ```objc AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager manager]; manager.responseSerializer = [AFXMLParserResponseSerializer serializer]; // 許可するContentTypeの設定が必要 manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"application/rss+xml"]; ``` 結果のresponseObjectが`NSXMLParser`なので自前でパースする必要があります。 ここも自動でパースしてくれれば言う事無しです。 他にもリクエストパラメータをJSONにしたい場合には、 ```objc AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; manager.requestSerializer = [AFJSONRequestSerializer serializer]; ``` だけで自動でやってくれます。 ## 通信ログについて 通信の詳細なログが見たい時は`AFNetworkActivityLogger`を別途インストールするとリクエストヘッダー、レスポンスヘッダーもログ出力してくれます。 ``` platform :ios, '7.0' pod "AFNetworking", "~> 2.0" pod "AFNetworkActivityLogger", "~> 2.0" ``` Podfileに`AFNetworkActivityLogger`を追加してインストールします。 `AppDelegate.m -application:didFinishLaunchingWithOptions::`にログを開始する処理を記述します。 ```objc [[AFNetworkActivityLogger sharedLogger] startLogging]; ``` ログレベルを`AFLoggerLevelDebug `にすればリクエストヘッダー、レスポンスヘッダーを含む通信のやりとりをログに出力できます。 ```objc [[AFNetworkActivityLogger sharedLogger] setLevel:AFLoggerLevelDebug]; ``` GTMHttpFetcherのように通信ログをHTMLファイルで別途出力してくれれば言うことなしなんですけどね。 ## キャッシュポリシー、タイムアウトについて `AFHTTPRequestOperationManager `にはキャッシュポリシーとタイムアウトを設定するパラメータがありません。 現状で対応しようとすると`AFHTTPRequestOperationManager`を継承したクラスを作って下記メソッドを追加するといった個別の対応をする必要があります。 カテゴリを使ってメソッドを追加してもよさそうです。 ```objc - (AFHTTPRequestOperation *)GET:(NSString *)URLString parameters:(NSDictionary *)parameters timeoutInterval:(NSTimeInterval)timeoutInterval cachePolicy:(NSURLRequestCachePolicy)cachePolicy success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters]; request.timeoutInterval = timeoutInterval; request.cachePolicy = cachePolicy; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; [self.operationQueue addOperation:operation]; return operation; } ``` もしくは`AFHTTPRequestOperationManager`を使用せず`AFHTTPRequestOperation`を直接使用することでも対応できます。 ```objc NSURL *URL = [NSURL URLWithString:@"http://hoge/sample.json"]; NSURLRequest *request = [NSURLRequest requestWithURL:URL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0]; AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"response: %@", responseObject); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"Error: %@", error); }]; [operation start]; ``` ## セキュリティポリシー テスト環境などでSSL通信をする際にオレオレ証明書(自己署名証明書)を使っている場合は、`AFHTTPRequestOperationManager`にオレオレ証明書を許可する設定を行うことで通信できます。 ```objc AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; manager.securityPolicy.allowInvalidCertificates = YES; // 以降は通常通りAFHTTPRequestOperationManagerを使って通信を行う ``` ## まとめ `NSURLSession`の登場によって今後は`NSURLConnection`がベースとなっている`AFHTTPRequestOperationManager`ではなく`AFHTTPSessionManager`が主流になると思います。 現状ではiOS6のサポートが必要なアプリも多いと思いますので、`AFHTTPRequestOperationManager`をメインに使いつつ、可能なところから`AFHTTPSessionManager`にシフトしていけばよいでしょう。 |
|
| 250位 |
|
|||
|
23:34:20 |
(Wantedly, Inc. 所属) |
|
Rubyの言語仕様で重要なものをピックアップ。 ## Class http://www.ruby-doc.org/core-1.9.3/Class.html RubyではClassはClassオブジェクトとして定義されている。 ```:methods ::new #allocate #inherited #new #superclass ``` インスタンス化に関係する > ::new, #new, #allocate と継承に関係する > #superclass, #inhereted しか定義されていない。 その他のメソッドはすべてスーパークラスであるModuleに定義されている。Classはインスタンス化できるModuleと考えれば良い。 ### SuperClassの関係 ```bash: [1] pry(main)> Class.superclass => Module [2] pry(main)> Module.superclass => Object [3] pry(main)> Object.superclass => BasicObject [4] pry(main)> BasicObject.superclass => nil [5] pry(main)> nil.superclass NoMethodError: undefined method `superclass' for nil:NilClass [6] pry(main)> NilClass.superclass => Object ``` ### Classの関係 ```bash: [1] pry(main)> Class.class => Class [2] pry(main)> Module.class => Class [3] pry(main)> Object.class => Class [4] pry(main)> BasicObject.class => Class [5] pry(main)> nil.class => NilClass [6] pry(main)> NilClass.class => Class ``` ### Moduleも含めた祖先 ```bash: [1] pry(main)> Class.ancestors => [Class, Module, Object, PP::ObjectMixin, Kernel, BasicObject] ``` ここで、`PP::ObjectMixin`は`pry`が出力の整形用に勝手に突っ込んだもの。通常は入っていない。 ```bash: irb(main):001:0> Class.ancestors => [Class, Module, Object, Kernel, BasicObject] ``` ## Module http://www.ruby-doc.org/core-1.9.3/Module.html ```:methods ::constants ::nesting ::new #< #<= #<=> #== #=== #> #>= #alias_method #ancestors #append_features #attr #attr_accessor #attr_reader #attr_writer #autoload #autoload? #class_eval #class_exec #class_variable_defined? #class_variable_get #class_variable_set #class_variables #const_defined? #const_get #const_missing #const_set #constants #define_method #extend_object #extended #freeze #include #include? #included #included_modules #instance_method #instance_methods #method_added #method_defined? #method_removed #method_undefined #module_eval #module_exec #module_function #name #private #private_class_method #private_instance_methods #private_method_defined? #protected #protected_instance_methods #protected_method_defined? #public #public_class_method #public_instance_method #public_instance_methods #public_method_defined? #remove_class_variable #remove_const #remove_method #to_s #undef_method ``` 基本機能 - 比較関係 > #< #<= #<=> #== #=== #> #>= - 文字列化 > #name #to_s - freeze > #freeze Constants関係 > ::constants ::nesting #const_defined? #const_get #const_missing #const_set #constants #remove_const Module機能 > ::new #ancestors - autoload > #autoload #autoload? - include > #append_features #include #include? #included #included_modules - extend > #extend_object #extended Class関係 - class_variables (`@@foo` etc.) > #class_variable_defined? #class_variable_get #class_variable_set #class_variables #remove_class_variable - methods > #alias_method #define_method #method_added #method_defined? #method_removed #method_undefined #remove_method #undef_method > #instance_methods > #module_function - attr > #attr #attr_accessor #attr_reader #attr_writer - eval, exec > #class_eval #class_exec #module_eval #module_exec private,protected,publicのスコープ概念の導入 > #private #private_class_method #private_instance_methods #private_method_defined? > #protected #protected_instance_methods #protected_method_defined? > #public #public_class_method #public_instance_method #public_instance_methods #public_method_defined? ## Object http://www.ruby-doc.org/core-1.9.3/Object.html ```:methods #!~ #<=> #=== #=~ #class #clone #define_singleton_method #display #dup #enum_for #eql? #extend #freeze #frozen? #hash #inspect #instance_of? #instance_variable_defined? #instance_variable_get #instance_variable_set #instance_variables #is_a? #kind_of? #method #nil? #object_id #public_method #public_send #remove_instance_variable #respond_to? #respond_to_missing? #send #singleton_class #singleton_methods #taint #tainted? #tap #to_enum #to_s #trust #untaint #untrust #untrusted? ``` 基本機能 - 比較関係 > #!~ #<=> #=== #=~ #eql? #nil? - 文字列化 > #to_s #inspect - Enum関係 > #enum_for #to_enum #tap - copy > #clone #dup - Hash値, Object値 > #hash #object_id - display > #display - freeze > #freeze #frozen? - マーク付け > #taint #tainted? #untaint > #trust #untrust #untrusted? Instance機能 - classとの関係性チェック > #instance_of? #is_a? #kind_of? #class - instance_valiable (`@foo` etc.) > #instance_variable_defined? #instance_variable_get #instance_variable_set #instance_variables #remove_instance_variable - method check > #method #public_method > #respond_to? #respond_to_missing? - method call > #send #public_send - extend > #extend - singleton_method、singleton_class > #define_singleton_method #singleton_class #singleton_methods これは日本語では特異メソッド、特異クラスと呼ばれている。これの解説は以下がわかり易かった。 [結局、Rubyの特異メソッドって何なの? - (゚∀゚)o彡 sasata299's blog](http://blog.livedoor.jp/sasata299/archives/51497378.html) これを読めば、`class << self`とかの意味もわかるはず! ## BasicObject http://www.ruby-doc.org/core-1.9.3/BasicObject.html すべてオブジェクトで共通で使える、Objectの親に当たるクラス。Kernelモジュールがincludeされていないのが特徴。 ```:methods ::new #! #!= #== #__id__ #__send__ #equal? #instance_eval #instance_exec #method_missing #singleton_method_added #singleton_method_removed #singleton_method_undefined ``` - 比較関係 > #! #!= #== #equal? - eval, exec > #instance_eval #instance_exec - method_missing > #method_missing - singleton_methodのチェック関係 > #singleton_method_added #singleton_method_removed #singleton_method_undefined - その他 > # ::new `#__id__` `#__send__` ## Kernel http://www.ruby-doc.org/core-1.9.3/Kernel.html open, system, eval, exit, raise, putsなどのprint系関数, Array, Integer, Stringなどのキャストのような関数など、一般的な言語でメソッドではなく関数として定義されているような、基本的な動作が定義されているモジュール。 ここに定義されているすべてのメソッドは一度見ておくと良さそう。 ## Proc/Method http://www.ruby-doc.org/core-1.9.3/Proc.html http://www.ruby-doc.org/core-1.9.3/Method.html `binding`(束縛変数)を持つProcと、`receiver`を持つMethod。Blockを使う時なんかにも必要なので、理解しておくと良い。 Proc/Method, block/proc/lambdaの違いについては以下がわかり易かった。 [Ruby の Proc オブジェクトと Method オブジェクトの違い (proc, lambda, ブロック, メソッドについて) - vivid memo](http://d.hatena.ne.jp/vividcode/20100813/1281709854) ## その他、基本型 String/Symbol - http://www.ruby-doc.org/core-1.9.3/String.html - http://www.ruby-doc.org/core-1.9.3/Symbol.html Number/Math - http://www.ruby-doc.org/core-1.9.3/Fixnum.html - http://www.ruby-doc.org/core-1.9.3/Bignum.html - http://www.ruby-doc.org/core-1.9.3/Integer.html - http://www.ruby-doc.org/core-1.9.3/Float.html - http://www.ruby-doc.org/core-1.9.3/Rational.html - http://www.ruby-doc.org/core-1.9.3/Complex.html - http://www.ruby-doc.org/core-1.9.3/Numeric.html - http://www.ruby-doc.org/core-1.9.3/Math.html Boolean - http://www.ruby-doc.org/core-1.9.3/TrueClass.html - http://www.ruby-doc.org/core-1.9.3/FalseClass.html 集合関係 - http://www.ruby-doc.org/core-1.9.3/Enumerable.html - http://www.ruby-doc.org/core-1.9.3/Range.html - http://www.ruby-doc.org/core-1.9.3/Array.html - http://www.ruby-doc.org/core-1.9.3/Hash.html - http://www.ruby-doc.org/stdlib-1.9.3/libdoc/set/rdoc/Set.html Time/Date - http://www.ruby-doc.org/core-1.9.3/Time.html - http://www.ruby-doc.org/stdlib-1.9.3/libdoc/date/rdoc/Date.html - http://www.ruby-doc.org/stdlib-1.9.3/libdoc/date/rdoc/DateTime.html |
|
| 251位 |
|
|||
|
04:01:17 |
(Magnet Inc 所属) |
|
Ruby 開発環境 AdventCalendar 10 日目です。前日は、 [aereal さん](http://qiita.com/users/aereal)でした。
さて、皆さんコマンド打ちまくってますか?僕は Ctrl+R で履歴から引っぱり出さないとタイポで撃沈します。 そんなこんなで皆さんいろんな CLI ツールをご利用中だと思います。 vim とか emacs とか、 rails g とか、 guard とか。実に便利なものたちですが、現実の開発現場に即した、素晴らしいニッチなツールというのは、得てしてそんなにないものです。 **「こんなに重厚でなくていい」** **「もうちょっとざっくりとした……なんというか痒いところに手が届くような……」** などなど、いろいろあると思います。例えば「チームで開発しているのだが、今行ったコミットのレビューを依頼するメールを書くのが面倒なので社内 Twitter 的なものに書こうかと思うのだがそれもめんどくさいのでコミット Diff を見れる URL をぺたっと社内 Twitter に貼ってくれるだけ」のツールとか、欲しくなるもんです。無いです。 ということで、実際に弊社、株式会社 Qterasで利用されている、社内製 CLI ツールの配布とその開発についてお話します。 ## 具体的な開発 ### 実装編 さて、実際に作って皆で使ってスペシャルハッピーになりたいのですが、Ruby でCLI ツールを作るのはすごく簡単です。 ```shell:bundlerの偉大さ $ bundle gem special_happy -b create special_happy/Gemfile create special_happy/Rakefile create special_happy/LICENSE create special_happy/README.md create special_happy/.gitignore create special_happy/special_happy.gemspec create special_happy/lib/special_happy.rb create special_happy/lib/special_happy/version.rb create special_happy/bin/special_happy Initializating git repo in ~/tmp/special_happy ``` ひな形が一撃で作れました。ということで実際に実装していくわけですが、とりえあえず [Thor gem](https://github.com/wycats/thor) を導入します。あと、このリポジトリを必ず **Git で管理してください**。そして社内のリポジトリサーバーにアップしてください。**これは配布編で必要な手順です。** Thor は CLI ツールフレームワークで、自分で optparse なんかを使うより断然簡単に CLI ツールを実装することが出来ます。とりあえずやってみましょう。 まずは special_happy.gemspec に Thor を追加します。 ```ruby:special_happy.gemspec # -*- encoding: utf-8 -*- require File.expand_path('../lib/special_happy/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["Sho Kusano"] gem.email = ["rosylilly@aduca.org"] gem.description = %q{TODO: Write a gem description} gem.summary = %q{TODO: Write a gem summary} gem.homepage = "" gem.files = `git ls-files`.split($\) gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.name = "special_happy" gem.require_paths = ["lib"] gem.version = SpecialHappy::VERSION gem.add_dependency 'thor' # ここ追加 end ``` お次は肝心の起動部分です。さらっと。 ```ruby:bin/special_happy #!/usr/bin/env ruby require 'special_happy' SpecialHappy::CLI.start # ここ追加 ``` そしたらこの SpecialHappy::CLI が読み込まれるように。 ```ruby:lib/special_happy.rb require "special_happy/version" require "special_happy/cli" # ここ追加 module SpecialHappy # Your code goes here... end ``` 後は新規でファイルを作って中身をさっくり作ります。とりあえず機能は文字列を引数で与えると赤色で出力してくれるというものにしましょう。便利そうです。 ```ruby:lib/special_happy/cli.rb require 'special_happy' require 'thor' module SpecialHappy class CLI < Thor desc "red WORD", "red words print." # コマンドの使用例と、概要 def red(word) # コマンドはメソッドとして定義する say(word, :red) end end end ``` これで準備は整いました。さっそく試しに使ってみましょう。 ```shell:試運転 $ bundle install # Thor なんかをインストールするために $ bundle exec bin/special_happy red 赤い! 赤い! ``` カラーの端末なら、"赤い!"の文字が赤く表示されるはずです。少々あっけなかったですか?これでいいのです。あとは Ruby でコマンドを好きなように実装することが出来ます。詳しい Thor の解説などは、[本家 wiki](https://github.com/wycats/thor/wiki) を見るとよいでしょう。 ここまで作業したリポジトリは[僕の Github](https://github.com/rosylilly/git-advent-calendar-sample-special-happy) にあがっています。 ### 配布編 さて、さっくり作れたコマンドラインツールを別のプロジェクトからさっくり使いましょう。 もちろん gem として作っているので、思い切りよく Rubygems に公開してしまうのも OK ですが、作っているツールはニッチですから、無駄に Rubygems の名前空間を消費しそうなら、 [pebbles](http://randd.kwappa.net/2010/12/19/192) 配下にしておくなどの配慮をすると喜ばれるでしょう。 さて、後はチームメンバーへの配布のみとなる訳ですが、ここで gem 化すると Rubygems に上げないとアップデートできないなどの問題が発生して面倒です。そこで、 Gemfile を利用します。 プロジェクトルートディレクトリに以下のような Gemfile を作ります。 ```ruby:Gemfile gem "special_happy", :git => "git://github.com/rosylilly/git-advent-calendar-sample-special-happy.git" # URI は適宜書き換えてください ``` こうすることで、 Rubygems を経由することなく、閉じた環境で gem のインストールを行うことが出来ます。 これなら社内用の秘匿ツール(表だしするとマズイツール)なども開発、運用が容易です。利用者は以下のコマンドを実行するだけです。 ```shell:簡単! $ bundle install $ bundle update # たまにやろう ``` ツールが依存する gem も同時にインストールされるので、環境ごとに頑張る必要もほぼありません(Windows 環境だと gzip とか ssl 周りで苦労しますが、乗り越えればこっちのものです)。 するとあとは ```shell:楽ちん! $ bundle exec special_happy red 赤! 赤! ``` という具合に利用することが出来ます。 ### 実運用 こんな具合に簡単に使うことが出来ました。実際に Qteras では - JS の require を解決して結合する(Sprockets の流用) - JS-Beautify をかけて記法を綺麗にする - Google Closure Compiler を使って圧縮、型チェックを行う というひと通りの動作を一気に行なってくれるツールが開発・運用されています。これを導入する前は個別に Closure Compiler などをインストールしていたのですが、そこは Gemfile と git submodule の妙技により、新規メンバーは `bundle install` だけで即開発に入れるようになりました。 また、「もしも Rubygems が突如撃沈しており、まったく無反応である」と言った場合にも、依存 gem がインストール出来るように ```shell:何からなにまで世話になる $ bundle package ``` のコマンドによって、依存 gem の .gem ファイルをローカルにバックアップしておくのもよいです。バックアップしてある .gem は ```shell:足を向けて寝れない $ bundle install --local ``` でインストール可能です。 ## 小さなしあわせ生活 とまあこのようにして社内用の小さなツールを作る、運用する、という点においては今のところ僕は Ruby より便利な環境を知りません。 C++ なんかで実装したほうがバイナリ配布だけで済むとかあるのでしょうが、それをここまで手早く柔軟に作れるかというと、また別だと思います。 社内ツールというのは往々にして利益を望みにくく、またその効果も実感として得難いものです。実際に利益を受けて便利になった人にとってすら、便利さというのはそのひととき味わえば、数刻後には普通のことになってしまって、小さなしあわせです。 僕はどちらかと言えば派手な仕事よりこういう「隣にいる人」の小さなしあわせを眺める方が好きなので、ちょこちょこ作っては同僚に押し付けたりしています。あまりよくないことですが、しかし便利なのも事実なのです。老後は「エンジニアリングサポート技術基盤部」とかそういう部署に腰を落ち着けたいものです。 そんなこんなで、もし興味が湧いたのでしたら、ぜひあなたもチャレンジしてみてはいかがでしょうか?ディスプレイの向こうのユーザーさんが喜ぶのも嬉しいですが、隣の人の「いやー便利だな」という小さなつぶやきも、負けず劣らずの嬉しさですよ。 |
|
| 252位 |
|
|||
|
14:39:47 |
|
|
iOSアプリ開発で自分がよく使うツールとサービスについてまとめてみました。 ## BaaS: [Parse.com](http://parse.com) ソーシャルな機能は持たせたいけどサーバ作るのだるい、そんなアプリ開発者の味方がBaaS(Backend as a Service)です。最近熱いですね。[Kinvey](http://kinvey.com), [StackMob](http://stackmob.com)なども試してみましたが、機能的にも料金的にも一番おすすめなのがParse.comです。100万APIまで無料、100万〜1500万APIまで$199という価格設定です。サーバは書くのが嫌というよりデプロイがめんどいくさいんですよね。BaaSなら一瞬でアクティブになりますので捗ります。 ## リファレンス: [Dash](http://kapeli.com/dash/) 高速にリファレンスを検索したい時、Dashが使えます。 iOS以外にもjQuery,Android,Rails等ひと通り揃っています。 ## リポジトリ: [github](http://github.com) リポジトリは何でも言いっちゃ何でもいいんですが、githubはUIがかっこいい(重要)ので使ってます。個人開発だとブランチ切る必要もあんまりないんですね。Issueはチケット管理として使ってます。 ## クラッシュレポート: [Crashlytics](http://beta.crashlytics.com/) Crashlyticsを使うと、コードに1行追加するだけで、クラッシュレポートがバンバン飛んでくるようになります。レポートにはスタックトレースが付いてくるのでわかる時はすぐ直せますね。現在はまだベータ中なので無料です。 ## AppStore分析: [AppAnnie](http://www.appannie.com/) AppAnnieはアプリのAppStore順位変動、ダウンロード数変動などを見るのに使っています。 最近ちょっとやらかして問題になっていますが、とてもシンプルでわかりやすいです。 同様のサービスに[Distimo](http://www.distimo.com/)というのもあります。 ## 多言語化: [Linguan](http://www.cocoanetics.com/apps/linguan/) 多言語化は初回はいいけど、メンテが大変。ちょっと油断するとすぐ翻訳漏れが出てきてしまいます。Linguanを使えば翻訳漏れを一発で検出してくれます。文言の入力もすぐに言語ファイルに反映してくれます。自分の場合はアップデート申請する直前にLinguanでチェックしてまとめて英語化しています。 ## アクセス解析: [Google Analytics](http://www.google.com/intl/ja/analytics/) 動線の最適化、無断な通信の検出、そして何より本当にユーザが使っているという実感を得るために、アクセス解析ツールは重要です。 Google AnalyticsはWebだけじゃなくてiOS/Android等のモバイルアプリにも対応しています。 自分の場合は基本的にビューコントローラーのviewWillAppearでPVカウント、通信リクエスト時にイベントカウントとして計測対象としています。 ## 実機ファイラ: [iExplorer](http://www.macroplant.com/iexplorer/) iExplorerを使うとUSB接続したiPhoneやiPadのフォルダ/ファイルをMacから操作できるようになります。 ローカルにファイルを保存するタイプのアプリでは実機のファイル構造を覗けるツールがあるととても捗ります。 またセキュリティ的に危ないファイルが見える状態になっていないか、情報漏洩のチェックツールとしても使えます。 ## メモリリーク検査: Istruments/Leaks InstrumentsはXCodeインストールすると一緒に入ってくるプロファイラツールです。 いろいろ見れるんですが、メモリリークを教えてくれるLeaksが一番使えます。ただ検知されるとどうしても気になってしまいますが、実害がない場合も多いので対応はほどほどにした方が良さそうです。 あとLeaksでチェックする前にXCodeのAnalyzeを先にかけておきましょう。 ## まとめ ざっと自分がiOSアプリ開発時に使っているツールを上げてみました。 どれもこれも、あるとないとでは大分捗りが変わってくると思います。 他にもこんなツールもあるぜって情報お待ちしています! |
|
| 253位 |
|
|||
|
18:00:05 |
|
|
vimでプログラムをペーストするときにそのままだと自動的にインデントがかかって、
下に行くほどコードが右に寄ってしまう。 そんな時、自動的にインデントせずにペーストするには、 1. ":a!" 2. ペースト 3. エスケープキー でペーストできるみたい。 ちなみに":i!"だとインサートモードでペーストになります。 |
|
| 254位 |
|
|||
|
23:48:16 |
|
|
JavaScriptバリバリなページはNokogiriなどのスクレイピングツールで扱うのが難しいですが、Seleniumでブラウザ自動操作すると大体のものはできるようになるので、使い方をまとめておきます。 Javaから使うのと比べて、Rubyバインディングだとずいぶんシンプルで良かったです。 ## リファレンス http://selenium.googlecode.com/svn/trunk/docs/api/rb/frames.html 【2015-11-12 追記】上記のリンクは更新停止しているようなのでこちらを見たほうが良さそうです http://www.rubydoc.info/gems/selenium-webdriver/ ## 使い方 gemをインストールする。 ```bash gem install selenium-webdriver ``` seleniumなんちゃらというgemがたくさんあるけどこれだけでよい。 コード内で `require 'selenium-webdriver'` すれば使えるようになる。 ## 起動と終了 ```ruby driver = Selenium::WebDriver.for :firefox # ブラウザ起動 driver.quit # ブラウザ終了 ``` :firefox のところは :ieとか:chromeとか:operaとか好きなのを ## ページ、フレーム移動 ```ruby driver.navigate.to 'http://example.com' # URLを開く driver.switch_to.frame(1) # 1つめの子フレームに移動 driver.switch_to.frame("frameid") # フレームのnameを指定して移動 ``` ## 要素を指定 ```ruby element = driver.find_element(:name, 'calendar') # nameで指定 element = driver.find_element(:id, 'calendar') # idで指定 element = driver.find_element(:class, 'right_box') # classで指定 element = element.find_element(:tag_name, 'table') # 要素名で指定 element = element.find_element(:xpath, 'tr[2]/td[3]/a') # XPathで指定 element = driver.find_element(:class, 'body').find_element(:name, 'form').find_elements(:xpath, './/input')[2] # メソッドチェーン ``` DriverとElementがどちらもfind_element・find_elementsメソッドを持っている。 ## 入力 ```ruby # テキストフィールドへ入力・削除 element = driver.find_element(:name, 'userId') element.send_keys('username') element.clear # ドロップダウンリスト選択 select = Selenium::WebDriver::Support::Select.new(driver.find_element(:id, 'dropdown')) select.select_by(:value, 'item1') # valueの値で選択 select.select_by(:text, 'どちらでもない') # 表示テキストで選択 select.select_by(:index, 2) # インデックス(0始まり)で選択 # ラジオボタン選択 driver.find_elements(:name, 'selectOne')[2].click # リンクやボタンを押す driver.find_element(:xpath, '//table[2]/tr[3]/td[1]/a').click ``` ## 情報取得 ```ruby # 要素の内容テキスト puts element.text # Firefoxでダウンロードのリンクを踏んだとき保存確認ダイアログを出さずに指定のフォルダに保存 profile = Selenium::WebDriver::Firefox::Profile.new profile['browser.download.folderList'] = 2 profile['browser.download.useDownloadDir'] = true profile['browser.download.dir'] = 'path/to/downloadFolder' profile['browser.helperApps.neverAsk.saveToDisk'] = 'text/csv' driver = Selenium::WebDriver.for :firefox, :profile => profile # スクリーンショットを撮る # !!private APIにつき動作保証なし!! driver.save_screenshot('path/to/filename.png') ``` ## おわりに ここにもう少し詳しく書いてあります(英語) http://code.google.com/p/selenium/wiki/RubyBindings |
|
| 255位 |
|
|||
|
23:58:44 |
|
|
Qiita Markdownのコードブロックで指定できる言語は以下の通りです。
例えばCucumberを利用する場合は Qiita supports syntax highlighting of following languages. For example, when you want to use Cucumber, please specify Cucumber as follows. > \`\`\`rb > ... > \`\`\` のように指定してください。  Qiita Markdownについては [Qiita Markdown](http://qiita.com/Qiita/items/c686397e4a0f4f11683d) をご覧下さい。 For more information about Qiita Markdown, please refer to [Qiita Markdown](http://qiita.com/Qiita/items/c686397e4a0f4f11683d) . *** * Cucumber, cucumber, Gherkin, gherkin: * Gherkin (filenames *.feature) * abap: * ABAP (filenames *.abap) * ada, ada95ada2005: * Ada (filenames *.adb, *.ads, *.ada) * ahk: * autohotkey (filenames *.ahk, *.ahkl) * antlr-as, antlr-actionscript: * ANTLR With ActionScript Target (filenames *.G, *.g) * antlr-cpp: * ANTLR With CPP Target (filenames *.G, *.g) * antlr-csharp, antlr-c#: * ANTLR With C# Target (filenames *.G, *.g) * antlr-java: * ANTLR With Java Target (filenames *.G, *.g) * antlr-objc: * ANTLR With ObjectiveC Target (filenames *.G, *.g) * antlr-perl: * ANTLR With Perl Target (filenames *.G, *.g) * antlr-python: * ANTLR With Python Target (filenames *.G, *.g) * antlr-ruby, antlr-rb: * ANTLR With Ruby Target (filenames *.G, *.g) * antlr: * ANTLR * apacheconf, aconf, apache: * ApacheConf (filenames .htaccess, apache.conf, apache2.conf) * applescript: * AppleScript (filenames *.applescript) * as, actionscript: * ActionScript (filenames *.as) * as3, actionscript3: * ActionScript 3 (filenames *.as) * aspx-cs: * aspx-cs (filenames *.aspx, *.asax, *.ascx, *.ashx, *.asmx, *.axd) * aspx-vb: * aspx-vb (filenames *.aspx, *.asax, *.ascx, *.ashx, *.asmx, *.axd) * asy, asymptote: * Asymptote (filenames *.asy) * basemake: * Makefile * bash, sh, ksh: * Bash (filenames *.sh, *.ksh, *.bash, *.ebuild, *.eclass) * bat: * Batchfile (filenames *.bat, *.cmd) * bbcode: * BBCode * befunge: * Befunge (filenames *.befunge) * blitzmax, bmax: * BlitzMax (filenames *.bmx) * boo: * Boo (filenames *.boo) * brainfuck, bf: * Brainfuck (filenames *.bf, *.b) * c-objdump: * c-objdump (filenames *.c-objdump) * c: * C (filenames *.c, *.h) * cfm: * Coldfusion HTML (filenames *.cfm, *.cfml, *.cfc) * cfs: * cfstatement * cheetah, spitfire: * Cheetah (filenames *.tmpl, *.spt) * clojure, clj: * Clojure (filenames *.clj) * cmake: * CMake (filenames *.cmake, CMakeLists.txt) * coffee-script, coffeescript: * CoffeeScript (filenames *.coffee) * common-lisp, cl: * Common Lisp (filenames *.cl, *.lisp, *.el) * console: * Bash Session (filenames *.sh-session) * control: * Debian Control file (filenames control) * cpp, c++: * C++ (filenames *.cpp, *.hpp, *.c++, *.h++, *.cc, *.hh, *.cxx, *.hxx) * cpp-objdump, c++-objdumb, cxx-objdump: * cpp-objdump (filenames *.cpp-objdump, *.c++-objdump, *.cxx-objdump) * csharp, c#: * C# (filenames *.cs) * css+django, css+jinja: * CSS+Django/Jinja * css+erb, css+ruby: * CSS+Ruby * css+genshitext, css+genshi: * CSS+Genshi Text * css+mako: * CSS+Mako * css+myghty: * CSS+Myghty * css+php: * CSS+PHP * css+smarty: * CSS+Smarty * css: * CSS (filenames *.css) * cython, pyx: * Cython (filenames *.pyx, *.pxd, *.pxi) * d-objdump: * d-objdump (filenames *.d-objdump) * d: * D (filenames *.d, *.di) * delphi, pas, pascal, objectpascal: * Delphi (filenames *.pas) * diff, udiff: * Diff (filenames *.diff, *.patch) * django, jinja: * Django/Jinja * dpatch: * Darcs Patch (filenames *.dpatch, *.darcspatch) * duel, Duel Engine, Duel View, JBST, jbst, JsonML+BST: * Duel (filenames *.duel, *.jbst) * dylan: * Dylan (filenames *.dylan, *.dyl) * el: * Emacs Lisp * erb: * ERB * erl: * Erlang erl session (filenames *.erl-sh) * erlang: * Erlang (filenames *.erl, *.hrl) * evoque: * Evoque (filenames *.evoque) * factor: * Factor (filenames *.factor) * felix, flx: * Felix (filenames *.flx, *.flxh) * fortran: * Fortran (filenames *.f, *.f90) * fsharp * FSharp (filenames *.fs, *.fsi) * gas: * GAS (filenames *.s, *.S) * genshi, kid, xml+genshi, xml+kid: * Genshi (filenames *.kid) * genshitext: * Genshi Text * glsl: * GLSL (filenames *.vert, *.frag, *.geo) * gnuplot: * Gnuplot (filenames *.plot, *.plt) * go: * Go (filenames *.go) * gooddata-cl: * GoodData-CL (filenames *.gdc) * groff, nroff, man: * Groff (filenames *.[1234567], *.man) * groovy: Groovy (filenames *.groovy) * haml, HAML: * Haml (filenames *.haml) * haskell, hs: * Haskell (filenames *.hs) * html+cheetah, html+spitfire: * HTML+Cheetah * html+django, html+jinja: * HTML+Django/Jinja * html+evoque: * HTML+Evoque (filenames *.html) * html+genshi, html+kid: * HTML+Genshi * html+mako: * HTML+Mako * html+myghty: * HTML+Myghty * html+php: * HTML+PHP (filenames *.phtml) * html+smarty: * HTML+Smarty * html+velocity: * HTML+Velocity * html: * HTML (filenames *.html, *.htm, *.xhtml, *.xslt) * hx, haXe: * haXe (filenames *.hx) * hybris, hy: * Hybris (filenames *.hy, *.hyb) * ini, cfg: * INI (filenames *.ini, *.cfg) * io: * Io (filenames *.io) * ioke, ik: * Ioke (filenames *.ik) * irc: * IRC logs (filenames *.weechatlog) * jade, JADE: * Jade (filenames *.jade) * java: * Java (filenames *.java) * js+cheetah, javascript+cheetah, js+spitfire, javascript+spitfire: * JavaScript+Cheetah * js+django, javascript+django, js+jinja, javascript+jinja: * JavaScript+Django/Jinja * js+erb, javascript+erb, js+ruby, javascript+ruby: * JavaScript+Ruby * js+genshitext, js+genshi, javascript+genshitext, javascript+genshi: * JavaScript+Genshi Text * js+mako, javascript+mako: * JavaScript+Mako * js+myghty, javascript+myghty: * JavaScript+Myghty * js+php, javascript+php: * JavaScript+PHP * js+smarty, javascript+smarty: * JavaScript+Smarty * js, javascript: * JavaScript (filenames *.js) * jsp: * Java Server Page (filenames *.jsp) * lhs, literate-haskell: * Literate Haskell (filenames *.lhs) * lighty, lighttpd: * Lighttpd configuration file * llvm: * LLVM (filenames *.ll) * logtalk: * Logtalk (filenames *.lgt) * lua: * Lua (filenames *.lua, *.wlua) * make, makefile, mf, bsdmake: * Makefile (filenames *.mak, Makefile, makefile, Makefile.*, GNUmakefile) * mako: * Mako (filenames *.mao) * maql: * MAQL (filenames *.maql) * mason: * Mason (filenames *.m, *.mhtml, *.mc, *.mi, autohandler, dhandler) * matlab, octave: * Matlab (filenames *.m) * matlabsession: * Matlab session * minid: * MiniD (filenames *.md) * modelica: * Modelica (filenames *.mo) * modula2, m2: * Modula-2 (filenames *.def, *.mod) * moocode: * MOOCode (filenames *.moo) * mupad: * MuPAD (filenames *.mu) * mxml: * MXML (filenames *.mxml) * myghty: * Myghty (filenames *.myt, autodelegate) * mysql: * MySQL * nasm: * NASM (filenames *.asm, *.ASM) * newspeak: * Newspeak (filenames *.ns2) * nginx: * Nginx configuration file * numpy: * NumPy * objdump: * objdump (filenames *.objdump) * objective-c, objectivec, obj-c, objc: * Objective-C (filenames *.m) * objective-j, objectivej, obj-j, objj: * Objective-J (filenames *.j) * ocaml: * OCaml (filenames *.ml, *.mli, *.mll, *.mly) * ooc: * Ooc (filenames *.ooc) * perl, pl: * Perl (filenames *.pl, *.pm) * php, php3, php4, php5: * PHP (filenames *.php, *.php[345]) * postscript: * PostScript (filenames *.ps, *.eps) * pot, po: * Gettext Catalog (filenames *.pot, *.po) * pov: * POVRay (filenames *.pov, *.inc) * prolog: * Prolog (filenames *.prolog, *.pro, *.pl) * properties: * Properties (filenames *.properties) * protobuf: * Protocol Buffer (filenames *.proto) * py3tb: * Python 3.0 Traceback (filenames *.py3tb) * pycon: * Python console session * pytb: * Python Traceback (filenames *.pytb) * python, py: * Python (filenames *.py, *.pyw, *.sc, SConstruct, SConscript, *.tac) * python3, py3: * Python 3 * ragel-c: * Ragel in C Host (filenames *.rl) * ragel-cpp: * Ragel in CPP Host (filenames *.rl) * ragel-d: * Ragel in D Host (filenames *.rl) * ragel-em: * Embedded Ragel (filenames *.rl) * ragel-java: * Ragel in Java Host (filenames *.rl) * ragel-objc: * Ragel in Objective C Host (filenames *.rl) * ragel-ruby, ragel-rb: * Ragel in Ruby Host (filenames *.rl) * ragel: * Ragel * raw: * Raw token data * rb, ruby, duby: * Ruby (filenames *.rb, *.rbw, Rakefile, *.rake, *.gemspec, *.rbx, *.duby) * rbcon, irb: * Ruby irb session * rconsole, rout: * RConsole (filenames *.Rout) * rebol: * REBOL (filenames *.r, *.r3) * redcode: * Redcode (filenames *.cw) * rhtml, html+erb, html+ruby: * RHTML (filenames *.rhtml) * rst, rest, restructuredtext: * reStructuredText (filenames *.rst, *.rest) * sass, SASS: * Sass (filenames *.sass) * scala: * Scala (filenames *.scala) * scaml, SCAML: * Scaml (filenames *.scaml) * scheme, scm: * Scheme (filenames *.scm) * scss: * SCSS (filenames *.scss) * shell-session: * Bash Session * smalltalk, squeak: * Smalltalk (filenames *.st) * smarty: * Smarty (filenames *.tpl) * sourceslist, sources.list: * Debian Sourcelist (filenames sources.list) * splus, s, r: * S (filenames *.S, *.R) * sql: * SQL (filenames *.sql) * sqlite3: * sqlite3con (filenames *.sqlite3-console) * squidconf, squid.conf, squid: * SquidConf (filenames squid.conf) * ssp: * Scalate Server Page (filenames *.ssp) * tcl: * Tcl (filenames *.tcl) * tcsh, csh: * Tcsh (filenames *.tcsh, *.csh) * tex, latex: * TeX (filenames *.tex, *.aux, *.toc) * text: * Text only (filenames *.txt) * trac-wiki, moin: * MoinMoin/Trac Wiki markup * v: * verilog (filenames *.v, *.sv) * vala, vapi: * Vala (filenames *.vala, *.vapi) * vb.net, vbnet: * VB.net (filenames *.vb, *.bas) * velocity: * Velocity (filenames *.vm, *.fhtml) * vim: * VimL (filenames *.vim, .vimrc) * xml+cheetah, xml+spitfire: * XML+Cheetah * xml+django, xml+jinja: * XML+Django/Jinja * xml+erb, xml+ruby: * XML+Ruby * xml+evoque: * XML+Evoque (filenames *.xml) * xml+mako: * XML+Mako * xml+myghty: * XML+Myghty * xml+php: * XML+PHP * xml+smarty: * XML+Smarty * xml+velocity: * XML+Velocity * xml: * XML (filenames *.xml, *.xsl, *.rss, *.xslt, *.xsd, *.wsdl) * xquery, xqy: * XQuery (filenames *.xqy, *.xquery) * xslt: * XSLT (filenames *.xsl, *.xslt) * yaml: * YAML (filenames *.yaml, *.yml) * zsh: * Zsh |
|
| 256位 |
|
|||
|
09:55:00 |
|
|
しょっちゅう忘れるのでメモ。
### mysqlに切り替え ``USE mysql`` ### 登録されているユーザを確認 ``SELECT user, host FROM user;`` ### 権限を表示 ``SHOW GRANTS for 'hoge'@'%';`` ### ユーザを作成 ``CREATE USER user_name;`` ``CREATE USER user_name IDENTIFIED BY [PASSWORD] 'password';`` ※ユーザ指定の書式例 ``user_name@host_name`` ※ワイルドカードをホストに使うときはシングルクォートでくくる ``user_name@'%'`` ``'username'@'192.168.128.%'`` ### 権限付与 ``GRANT ALL PRIVILEGES ON `DB名`.テーブル TO 'ユーザ名'@'ホスト名';`` ``GRANT SELECT,UPDATE,INSERT,DELETE ON `DB名`.テーブル TO 'ユーザ名'@'ホスト名';`` など ### 権限の反映 ``FLUSH PRIVILEGES;`` ※ご指摘いただき、公式ドキュメントを確認したところ、下記のような記載がありましたので、 この手順で権限を付与した場合、``FLUSH PRIVILEGES;``は実行する必要が無いようです。 >GRANT, REVOKE, or SET PASSWORD などのステートメントを使用して、間接的に権限テーブルを変更する場合は、サーバがこれらの変更を認識し、その変更があった直後に権限テーブルをメモリへリロードします。 >INSERT、UPDATE、DELETE などのステートメントを使用して、直接に権限テーブルを変更する場合は、サーバを再起動するか、またはテーブルのリロードを行なうまでその権限チェックは施行しません。手動で権限テーブルをリロードするには、FLUSH PRIVILEGES ステートメントを発行するか、mysqladmin flush-privileges または mysqladmin reload コマンドを実行します。 |
|
| 257位 |
|
|||
|
19:52:05 |
(Increments inc. 所属) |
|
[Capistrano](http://www.capistranorb.com/)はバージョン3から汎用的なデプロイフレームワークになりました。タスクのフックを利用することで簡単に自分のアプリケーション環境に特化したデプロイプロセスを記述することができます。
本稿では、この汎用化されたデプロイ機能の使い方に焦点を絞って解説したいと思います。より基本的なCapistrano3の解説は * [入門 Capistrano 3 ~ 全ての手作業を生まれる前に消し去りたい | GREE Engineers' Blog](http://labs.gree.jp/blog/2013/12/10084/) がよくまとまっているので、そちらを参考にしてください。この参考記事では "5. Capistranoデフォルトタスクの消去" でCapistranoの新規導入時のコストを下げる目的で、このフレームワーク機能を消去しています。本稿はこのフレームワーク機能の使い方を解説するものです。 ## deployとframeworkの2つの抽象度が用意されている Capistrano3をインストールするといきなりこんな感じのCapfileが作られます ```rb:Capfile # Capistranoの設定を読み込む。おまじない require 'capistrano/setup' # デプロイフレームワークを読み込み require 'capistrano/deploy' # `lib/capistrano/tasks' に定義されたタスクを読み込む Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r } ``` この[capistrano/deploy](https://github.com/capistrano/capistrano/blob/master/lib/capistrano/deploy.rb)が内部で[capistrano/framework](https://github.com/capistrano/capistrano/blob/master/lib/capistrano/framework.rb)を読み込みます。つまり、Capistrano3のデプロイ機能には2つのレベルが存在しています。 1. capistrano/framework もっとも汎用的なデプロイフレームワーク。デプロイの開始、コードのアップデート、システムの公開、デプロイの完了、という流れを定義しているだけで具体的な処理は一切含まない。フックを提供するだけ 2. capistrano/deploy より具体的なタスクが追加されたもの。デプロイ可能かのチェック、デプロイ先ディレクトリの存在確認などなど、ほとんどのデプロイ時に必須になるデプロイ環境の準備系のタスクが定義されている。 Capistrano3のデプロイフレームワークを利用する場合は、capistrano/frameworkのタスクにRake由来の `before` や `after` を使って自アプリケーションに固有の処理を埋め込んでいくことで実装します。 ## capistrano/frameworkが提供するデプロイの流れ ```bash % cat Capfile require 'capistrano/setup' require 'capistrano/framework' % cap -T cap deploy # Deploy a new release cap deploy:finished # Finished cap deploy:finishing # Finish the deployment, clean up server(s) cap deploy:finishing_rollback # Finish the rollback, clean up server(s) cap deploy:published # Published cap deploy:publishing # Publish the release cap deploy:reverted # Reverted cap deploy:reverting # Revert server(s) to previous release cap deploy:rollback # Rollback to previous release cap deploy:started # Started cap deploy:starting # Start a deployment, make sure server(s) ready cap deploy:updated # Updated cap deploy:updating # Update server(s) by setting up a new release cap install # Install Capistrano, cap install STAGES=staging,production ``` だいたい分かると思いますが `cap staging deploy` を実行すると下記の順序で空のタスクが実行されます。 1. deploy:starting 2. deploy:started 3. deploy:updating 4. deploy:updated 5. deploy:publishing 6. deploy:published 7. deploy:finishing 8. deploy:finished そして `cap staging deploy:rollback` を実行すると下記の順序で実行します。 1. deploy:starting 2. deploy:started 3. deploy:reverting 4. deploy:reverted 5. deploy:publishing 6. deploy:published 7. deploy:finishing_rollback 8. deploy:finished これらは全て中身の無い、空のタスクです。これらの前後にタスクを埋め込んでいくことでデプロイプロセスを記述していきます。 ## capistrano/deployが提供するデプロイの流れ capistrano/frameworkよりもう一歩踏み込んでデプロイフローを提供するのがcapistrano/deployです。 `cap staging deploy` すると下記のような順序でタスクが実行されるようになります。 1. deploy:starting 1. deploy:check 1. git:check 2. deploy:check:directories 3. deploy:check:linked_dirs 4. deploy:check:make_linked_dirs 5. deploy:check:linked_files 2. deploy:started 3. deploy:updating 1. git:create_release 2. deploy:symlink:shared 1. deploy:symlink:linked_files 2. deploy:symlink:linked_dirs 4. deploy:updated 5. deploy:publishing 1. deploy:symlink:release 6. deploy:published 7. deploy:finishing 1. deploy:cleanup 8. deploy:finished 個々のタスクの内容について詳しくは説明しませんが、名前から判断するにcapistrano/deployがcapistrano/frameworkが提供するデプロイフローのセマンティックにあうように、タスクを追加していることが分かります。 例えば、deploy:startingでデプロイ先のディレクトリの準備などは全てcapistrano/deployがやってくれるので、deploy:startedに紐付けるタスクはディレクトリが存在する前提で記述してOKな訳です。 ちなみにここでgit:checkなどのようにGitをVCSとして利用しているのはあくまでもデフォルト値であって、hgを使ったり、他のツールを指定することもできます(その場合はそのツールに対してVCS:checkなどのタスクを用意する必要があります) ## 具体例: HipChat通知する 例えばデプロイ開始時と終了時ににHipChatのDevルームに通知したい、という場合は ```rb:lib/capistrano/tasks/hipchat_notification.rb require 'hipchat' client = HipChat::Client.new(HIPCHAT_TOKEN)['Dev'] namespace :hipchat do namespace :update do task :start do run_locally do client.send('Deploy', 'デプロイ開始') end end task :finish do # .. end end namespace :rollback do # .. end end before 'deploy:updating', 'hipchat:update:start' after 'deploy:finishing', 'hipchat:update:finish' before 'deploy:reverting', 'hipchat:rollback:start' after 'deploy:finishing_rollback', 'hipchat:rollback:finish' ``` のように書けばよい訳です。capistrano/frameworkを骨格にして、各々のタスクが肉付けをしていくことで、それぞれのタスクを疎に保ちながらデプロイプロセスを記述することができます。 |
|
| 258位 |
|
|||
|
01:34:33 |
|
|
忘れやすい自分用の備忘録。
```py3 # Python 最速文法 # 参考 # みんなのPython 第3版 # [python] 年末大感謝祭!初心者脱出のためのPythonTipsを50個紹介 # http://kwatch.houkagoteatime.net/blog/2013/12/07/python-tips/ # エンコード指定 # -*- coding: utf-8 -*- # コードの区切り=;(セミコロン) # モジュールのインポート import moduleName from moduleName import functionName from math import * # import all functions from math import e from math import e as newName from math import (e, pi) # 複数インポートするなら括弧で囲む ## from文を使うとモジュール名省略可能になる import math math.sin(60) from math import sin sin(60) ## モジュールファイルの作成 ## (スクリプトファイルをそのままモジュールとして利用可) # 1. moduleName.py に関数を書く # 2. import moduleName # 行の途中で改行したい: 行末にバックスラッシュ (\) import urllib, poplib, sys, calendar, \ datetime, re # リテラル b = 0b1000 # 2進数 c = 0x1ff # 16進数 # Bool型 True False # Null None # シンボルの大文字と小文字は区別される python = 100 Python = 200 print(python, Python) #=> 100 200 # 標準出力への出力(改行つき) print("abc") print("abc", "def") # 半角スペースで連結 print("abc", file=sys.stderr) # 標準エラー出力 # 改行なしで連結 for i in range(10): print("abc", end = ' ') #=> abc abc abc abc abc abc abc abc abc abc # 標準入力からの取得 s = raw_input() #s = input() # == eval(raw_input()) # 四則演算 x += 1 x -= 1 x *= 1 x /= 1 x = 2 ** 10 # 累乗 x = 3 / 2 # 返り値=浮動小数点 x = 3 // 2 # 返り値=整数 x = 5 % 3 # 剰余 x++ # この書き方は使えない x-- # この書き方は使えない # 文字列リテラル s = "abc'def" s = 'abc"def' s = r'\d+\s\n' # raw文字列(見たままの文字として扱う) # 文字列操作 "abcdef"[0] "abcdef"[0] = "x" # => エラー "abcdef"[-1] "abcdef".title() "abcdef".lower() "abcdef".upper() "abcdef".endswith('f') "abcdef".startswith('a') "abcdef".strip() "abcdef".count('c') "abcdef" + "ghijkl" "abcdef" * 3 "abcdef".replace("abc","replaced") "abcdef".find('x') #=> -1 "abcdef".index('x') #=> ValueError # 左寄せ、右寄せ、中寄せ "abcdef".ljust(10," ") "abcdef".rjust(10," ") "abcdef".center(10," ") "abc def".split(" ") " ".join(["abc","def"]) len("abcdef") ord("a") chr(52) if "a" in "abcdef": do_something() # 文字列のフォーマット "{0} {1} {0}".format("Mika", "Mike") "{name0} {name1} {name0}".format(name0="Mika", name1="Mike") "{:.2%}".format(6260 / 12776) # %表記 # 値の交換(丸括弧を使わずタプルを生成) b, a = a, b # ヒアドキュメント lines = ''' このように 複数行 記述できます 改行が入れたくない場合、 \ 末尾にバッククオート(\)をつけて ''' # インデントを維持したい場合は()を使う(カンマ無し) lines = ("aaaaaa" "bbbbb" "ccccc") # 条件演算子(3項演算子) return "odd" if odd(x) else "even" s = "odd" if odd(x) else "even" # 型変換 int("123") hex(1023) bin(15) oct(7) float("1.3") str(123) tuple([1,2,3]) list((1,2,3)) # 進数変換 int("0x100", 16) int("0b1111", 2) int("0o1777", 8) # ビット演算 x | y # or x & y # and x ^ y # xor x << y # left shift x >> y # right shift # 関数 def functionname(x): """ 関数のコメント """ do_something() return result def moveTo(x,y=0): # 引数のデフォルト値 do_something() moveTo(x=1, y=20) # 引数のキーワード指定 # 条件分岐 if s == "abc": # = ではなく == statementA() elif s == "def": statementB() else: statementC() if min <= value <= max: # 比較演算子を連続して書ける do_something() # 複数の返り値(アンパック代入) def foo(): minValue = 1 maxValue = 9 return minValue, maxValue minValue, maxValue = foo() (minValue, # 最小値 maxValue, # 最大値 ) = foo() # 可変引数 def foo(a, b, *vals): print(a, b, vals) foo(1, 2, 3, 4) # => 1, 2, (3, 4) # 可変キーワード引数 def foo(a, b, **args): print(a, b, args) foo(1, 2, c=3, d=4) # => 1, 2, {'c': 3, 'd': 4} # 条件演算子: == != > < <= >= not in # 論理演算子: and or xor # switch文はpythonにはない # 繰り返し(指定回数) for i in range(6): # i = 0 to 5 do_something() for i in range(1, 10, 2): # 開始, 終了, ステップ i= 1,3,5,7,9 do_something() # ループ制御 continue break # While文 while condition: do_something() # python に do ~ while はない for loopCounter, item in enumerate(seq): # ループカウンター付き print(loopCounter, item) for no, name in zip([1, 2, 3, 4], ['name1', 'name2', 'name3', 'name4']): # 2つのシーケンスを結合 print(no, name) # 例外処理 try: if condition: raise Exception() # 例外を故意に発生 except Exception as e: # エラー処理 msg = "{1}(err={2})".format(e.errno, e.strerror) sys.stderr.write(msg) else: # 例外が発生しなかった時の処理 finally: # 後処理(必ず実行される) try: ... except Exception as e: print e, 'error occurred' # with文 : ブロックの実行を効率的に行えるようにする # 処理に失敗した場合は、ブロックを実行しない with open(fn) as f: # ここで失敗したらブロック内の処理は実行しない for line in f : print(line) ## tracebackモジュールで例外を取り扱う import traceback try: do_something() except: traceback.print_exc() # 例外を表示 message = traceback.format_exc() # 文字列として取得 # リスト a = [1, 2, 3, 4, 5] a[0] a[-1] # 負の数なら末尾から a[5] # => Error # スライス:取り出したい要素の最後のインデックス+1を与える a[1:3] #=> a[1] ~ a[3 - 1] まで取得 => [2, 3] a[2:] #=> [3, 4, 5] a[:3] #=> [1, 2, 3] a[2:100] #=> [3, 4, 5] a * 2 #=> [1, 2, 3, 4, 5, 1, 2, 3, 4, 5] a + [6,7] #=> [1, 2, 3, 4, 5, 6, 7] a[1] = "x" #=> [1, "x", 3, 4, 5, 6, 7] del a[1] #=> [1, 3, 4, 5, 6, 7] a.index(1) if 1 in a: do_something() max(a) min(a) a.sort() # 書き換わるので注意 a.sort(key=compareFuncName) a.reverse() # 書き換わるので注意 a.append(6) # add() ではない a.extend([6, 7, 8]) a.remove(1) a.pop(0) # 多次元配列 matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] matrix[0][2] # タプル # (書き換えのできないリスト / メモリ効率良 / ディクショナリのキーとして利用可) t = (1, 2, 3, 4, 5) # その他はリストと同じ # 使い方の例 ipmap = {(192.168.0.1):"host1.name.com", (192.168.0.2):"host2.name.com",} # ディクショナリ dict = { "key1":"東京都", "key2":"大阪府" } dict["key1"] # .get():キーが無くてもエラーにならず指定値を返す for word in lines.split(): wordcount[word] = wordcount.get(word, 0) + 1 dict["key1"] = "北海道" del dict["key1"] dict.keys() dict.values() dict.items() # key,valueのタプルのリストを返す if "key1" in dict: do_something() # 合成 dict1.update(dict2) # 同じキーは上書き # set(集合) s = {1, 2, 3, 4} s2 = {5, 6} s.remove(7) # KeyError s.discard(7) # no error s.add(7) s |= {7} if s == s2: do_something() if 1 in s: do_something() s.union(s2) # s | s2 和集合 s.intersection(s2) # s & s2 共通集合 s.difference(s2) # s - s2 差集合 s.symmetric_difference(s2) # s ^ s2 対称的差集合 # map(シーケンスの各要素を変換) map1 = map(str, range(5)) # Null判定 if x is (not) None: do_something() # ファイル読み込み f = open("foo.txt", "r", encoding="utf-8") s = f.read() line = f.readline() # 1行 lines = f.readlines() # 全行 f.close() # ファイル書き込み f = open("foo.txt", "w", encoding="utf-8") f.write(s) f.writelines(seq) # 改行を付加しないので注意 f.close() # 内包表記(コンプリヘンション) sq = [x ** 2 for x in range(5)] sq #=> [0, 1, 4, 9, 16] val = 10 [x for x in range(1,val) if val % x == 0] #=> [1, 2, 5] filter2 = [x for x in ['cat','dog', 'bird'] if 'cat' in x] filter2 #=> ['cat'] tz = {"GMT" : "+000", "BST" : "+100", "EET" : "+200", "JST" : "+900"} revtz = {value : name for name, value in tz.items()} revtz #=> {'+200': 'EET', '+100': 'BST', '+000': 'GMT', '+900': 'JST'} names = ['BOB', 'Burton', 'dave', 'bob'] unames = {x.title() for x in names} unames #=> {'Bob', 'Dave', 'Burton'} # 何もしない命令 pass # ユニットテスト from Hoge import * # テスト対象 import unittest class HogeClassTestCase(unittest.TestCase): def setUp(self): pass def tearDown(self): pass def testFooMethod(self): self.assertEqual(1, Hoge().foo()) if __name__ == "__main__": unittest.main() # コード片の時間計測 import timeit timeit.timeit('"-".join(str(n) for n in range(100))', number=10000) # コマンドライン取得 for arg in sys.argv[1:] # argv[0]=スクリプト自身の名前 print(arg) # コマンドラインオプション解析 from arse import OptionParser VERSION = '1.01' def main(): version = '%prog version ' + VERSION usage = "usage: %prog [options] directories..." description = 'Delete empty directories.' parser = OptionParser(version=version, usage=usage, description=description) parser.add_option("-f", "--file", dest="filename", help="read data from FILENAME") parser.add_option("-v", "--verbose", action="store_true", dest="verbose") parser.add_option("-q", "--quiet", action="store_false", dest="verbose") (options, args) = parser.parse_args() if len(args) != 1: parser.error("incorrect number of arguments") if options.verbose: print "reading %s..." % options.filename if __name__ == "__main__": main() # イテレータ(外部イテレータ) # (組み込みオブジェクトはすべてイテレータに変換可能) # (定義するためにはクラスを定義する必要があり面倒) i = iter([1, 2, 3]) # イテレータオブジェクトに変換 next(i) # 次の要素を取りだす(要素がなくなればStopIteration例外) for line in open("test.txt"): # ファイルの各行を処理 print(line, end=" ") # ジェネレータ(内部ジェネレータ) # (イテレータのような機能を関数を使って定義) # yield文を使う def get_primes(x=2): while True: for i in range(2, x): if x % i == 0: break else: yield x x += 1 i = get_primes() for c in range(10): print(next(i)) # ジェネレータ式による生成 gen = (ord(s) for s in 'Python') next(gen) for code in (ord(s) for s in 'Python'): print(code) # ユニットテスト #TODO:後で書く # ラムダ式 lambda x: x * 10 # メインで実行された時だけ実行するブロック # (他のスクリプトからインポートされていない) if __name__ == '__main__': do_something() # クラス定義とインスタンス生成 class Person(SuperClassName): # クラス名は大文字 def __init__(self, name, age): # 初期化(第1引数self必須) self.name, self.age = name, age self.address = None bob = Person('Bob', 15) print bob.name bob.address = 'USA' ## クラスの継承(多重継承可) class Person(SuperClassName): def __init__(self, width): super().__init__() # 親クラスの呼び出しはsuper() self.width = width # アトリビュート(インスタンス変数)追加の制限 class Klass: __slot__ = ['width', 'height'] # ここに書いてないアトリビュートは追加不可 def __init__(self): self.width = 0 self.height = 0 i = Klass() i.width = 1 i.c = 0 # AttributeError # メソッドやアトリビュートの隠蔽(カプセル化) 名称の頭に_をつける self._size # クラス外部からアクセス不可になる self.__size # クラス外部からアクセス不可になる ## Setter / Getter(データを変更もしくは参照する専用のメソッド) ## プロパティ(Setter / Getterを手軽に定義するための機能) ## アトリビュートのようにクラスの外側から好き勝手書き換えられないようにする class Prop(object): def __init__(self): # 内部で使うのはプロパティ名xとは違う名前(無限ループ防止) self.__x = 0 def getx(self): # Getter return self.__x def setx(self, x): # Setter self.__x = x # プロパティxのgetter, Setterを設定 x = property(getx, setx) i = Prop() i.x = 0 ## 演算子のオーラーライド(再定義) __add__(self, other) # + (self + other) __sub__(self, other) # - __mul__(self, other) # * __truediv__(self, other) # / __floordiv__(self, other) # // __iadd__(self, other) # += __isub__(self, other) # -= __imul__(self, other) # *= __itruediv__(self, other) # /= __ifloordiv__(self, other) # //= __or__(self, other) # | (or) __and__(self, other) # & (and) ## 比較演算子のオーラーライド ## equal, not equal, lesser than, greater than, ... __eq__(self, other) # == __ne__(self, other) # != __lt__(self, other) # < (self < other) __gt__(self, other) # > __le__(self, other) # <= __ge__(self, other) # >= ## 型変換のオーバーライド __int__(self) # int() __bytes__(self) __float__(self) __str__(self) __repr__(self) # 印字可能な文字列表現に変換 __format__(self, form_spec) # format()でのフォーマットの再定義 ## コンテナ型で利用する特殊メソッド __len__(self) # len() __getitem__(self, key) # obj[key] __setitem__(self, key, value) # obj[key] = value __delitem__(self, key) # del obj[key] __iter__(self) # iter() : イテレータオブジェクトを返す # (内部で__next_()というメソッドを定義する必要あり) __getattr__(self, attributeName) # オブジェクトに存在しない # アトリビュートが参照されるときに呼ばれる # 属性が存在しないことにしたければ # raise AttributeError __getattrubute__(self, attributeName) # 無条件で呼ばれる__getattr__ __setattr__(self, attributeName, value) # 代入するときに呼び出される __call__(self[, args...]) # オブジェクトの名前に続いて丸括弧が添えられ # 関数として呼びだされた時に呼ばれる __del__(self) # デストラクタ __hash__(self) # hash() ハッシュ値を整数で返す ## オブジェクトの型判定 isinstance(var, type) # 型判定(ype= str, int, float, ...) ## アトリビュート(メソッド含む)の取得 s = "abcde" s.find("cd") getattr(s, "find")("cd") # find()メソッドを呼び出す # 標準ライブラリ ## Webサーバからデータを取得 from urllib import request src = request.urlopen('http://www.python.org/').read() from urllib import request, parse url = 'http://www.python.org/somefile.zip' filename = parse.urlparse(url)[2].split('/')[-1] request.urlretrieve(url, filename) ## 順番付きディクショナリ from collections import OrderedDict od = OrderedDict() ## デフォルト値付きディクショナリ from collections import defaultdict animals = [('猫', 'メインクーン'), ('猫', 'アビシニアン'), ('犬', 'パグ'), ('犬', 'シベリアンハスキー'), ('犬', 'ブルドッグ') ] dd = defaultdict(list) # 空のリストを初期値に持つディクショナリ for k, v in animals : dd[k].append(v) dd # => defaultdict(<class 'list'>, {'猫': ['メインクーン', 'アビシニアン'], '犬': ['パグ', 'シベリアンハスキー', 'ブルドッグ']}) ## ソート済みのリストへ要素を追加する支援 import bisect ## 日時を取り扱う import datetime datetime.date.today() # 今日 datetime.datetime.now() # 現在の日時 datetime.datetime(2014, 3, 2, 16, 32, 00) datetime.time(16, 32, 00) datetime.date(2014, 3, 2) d = datetime.date(2014, 3, 2) d.year #2014 d.month # 3 d.day # 2 d.weekday() # 6:日用日 d.strftime('%Y/%m/%d %H:%M:%S') # '2014/03/02 00:00:00' d = datetime.strptime('2014/03/02 00:00:00', '%Y/%m/%d %H:%M:%S') ### 日時の差(比較や乗除算も可能) delta = datetime.timedelta(days=5) d1 = datetime.date(2014, 2, 6) d2 = d1 + delta print(d2) # 2014-02-11 d2 = datetime.date(2014, 3, 2) d1 = datetime.date(2014, 2, 6) d2 = datetime.date(2014, 3, 2) delta = d2 - d1 print(delta) # 24 days, 0:00:00 ## カレンダーに関する情報を取得 import calendar ### 指定年月の曜日と日数 (days, first_weekday) = calendar.monthrange(2014, 2) print(days, first_weekday) #5 28 calendar.month(2014, 2) # カレンダーを出力 ## 正規表現 import re re.findall(r'[abcde]', 'abcdefg') # ['a', 'b', 'c', 'd', 'e'] re.split(r',', 'ab,cde,fg') # ['ab', 'cde', 'fg'] re.sub(r'a', 'b', 'abcde') # 'bbcde' match = re.search(r'a', 'abcdea') print(match) match = re.match(r'a', 'abcdea') print(match) for match in re.finditer(r'[a-z]+', 'This is a pen.'): print(match.group(0), end =' ') # his is a pen s = 'abcd,efgh,ijkl' r = re.compile(r'[abcde]', re.IGNORECASE | re.MULTILINE | re.DOTALL) r.findall(s) ## システムパラメータを取得・操作 import sys sys.argv[0] # 実行中のスクリプト自身 sys.argv[1:] # コマンドラインパラメータ sys.exit(0) sys.getdefaultencoding() ## ファイルシステム・プロセス等のOS依存の情報を取得・操作 import os os.getenv(key, default=None) os.chdir(path) os.getcwd() os.remove(path) os.rename(src, dest) os.mkdir(path, mode) os.mkdirs(path, mode) # 途中のパスも作成 os.rmdir(path) os.removedirs(path) # 途中も削除 os.path.exists(path) os.path.getsize(path) os.path.isfile(path) os.path.isdir(path) os.path.join(path, path2, ...) dirname, filename = os.path.split(path) path = r'c:\users\default\desktop.ini' os.path.dirname(path) # ディレクトリ名 os.path.basename(path) # ファイル名 path_without_ext, ext = os.path.splitext(path) # 拡張子 ## 数学モジュール import math math.pi mati.sqrt(2) mati.sin(60) mati.cos(60) mati.tan(60) ## 乱数 import random random.seed(value) # 乱数の種 x = random.random() # 0 <= x <= 1 y = random.randint(1, 10) # 1~10 の整数 ### 階層を渡り歩く for dirpath, dirnames, filenames in os.walk(path, topdown=True, oneerror=None): do_something() ### プロセス / 外部コマンドの実行 exit_status = os.system(command) # プロセス実行し終了まで待つ(終了コードを返す) os.startfile(path) # WindowsのStartコマンド import commands stdout_text = commands.getoutput(command) # プロセス実行し終了まで待つ(標準出力を返す) ``` IPythonのインストール ``` >pip install ipython ``` IPython の起動 ``` >ipython ``` IPython Notebook の起動 ``` >ipython notebook --inline ``` |
|
| 259位 |
|
|||
|
14:37:27 |
(Atrae, Inc 所属) |
|
##リアルタイムweb?
リアルタイムにwebの情報をサーバからのpush通知で更新する。 有名どころでいうとFB、Chatwork、Twitterとか。 技術的には方法が大きく3つある。 ####・ポーリング(Polling) 一定の時間に一度、Ajaxでサーバに接続させ 新しい情報がないかどうか調べる。擬似的なプッシュ型。 ####・コメット(Comet) クライアントから送られてきたレスポンスをすぐに返さずに処理中の形を取ってコネクションを張ったままにする。 新着の情報があったタイミングでレスポンスを返す。 ####・Websocket HTML5より作られた新しい通信規格。独自のプロトコルを持つ。 先程の2通りのデメリットを補いより効率よく双方向通信が可能。 今回は技術的な流れも含め、Websocketを用いる。 ##railsでどうやって構築する? railsを用いる事をmustとするなら調べた中だと多く3つ。 ####・Live Streming rails4より導入された Live Streming機能。 HTTPコネクションで行うようなので、Polling/Comet系 Rails側のバージョンアップで今後は進化するのか? ####・Node.js + Socket.io の併用 DB周りのみrailsに担当させて、Redis + Node.js + Socket.ioでクライアントとはやり取りをする。 だったらrailsってこだわる必要あるのか?とも思ったり。 ####・websocket-rails railsでwebsocketを使用する方法では、一番主流? https://github.com/websocket-rails/websocket-rails EventMachine使ってるし、イベント駆動型なので パフォーマンス的にも大丈夫かなー・・・ 今回は一旦 websocket-railsで。実行。 ##rubymotionでどうやって構築する? squareが作ってるSocketRocketというライブラリを使う。 ##インストール まずはwebsocket-railsから。 ```Gemfile gem "websocket-rails" ``` ``` $ bundle $ rails g websocket_rails:install ``` 開発環境ではRack::Lockを無効にしないとエラーになるので以下のように設定。 ```config/environments/development.rb config.middleware.delete Rack::Lock ``` 次にSocketRocket。 ```Rakefile app.pods do pod 'SocketRocket' end ``` これで完了。 ##実装 まずはwebsocket_rails側から。(※基本は以下見ればわかります) https://github.com/websocket-rails/websocket-rails websocket_railsが用意してくれる "/websocket"につなぐ。 ```test.js var dispatcher = new WebSocketRails("ws://yourhost/websocket"); dispatcher.on_open = function() { return console.log("Connection has been established: "); }; dispatcher.bind('hoge', function(){ alert('fuga'); }); $('#hoge').click(function(){ dispatcher.trigger('hoge.send', 'hogefuga'); }); ``` ```config/events.rb WebsocketRails::EventMap.describe do namespace :hoge do subscribe :send, :to => HogeController, :with_method => :send end end ``` ```app/controllers/hoge_controller.rb class HogeController < WebsocketRails::BaseController def send broadcast_message :hoge, message end end ``` ダラダラ書きましたが、上記のような形でクライアントとサーバのやり取りが可能です。 クライアント→サーバでのやりとりは、triggerを使ってjs側で設定し、それをevent.rbに明記してcontroller側に送る。 といった至って簡単な構造。 ちなみにbroadcast_messageとすると全てのクライアントに対して送信してしまうので、分けたい場合はchannelというものを使うと実装可能。 またサーバ→クライアントの通信だけなのであれば、 ``` WebsocketRails[:'channel名'].trigger 'hoge', 'fuga' ``` とすればchannel名をsubscribeしているものにだけpush通知ができたり。何とも簡単。 次にrubymotion。 ``` url = NSURL.URLWithString("ws://49.212.87.13:3006/websocket") @socket = SRWebSocket.alloc.initWithURLRequest(NSURLRequest.requestWithURL(url)) @socket.delegate = self @socket.open def webSocket(webSocket, didReceiveMessage:message) p message end ``` っていう感じに書いておけば、rakeしたときに通信してくれる。 rails側でlog/websocket_rails.logのファイルを確認してみると確かにrubymotionからもwebsocketで繋がれている。 その他はそれぞれのgithubでも見ながら追加していってください。 参考文献 - websocket-railsで簡単なPush通知を実装する http://qiita.com/naoty_k/items/3a2b5d8cfc1619e6145b - RubyMotionからWebSocketを使う http://qiita.com/bellx2/items/84bbcdcaf1b37b5483f3 - websocket-rails https://github.com/websocket-rails/websocket-rails - SocketRocket https://github.com/square/SocketRocket |
|
| 260位 |
|
|||
|
23:01:41 |
|
|
windowsの開発環境はpower shellの入っている環境なら手作業をほとんどなくして整えることが可能だ。
`chocolatey`を使います。インストールするのはピカイチに簡単。コマンドプロンプトから以下をコピペして実行してね。 ```bat @powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%systemdrive%\chocolatey\bin ``` 勝手にインストールが始まるから放置しとく、結構すぐ終わった気がする。 Windows updateを怠ってたりすると.NETがねぇよって言われることあるけど再起動したら勝手に入ってた。 ちなみにホームページは[Chocolatey Gallery](https://chocolatey.org/)です。 `chocolatey`はmacでいうhomebrewとかdebian,ubuntuでのapt-getといったパッケージ管理ソフトなんだけど、こいつの真価はxml形式で記述したファイルを使えば簡単にインストールするパッケージを管理できることだと思う。 だから以下のようにひとつずつインストールする必要などない。 ```bat cinst vim chocolatey install vim ``` みたいな感じで、どちらでもインストールできるだけど、コマンドプロンプトの実行時のディレクトリにxmlファイルがあれば一瞬だし後からも分かりやすい。 ```xml:packages.config <?xml version="1.0"?> <packages> <package id="GoogleChrome" /> <package id="Firefox" /> <package id="Gpg4win" /> <package id="ctags" /> <package id="curl" /> <package id="git" /> <package id="hg" /> <package id="dropbox" /> <package id="mingw" /> <package id="mingw-get" /> <package id="nodejs" /> <package id="python" /> <package id="python3" /> <package id="ruby" /> <package id="Everything" /> <package id="Apple_iCloud_Control_Panel" /> <package id="winmerge" /> <package id="Desktops" /> <package id="imagemagick" /> </packages> ``` idにインストールしたいパッケージを記述しておけばオーケーです。あとは ```bat cinst packages.config ``` これだけでxmlに記載されているパッケージを全てインストールして、PATHも設定してくれる。 これでそれぞれのWebページに訪れてひとつひとつインストールする作業は一気に楽になるよね。 パッケージ一覧については ```bat chocolatey list ``` で取得できるので、ここから必要なパッケージをダウンロードするといいよ。 パッケージを更新する場合は ```bat chocolatey upgrade ``` でアップデートできます。 |
|
| 261位 |
|
|||
|
01:04:51 |
(Souzoh, Inc. (affiliated by Mercari, Inc.) 所属) |
|
[元ネタ](http://d.hatena.ne.jp/gfx/20130909/1378741015) あるプログラミング言語で実際にWebAppを開発できるようになるまで、何が必要だろうか。言語仕様の習得は終えているとしよう。おそらく、最低限以下のような知識が必要だと思われる。とりあえずGo言語について知っていることを書いた。 ##パッケージマネージャ 標準でついてるgoツールを使おう。必要なライブラリはリポジトリから、go getで取ってこよう。 ``` go get <リポジトリ> ``` * http://golang.org/cmd/go/#hdr-Download_and_install_packages_and_dependencies ##アプリケーションサーバ 標準パッケージの[net/httpパッケージ](http://golang.org/pkg/net/http)を使えばhttpサーバを動かすことができる。apacheやnginxで動かしたかったら、標準パッケージの[net/http/cgiパッケージ](http://golang.org/pkg/net/http/cgi/)や[net/http/fcgiパッケージ](http://golang.org/pkg/net/http/fcgi)を使えばいい。 * http://golang.org/pkg/net/http * http://golang.org/pkg/net/http/cgi/ * http://golang.org/pkg/net/http/fcgi ##ルーティングとリクエストパラメータの処理 標準パッケージだと[net/http.Request](http://golang.org/pkg/net/http/#Request)や[net/http.ServeMux](http://golang.org/pkg/net/http/#ServeMux)あたりを使えばいける。もっと機能が欲しければ[goweb](https://github.com/stretchr/goweb)や[gorilla](http://www.gorillatoolkit.org/)も便利。 * http://golang.org/pkg/net/http/#Request * http://golang.org/pkg/net/http/#ServeMux * https://github.com/stretchr/goweb * http://www.gorillatoolkit.org/ ##データベース 標準でRDB用のインタフェースを[database/sqlパッケージ](http://golang.org/pkg/database/sql/)で定義している。実際は、各RDBMSの[ドライバ](https://code.google.com/p/go-wiki/wiki/SQLDrivers)使う。ORMなら[gorp](https://github.com/coopernurse/gorp)がいい[らしい](http://mattn.kaoriya.net/software/lang/go/20120914222828.htm)。 * http://golang.org/pkg/database/sql * https://code.google.com/p/go-wiki/wiki/SQLDrivers * https://github.com/coopernurse/gorp * http://mattn.kaoriya.net/software/lang/go/20120914222828.htm ## ビューのレンダリング HTMLなら標準パッケージの[html/templateパッケージ](http://golang.org/pkg/html/template)を使えば、テンプレートエンジンが使える。JSONやXMLなら標準パッケージの[encodingパッケージ](http://golang.org/pkg/encoding/)以下のパッケージを使えばいい。サードパーティ製であれば[MessagePack](https://github.com/ugorji/go/tree/master/codec)もある。 * http://golang.org/pkg/encoding/ * http://golang.org/pkg/html/template * https://github.com/ugorji/go/tree/master/codec ## HTTPクライアント 標準パッケージの[net/http.Client](http://golang.org/pkg/net/http/#Client)を使えばいいと思う。 * http://golang.org/pkg/net/http/#Client ## テストフレームワーク goツールの`go test`を使えばいい。 テストプログラムを書くには、標準パッケージの[testingパッケージ](http://golang.org/pkg/testing)を使う。webアプリのテストなら標準パッケージの[net/http/httptestパッケージ](http://golang.org/pkg/net/http/httptest)も使えるだろう。 * http://golang.org/doc/code.html#Testing * http://golang.org/pkg/net/http/httptest * http://golang.org/pkg/testing ## WAF 私個人はそんなに必要性を感じないが、[goweb](https://github.com/stretchr/goweb)は便利だと思った。 * https://github.com/stretchr/goweb ## マスコット 標準パッケージではないが、Gopher君というマスコットがかわいい。 Webアプリには関係ない。 * https://github.com/golang-samples/gopher-vector 以上。他にもあれば指摘してほしい。 |
|
| 262位 |
|
|||
|
15:06:21 |
(株式会社スピカ 所属) |
|
# 背景
WEBサービスで駅とか路線のマスター情報とか使いたいときが結構あると思います。自分でマスター管理するのは大変なので、どこかにお願いしたいですよね。そんな時はこちら。 # 無料 ## [駅データ.jp](http://www.ekidata.jp/) - 無料(有料プランもあり) - 事業者マスタ、路線マスタ、駅マスタ、接続駅マスタがCSVで提供されます - APIでの提供も用意されています - 商用利用やデータの改変についても制約がなく使い勝手が良さそうです - 有料契約すると取得できる情報がちょっと詳細になります ## [リクルートWEBサービス](http://webservice.recruit.co.jp/manabi/reference.html) - 無料 - APIでの情報提供になりますが、沿線マスタAPIで全件取得できますので、それをもとにAPIをぐるぐる回せば自分で駅マスターは作れそうです - ただ利用者としてリクルート社のコンテンツをマッシュアップしてサービス提供したい人向けのようなので、全く無関係のシステム・サービスで利用しようとしている場合は利用許可がでないかもしれません ## [HeartRails Express](http://express.heartrails.com/) - 基本無料(マスターデータ販売は有料) - 無料で利用できるのはAPIです。こちらも工夫すればAPI経由でマスターデータが作れそうです - それが面倒な場合は有料でマスターデータ購入しましょう ## [国土交通省国土政策局国土情報課](http://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N02-v2_2.html) - xml形式なのでDBにはそのまま入れづらいので一手間かける必要があります - 線路の座標も取得できるようです (情報提供 @tamura_CD さん) # 有料 ## [国土地理協会](http://www.kokudo.or.jp/database/004.html) - 有料(初年度160万円、次年度以降30万円) - 沿線マスタ、駅マスタ、事業者マスタがCSVで提供されます - 年4回更新 ## [駅すぱあとWeb Service](http://webservice.ekispert.com/) - 有料(APIの一部機能は無料で利用できます) - [無償版駅すぱあとWebサービスの紹介](http://webservice.ekispert.com/free_provision/) - APIによるデータ提供だけでなく、HTML5、Flashを使ったWEBパーツとしての提供方式も用意されています。 - 路線図、時刻表、運行情報、料金情報などAPI経由で取得することができます - マスターデータとしての配布はしてないようですので、自分のシステムにデータを持っておきたい場合はAPI叩いてローカルに保持する必要がありそうです ## [駅探法人向けサービス](http://go.ekitan.com/info/index.shtml) - 有料 - CSVによるマスターデータ提供、APIによる検索提供、ASPサービスとしての提供など豊富なラインナップ - サービスメニューが豊富で柔軟に対応してくれそうな雰囲気です ## [マピオン法人向け地図画像、地図検索サービス](http://www.mapion.co.jp/sales/) - 有料 - 駅・路線情報に関しては検索APIという形で提供されています - マスターデータとしての配布はしてないようですので、自分のシステムにデータを持っておきたい場合はAPI叩いてローカルに保持する必要がありそうです # 仲間募集 [株式会社スピカ](http://spika.co.jp)では一緒に切磋琢磨できるエンジニアの方を募集しています!ぜひ、[こちら](https://www.wantedly.com/companies/spika/projects)をご覧ください!! |
|
| 263位 |
|
|||
|
08:06:33 |
(株式会社メルカリ 所属) |
|
ジェネレータが5.5から入ったことで完全に空気と化した(?)PHPのイテレータを、ちょっと違う面からまとめたいと思います。 ## コードをまとめるということ Don't Repeat Yourself(DRY)という言葉があります。達人プログラマーという本に出てくる言葉です。 信頼性の高いソフトウェアを開発して、開発そのものを簡単に理解したりメンテナンスできるようにする唯一の方法は、DRY原則に従うことです。 「すべての知識はシステム内において、単一、かつ明確な、そして信頼できる表現になっていなければならない。」 (p. 27) 端的に言えば「同じことを二度書いてはいけない」ということですね。この原則を当てはめなくてもいい例外のパターンもいくつかあるのですが。。 コードにおいて「同じことを二度書いてはいけない」を忠実に守ろうとすると、同じコードを何度も書きたくなったら、何らかの方法でそのコードをまとめる必要があります。 ## サブルーチン / 関数 サブルーチン化/関数化はコードをまとめる技術として最も基本的なものです。 例えば、libcurlで3つのURLに存在するHTMLを取得するコードを書いてみます。 ```php <?php $ch1 = curl_init('http://www.yahoo.co.jp/'); curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); $responseHtml1 = curl_exec($ch1); curl_close($ch1); $ch2 = curl_init('http://www.google.co.jp/'); curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); $responseHtml2 = curl_exec($ch2); curl_close($ch2); $ch3 = curl_init('http://www.facebook.com/'); curl_setopt($ch3, CURLOPT_RETURNTRANSFER, true); $responseHtml3 = curl_exec($ch3); curl_close($ch3); ``` はい、不毛なコードですね。関数が一つあればすっきりします。 ```php <?php function getHtml($url) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); return curl_exec($ch); } $responseHtml1 = getHtml('http://www.yahoo.co.jp/'); $responseHtml2 = getHtml('http://www.google.co.jp/'); $responseHtml3 = getHtml('http://www.facebook.com/'); ``` 似た部分が関数としてまとまったので、DRYになりましたね。 …当たり前すぎるって? いえいえ、関数はDRYなコードを書くための最も基本的な道具ですので、おろそかにすることはできません。 ## 高階関数 しかし、関数ではまとめられないコードというのも存在します。以下の例を見てください。 ```php <?php try { doSomething1(); } catch (Exception $e) { handleException($e); } //... try { doSomething2(); doSomething3(); } catch (Exception $e) { handleException($e); } //... try { doSomething3(); doSomething4(); } catch (Exception $e) { handleException($e); } ``` よく似たtry~catchブロックが何度も登場しています。しかしブロック内部の処理が異なるので、先ほどのように単純に関数化することはできません。 単純な関数化というのは、連続した複数の文を抜き出し、まとめることを意味します。  今回のケースだと「ブロックの外側」だけが共通で、中の部分だけ再利用性がない、という図式です。 コードを書いていればよく遭遇します。  ではどう対処するかというと、可変箇所自体を引数にして、外から突っ込めるようにすればいいのです。 こういう「関数を引数に取る関数」のことを高階関数と言います。高階関数にすると、真ん中だけ抜け落ちた関数として「ブロックの外側」をまとめることができます。 プログラミング言語に無名関数を作る文法があると、高階関数は非常に利用しやすくなります。 ```php function handleError(callable $func) { try { $func(); //関数を外から突っ込んで実行 } catch (Exception $e) { handleException($e); } } handleError('doSomething'); handleError(function(){ doSomething2(); doSomething3(); }); handleError(function(){ doSomething3(); doSomething4(); }); ``` ブロック内部の可変箇所だけ、外から関数として突っ込むことで、うまく共通化することができました。 こんな風に高階関数を使いこなすことで、複雑なコードでもまとめやすくなります。 ## イテレータ 正直なところ、高階関数まで使いこなせれば、だいたい何でもまとめることができます。JavaScriptなんかは、関数と高階関数でほとんど何でも書いてしまいますよね。オブジェクト指向で書く場合はコードの単位がオブジェクトになるので、もう少し違う形になりますが、発想は同じです。 ただ、PHPにはもう少し用途に特化した文法がいくつか存在し、そのうちの一つにイテレータがあるのです。 イテレータは手続き型言語特有の、ループを抽象化する機構です。ループのロジックのみをまとめる機構、とも言えます。 ちょっと例を見てみましょう。 ```php <?php // 0~100のうちの奇数のみを足した数を出力する。 $sum = 0; foreach (range(0,100) as $i) { if ($i % 2) { $sum += $i; } } echo $sum, PHP_EOL; ``` ```php <?php //data.txtの奇数行だけを出力する。 foreach (new SplFileObject('data.txt') as $i => $line) { if ($i % 2) { echo $line; } } ``` まずこの二つのコードですが、配列とファイルオブジェクトという全く異なる性質の値を扱っていますが、両方ともforeach構文でぐるぐる回して処理することができます。これはPHPのforeach文がIteratorに対応しているおかげなんですが、今更ピンとこないですし、もはや当たり前すぎる気がするので解説は省略します。 これらのコードですが、「ループを回して奇数の時だけ特定の動作をする」というようなロジックになっています。よく似ていますよね。まとめられそうです。 これも「ブロックの外側」が共通になっているタイプなので、高階関数を使えばまとめられるんですが、今回はイテレータで処理をまとめてみたいと思います。 ## イテレータの加工 イテレータで処理をまとめる時の発想は、「ループを回す部分のロジックを全部イテレータの中にぶち込む」というところです。 上記の例では、「ループの奇数番目だけ処理を行う」というロジックが共通です。例えばCallbackFilterIteratorを使うと、ループの奇数番目を取り出す作業を共通化できます。 ```php <?php function filterOdd($ite) { if (is_array($ite)) { $ite = new ArrayIterator($ite); } elseif (! $ite instanceof Traversable) { throw new InvalidArgumentException; } return new CallbackFilterIterator($ite, function($v, $key, $ite){ return $key % 2; }); } // 0~100のうちの奇数のみを足した数を出力する。 $sum = 0; foreach (filterOdd(range(0,100)) as $i) { $sum += $i; } echo $sum, PHP_EOL; //data.txtの奇数行だけを出力する。 foreach (filterOdd(new SplFileObject('data.txt')) as $line) { echo $line; } ``` ループ中のif文が消えたことがわかるでしょうか? こんな風に「イテレータを加工する関数」を用意してやると、if文なども含めてループが再利用できるようになります。 [IteratorIterator](http://php.net/manual/ja/class.iteratoriterator.php)はイテレータを引数にとって新たなイテレータを返します。CallbackFilterIteratorもIteratorIteratorの仲間です。PHPに標準で付属するSPLのIteratorは妙に充実していて、多彩な加工が可能です。 - [AppendIterator](http://php.net/manual/ja/class.appenditerator.php):複数のIteratorを連結する - [CachingIterator](http://php.net/manual/ja/class.cachingiterator.php):イテレータの結果をキャッシュする - [FilterIterator](http://php.net/manual/ja/class.filteriterator.php):イテレータの一部をフィルタし、残った値だけをループに渡す - [InfiniteIterator](http://php.net/manual/ja/class.infiniteiterator.php):イテレータを無限に繰り返す - [LimitIterator](http://php.net/manual/ja/class.limititerator.php):イテレータの一部分を抜き出す これらのIteratorIteratorを使うと、結構複雑なループロジックでもイテレータだけで実装できます。 FilterIteratorとRecursiveTreeIteratorを使う例を昔書いたので、以下も参考にしてください。 参考:[PHP - RecursiveTreeIteratorでtreeコマンドを実装 - Qiita [キータ]](http://qiita.com/Hiraku/items/4da9fe08ea83773f9631) ## それジェネレータでもできるよ この記事の冒頭に「ジェネレータが追加されたのでイテレータが空気になった」と書きました。基本的にイテレータで実現することはジェネレータでも実現でき、ジェネレータの方がパフォーマンスもよく、しかも読みやすいです。 ```php <?php function filterOdd($ite) { $i = 0; foreach ($ite as $v) { if ($i++ % 2) yield $v; } } // 0~100のうちの奇数のみを足した数を出力する。 $sum = 0; foreach (filterOdd(range(0,100)) as $i) { $sum += $i; } echo $sum, PHP_EOL; //data.txtの奇数行だけを出力する。 foreach (filterOdd(new SplFileObject('data.txt')) as $line) { echo $line; } ``` 5.5以降のバージョンを使っているなら、積極的にジェネレータを使うといいのではないでしょうか。 SPLにあらかじめ用意されているクラスをnewするだけで使える場合は、IteratorIteratorを使っても簡易に書けるでしょうけど。。 ## まとめ - コードをまとめる技術として、一般には関数や高階関数などがあります - イテレータはそのうちの「ループに関する部分」に特化してコードをまとめる能力があります - ジェネレータ使ったほうが楽なので、移行していくといいと思います |
|
| 264位 |
|
|||
|
15:30:16 |
(Increments Inc 所属) |
|
hubコマンド([defunkt/hub](https://github.com/defunkt/hub))を入れるとGitHubの操作あれこれをコマンドラインでできるようになる.
## インストール&設定 インストールは https://github.com/defunkt/hub#installation の通り. `hub XXX`を`git XXX`で使えるようにするために以下を.bashrcや.zshrcに書く ```bash:~/.XXXrc function git(){hub "$@"} # zsh eval "$(hub alias -s)" # bash ``` ちなみにプロンプトにブランチ名を表示している場合は,プロンプト表示速度の低下を避けるために以下の設定を忘れずに. [hubコマンドを利用&&プロンプトにgitのブランチ等を表示している人向けのzsh高速化 - Qiita](http://qiita.com/yaotti/items/0af5d50f4f52d22a46fe) ## よく使うコマンド ### hub browse [user/repo] `hub browse`でいまいるレポジトリのgithubページを開く.`hub browse defunkt/hub`みたいにレポジトリ指定もできる. `browse`は打ちにくすぎるので,seeにエイリアスしてる ```bash $ git config --global alias.see browse ``` ### hub clone user/repo `git clone https://github.com/rails/rails.git`と同じことを`hub clone rails/rails`でできる.ちょっと便利. gemのcloneなら`gem-browse`コマンドを入れるともっと便利. 参考: [gem の github のコードを簡単に clone する](http://qiita.com/items/d19efc9842d0939290b8) ### hub pull-request pull requestをコマンドラインから送ることができる. ブランチをGitHubにpublishしたあとに`hub pull-request`を実行するとエディタが立ち上がり,メッセージを入力するとpull requestが送られる.便利. ### hub fork clone後にローカルで開発してpull requestを送る準備ができたら,hub fork -> git push <myname> <branch> -> hub pull-request,という流れでコマンドラインのみで完結できる. ## こちらも参考に - [hub README](https://github.com/defunkt/hub/blob/master/README.md) - `git help hub` - `hub --noop XXX` - `hub XXX`によりどういうコマンド/処理が実行されるか表示する.実際に実行はしない. |
|
| 265位 |
|
|||
|
20:15:14 |
(Coach United Inc. 所属) |
|
## ①ブラウザに一切、キャッシュさせたくない場合
サーバからクライアントへのHTTP応答ヘッダ → `Cache-Control "no-cache"` アクセス毎に内容が変わったり、サーバにアクセスしてもらわないと困るようなコンコンテンツの場合です。 スクリプト言語等で生成する動的コンテンツは、このようにした方が安全です。 例えば対象コンテンツが画像である場合、ブラウザで同じ画像のURLが含まれたHTMLを開いた場合は、 * もちろんローカルにキャッシュがないので、サーバへ問い合わせを行う * 条件つきリクエスト(If-Modified-Since、If-None-Match)もサーバへ送ってこない ## ②ブラウザにキャッシュさせるけど、変更ないか都度確認するようにしたい サーバからクライアントへのHTTP応答ヘッダ → `Cache-Control "max-age=0"` → `Expires "Mon, 26 Jul 1997 05:00:00 GMT"` ブラウザ毎に片方しかサポートしていない場合があるので、両方のヘッダを指定しておいた方がいいです。 最初から既に期限切れ、という意味です。 ブラウザで同じ画像のURLが含まれたHTMLを開いた場合は、 * 期限が切れてるので、サーバへ問い合わせを行う * 条件つきリクエスト(If-Modified-Since、If-None-Match)をサーバへ送ってくる ただし条件つきリクエストを送ってくるのは、前回にサーバから Last-Modified、Etag を送ってある場合のみです。 またサーバ側は、もし If-Modified-Since、If-None-Match がクライアントから送られてきたら、変更ありなしを判断して 304レスポンスを返せば通信量が減らせます(Apacheなど普通のWEBミドルウェアなら静的コンテンツの場合は勝手に処理してくれるでしょうけど)。 サーバから304レスポンスを受け取った場合、ブラウザはローカルのキャッシュを利用します。 ## ③ブラウザにキャッシュさせ、都度確認はそれほど必要ない 上記②の時から次のように変更すればいいです。 サーバからクライアントへのHTTP応答ヘッダ → `Cache-Control "max-age=秒数"` 例:86400など(この場合は1日) → `Expires "ここに期限の日時"` その期限に達していない限り、ブラウザはサーバに問い合わせを行いません。 期限がきれた後の挙動は、②と同じです。 ## 補足 iPhoneやAndoid等のネィティブアプリ内のWebViewはキャッシュが強いので、サーバからのキャッシュコントロールに関わらず、ローカルのキャッシュが利用されることがあります(恐らくWebViewの設定でキャッシュポリシーを変更できるかとは思います)。 |
|
| 266位 |
|
|||
|
15:53:44 |
|
|
FrogApps 技術ブログ始めました!
RailsやiOS、HTML5の情報を発信中!! → http://qiita.com/teams/frogapps --- Railsでアプリを作っていると、app/models/以下にあるモデルファイルの行数が非常に長くなることになります。 そこでincludeメソッドを使ってモデルファイルを機能毎に分割してみましょう。 ファインダー関係のメソッドだけを、user/finder.rbに切り出します。 このときクラスメソッド関係は通常の定義方法が異なります。 クラスメソッドの定義は、ClassMethodsモジュールの中で行う様にします。 has_many, belongs_toなどのクラスメソッドの実行は、self.includedの中でbase.has_manyの様にして呼び出します。 ```ruby:app/models/user.rb class User < ActiveRecord::Base include User::Finder end ``` ```ruby:app/models/user/finder.rb module User::Finder def self.included(base) base.extend ClassMethods # クラスメソッドの呼び出し base.has_many :items end # クラスメソッドの定義 module ClassMethods def hot ... end end # インスタンスメソッドの定義 def search(str) ... end end ``` このようにしてファイルを分けることで、モデルファイルが大きくなるのを避けられ機能毎に整理することができます。 |
|
| 267位 |
|
|||
|
12:30:35 |
|
|
こんにちは。
自前でvagrantのboxを作る際には、変な設定が入らないようにとりあえず何も入れずに作ると思うのですが、検証作業が始まると最初から入っているべき設定が入っているほうが楽なことがよくあります。(例えば管理ユーザを事前に作成しておきたい場合など。毎回同じコマンドを打つのは面倒ですよね。) vagrantには今のインスタンスの状態をboxにするpackageコマンドがあるので、これを使ってカスタマイズ済みのboxを登録しておくと作業が捗ります。 ## vagrant package ``` vagrant-init # debian(仮のbox名)でVagrantfileを作る vagrant init debian vagrant up # sshして適当に作業をする vagrant ssh # sshを抜ける # 停止する vagrant halt # 今の状態でpackage.boxを作成する vagrant package ``` ## vagrant box add ```vagrant-box-add vagrant box add customized_debian package.box ``` このように作成されたboxを追加すれば、同じように`vagrant init [box名]`に使うことができます。超お手軽。 ```vagrant-init vagrant init customized_debian vagrant up ``` |
|
| 268位 |
|
|||
|
12:04:52 |
|
|
expressの使い方を逆引き形式でまとめてみた。参考にしたのは2014年2月20日時点(express 3.4.8時点)の公式ドキュメントおよびソースコード、サンプルなど。 - [公式ドキュメント](http://expressjs.com/api.html) - [ソースコード](https://github.com/visionmedia/express) - [サンプル](https://github.com/visionmedia/express/tree/master/examples) ## ルーティングを設定する - ルーティングを設定するには(i.e. パスとコントローラ関数をマッピングするには)```app.get(path, func)```や```app.post(path, func)```など各HTTP Verbに対応した関数を使う - ルーティング設定は書かれた順番に評価され、リクエストと```path```の一致したものが適用される - ```path```は文字列または正規表現を指定可能 - ```func```のシグネチャは```function(req, res[, next])``` - ```next```はコールバック関数 - 引数無し```next()```で呼び出すと、次に一致するルーティング設定に処理が移る - 引数有り```next(object)```で呼び出すと、エラーハンドリング用の関数に処理が移る(エラーハンドリングについては[別項目](#2-14)参照のこと) ```javascript app.get('path_to_somewhere', function(req, res) { : // something : }); app.post(/^\/items\/[0-9]+\.html$/, function(req, res) { : // something : }); /* app.get()、app.post()以外のルーティング設定関数は以下の通り。それぞれ同名のHTTP Verbに対応している。 app.put() app.head() app.delete() app.options() app.trace() app.copy() app.lock() app.mkcol() app.move() app.propfind() app.proppatch() app.unlock() app.report() app.mkactivity() app.checkout() app.merge() app.m-search() app.notify() app.subscribe() app.unsubscribe() app.patch() app.search() */ ``` ## テンプレートをレンダリングする - テンプレートをレンダリングするには```response.render(templateName)```を使う - ```templateName```はテンプレートファイルの名称であり、```app.set('views', path)```で指定したパス直下に置く必要がある - ```templateName```では拡張子を省略可能。省略した場合```app.set('view engine', ext)```で指定したデフォルト拡張子であると解釈される - レンダリングに利用するテンプレートエンジンは```app.engine(ext, engineFunc)```で登録する ```javascript // テンプレートファイルを置くパスを設定。デフォルトは "process.cwd() + '/views'" app.set('views', 'path_to_somewhere'); // レンダリングに利用するテンプレートエンジンを登録 var jadeEngine = require('jade').someFunc; app.engine('jade', jadeEngine); var ejsEngine = require('ejs').someFunc; app.engine('ejs', ejsEngine); /* app.engine()の第二引数はfunction(path, options, callback)のシグネチャを持つ必要がある。consolidate.js(https://github.com/visionmedia/consolidate.js)を使うと様々なテンプレートエンジンのシグネチャをこの形式に合わせてくれるので便利。 */ // templateNameで拡張子が省略された場合のデフォルト拡張子 app.set('view engine', 'ejs'); //コントローラーの処理 app.get('path_to_somewhere', function(req, res) { : // レンダリング実行 res.render('index'); //この例の場合index.ejsファイルとして解釈される : }); ``` ## テンプレートに値を埋め込む - テンプレートに値を埋め込む方法は4つ 1. ```response.render(templateName, object)```の```object```のプロパティで指定 1. ```response.locals.name = value```で指定 1. ```app.locals.name = value```で指定 1. ```app.set(name, value)```で指定 - 1〜3の方法で埋め込んだ値は、テンプレートからはグローバルオブジェクトのプロパティとして参照可能 - 4の方法で埋め込んだ値は、テンプレートからは```settings```オブジェクトのプロパティとして参照可能 ```javascript app.get('path_to_somewhere', function(req, res) { : // 方法1 res.render('index', {title1: 'exposed via res.render'}); : }); app.get('path_to_somewhere', function(req, res) { : // 方法2 res.locals.title2 = 'exposed via res.local'); res.render('index'); : }); // 方法3 app.locals.title3 = 'exposed via app.local'); // 方法4 app.set('title3', 'exposed via app.set'); ``` ```html:テンプレート側(ejsの例) <h1> <%= test1 %> </h1> <!-- グローバルオブジェクトのプロパティとして参照可能 --> <h1> <%= test2 %> </h1> <!-- グローバルオブジェクトのプロパティとして参照可能 --> <h1> <%= test3 %> </h1> <!-- グローバルオブジェクトのプロパティとして参照可能 --> <h1> <%= settings.test3 %> </h1> <!-- settingsオブジェクトのプロパティとして参照可能 --> <!-- 以下のように出力される <h1>exposed via res.render</h1> <h1>exposed via res.local</h1> <h1>exposed via app.local</h1> <h1>exposed via app.set</h1> --> ``` ## レスポンスのヘッダーを指定する - レスポンスのヘッダーを指定するには```response.set(name, value)```を使う - 複数のヘッダーをまとめて指定する場合は```response.set```の引数にオブジェクトを渡す - ```response.set```の別名として```response.header```を使っても良い(名前が違うだけで機能は全く同じ) ```javascript app.get('path_to_somewhere', function(req, res) { : res.set('Cache-Control': 'max-age=600'); res.set('ETag': '12345'); : }); app.get('path_to_somewhere', function(req, res) { : // オブジェクトを引数に渡して複数同時に指定 res.set({ 'Cache-Control': 'max-age=600', 'ETag': '12345' }); : }); ``` ## レスポンスのステータスコードを指定する - レスポンスのステータスコードを指定するには```response.status(code)```を使う ```javascript app.get('path_to_somewhere', function(req, res) { : res.status(501); : }); ``` ## レスポンスにCookieを含める - レスポンスにCookieを含めるには```response.cookie(name, value[, option])```を使う - ```value```には値だけでなくオブジェクトも指定可能(JSON化してシリアライズされる) - ```option```には以下の値を指定できる - *domain*: Cookieを有効とするドメイン(初期値:ホストのドメイン) - *path*: Cookieを有効とするパス(初期値:'/') - *httpOnly*: HTTPだけで有効にするか(初期値:false) - *maxAge*: 破棄されるまでの時間(ミリ秒)。このオプションはexpire属性のシンタックスシュガー(初期値:session) - *secure*: Cookieに署名するか(初期値:false) - ```secure```オプションを```true```にする場合は、cookieParserミドルウエアを読み込んで、署名用の鍵を指定する必要がある(cookieParserミドルウエアの読み込みは[別項目](#2-11)参照のこと) ```javascript app.get('path_to_somewhere', function(req, res) { : res.cookie('cookie_name', 'cookie_value', {maxAge: 900000, httpOnly: true}); : }); ``` ## レスポンスからCookieを取り除く - レスポンスからCookieを取り除くには```response.clearCookie(name)```を使う ```javascript app.get('path_to_somewhere', function(req, res) { : res.clearCookie('cookie_name'); : }); ``` ## リダイレクトする - リダイレクトするには```response.redirect([status, ] url)```を使う - ```status```を省略した場合は302(Found)が使われる ```javascript app.get('path_to_somewhere', function(req, res) { : res.redirect(302, "../login"); : }); ``` ## ミドルウエアを使う - ミドルウエアとはexpressにプラグインして使う機能のことで、[connect](http://www.senchalabs.org/connect/)フレームワークを基盤としている - expressに同梱されているミドルウエアは[expressのドキュメント](http://expressjs.com/api.html#middleware)、または[connectのドキュメント](http://www.senchalabs.org/connect/)を参照のこと - 利用するミドルウエアは```app.use([path, ] middlewareFunc)```で読み込む - ```path```を指定した場合、リクエストと```path```が一致した場合のみそのミドルウエアが利用される。```path```が省略された場合は```'/'```として扱われる - ミドルウエアの実態は```middlewareFunc```関数で、そのシグネチャは```function(req, res, next)``` - ミドルウエアは```app.use```された順番に適用される(ルーティング設定の適用順序と同じ考え方) ```javascript app.use(middlewareFactory(someOption)); /* ミドルウエアはファクトリ関数(関数をreturnする関数)を使って生成するものが多い。この例の場合、middlewareFactory関数はfunction(req, res, next)というシグネチャを持った関数を返す */ ``` ## staticファイルを公開する - 画像ファイルやCSSファイル、JavaScriptファイルなどのstaticファイルを公開するには、staticミドルウエアを利用する - staticミドルウエアのファクトリ関数は```express.static(path[, option])``` - ```path```には、staticファイルを公開したいフォルダ / ファイルへのパスを指定 - ```option```には以下の値を指定できる - *maxAge*: Browser cache maxAge in milliseconds. defaults to 0 - *hidden*: Allow transfer of hidden files. defaults to false - *redirect*: Redirect to trailing "/" when the pathname is a dir. defaults to true - *index*: Default file name, defaults to 'index.html' ```javascript app.use(express.static('path_to_somewhere', {hidden: true})); ``` ## リクエストに含まれるCookieを読み出す - リクエストに含まれるCookieを読み出すには、cookieParserミドルウエアを使う(このミドルウエアは将来的にdeprecatedになると予告されており、cookiesミドルウエアに置き換わる予定) - cookieParserを使うと、リクエストに含まれるCookieの情報が```request.cookies[]```メンバに格納される - cookieParserミドルウエアのファクトリ関数は```express.cookieParser([secret])``` - ```secret```には署名付きCookie用の鍵を指定する ```javascript app.use(express.cookieParser('some_secret')); ``` ## セッションを使う - セッション機能を使うには、sessionミドルウエアを利用する - cookieを利用してセッションIDを管理するのでcookieParserミドルウエアも必須 - sessionミドルウエアを使うと、セッション情報を保持するオブジェクト```request.session```が生成される - ```request.session```オブジェクトにはセッションをリセットしたり破棄するための関数が用意されている。詳細は[connectのドキュメント](http://www.senchalabs.org/connect/session.html)を参照のこと - sessionミドルウエアのファクトリ関数は```express.session([option])``` - ```option```には以下の値を指定できる - *key*: cookie name defaulting to connect.sid - *store*: session store instance - *secret*: session cookie is signed with this secret to prevent tampering - *cookie*: session cookie settings, defaulting to { path: '/', httpOnly: true, maxAge: null } - *proxy*: trust the reverse proxy when setting secure cookies (via "x-forwarded-proto") ```javascript // ミドルウエアの読み込み app.use(express.cookieParser()); app.use(express.session({secret: 'secret_for_sign'})); // request.sessionオブジェクトを利用する(この例では訪問回数を記録している) app.get('path_to_somewhere', function(req, res) { if (req.session.counter) { res.end('you already visit this page ' + req.session.counter + '-th time'); req.session.counter++; } else { req.session.counter = 1; res.end('this is first time you visit this page!'); } }); ``` ## エラーを発生させる - コントローラー関数の中でエラーを発生させるには、例外をthrowする、または```next()```コールバックの引数に例外オブジェクトを渡す ```javascript // 例外をthrowする app.get('path_to_somewhere', function(req, res) { : throw new Error('this is error'); }); // next()コールバックを呼び出す app.get('path_to_somewhere', function(req, res) { : next(new Error('this is error'); }); ``` ## エラーをハンドリングする - エラーをハンドリングするには、```app.use()```を使ってエラーハンドリング用関数を指定する(つまりエラーハンドリングもミドルウエアと同じ扱い) - エラーハンドリング用関数のシグネチャは```function(err, req, res, next)```でなければならない(その他ミドルウエアとはシグネチャが異なる。ミドルウエアには```err```が含まれない) - エラーハンドリング用関数の中で```next()```を引数なしで呼び出した場合は、その他のミドルウエアに処理が移る - ```next()```をerr引数付きで呼び出した場合は、その他のエラーハンドリング用関数だけが実行される(その他のミドルウエアは実行されない) ```javascript app.use(function(err, req, res, next) { : next(err); // もう一つのエラーハンドリングコールバックに処理を渡す }); app.use(function(err, req, res, next) { : res.status(500); res.render('500'); }); ``` ## CSRF対策する - csrfミドルウエアを使って以下を行う - トークンを生成してセッションに保存(csrfミドルウエアが自動で行う) - Formをレンダリングする際にトークンを埋め込む(要実装) - FormからのPOSTに含まれるトークンとセッションに保存されたトークンを比較し、異なっている場合はエラーを発生させる(csrfミドルウエアが自動で行う) - エラーを適切にハンドリングする(要実装、詳細は[別項目](#2-14)参照のこと) - セッションを利用するのでcookieParserミドルウエアとsessionミドルウエアも必須 - csrfミドルウエアのファクトリ関数は```express.csrf()``` ```javascript // sessionミドルウエアの読み込み app.use(express.cookieParser()); app.use(express.session({secret: 'secret_to_sign'})); // csrfミドルウエアの読み込み app.use(express.csrf()); // エラーハンドリングの実装 app.use(function(err, req, res, next) { : // something : }); // ルーティング設定 app.get('path_to_form', function(req, res, next) { : // セッションに保存されたtokenをFormに埋め込む res.local.token = req.session._csrf; res.render('index'); }); ``` ```html:View側(ejsの例) <form action='/form', method> <input type='hidden' name='_csrf' value='<%=token%>'/> </form> ``` ## アクセスログ / エラーログを残す - アクセスログやエラーログを残すには、loggerミドルウエアを利用する - loggerミドルウエアのファクトリ関数は```express.logger([string | function | option])``` - 引数で出力フォーマットや出力先を指定できる。詳細は[loggerミドルウエアのマニュアル](http://www.senchalabs.org/connect/logger.html)を参照のこと - 引数を省略した場合は、Apacheと同等の出力フォーマットでstdoutに出力される ```javascript app.use(express.logger()); ``` ## ルーティングとミドルウエアの実行順序を明示する - 原則として、ミドルウエアやルーティング設定はソースコードに書かれた順番に実行される - ただし、ルーティング設定(```app.get()```や```app.post()```)は、express内部で一つにまとめられ、一つのミドルウエアのように扱われる(ここではルーティングミドルウエアと呼ぶ) - このルーティングミドルウエアが実行されるのは、最初のルーティング設定が書かれた箇所となる(以下の例参照) ```javascript // ①〜⑥の順序で実行される // 最初のルーティング設定 // この位置にルーティングミドルウエアが入る app.get('path_to_somewhere', function(req, res) { // ① : }); app.get('path_to_somewhere', function(req, res) { // ② : }); app.use(someMiddleware); // ⑤ app.use(otherMiddleware); // ⑥ app.get('path_to_somewhere', function(req, res) { // ③ : }); app.get('path_to_somewhere', function(req, res) { // ④ : }); ``` - このままではソースコード全体を見なければ実行順序が分からない(特に大規模なシステムの場合) - そこで、実行順序を見通し良く記述するために```app.use(app.router)```を使うとよい - ```app.use(app.router)```はルーティングミドルウエアそのものであり、これが書かれた位置にルーティングミドルウエアが入ることになる ```javascript // ①〜⑥の順序で適用される app.get('path_to_somewhere', function(req, res) { // ③ : }); app.get('path_to_somewhere', function(req, res) { // ④ : }); // // 全体を見なくてもここだけ見れば実行順序が判る // app.use(someMiddleware); // ① app.use(otherMiddleware); // ② app.use(app.router); // この位置にルーティングミドルウエアが入る app.get('path_to_somewhere', function(req, res) { // ⑤ : }); app.get('path_to_somewhere', function(req, res) { // ⑥ : }); ``` ## 全てのHTTP Verbに一致するルーティングを指定する - HTTP Verb毎のルーティング設定は[前述](#2-1)の通りだが、全てのVerbに対して適用するルーティング設定は```app.all()```で行う ```javascript app.all('path_to_somewhere', function(req, res, next) { : }); ``` ## コンテンツをgzip / deflatで圧縮して返す - compressミドルウエアを利用する ```javascript app.use(express.compress()); ``` ## ディレクトリをリスティングする - ディレクトリをリスティングするには、directoryミドルウエア利用する - direcotoryミドルウエアのファクトリ関数は```express.directory(path [,option])``` - ```path```には、directoryを公開したいフォルダへのパスを指定する - ```option```には以下の値を指定できる - *hidden*: display hidden (dot) files. Defaults to false. - *icons*: display icons. Defaults to false. - *filter*: Apply this filter function to files. Defaults to false. ```javascript app.use(express.directory('path_to_somewhere'), {hidden: false}); ``` ## faviconを指定する - faviconが[staticファイルとして公開](#2-10)されていれば何もしなくてもよい - ただし、faviconへのアクセスをログに残したくない場合は、faviconミドルウエアを利用して以下のようにするとよい ```javascript // 先にfaviconミドルウエアを適用 app.use(express.favicon('path_to_favicon')); // その後loggerミドルウエアを適用 app.use(express.logger()); ``` ## デバッグ関連 ### ルーティング設定を確認する - ルーティングの設定は```app.routes```オブジェクトに格納されている - このオブジェクトを```console.log```に出力すれば、登録されたルーティング設定を確認できる ```javascript console.log(app.routes); /* 出力の例 { get: [ { path: '/', method: 'get', callbacks: [Object], keys: [], regexp: /^\/\/?$/i }, { path: '/user/:id', method: 'get', callbacks: [Object], keys: [{ name: 'id', optional: false }], regexp: /^\/user\/(?:([^\/]+?))\/?$/i } ], delete: [ { path: '/user/:id', method: 'delete', callbacks: [Object], keys: [Object], regexp: /^\/user\/(?:([^\/]+?))\/?$/i } ] } */ ``` ### express内部のデバッグログを出力する - ```DEBUG```環境変数に```express:*```を設定した上でnodeを起動すれば、デバッグログが出力される ```bash $ DEBUG=express:* node app.js # 出力の例 # express:application booting in development mode +0ms # express:router defined get /hello.txt +0ms # express:router defined get /hello.txt +1ms ``` |
|
| 269位 |
|
|||
|
17:08:11 |
(XICA CO.,LTD. 所属) |
|
2014/07/07 追記
手前味噌ですが、Pythonの環境については下記も参考にしていだければと - [10分でわかるPythonの開発環境](http://www.slideshare.net/who_you_me/10-32435124) - [Pythonの開発環境についてもう一度考えてみる](http://qiita.com/who_you_me/items/1d37d964c1459b9625fa) ----- Pythonでパッケージ管理といえばpipですね。 pipを入れようとしたら、以前なら「setuptools or distribute をインストール→`easy_install pip`を叩く」という手順でしたが、いつの間にかセットアップが楽になってました。 http://www.pip-installer.org/en/latest/installing.html get-pip.pyというのを実行すれば一発で入るらしい。 なので、下記のコマンド一つでインストール可能になりました。 ``` curl -kL https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python ``` もちろん、パイプの後は各自の環境に合わせsudoつけたりpython3にしたりとか、変えてくださいね。 てなわけで、Pythonの開発環境構築(pip, virtualenv, virtualenvwrapperのインストール)が随分楽になりました。 ``` curl -kL https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python pip install virtualenv virtualenvwrapper vi ~/.bashrc # 下記の3行を追記 export WORKON_HOME=$HOME/.virtualenvs export PROJECT_HOME=$HOME/Devel source /path/to/your/virtualenvwrapper.sh ``` 以前は環境作るだけで結構苦労してたと思いますが、いい時代になりましたね(遠い目 |
|
| 270位 |
|
|||
|
23:05:06 |
|
|
天気情報を取得できるAPIはたくさんありますが、 今回は、<a href="http://openweathermap.org/" title="OpenWeatherMap - actual and forecast weather.">OpenWeatherMap - actual and forecast weather</a>という無料サービスを使って、天気情報をjsonで取得します。 ##都市名で取得する ```php http://api.openweathermap.org/data/2.5/weather?q="都市名","国" //例 http://api.openweathermap.org/data/2.5/weather?q=Tokyo,jp ``` ##緯度、経度で取得する ```php http://api.openweathermap.org/data/2.5/weather?lat="緯度"&lon="経度" //例 http://api.openweathermap.org/data/2.5/weather?lat=35&lon=140 ``` ###取得結果 ```json { "base": "cmc stations", "clouds": { "all": 36 }, "cod": 200, "coord": { "lat": 35.64, "lon": 139.68 }, "dt": 1387204059, "id": 1864381, "main": { "humidity": 18, "pressure": 1019, "temp": 279.63, "temp_max": 282.04, "temp_min": 277.59 }, "name": "Denenchōfu", "sys": { "country": "JP", "message": 0.0086, "sunrise": 1387143858, "sunset": 1387178986 }, "weather": [ { "description": "scattered clouds", "icon": "03n", "id": 802, "main": "Clouds" } ], "wind": { "deg": 306, "gust": 4.63, "speed": 4.11 } } ``` ##取得できる値 | パラメータ|説明 | |:-----------|:-------------| | temp | 気温 | | temp_min | 最低気温 | | temp_max | 最高気温 | | icon | 天気アイコン | | main | 天気 | | lat | 緯度 | | lon | 経度 | | pressure | 気圧 | | sea_level | 海面 | | humidity | 湿度 | | speed | 風速 | | name | 都市名 | | country | 国名 | | sunrise |日の出時間| |sunset| 日没時間| ##天気アイコン取得方法 ```php http://openweathermap.org/img/w/"iconの値".png ``` 説明:<a href="http://openweathermap.org/wiki/API/Weather_Condition_Codes" title="API/Weather Condition Codes - OpenWeatherMap">API/Weather Condition Codes - OpenWeatherMap</a> ##その他 ・天気の更新は10分単位 ・対応は世界7万都市 ・利用料無料 |
|
| 271位 |
|
|||
|
18:46:18 |
(everyleaf.com 所属) |
|
環境壊しました。一から作ります。 あと最近使い方聞かれることが多いのでメモ的に。 初心者 Rubyist 向けです。 SublimeText3 で動作確認してます。 ## 設定 `Preferences` -> `Settings - User` というところが設定ファイル。 ### 設定 ```json { "color_scheme": "Packages/Theme - Flatland/Flatland Dark.tmTheme", "draw_white_space": "all", "ensure_newline_at_eof_on_save": true, "flatland_square_tabs": true, "font_size": 18, "highlight_line": true, "ignored_packages": [ "Vintage" ], "itg_small_tabs": true, "tab_size": 2, "theme": "Cobalt2.sublime-theme", "translate_tabs_to_spaces": true, "font_options" : ["gray_antialias"] } ``` ちなみに設定ファイルは `json` っぽい感じなので、カンマの有無に気をつけましょう。最後はカンマつけたら怒られます。(sublime 3 は怒られません。やった!) `Preferences` -> `Settings - Default` というのがデフォの設定なので、こいつを参考に User の方をいじるとよいでしょう。 これを読むとだいたいわかるとおもいます。 "Sublime Text 2 のDefault設定ファイルについて" https://gist.github.com/BlackKetchupTea512/3620658 ## メニューからの変更 - `View` -> `Hide Minimap` - これは僕には邪魔なので消します。 ## メモ + 基本操作など ### メニューバーの `Help` - インクリメンタルサーチしてくれて結構便利なので使いましょう。 ### 検索 - `cmd` + `f` : そのファイル内を検索 - `cmd` + `opt` + `f` : そのファイル内を置換 - `cmd` + `shift` + `f` : 全体検索 + 置換 ( `Where` に何も指定しないと、開いているプロジェクト全体になる) ### ファイルを開く - `cmd` + `t`: いい感じにインクリメンタルサーチしてくれるので、たとえば `controllers/hoges_controller.rb` を開きたい場合、`cont hoge` などでいける。 ### 操作 - `ctrl` + `0` : サイドバーにフォーカスする - `cmd` + `opt` + 2 : 画面を左右分割する - `cmd` + `opt` + 1 : 画面を一つにする ## PackageControll ### インストール 1. `View` ->`Show Console` すると画面の下の方にコンソールウィンドウが出るので、そこに↓のコードを貼り付けて `Enter` キーを押下。 - SublimeText2 ( SublimeText の再起動が必要) https://sublime.wbond.net/installation#st2 - SublimeText3 https://sublime.wbond.net/installation#st3 ## インストールしたパッケージ ### パッケージのインストール方法 - `Shift` + `Cmd` + `p` で表示されるランチャー?から、`Package Controll: Install Packag` を選択して `Enter` を押下。(インクリメンタルサーチしてくれるので `pac ins` などと打つとみつけやすいと思います。) - パッケージリストをインクリメンタルサーチしてくれるので気になる単語(git とか ruby とかで探しましょう。) - オススメプラグインなどググると出てくるので参考にしましょう。 - 基本 PackageControll から名前で検索してインスールできるかと。 ### ★★★ Flatland - https://github.com/thinkpixellab/flatland - テーマがかっちょよくなってテンションが上がって便利! - PackageControll から インストールして、https://github.com/thinkpixellab/flatland#activating-the-theme を読んで設定ファイルに追加しましょう。 - アイコンもかっちょよくできるので "Sublime Text 2 のテーマをフラットデザインでかっこいい「Flatland Theme」に変更してみた | memobits" http://m.designbits.jp/13050911/ を見ましょう。 ### ★★★ cobalt2 - [wesbos/cobalt2]( https://github.com/wesbos/cobalt2 ) - サイドバーアイコンが使えて便利! - Preferences → Settings - User で↓を設定 ``` "color_scheme": "Packages/Theme - Cobalt2/cobalt2.tmTheme", "theme": "Cobalt2.sublime-theme", ``` ### SideBarEnhancements - サイドバー右クリック時にいろんなメニューを出してくれて便利! - https://github.com/titoBouzout/SideBarEnhancements - デフォルトので十分かも。必要になったらまたいれよう ### ★★★ GitGutter - Git で管理してるコードの差分行をリアルタイムで表示してくれて便利! - https://github.com/jisaacks/GitGutter ### ★★★ TrailingSpaces - 余計なスペースを色つけてくれて、保存時に削除してくれて便利! - https://github.com/SublimeText/TrailingSpaces ### ★★★ Rails-Latest-Migration - Rails の 直近の Migration ファイルを開いてくれるので便利! - https://github.com/alexpls/Rails-Latest-Migration - なくても困りませんが、地味にいいですよ。 ### Gist - Gist と連携できて便利! - https://github.com/condemil/Gist - github で token を発行し、Sublime の Gist.sublime-setting に設定が必要。この設定ファイルは↓の操作とか Gist の機能を使おうとすると表示される。github token 発行は https://github.com/condemil/Gist#web のあたりを読もう - `cmd` + `shift` + `p` で `gist:` って打つとメニューがでてくるので、`open gist` とか選ぶと、自分の gist をファイル単位で開くことができる。 - まあ、gist は web から開いてコピペすればいいので、別になくても困りませんね ### ★★★ Linter 系 - 入れておくとリアルタイムにシンタックスエラーとかがわかって便利! - SublimeLinter - SublimeLinter-ruby - 日本語でエラーになる場合は、Package Settings -> SublimeLinter -> Settings User から args に "-Ku" を追加 ```json "ruby": { "@disable": false, "args": ["-Ku"], "excludes": [] } ``` - SublimeLinter-haml - SublimeLinter-json - SublimeLinter-jshint - SublimeLinter-coffeelint - SublimeLinter-rubocop ### Snipets 系 - Ruby とか Rails とかで Package 調べるといろいろある。 - 必要になったらいれて、コレ更新しよう。 ### ★ sass-textmate-bundle - sass の シンタックスとか Snipets とか便利。 - https://github.com/nathos/sass-textmate-bundle - このあたり複数あるけどなにがいいのかいまいちわかってない ### ★★★ SyncedSideBar - サイドバーで、今開いてるファイルに色を付けてくれて便利! - https://github.com/sobstel/SyncedSideBar ### ★★★ sublime-text-2-git - Package Controll から Git で見つかるやつ - https://github.com/kemayo/sublime-text-2-git - SublimeText3 でも動いた - 主に blame してます(犯人探しじゃなくて、質問したいときなどに) - SublimeGit ( https://sublimegit.net/ )という有料のがあるのでそちらの方がいいのかもしれない ### Tag - 閉じタグ `</` 打つと補完してくれるので地味に便利! - https://github.com/SublimeText/Tag ### ★★★ RubyTest - Rails の実装とテストが `cmd` + `.` で切り替えられて便利! - https://github.com/maltize/sublime-text-2-ruby-tests - あとカーソル行や開いてるファイルのテストが実行できたりなど便利! - RubyTest.sublime-settings に下記のようなのを環境に合わせて設定を追加して `cmd` + `shift` + `r` で実行 ``` { # 詳しくは README を "check_for_bundler": true, "check_for_rbenv": true } ``` ### BeautyRuby - https://github.com/CraigWilliams/BeautifyRuby - auto-format してくれるはずなんだけど、rbenv でうまく動かない… - まあ、使えなくてもあまり気にならないです、僕は。 ### SublimeRailsNavigator - model とか controller とかのリストを表示してくれて便利 - https://github.com/noklesta/SublimeRailsNav - ST2 は PackageControll で入るけど、ST3 は以下の様な感じです ``` $ cd ~ $ git clone https://github.com/noklesta/SublimeRailsNav.git $ git chechout ST3 $ mv SublimeRailsNav ~/Library/Application\ Support/Sublime\ Text\ 3/Packages ``` - 設定 - `Preference` -> `PackageSettings` -> `Key Bindings - User` から以下のような感じに ``` [ // SublimeRailsNav { "keys": ["super+ctrl+m"], "command": "list_rails_models" }, { "keys": ["super+ctrl+c"], "command": "list_rails_controllers" }, { "keys": ["super+ctrl+v"], "command": "list_rails_views" }, { "keys": ["super+ctrl+h"], "command": "list_rails_helpers" }, { "keys": ["super+ctrl+x"], "command": "list_rails_fixtures" }, { "keys": ["super+ctrl+t"], "command": "list_rails_tests" }, { "keys": ["super+ctrl+i"], "command": "list_rails_javascripts" }, { "keys": ["super+ctrl+y"], "command": "list_rails_stylesheets" } ] ``` ### SublimeSuperNavigator - https://github.com/jugyo/SublimeSuperNavigator - カレント行をナビゲートしてくれて便利! - 設定 - `Preference` -> `PackageSettings` -> `Key Bindings - User` から以下のような感じに(ひとまず ctrl + s にしてみました) ``` [ // SublimeSuperNavigator { "keys": ["ctrl+s"], "command": "super_navigate" }, ] ``` ### RecentActiveFiles - https://github.com/jugyo/SublimeRecentActiveFiles - 最近触ったファイルにアクセスしやすくて便利! - 設定 - `Preference` -> `PackageSettings` -> `Key Bindings - User` から以下のような感じに(ひとまず ctrl + r にしてみました) ``` [ // RecentActiveFiles { "keys": ["ctrl+r"], "command": "recent_active_files" }, ] ``` ### ★★★ BetterCoffeeScript - "aponxi/sublime-better-coffeescript" https://github.com/aponxi/sublime-better-coffeescript - CoffeeScript まわりに便利 ### Emmet - "sergeche/emmet-sublime" https://github.com/sergeche/emmet-sublime - ctrl + e で HTML が楽に書けて便利〜 ### SublimeHtml2Haml - "pavelpachkovskij/sublime-html-to-haml" https://github.com/pavelpachkovskij/sublime-html-to-haml - html を haml に変換できて便利 ### Diffy - https://github.com/zsong/diffy - 差分が見れて便利 ``` [ { "keys": ["super+k", "super+d"], "command": "diffy" } { "keys": ["super+k", "super+c"], "command": "diffy", "args": {"action": "clear"} } ] ``` ### ★★★ CTags - [SublimeText/CTags](https://github.com/SublimeText/CTags) - メソッド定義場所に飛べたり戻れたりして便利 - `brew install ctags` で ctags をインストール - Ctags の設定ファイル(`Preferences -> Package Setting -> Ctags -> Setting – User`) ``` { "command": "/usr/local/bin/ctags -R -f .tags", } ``` - プロジェクトを右クリックして、`CTags: Rebuild Tags` ### ★★ DashDoc * [farcaller/DashDoc]( https://github.com/farcaller/DashDoc ) * ctrl + h で Dash が引けて便利 ## Syntax ハイライトの設定 - (だいたいプラグイン入れれば自動で設定された気がするんだけど) coffee とか sass とかならないものがあった。毎回 Syntax ハイライトの設定するのも面倒。 - 対象のファイルを開いた状態で `View` -> `Syntax` -> `Open all with current extension as …` から設定したい Syntax を選択。するとその設定が保存され、次からその拡張子には設定した Syntax ハイライトがつく ## ターミナルからの起動をらくにしよう - ターミナルから、SublimeText で、そのディレクトリ配下を開く、みたいなことができるととてもらくなので↓の設定などをしてみるとらくです。 - "ターミナルからSublime Text 2を起動するための設定 | DevAchieve" http://wada811.blogspot.com/2013/01/setting-sublime-text-2-alias.html - ちなみに僕は s に割り振ってます。 |
|
| 272位 |
|
|||
|
21:24:33 |
(シナプス株式会社 所属) |
|
# 予備知識
## スーパーグローバル変数とは? 「スーパーグローバル変数って何?」って感じの駆け出しPHPプログラマのために念のためマニュアルへのリンクを記載しておきます.全然知らない人は軽く読んでおいてください. - [PHP Manual - 変数のスコープ](http://www.php.net/manual/ja/language.variables.scope.php) - [PHP Manual - スーパーグローバル](http://www.php.net/manual/ja/language.variables.superglobals.php) ## HTTPとは? **リクエストヘッダー・レスポンスヘッダー** と聞いてピンと来ない人はまず下記サイトにて予習をお願いします.細かいことは覚える必要は無いので,大雑把に「ヘッダーとはどんなものか」ということを理解してください. - [Qiita - 【PHP超入門】HTTP(GET・POST)について](http://qiita.com/7968/items/4bf4d6f28284146c288f)<br>[Qiita - 【PHP超入門】Cookieとセッションについて](http://qiita.com/7968/items/ce03feb17c8eaa6e4672)<br>@7968さんによるQiitaの記事です.右も左もわからない人はまずこれで.<br><br> - [とほほのWWW入門 - HTTP入門](http://www.tohoho-web.com/ex/http.htm) 最初の記事をもう少し体系的にまとめた感じです.ただし,Cookieに関する情報が欠落しているところが少し惜しいです…<br><br> - [Studying HTTP](http://web.archive.org/web/20140303140503/http://www.studyinghttp.net/)<br>技術文書を読むことに慣れている人にはこちらが適当でしょう.厳密な定義が読みやすくまとめられています.但し,素晴らしいサイトであったにも関わらず,ドメイン自体が消滅してしまっているので,WebArchiveでの閲覧となります.<br><br> - [Live HTTP Headers](https://addons.mozilla.org/ja/firefox/addon/live-http-headers/)<br>Firefoxのアドオンです.実際にこういった通信の内容をブラウザ上で確認したい場合などに活用してください. # スーパーグローバル変数の生成 リクエストが来たとき,それに応じた `$_GET` `$_POST` `$_COOKIE` が生成される様子を見ていきましょう. ## 連想配列のパラメータを文字列で表現する まず,このような連想配列のデータがあったとき… ```php $data = [ 'A' => 'B', 'C' => [ 'D' => 'E', ], ]; ``` それを文字列として表現するには,大雑把に言うと2種類の形式があることを覚えてください.それぞれ, **クエリ―ストリング** および **Cookieヘッダー** といいます. ```text:クエリ―ストリングの場合 A=B&C[D]=E ``` ```text:Cookieヘッダーの場合 Cookie: A=B; C[D]=E ``` なんとなくわかりますかね? - キー`A`の値は`B` - キー`C`に対応する値は配列で,さらにそのキー`D`に対応する値がE 基本はこれだけで十分です.<ins>PHPはこれらの文字列を解析し,もとの配列に復元する仕事をしています</ins>.細かなパース規則については[付録](#%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%AE%E3%83%91%E3%83%BC%E3%82%B9%E8%A6%8F%E5%89%87)にしておきましたので,厳密にいろいろ試したい人は読んでください. ## PHPがWebブラウザからパラメータを受け取る方法 (基本編) 以下の2点に留意してお読みください. - ヘッダーの1行1行を区切るために使用される改行コードは **CRLF** `"\r\n"` である. - ヘッダーの生成と送信はWebブラウザが全自動でやってくれるので,普段私たちが普通にWebブラウザを使う限りは意識することはない. ### URLにクエリ―ストリングを含める クエリ―ストリングはURLの `?` より後ろの部分に記述されます. #### <font color="silver">送信側</font> ```html:リンクをクリックするとリクエストが実行される <a href="http://example.com/test.php?a=b">Test</a> ``` ```html:フォームを送信するとリクエストが実行される <form method="get" action="http://example.com/test.php"> <input type="hidden" name="a" value="b"> <input type="submit" value="送信"> </form> ``` ```http:リクエストヘッダー GET /test.php?a=b HTTP/1.1 Host: example.com Connection: Close ``` #### <font color="silver">受信側</font> クエリ―ストリングは **`$_SERVER['QUERY_STRING']`** に格納されます. ```php:文字列としてこのようにセットされる,もしくは未定義 $_SERVER['QUERY_STRING'] = 'a=b'; ``` 私のテストした環境では `?` がURL中に存在しない場合でもこのインデックスは **空文字列** として常に存在するようですが,マニュアルでは未定義になる可能性が示唆されているため,使用する場合は以下のようにしてください. - [isset](http://www.php.net/manual/ja/function.isset.php) でチェックする - `??`演算子でチェックする (PHP7.0以降) - [filter_input](http://www.php.net/manual/ja/function.filter-input.php) 関数経由で取得する ```php:これらのうち,いずれかの記述を採用する $query_string = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : ''; $query_string = $_SERVER['QUERY_STRING'] ?? ''; $query_string = (string)filter_input(INPUT_SERVER, 'QUERY_STRING'); ``` これをパースして配列に格納したものが `$_GET` です.この変数は常に存在しています. ```php:配列としてこのようにセットされる $_GET = ['a' => 'b']; ``` ### ポストフィールドにクエリ―ストリングを含める #### <font color="silver">送信側</font> クエリ―ストリングはヘッダー群の一番下に空行を1つ挟んで配置され,ここは **ポストフィールド** と呼ばれることがあります.以下のヘッダーを必ず伴います. ##### `Content-Length` クエリ―ストリングの **バイト数** を表します. ##### `Content-Type` [application/x-www-form-urlencoded](http://www.wdic.org/w/WDIC/application/x-www-form-urlencoded) 固定です. ```html:フォームを送信するとリクエストが実行される <form method="post" action="http://example.com/test.php"> <input type="hidden" name="a" value="b"> <input type="submit" value="送信"> </form> ``` ```http:リクエストヘッダー POST /test.php HTTP/1.1 Host: example.com Content-Length: 3 Content-Type: application/x-www-form-urlencoded Connection: Close a=b ``` #### <font color="silver">受信側</font> クエリ―ストリング部分が文字列で欲しい場合, **`php://input`** をファイルのように取得させます. ```php:文字列で取得できる $postfields = file_get_contents('php://input'); ``` これをパースして配列に格納したものが `$_POST` です.この変数は常に存在しています. ```php:配列としてこのようにセットされる $_POST = ['a' => 'b']; ``` ### Cookieヘッダーを送信する #### <font color="silver">Cookieを保存させる</font> [setcookie](http://www.php.net/manual/ja/function.setcookie.php) 関数または [setrawcookie](http://www.php.net/manual/ja/function.setrawcookie.php) 関数を使うと,ブラウザに対して簡単に整形された **Set-Cookieヘッダー** を送信することが出来ます.前者は<ins>値のみパーセントエンコード</ins>し,後者はキー・値ともそのままセットします. ```php:PHPでこれを実行する setcookie('a', 'b'); ``` ```text:Webブラウザに送信されるSet-Cookieヘッダー <br>(基本形であって,実際にはこれの後ろにいろいろな情報が付加される) Set-Cookie: a=b ``` これをブラウザが受理すると,<ins>次回から</ins>同一サイトに対してCookieヘッダーを送信してくれるようになります.この関数を実行したタイミングではCookieヘッダーは送信されてきていないことに注意してください.また,これらの関数で以下のパラメータを設定すると,Cookieの挙動を詳細に制御することが出来ます. ##### `$expire` 有効期限.タイムスタンプとして表される. - 省略または `0` を指定した場合,Webブラウザを閉じるまでが有効期限となる. - `0` より大きく現在より小さい値(過去)を指定した場合,<ins>即座にWebブラウザで削除が行われる</ins>.但し, **共通な `$path` `$domain`** を指定していなければならない. ```php:よく見かける例 setcookie('foo', '', time() - 3600); ``` ```php:実はこれでもOK setcookie('foo', '', 1); ``` ##### `$path` どこの階層から送信を行うかを表したパス.省略した場合は **スクリプトが存在しているディレクトリ** を指定したと見なされる. ##### `$domain` 対象となるドメイン名.省略した場合はスクリプトが存在するサーバーのものがそのまま使われる. ##### `$secure` HTTPS接続のみに限定するかどうか.デフォルト値は `false` . ##### `$httponly` JavaScriptからのアクセスをブロックするかどうか.デフォルト値は `false` . これらを指定したとき,送出されるヘッダーは以下のようになります. ```php:(実行例) PHPコード setcookie('a', 'b', 992174400, '/foo/bar', 'example.com', false, true); ``` ```text:(実行結果) Set-Cookieヘッダー Set-Cookie: a=b; expires=Sun, 10-Jun-2001 12:00:00 GMT; path=/foo/bar; domain=example.com; httponly ``` #### <font color="silver">Cookieを受信する</font> 文字列のままのCookieは<ins>頭の</ins>**`Cookie: `**<ins>を除いて</ins> **`$_SERVER['HTTP_COOKIE']`** に格納されます. ```php:文字列としてこのようにセットされる,もしくは未定義 $_SERVER['HTTP_COOKIE'] = 'a=b'; ``` Cookieヘッダーが無い場合,このインデックスは存在しません.使用する場合は `$_SERVER['QUERY_STRING']` と同様のチェックが必要です. ```php:これらのうち,いずれかの記述を採用する $cookie = isset($_SERVER['HTTP_COOKIE']) ? $_SERVER['HTTP_COOKIE'] : ''; $cookie = $_SERVER['HTTP_COOKIE'] ?? ''; $cookie = (string)filter_input(INPUT_SERVER, 'HTTP_COOKIE'); ``` これをパースして配列に格納したものが `$_COOKIE` です.この変数は常に存在しています. ```php:配列としてこのようにセットされる $_COOKIE = ['a' => 'b']; ``` ## PHPがWebブラウザからパラメータを受け取る方法 (応用編) ### URLにパス情報を含める ファイルをディレクトリのように見せ,その後ろに追加で情報を階層的に記述することが可能です. #### <font color="silver">送信側</font> ```html:リンクをクリックするとリクエストが実行される <a href="http://example.com/test.php/foo/bar">Test</a> ``` ```http:リクエストヘッダー GET /test.php/foo/bar HTTP/1.1 Host: example.com Connection: Close ``` #### <font color="silver">受信側</font> パス情報は **`$_SERVER['PATH_INFO']`** に格納されます. ```php:文字列としてこのようにセットされる,もしくは未定義 $_SERVER['PATH_INFO'] = '/foo/bar'; ``` この部分が無い場合,このインデックスは存在しません.使用する場合は `$_SERVER['QUERY_STRING']` と同様のチェックが必要です (省略) . **【注意】** `+` は半角スペースに置換され**ません**. [rawurldecode](http://www.php.net/manual/ja/function.setcookie.php) 関数相当の処理です. ### ポストフィールドにマルチパートコンテンツを含める パラメータが<ins>パーセントエンコードされない</ins>のが特徴です.そのため,通信量が肥大化しやすいファイルのアップロードに利用されます. #### <font color="silver">送信側</font> マルチパートコンテンツはヘッダー群の一番下に空行を1つ挟んで配置され,以下のヘッダーを必ず伴います. ##### `Content-Length` マルチパートコンテンツの **総バイト数** を表します. ##### `Content-Type` `multipart/form-data; boundary=境界文字列` という形式です.改行などを除く任意の文字列を設定できますが,送信するデータと衝突する可能性の無いランダムなハッシュ値を採用することが多いと思います.ブラウザによって実装は異なるようです. 各コンテンツは以下のような形式です. ```text:通常のパラメータ --境界文字列 Content-Disposition: form-data; name="NAME属性値" 送信するVALUE属性値 ``` ```ファイルであることを明示するパラメータ --境界文字列 Content-Disposition: form-data; name="NAME属性値"; filename="ファイル名" Content-Type: MIMEタイプ(ブラウザが自動判定) 送信するファイルデータ ``` これらが連続で配置され,最後のパラメータの後には次の1行が挿入されます. ```text:終端行 --境界文字列-- ``` 以下にリクエストの例を示します. ```html:フォームを送信するとリクエストが実行される <form method="post" action="http://example.com/test.php" enctype="multipart/form-data"> <input type="hidden" name="hidden" value="あいうえお"> <input type="file" name="file"> <input type="submit" value="送信"> </form> ``` ```http:リクエストヘッダー POST /test.php HTTP/1.1 Host: example.com Content-Length: 257 Content-Type: multipart/form-data; boundary=-----hogehoge Connection: Close -------hogehoge Content-Disposition: form-data; name="hidden" あいうえお -------hogehoge Content-Disposition: form-data; name="file"; filename="sample.txt" Content-Type: text/plain サンプルファイルデータ -------hogehoge-- ``` #### <font color="silver">受信側</font> ファイルに関するものは **`$_FILES`** ,それ以外のものは `$_POST` に格納されます.これ以上の説明はここでは割愛しますので,参考リンクだけ記載しておきます. - [Qiita - ファイルアップロードの例外処理はこれぐらいしないと気が済まない](http://qiita.com/mpyw/items/939964377766a54d4682) ### `$_GET` `$_POST` に同時に格納させる method属性がpostのとき,action属性の値に `?` に続いてクエリを付加することで実現できます.ただし,あまり出番はありません.というよりもややこしいので,使わないほうがいいです. ```html:フォームを送信するとリクエストが実行される <form method="post" action="http://example.com/test.php?a=b"> <input type="hidden" name="c" value="d"> <input type="submit" value="送信"> </form> ``` ```php:配列としてこのようにセットされる $_GET = ['a' => 'b']; $_POST = ['c' => 'd']; ``` ### `$_GET` `$_POST` `$_COOKIE` をまとめて扱う `$_GET` → `$_POST` → `$_COOKIE` の順に上書き代入を行い,生成されるスーパーグローバル変数が **`$_REQUEST`** です.この変数は常に存在しています.これもややこしさを助長するだけですので,使わないほうがいいです. # PHPから他のスクリプトにリクエストを送信する方法 これまでに紹介した方法は全て **Webブラウザ** から送信されてきた情報を **自身のスクリプト** で処理する方法についてでした.これとは異なり, **自身のスクリプト** から **他のスクリプト** に情報を送信することも可能です.<ins>PHPスクリプトにWebブラウザの代役をさせる</ins>と考えれば分かりやすいでしょう. よく使われる関数を紹介しておきます. - [PHP Manual - file_get_contents](http://www.php.net/manual/ja/function.file-get-contents.php) - [PHP Manual - Client URL Library](http://www.php.net/manual/ja/book.curl.php) またこれらを用いて,Webページから必要な情報を抜き出す手法もあります.それは**スクレイピング**と呼ばれます. - [Qiita - PHPネイティブのDOMによるスクレイピング入門](http://qiita.com/mpyw/items/c0312271819baee09132) # セッション関連あれこれ ここからは [セッション](http://www.php.net/manual/ja/book.session.php) を扱っていきます.前半で紹介した知識を前提としている箇所もあるので,忘れてしまったら戻って読み返してみてください. ## セッションって何? なぜセッションが必要なの? これまで紹介したスーパーグローバル変数 `$_GET` `$_POST` `$_COOKIE` `$_REQUEST` は,情報をユーザー側に送信させるものでした.これは,たとえ `$_GET` や `$_POST` 用のパラメータがHTMLの中に埋め込まれているものである,また一目にはユーザーにその存在を感じさせない `$_COOKIE` であったとしても,閲覧なんて当たり前,更には<ins>改竄しようと思えばいくらでも改竄できる</ins>こということを示しています.ブラウザゲームで自分の所持金をCookieで管理していたら, ```php:【A】文字列になるはずのところに配列をセットしてプログラムを誤作動させる $_COOKIE['money'] = []; ``` ```php:【B】所持金MAX!! $_COOKIE['money'] = '99999999'; ``` これに相当することが出来てしまうのです.【A】に関しては [Qiita - $_GET, $_POSTなどを受け取る際の処理](http://qiita.com/mpyw/items/2f9955db1c02eeef43ea) ここで紹介しているような ```php $money = (int)filter_input(INPUT_COOKIE, 'money'); ``` こういったフィルタリングを行うことでエラーの発生を回避させることは出来ますが,【B】に関しては本当にそれがあり得る範囲の値であればどうしようもないですよね.これがCookieだけで管理できる情報の限界です. そこで,ブラウザ側ではなく<ins>サーバー側に情報を保存する</ins>というスタンスで生まれたのが **セッション** なのです. ## 基本的な使い方は? [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数により `$_SESSION` 変数を準備し,あとは値の取得/代入といった操作を実行すればいいだけです. ```html+php:マニュアルより8割ぐらい引用 <?php // セッションを開始 @session_start(); if (!isset($_SESSION['count'])) { // 初回にはcountを0に初期化 $_SESSION['count'] = 0; } // インクリメント ++$_SESSION['count']; header('Content-Type: text/html; charset=utf-8'); ?> <!DOCTYPE html> <html> <head> <title>あなた専用カウンター</title> </head> <body> <p> こんにちは,あなたがこのページに来たのは<?=$_SESSION['count']?>回目ですね.<br> F5で更新すると再読込します. </p> </body> </html> ``` [session_start](http://www.php.net/manual/ja/function.session-start.php) の前に `@` をつけているのは,不正なリクエストが来ると **E_NOTICE** 更には **E_WARNING** レベルのエラーも発生し得るからです.PHP言語の欠陥ですね,これは…. (しかし,実際にWebサービスとして運用するときにはエラーは非表示になるので,必ずしもこれは必須ではありません.また,いきなりこれを付加してしまうとセッションが何らかの理由で開始出来なかったときに原因不明のバグに悩まされることもあるので,十分に注意してください.) 以降では,`@`をつけないことにします. ## スーパーグローバル変数への代入ってタブーじゃないの? 以下のように変数の存在をチェックして初期化するコードはよく見かけますよね. ```php:三項演算子を用いた例 $data = isset($_POST['data']) ? $_POST['data'] : ''; ``` ```php:filter_input関数を活用して型チェックまで行う例 $data = (string)filter_input(INPUT_POST, 'data'); ``` 一方,こんな風に上書き代入しちゃうコードはあんまり見かけませんね.コーディング規約でも禁止している場合が多いと思います. ```php if (!isset($_POST['data'])) { $_POST['data'] = ''; } ``` ところが `$_SESSION` だけは例外で,<ins>直接操作することが広く認められている唯一のスーパーグローバル変数</ins>となります. (但し, [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数のエラーの発生を `@` 演算子無しに防ぐために `$_COOKIE` を操作することは例外的に認められるでしょう.このコードは後ほど出てきます.) ## セッションに格納できるキーは? **10進整数** と等価でない文字列であれば何でも格納できます. ```php:GOOD $_SESSION['nine'] // 通常の文字列 $_SESSION['010'] // 8進表記文字列 ``` ```php:BAD $_SESSION[8] // 10進表記整数 $_SESSION[010] // 8進表記整数(実質的には上記と等しい) $_SESSION['8'] // 10進表記文字列 $_SESSION['0'] // 10進表記文字列 ``` ## セッションに格納できる値は? `$_GET` `$_POST` `$_COOKIE` `$_REQUEST` などは **文字列** と **配列** しか扱えませんでしたが,`$_SESSION` はリソース型以外の **任意のデータ** を保存することが出来ます.但しオブジェクトを保存する場合,そのクラスの定義が [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数のコールよりも<ins>前に</ins>行われている必要があります. ```php:GOOD require 'Test.class.php'; session_start(); ``` ```php:BAD session_start(); require 'Test.class.php'; ``` ## Cookieとどう違うの? 既に述べたように - セッションの方が保存できるデータ型が多い - セッションはサーバー側に保存されるのでユーザーに干渉を受けない などの違いはありますが, **「Cookieとどう違うの?」** という質問自体に少し語弊があります.というのも,<ins>セッションはCookieのもとに成り立っている</ins>からです.サーバー側にデータを保存したところで,それが誰のものかが分からないと意味がないですよね.これをCookieによって判別するわけですが,デフォルトでは **`'PHPSESSID'`** という名前のCookieを使って判定することになっています.以下のような処理が為されます. 1. ユーザーが初めてページにアクセスしてくる. 2. [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数が初めてコールされ,このときに<br />`Set-Cookie: PHPSESSID=d41d8cd98f00b204e9800998ecf8427e; path=/`<br />のように他と重複しないランダムな値が自動生成されてユーザー側に送られる. 3. スクリプト内で `$_SESSION` に(必要があれば)代入などを行う. 4. スクリプト終了時, セッションファイル `sess_d41d8cd98f00b204e9800998ecf8427e` に, `$_SESSION` の内容が復元可能な形で自動的に書き込まれる. ここからは2回目のアクセスです. 1. ユーザーが2回目にページにアクセスしてくる. 2. [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数がコールされると,まず<ins>最初に</ins>**`$_COOKIE['PHPSESSID']`**<ins>が存在しているか</ins>のチェックが行われ,それを元に該当する名前のセッションファイルが存在しているかどうかを調べる. 3. 存在している場合に限り,セッションファイル `sess_d41d8cd98f00b204e9800998ecf8427e` からデータが復元され, `$_SESSION` が準備される. 4. スクリプト内で `$_SESSION` に(必要があれば)代入などを行う. 5. スクリプト終了時, セッションファイル `sess_d41d8cd98f00b204e9800998ecf8427e` に `$_SESSION` の内容が復元可能な形で自動的に書き込まれる. 以降は繰り返しです. **2016/07/23追記** 図を用いた分かりやすい記事があったので引用します - [PHPで確認するクッキーとセッションメモ - 訪問回数のカウント](http://qiita.com/kure/items/8539532ac097bf93fd24) ## セッションファイルってどこにどんな名前で保存されてるの? セッションを識別するための3つの概念を述べます. - セッション用Cookieの名前 - セッション用Cookieの値 - セッションファイルの保存パス これらは,以下の何れかの方法で設定または取得することができます. - php.iniの編集 - [ini_set](http://www.php.net/manual/ja/function.ini-set.php) 関数または [ini_get](http://www.php.net/manual/ja/function.ini-get.php) 関数 - [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数の第1引数 (PHP7.0以降のみで使えるオプション) - 専用の関数 ### セッション用Cookieの名前 ```php:これらのうちいずれかを使う // 取得 $path = session_save_path(); $path = ini_get('session.save_path'); // 設定 session_save_path('/tmp'); ini_set('session.save_path', '/tmp'); session_start(['save_path' => '/tmp']); // PHP7.0以降 ``` - 取得は常に可能です. - 設定は [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数をコールする **前** でなければなりません. - デフォルト値は空であり,その場合 [sys_get_temp_dir](http://www.php.net/manual/ja/function.sys-get-temp-dir.php) 関数の返り値で取れる,システム既定のテンポラリディレクトリが使われます. ### セッション用Cookieの名前 ```php:これらのうちいずれかを使う // 取得 $name = session_name(); $name = ini_get('session.name'); // 設定 session_name('MYSESSID'); ini_set('session.name', 'MYSESSID'); session_start(['name' => 'MYSESSID']); // PHP7.0以降 ``` - 取得は常に可能です. - 設定は [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数をコールする **前** でなければなりません. - デフォルト値があらかじめ **`'PHPSESSID'`** として設定されています. ### セッション用Cookieの値 ```php // 取得 $id = session_id(); // 設定 session_id('vmtflhhks040679m9rq6044ps0'); ``` - 取得は [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数をコールした **後** でなければなりません.コールする前には(設定を行っていなければ) **空文字列** が返されます. - 設定は [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数をコールする **前** でなければなりません. - 設定を行えば, [session_start](http://www.php.net/manual/ja/function.session-start.php) コール時に強制的にそれをセッションIDとして利用させることが出来ます. - 設定を行わなければ, [session_start](http://www.php.net/manual/ja/function.session-start.php) コール時に **`$_COOKIE[session_name()]`** の値が利用されます. 以上を踏まえて,セッションファイルが保存されているパスは以下のように表現できるでしょう. ```php $path = session_save_path() . '/sess_' . $_COOKIE[session_name()]; ``` [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数をコールした **後** であれば ```php $path = session_save_path() . '/sess_' . session_id(); ``` とすることも出来ます. ## 他のユーザーと競合しないの? 上で述べたとおり, `$_SESSION` は<ins>セッションIDごとに用意される</ins>ので,競合する心配はありません.但し, - 悪意のあるユーザーが何らかの手段を使って他のユーザーのセッションIDを **盗み出し** , `Cookie: PHPSESSID=セッションID` として送信した - スクリプト内で [session_id](http://www.php.net/manual/ja/function.session-id.php) 関数を使ってセッションIDを **固定して** いる これらに該当する場合は複数のユーザーでセッションが共有されることになるでしょう. ## 多重実行で自分自身と競合しないの? セッションファイルは [session_start](http://www.php.net/manual/ja/function.session-start.php) 実行からスクリプト終了時まで **排他ロック** されるので,競合の心配はありません.2回目以降のアクセスでまだ1回目の処理が終わっていないとき, [session_start](http://www.php.net/manual/ja/function.session-start.php) が記述されている行で<ins>1回目の処理が終わるまで待機し続けます</ins>. ## 同一サーバー内にある他のサイトのセッションと競合しないの? Cookieの項目で > ##### `$path` > どこの階層から送信を行うかを表したパス.省略した場合は **スクリプトが存在しているディレクトリ** を指定したと見なされる. と紹介しましたが,先ほどのセッションの項目では > [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数が初めてコールされ,このときに<br />`Set-Cookie: PHPSESSID=d41d8cd98f00b204e9800998ecf8427e; path=/`<br />のように他と重複しないランダムな値が自動生成されてユーザー側に送られる. として説明しました.これに違和感を感じられたのであればあなたは鋭い目をお持ちです.<ins>セッション用Cookieのデフォルトの</ins>**`$path`**<ins>は</ins>**`'/'`**<ins>となっています.</ins> これでも,`shopping.example.com`と`search.example.com`のようにサブドメインで分けてある場合は特に問題はありません.しかし, `example.com/shopping` `examle.com/search` のように**パスで分けてある場合**には衝突してしまいます. これを解決するためには,Cookieが利用するパスを変えておく必要があります. ```php:Cookieのパスを変更する ini_set('session.cookie_path', '/foo/bar/baz'); ``` ## 有効期限ってどうなってるの? 有効期限と一口にいっても, **「サーバー側のセッションファイルの有効期限」** と **「ユーザー側のクッキーファイルの有効期限」** の2種類が存在します. ### サーバー側のセッションファイルの有効期限 次のエントリに非常に詳しい説明があります.以下,このエントリの内容を更に噛み砕いて掲載します. - [Pentan.info - セッションの有効期間とか設定とか挙動とかを調べました](http://pentan.info/php/session_gc.html) > `session_start()` が行われたときに, `session.gc_probability` を分子, `session.gc_divisor` を分母とする確率で, `session.gc_maxlifetime` よりファイル更新日付の古いファイルを `session.save_path` から削除します. この個々の値はphp.iniを編集して恒久的に変更,または [ini_set](http://www.php.net/manual/ja/function.ini-set.php) 関数をコールして一時的に変更することが出来ます.設定は,[session_start](http://php.net/manual/ja/function.session-start.php) の実行よりも前にする必要があることに注意してください. ```php:これらのうちいずれかを使う // どのバージョンでも使える書き方 ini_set('session.gc_maxlifetime', 60 * 60 * 24 * 7); // PHP7.0以降で使える書き方 session_start(['gc_maxlifetime' => 60 * 60 * 24 * 7]); ``` デフォルトでは次のような挙動になります. - 有効期限は<ins>最終アクセスから</ins> **24分間** (1440秒) となる. - 有効期限の過ぎたセッションファイルを毎回の実行ごとに検索して削除させるのは負荷がかかるので,デフォルトでは $\frac{1}{100}$ の確率でこの処理を行うようになっている.つまり有効期限切れのセッションファイルが発生してもすぐには削除が行われず,ある程度たまった段階でまとめて削除が行われるということを意味する. - 有効期限の過ぎたセッションファイルにアクセスがあった場合,有効期限が<ins>再度延長される</ins>.つまり有効期限切れのセッションファイルはその時点で無意味なものになるのではなく,単にいつ削除されても構わない状態になるということを意味する. なお,この有効期限によるセッションファイルの削除は,サーバーごとに行われます.つまり,共用サーバーでは他のサイトの影響を受ける可能性があるということです.この問題を回避したい場合,有効期限の変更に加えて, [session_save_path](http://www.php.net/manual/ja/function.session-save-path.php) 関数または [ini_set](http://www.php.net/manual/ja/function.ini-set.php) 関数で自分専用のディレクトリを設けてください. ### ユーザー側のクッキーファイルの有効期限 以下のいずれかを実行します.設定は,[session_start](http://php.net/manual/ja/function.session-start.php) の実行よりも前にする必要があることに注意してください. ```php:これらのうちいずれかを使う // どのバージョンでも使える書き方 ini_set('session.cookie_lifetime', 60 * 60 * 24 * 7); // PHP7.0以降で使える書き方 session_start(['cookie_lifetime' => 60 * 60 * 24 * 7]); ``` デフォルト値は **`0`** **(ブラウザを閉じるまで)** となっています.それ以外の場合,有効期限は<ins>最初のアクセスから</ins>指定した期間までとなります.後ほど紹介しますが,再アクセスで自動延長したい場合には少し工夫が必要です. 【注意】 [setcookie](http://www.php.net/manual/ja/function.setcookie.php) 関数の **`$expire`** の表記の違いに注意してください.こちらは秒数ではなく**タイムスタンプ**での指定となっております.   ### どのように2つの有効期限が関連してくるか? 問題が発生する2通りのケースを考えてみましょう. #### <font color="silver">サーバー側にセッションファイルが残っているのに,セッション用のCookieが消滅してしまったとき</font> 単純に,サーバー側のセッションファイルが **24分間** の期限と $\frac{1}{100}$ の確率の削除を待つだけです. #### <font color="silver">セッション用のCookieが送信されているのに,サーバー側のセッションファイルが既に削除されていたとき</font> 送信されてきたセッションIDを元に,セッションファイルが再生成されます.また,もとから無効であった場合にもセッションファイルの生成は行われます.後者は特に **セッションアダプション** と呼ばれ,場合によっては脆弱性という扱いを受けます. この挙動を変更することが出来るのがphp.iniの [session.use_strict_mode](http://www.php.net/manual/ja/session.configuration.php#ini.session.use-strict-mode) ディレクティブです.デフォルトでは無効ですが,これを有効にすることで,無効なセッションIDであると判明した場合にセッションIDの再生成が行われ,ユーザーに向けて **Set-Cookieヘッダー** が新たに送信されます.ただし,<ins>このディレクティブにはほとんどセキュリティ対策の意味はありません</ins>.PHP7.1にて[デフォルトで有効するというRFC](https://wiki.php.net/rfc/session-use-strict-mode)もありましたが,賛成4対反対4で却下されています. この機能が存在しないPHP5.5.1よりも古い環境で,手動で「セッションファイルが存在するか」を調べてアダプションを検知し,対策することは可能です.しかしこの対策はあまり意味が無いので,特に拘りが無ければ最初のままでいいかもしれません. ```php:アダプション検知を付加したsession_start関数 (ついでにsession_start関数が吐きうるエラーに@演算子無しで対策している) function safe_session_start($name = null) { if ($name !== null) { session_name($name); } $name = session_name(); if ( isset($_COOKIE[$name]) and !ctype_alnum($_COOKIE[$name]) || !is_file(session_save_path() . '/sess_' . $COOKIE[$name]) ) { unset($_COOKIE[$name]); } return session_start(); } ``` ## 有効期限を長くするにはどうすればいいか? 上で紹介した2つの有効期限を同等に設定すれば可能です. ```php:有効期限を1週間にする例 // どのバージョンでも使える書き方 ini_set('session.gc_maxlifetime', 60 * 60 * 24 * 7); ini_set('session.cookie_lifetime', 60 * 60 * 24 * 7); session_start(); // PHP7.0以降で使える書き方 session_start([ 'gc_maxlifetime' => 60 * 60 * 24 * 7, 'cookie_lifetime' => 60 * 60 * 24 * 7, ]); ``` 但し,これでは<ins>再アクセスによるCookieの有効期限の延長は行われません</ins>.これを実現したければ,自前で **Set-Cookieヘッダー** を送信させる必要があります.以下の記事でこれを実現した関数を紹介しています. - [Qiita - ぼくのかんがえたさいきょうのsession_start](http://qiita.com/mpyw/items/7f4772e4d4d360fc100c) ## 実はCookie以外にもセッションIDを受け取る手段がある!? 今までセッションについて,Cookieで個人を判別するものとして話を進めてきましたが,実は他にも手段があります.以下,優先順位の高い順に述べます.なお,ここで登場するphp.iniのディレクティブのうち,`session.*` のものは全て [ini_set](http://www.php.net/manual/ja/function.ini-set.php) 関数で変更可能なものです. ### 1. `$_COOKIE` - [session.use_cookies](http://www.php.net/manual/ja/session.configuration.php#ini.session.use-cookies) が有効な場合のみ採用されます.デフォルトは有効です. - ユーザー側に **Set-Cookieヘッダー** が自動的に送信されるようになります. ### 2. `$_GET` - [session.use_only_cookies](http://www.php.net/manual/ja/session.configuration.php#ini.session.use-only-cookies) が無効な場合のみ採用されます.デフォルトは<ins>PHP5.2以前では無効</ins>,PHP5.3以降では有効です. - 以下の条件を全て満たしたとき,HTML中に存在するURLに対してクエリが自動で付加されます. - [session.use_trans_sid](http://www.php.net/manual/ja/session.configuration.php#ini.session.use-trans-sid) が有効である. - 出力バッファリングを行っていなければならない.つまり [ob_get_level](http://www.php.net/manual/ja/function.ob-get-level.php) 関数の返り値が1以上でなければならない. [output_buffering](http://www.php.net/manual/ja/outcontrol.configuration.php#ini.output-buffering) が無効な場合は最初に [ob_start](http://www.php.net/manual/ja/function.ob-start.php) 関数をコールしておく必要がある. - 相対URL(絶対パス・相対パス)でなければならない.外部サイトに送信してしまうリスクがあるため,<ins>絶対URLには付加されない</ins>. - 手動で付加する場合には,定数 **`SID`** を使用します.`'PHPSESSID=xxxxx'` のような値が自動的に設定されています. …とはいっても,PHP5.3以降デフォルトで使われるのは `$_COOKIE` 1つだけです.Cookieを使えないガラケーなんてもはやこの世に存在するかどうか怪しいですが,古代の端末向けにWebサイトを作らなければならない地雷案件があった場合に,設定を変更した上で他の手段を使うことがあるかもしれません. 3番以降の続きは[付録](#%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E5%80%8B%E4%BA%BA%E8%AD%98%E5%88%A5%E7%94%A8%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E5%AE%8C%E5%85%A8%E7%89%88-%E5%84%AA%E5%85%88%E5%BA%A6%E9%A0%86)に載せておきます.2番までは地雷案件で使うことがあったとしても,3番以降は本当に皆無でしょう… ## 「セッション固定攻撃」って何?どうやって対策するの? ### 攻撃対象 この攻撃手法においては,ログインする前から [session_start](http://www.php.net/manual/ja/function.session-start.php) 関数によりセッションが開始されているサイトが対象となります.多くのサイトがこの実装になっていると思います. ```php:ログイン前からセッションを開始してログイン状態のチェックを行っている例 session_start(); if (isset($_SESSION['logined'])) { .... } ``` 以下に攻撃を受けるケースを述べます. #### <font color="silver">Cookie以外からのセッションIDを受け入れる場合</font> 1. 攻撃者のあなたはログインフォームにアクセスして,発行された自分用のセッションID `PHPSESSID=xxxxx` を入手します. 2. `http://example.com/login.php?PHPSESSID=xxxxx` というURLをメールやTwitterなどの手段を使ってターゲットに踏ませます.この段階で,あなたとターゲットのセッションIDが共通なものになります.つまりサーバー側から見れば,<ins>あなたのWebブラウザとターゲットのWebブラウザが同じものと見なされます</ins>. 3. ターゲットがログイン状態になれば,あなたもターゲットのアカウントでログイン状態になります.攻撃成功です. #### <font color="silver">Cookie以外からのセッションIDを拒否しているが,XSS脆弱性がある場合</font> セキュリティコンサルタントをされている徳丸浩さんのTumblr記事で分かりやすい例が紹介されています.要はJavaScriptを実行できれば,クエリを踏ませなくてもセッションIDを共通なものに設定することが可能だということです.先ほど, > ただし,<ins>このディレクティブにはほとんどセキュリティ対策の意味はありません</ins>. とした理由もここで分かります. - [徳丸浩のtumblr - セッションアダプションがなくてもセッションフィクセイション攻撃は可能](http://tumblr.tokumaru.org/post/37676352092/session-adoption-and-session-fixation) ### 対策方法 ではどうやって対策すればいいか?答えは単純明快,ログイン直後に次のコードを実行するだけです. ```php:PHP5.1以降 (オプションで古いセッションファイルを自動的に削除可能) session_regenerate_id(true); ``` [session_regenerate_id](http://php.net/manual/ja/function.session-regenerate-id.php) 関数は,セッションIDを新規生成したものに乗り換える関数です.この関数は非常に重要なので,初めて見られた方は必ず覚えておいてください.上記のコードを実行した場合,以下の動作が行われます. 1. セッションファイルを新しい名前にしてコピーする 2. 古いセッションファイルを削除する 3. ユーザーに新たに **Set-Cookieヘッダー** を送信する 要は,ログインしたタイミングで<ins>新しいセッションに乗り換え</ins>れば,誰かに目をつけられていてもそこで振り切れるということです.**新しくセットした認証情報は古いセッションファイルには書き込まれない**ので,必ずしもオプションの指定が必須というわけではないですが,`true`を指定しておくほうが一般的です。 なおこの関数を全てのページに使っていると,当然サーバーに対してものすごく負荷がかかる上にセッションの引継ぎがうまくいかない問題も発生してくるようです.<ins>ログイン直後</ins>に限定するようにしましょう. ## セッションを破棄させるにはどうしたらいいか? 次のエントリに易しい説明があります. - [プログラマはサイコロを振らない - PHPでセッションを完全に破棄する方法](http://d.hatena.ne.jp/Kappuccino/20080726/1217049706) ここではスポーツクラブの比喩が用いられていますが,完全に理解にするにはこの比喩はかえって妨げになると感じたので,事実のままに述べることにします.なお,セッション変数の特定のインデックスのみを削除したい場合は単純に ```php unset($_SESSION['key']); ``` としてください.以下,セッション変数全体を空にする操作についてのみ言及します. ### (A) `$_SESSION` に空配列を代入する ```php $_SESSION = []; ``` サーバー側のメモリー上に存在している **`$_SESSION`** に格納された情報を全て削除します. - <ins>現在のスクリプト実行が終了するまで</ins>の間はディスク上に存在している **セッションファイル** に書き込まれている情報はリセットされません. - ユーザー側の **セッション用のCookie** には何の影響も与えません. ### (B) [session_destroy](http://www.php.net/manual/ja/function.session-destroy.php) 関数をコールする ```php session_destroy(); ``` サーバー側のディスク上に存在している **セッションファイル** 自体を強制的に削除します. - <ins>現在のスクリプト実行が終了するまで</ins>の間はメモリー上に存在している **`$_SESSION`** が保持している情報はリセットされません. - ユーザー側の **セッション用のCookie** には何の影響も与えません. ### (C) 有効期限を過去にして [setcookie](http://www.php.net/manual/ja/function.setcookie.php) 関数をコールする ```php setcookie(session_name(), '', 1, '/'); ``` ユーザー側のセッション用のCookieを破棄させます. **`$path`**<ins>を指定する必要がある</ins>ことに注意してください. - メモリー上に存在している **`$_SESSION`** が保持している情報には何の影響も与えません. - ディスク上に存在している **セッションファイル** に書き込まれている情報には何の影響も与えません. **A**, **B**, **C** それぞれの役割を明確にして箇条書きにしてみました.これを全部行っておけば万全なのですが,全て行う必要があるかどうかと言えばそうではありません. ### Aだけを行った場合 - ディスク領域的には問題ないですが,0バイトのセッションファイルが残ります. - 甲さんがログアウトした後すぐに乙さんが同じWebブラウザからログインしてきた場合,甲さんが使っていたセッションファイルに乙さんの情報が上書きされます.甲さんが自分のセッションIDを控えていた場合,乙さんになりすますことが出来ます.セッション固定攻撃の場合も同様です.但し,ログイン直後に <ins>[session_regenerate_id](http://www.php.net/manual/ja/function.session-regenerate-id.php) 関数をコールしている場合には安全</ins>です. ### Bだけを行った場合 - スクリプト終了時までに再度 `$_SESSION` にアクセスするようなコードを書いていた場合,アプリケーションが誤作動する可能性があります. - 甲さんがログアウトした後すぐに乙さんが同じWebブラウザからログインしてきた場合,甲さんが使っていたセッションファイルと同じファイル名で乙さんの情報が保存されます.甲さんが自分のセッションIDを控えていた場合,乙さんになりすますことが出来ます.セッション固定攻撃の場合も同様です.但し,ログイン直後に <ins>[session_regenerate_id](http://www.php.net/manual/ja/function.session-regenerate-id.php) 関数をコールしている場合には安全</ins>です. ### Cだけを行った場合 - スクリプト終了時までに再度 `$_SESSION` にアクセスするようなコードを書いていた場合,アプリケーションが誤作動する可能性があります. - 不要になったセッションファイルが削除されるまでの間無駄にディスク領域を圧迫してしまうことになります. - **A** , **B** のように共用パソコンでのトラブルは防げますが,これだけではセッション固定攻撃までは防げません.但し,そもそもセッション固定攻撃を成立させなくしてしまえばいいので,やはりログイン直後に <ins>[session_regenerate_id](http://www.php.net/manual/ja/function.session-regenerate-id.php) 関数をコールしている場合には安全</ins>です. 極論を言います. [session_regenerate_id](http://www.php.net/manual/ja/function.session-regenerate-id.php) 関数のコールを行ってしまえば,後はどうしようと安全なのです.お好みで選択されても構わないし,万全を期して全部行っておいても問題ないです.個人的には, **A** , **B** は両方行い, **C** は行わなくてもよいという意見です. 逆に,セッションを破棄させる上で**やってはいけないこと**を以下に挙げてみます. ### (D) シンボルテーブルから `$_SESSION` を削除する ```php unset($_SESSION); ``` そのスクリプト実行中にはスーパーグローバル変数の<ins>セッションに関わる機能を全て使えなくしてしまいます</ins>.但し,セッションの情報が消失するわけではありません.次回のスクリプト実行時には復活します. ### (E) `$_SESSION` という変数自体が配列以外になるように操作を加える ```php $_SESSION = null; ``` このケースは **D** と類似しているように思われますが,厳密には異なります.これを行った後に通常の代入操作を行う,例えば ```php $_SESSION = null; $_SESSION['foo'] = 'bar'; ``` とすると,セッションの機能は復活します.但し,それまでにあった情報は失われます. ```php $_SESSION = null; $_SESSION = ['foo' => 'bar']; ``` のように書いた方が分かりやすいかもしれません. ## 「CSRF攻撃」って何?どうやって対策するの? 以下の記事の4章で具体例を交えて説明しています. - [Qiita - 【PHP初心者向け】セキュアな掲示板を最小構成から作る](http://qiita.com/mpyw/items/2c54d0ea95423bd88f60) リンク先の記事の中で > 原理や対策方法について詳しく説明すると難解になってくるので,ここでは対策方法だけを簡単にまとめておきます.興味のある方は各自調べてみてください. と述べていますが,本記事を最後まで読まれた方なら自然と理解が進むと思います.対策の内容をまとめると,**「ページを表示している本人しか知らないセッションIDを元にした値を埋め込んでおくことで,本人の意図通りにフォームが送信されたかどうかわかる」**というところです. (但し,XSSなど別の脆弱性がある場合にはCSRF対策は機能しない) -------- -------- # 付録 ## リクエストパラメータのパース規則 ### `$_GET` `$_POST` `$_COOKIE` - 1つの要素は **`キー=値`** という構造を取る. - 同じキーが複数回現れた場合,後に出現したものが優先される. - **`キー`** または **`キー=`** とした場合,値は **空文字列** であると見なされる. - キーに存在する **`.`** は **`_`** に置換される. - キーが **空文字列** であるものは無視される. - [パーセントエンコーディング](http://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%82%BB%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0) に関して,デコード可能な部分はデコードし,不可能な部分はそのまま受け取る.つまり<ins>可能な限りデコードしようとする</ins>.キーと値どちらに対しても同様. - **`+`** はパーセントエンコーディングの `%20` と同様に **半角スペース** に置換される. - データ型は **文字列** と **配列** の2通りしかない. - **`[]`** をキーに添えることで **配列** が表現できるが,開きカッコと閉じカッコの数が合わない場合,以下の処理のどれかが適用される.バージョンによって挙動が異なる可能性があり,個人的には未定義として扱ったほうがいいように思うので詳細は割愛する. - 外側を優先してペアとしてパースし,残った内側の **`[`** を伴う部分は通常の文字列として扱う. - 無視される. - **`_`** に置換される. ### `$_GET` - 区切り文字には **`&`** が使われる. - パーセントエンコードされていない **半角スペース** と **改行** と **NULLバイト** の混入は一切認められない. - 他にも使用不可能な文字がいくつかあるが紹介は割愛する.基本的にパーセントエンコードが必須だと思ったほうがいい. ```text:デコード前 (見やすいように改行を入れています) raw_text=あ& encoded_text=%E3%81%82& raw_percent=%& encoded_percent=%25& raw_equals===& encoded_equals=%3D%3D& raw_array[]=value& encoded_array%5B%5D=value& assoc[key]=value ``` ```php:デコード後 (代入のイメージ) $_GET = [ 'raw_text' => 'あ', 'encoded_text' => 'あ', 'raw_percent' => '%', 'encoded_percent' => '%', 'raw_equals' => '==', 'encoded_equals' => '==', 'raw_array' => [ 0 => 'value', ], 'encoded_array' => [ 0 => 'value', ], 'assoc' => [ 'key' => 'value', ], ]; ``` ### `$_POST` - 区切り文字には **`&`** が使われる. - パーセントエンコードされていない **半角スペース** と **改行** と **NULLバイト** の混入は<ins>認められる</ins>.但し… - キーに存在する **半角スペース** のみ **`_`** に置換される. - キーに存在する **NULLバイト** のみ終端文字として扱われる **(<ins>手前で切り捨てられる</ins>)** . ```text:デコード前 (改行は実際に入れます) A = B & C = D &E = F ``` ```php:デコード後 (代入のイメージ) $_POST = [ "A_" => " B ", "\nC\n" => " D\n", "E\n_" => " F", ]; ``` ### `$_COOKIE` - 区切り文字には **`;`** が使われる. - Studying HTTP に以下のような記述があったが,手元の *Apache/2.4.4 (Win32) OpenSSL/0.9.8y PHP/5.4.16* で試したみたところ非対応のようであった. > **RFC 2109** のクッキーでは, **`;`** の他に **`,`** も使用できるようになりました.サーバは将来の互換性のために区切り子として **`,`** も受け入れるべきです. - パーセントエンコードされていない **半角スペース** の混入は<ins>認められる</ins>.但し… - **「`Cookie:`または`;`」** ~ **「半角スペース以外の文字」** の間の半角スペースは無視される. - **「半角スペース以外の文字」** ~ **`=`** の間の半角スペースは **`_`** に置換される. - パーセントエンコードされていない **改行** と **NULLバイト** の混入は一切認められない. ```text:デコード前 Cookie: A = B ; C = D ; . = . ; E ; F ``` ```php:デコード後 (代入のイメージ) $_COOKIE = [ 'A_' => ' B ', 'C_' => ' D ', '__' => ' . ', 'E_' => '', 'F' => '', ]; ``` ## 使ってはいけないセッション関数!? ### [session_register](http://www.php.net/manual/ja/function.session-register.php) 関数 PHPの歴史のお話をします.実は太古のPHP4.0まではスーパーグローバル変数が実装されておらず,以下のような変数を使う必要がありました.これらはただグローバルスコープに存在している変数であり,任意のスコープからアクセスすることができる変数ではありませんでした. ```php $HTTP_GET_VARS $HTTP_POST_VARS $HTTP_COOKIE_VARS $HTTP_SESSION_VARS ``` これらはスーパーグローバルでない上に変数名が非常に長く,かなり使い勝手が悪いです.そこで,PHP5.3まではリクエストされた配列をローカル変数として自動展開する,つまり ```php extract($HTTP_COOKIE_VARS, EXTR_SKIP); extract($HTTP_POST_VARS, EXTR_SKIP); extract($HTTP_GET_VARS, EXTR_SKIP); ``` に該当する処理をPHPスクリプト実行開始時にグローバルスコープで自動的に行う [register_globals](http://php.net/manual/ja/security.globals.php) という機能が存在していました.前者3つに関してはリクエストを送る<ins>ユーザー側が展開する変数を決定する</ins>ことになっていました.これは非常に危険な機能であるとの批判が集中したため,PHP5.4で削除されています. セッションに関しては [session_register](http://www.php.net/manual/ja/function.session-register.php) 関数を使って<ins>プログラマ側が手動登録して参照状態にする</ins>仕組みになっていました.つまり,指定したものに関してだけ ```php extract($HTTP_SESSION_VARS, EXTR_OVERWRITE | EXTR_REFS); ``` が行われるということです.いくらは register_globals よりはマシですが,これも設計上好ましくないという理由で,PHP5.4にて削除されています.したがって,これらの関数は使ってはいけない,というか使うことができません. ### [session_set_cookie_params](http://php.net/manual/ja/function.session-set-cookie-params.php) 関数 この関数はセッション用Cookieの挙動を制御する関数ですが,たとえば<ins>第3引数の値を設定するためには第1引数と第2引数も渡さなければならない</ins>という致命的な欠陥があり,非常に使い勝手が悪いです. ```php:httponlyを有効にする (session_set_cookie_paramsを使う場合) // 冗長 $p = session_get_cookie_params(); session_set_cookie_params($p['lifetime'], $p['path'], $p['domain'], $p['secure'], true); // 多少はマシ $p = session_get_cookie_params(); $p['httponly'] = true; call_user_func_array('session_set_cookie_params', $p); // PHP5.6以降,array_valuesがいるのがつらい $p = session_get_cookie_params(); $p['httponly'] = true; session_set_cookie_params(...array_values($p)); ``` 記事中で紹介しているとおり, [ini_set](http://php.net/manual/ja/function.ini-set.php) などを用いる方法で十分です. ```php:httponlyを有効にする (ini_setを使う場合) ini_set('session.cookie_httponly', true); ``` ### [session_unset](http://www.php.net/manual/ja/function.session-unset.php) 関数 次の2つの操作は等価ですが,後者の方を利用するようにかつてマニュアルが推奨していました. ```php session_unset(); ``` ```php $_SESSION = []; ``` 今は方針が変わっているみたいですが,敢えて関数を利用する必要はないかと思います. ## セッションの個人識別用パラメータ完全版 (優先度順) ### 1. `$_COOKIE` - [session.use_cookies](http://www.php.net/manual/ja/session.configuration.php#ini.session.use-cookies) が有効な場合のみ採用されます.デフォルトは有効です. - ユーザー側に **Set-Cookieヘッダー** が自動的に送信されるようになります. ### 2. `$_GET` - [session.use_only_cookies](http://www.php.net/manual/ja/session.configuration.php#ini.session.use-only-cookies) が無効な場合のみ採用されます.デフォルトは<ins>PHP5.2以前では無効</ins>,PHP5.3以降では有効です. - 以下の条件を全て満たしたとき,HTML中に存在するURLに対してクエリが自動で付加されます. - [session.use_trans_sid](http://www.php.net/manual/ja/session.configuration.php#ini.session.use-trans-sid) が有効である. - 出力バッファリングを行っていなければならない.つまり [ob_get_level](http://www.php.net/manual/ja/function.ob-get-level.php) 関数の返り値が1以上でなければならない. [output_buffering](http://www.php.net/manual/ja/outcontrol.configuration.php#ini.output-buffering) が無効な場合は最初に [ob_start](http://www.php.net/manual/ja/function.ob-start.php) 関数をコールしておく必要がある. - 相対URL(絶対パス・相対パス)でなければならない.外部サイトに送信してしまうリスクがあるため,<ins>絶対URLには付加されない</ins>. - 手動で付加する場合には,定数 **`SID`** を使用します.`'PHPSESSID=xxxxx'` のような値が自動的に設定されています. ```html+php:絶対URLに手動で付加する例 <a href="http://example.com/test.php?<?=htmlspecialchars(SID, ENT_QUOTES, 'UTF-8')?>">Test</a> ``` ### 3. `$_POST` - [session.use_only_cookies](http://www.php.net/manual/ja/session.configuration.php#ini.session.use-only-cookies) が無効な場合のみ採用されます.デフォルトは<ins>PHP5.2以前では無効</ins>,PHP5.3以降では有効です. - 送信は全て手動で行う必要があります. ### 4. `$_SERVER['REQUEST_URI']` `http://example.com/PHPSESSID=xxxxx/test.php` のように自由な形で埋め込むことを許可します. - [session.use_only_cookies](http://www.php.net/manual/ja/session.configuration.php#ini.session.use-only-cookies) が無効な場合のみ採用されます.デフォルトは<ins>PHP5.2以前では無効</ins>,PHP5.3以降では有効です. - 送信は全て手動で行う必要があります. ### 5. `$_SERVER['HTTP_REFERER']` リファラーに対しても2と4に関するチェックが行われます. - [session.use_only_cookies](http://www.php.net/manual/ja/session.configuration.php#ini.session.use-only-cookies) が無効な場合のみ採用されます.デフォルトは<ins>PHP5.2以前では無効</ins>,PHP5.3以降では有効です. - [session.referer_check](http://www.php.net/manual/ja/session.configuration.php#ini.session.referer-check) にURLに含まれるべき自サイト特有の文字列を指定します.こうすることで外部サイトのリファラーと区別することが出来ます.デフォルトは空文字列であり,このディレクティブは事実上無効となっています.<ins>このディレクティブにはほとんどセキュリティ対策の意味はありません</ins>. |
|
| 273位 |
|
|||
|
23:17:17 |
(ShouldBee 所属) |
|
ちょっと大げさなタイトルですが、あくまで私個人の環境構築手順をまとめたものです。WindowsからMacに乗り換えたばかりで、どうやってPHPの開発環境を整えるか分からないPHPerに向けの「開発環境を整えるためのガイド」と考えてください。
既にMacでAMP環境を構築したことがあるPHPerにとっても有用な情報を含めたつもりです。その例としては、php-buildとphp-versionで複数PHPバージョン環境の構築の手順、複数PHPバージョンとApacheの連携方法などがあります。 MacをUnix的に使えるようにするため、CUIで設定する手順を含んでいますが、怖がらずにやってみてください。ターミナルはあなたに噛み付いたりしません :) CUIに慣れてない方は、「難しい」と感じるかもしれませんが、きっとのちのちの財産になるはずです! 2012年版では「PHPerがMacbookAirを買ったら直ぐにすること」の[2011年版](http://suin.asia/2011/03/06/macbookair_kick_start_for_phper)をベースに、トレンドを取り入れたり、導入方法が変わっているものについては手順を修正しました。昨年版で紹介したMacPortsはHomebrewに置き換えました。またXcodeはAppStoreで入手できるようになったため手順を改めました。 ## PHPerがMacbookAirを買ったら直ぐにすること 1. ローカルアカウントの作成 1. Xcodeのダウンロード&インストール 1. エディター/IDEのダウンロード&インストール 1. ブラウザのダウンロード&インストール 1. 共有の設定 1. ターミナルのセットアップ 1. Homebrewのダウンロード&インストール 1. Apache + MySQL + PHP のセットアップ 1. SSHのセットアップ # ローカルアカウントの作成 「アカウント名」にコダワリのある人は注意して入力しましょう。アカウント名はユーザディレクトリのディレクトリ名になります。後で変えるのはめんどくさいです。私はWeb上のハンドルネーム suin を必ず入力します。 # Xcodeのダウンロード&インストール XcodeはiOS/Macアプリを開発するためのツールですが、iOS/Macアプリを作らないPHPerにとっても便利な開発ツールが詰まってるのでインストールしておくことをお勧めします。 XcodeはMac App Storeから無料でダウンロードできます。 Xcodeは容量が大きいため時間がかかるので、まず最初にダウンロードを始めておきます。ダウンロードするためにはApple IDが必要なのであらかじめ作っておきます。 # エディター/IDEのダウンロード&インストール 自分がよく使うエディタがあればそれを入れます。Macを始めたばかりでどんなエディタがあるかよく分からない人は、とりあえず [CotEditor](http://sourceforge.jp/projects/coteditor/)、[Coda](http://www.panic.com/jp/coda/)、[TextMate](http://macromates.com/)、[MacVim](http://code.google.com/p/macvim-kaoriya/)をすべてインストールしてみて自分に合うものを探しましょう。 PHP用のIDEとしてはPhpStromを入れておきます。 *vimを使わない人、vimがなんだか分からないは CotEditor を入れてください。このあとのチュートリアルで使います。* # ブラウザのダウンロード&インストール 主要なブラウザはこの際、いれておきましょう。 * [Firefox](http://mozilla.jp/firefox/) * [Google Chrome](http://www.google.co.jp/chrome/intl/ja/landing.html) * [Opera](http://jp.opera.com/) # ユーティリティのダウンロード&インストール * [Colors](http://mattpatenaude.com/): シンプルなカラーピッカー。 * [Dropbox](https://www.dropbox.com/): オンラインストレージ。 * [Hoster](http://www.redwinder.com/macapp/hoster/): hostsファイルをGUIで設定するアプリ。 * [Sequel Pro](http://www.sequelpro.com/): MySQLのGUIクライアント。phpMyAdminより便利。 * [Skype](http://www.skype.com/intl/ja/home/): チャット・音声通話アプリ。 * [Alfred](http://www.alfredapp.com/): アプリケーションランチャー。 * [Total Terminal](http://totalterminal.binaryage.com/): ショートカットキーでTerminalを起動できる。 # 共有の設定 Dockにある「システム環境設定」から「共有」を開き、共有の設定をします。 .png) ## 「コンピュータ名」を設定 デフォルトだと「○○ ○○ の MackBook Air」となっていますが、これだと長すぎるので短く簡潔な名前に変更します。例えば、私のアカウント名はsuinなので、SuinMacbookAirのようにします。こうすることで、HTTPでMAMPにアクセスするときや、SSHでリモートログインするときに、http://suinmacbookair.local/ や ssh suin@suinmacbookair.local のようにでき、複数のマシン・グループでのウェブ開発の利便性が高まります。 ## 「ファイル共有」をONにする 他のマシンからMacbookAirの共有フォルダを参照できるようになります。 ## 「リモートログイン」をONにする SSHで他のマシンからMacbookAirにログインできるようになります。 # ターミナルのセットアップ 「ターミナル」はWindowsでいう「コマンドプロンプト」です。コマンドラインに詳しくない人でも、そのうち必ず使うようになるので、設定しておきましょう。ターミナルは「アプリケーション」→「ユーティリティ」にあります。デフォルトだと味気ないアプリなので、いろいろ設定をしてやりましょう。 .png) ## ターミナルの見た目をプロにしよう * 「アプリケーション」→「ユーティリティ」→「ターミナル」を起動する。 * メニューバーから「ターミナル」→「環境設定」を開く。 * 「起動」タブを開く。 * 「起動時に開く:次の設定の新しいウィンドウ」のセレクトボックスから「Pro」を選択する。 .png) * 「設定」タブを開く。 * 「Pro」を選択状態にする。 * 「デフォルト」をクリックし、「Pro」をデフォルトにする。 .png) * 「設定」を閉じる。 * 一旦、ターミナルを閉じ、ターミナルを開いて、Proのテーマが適用されているか確認する。 .png) ## ターミナルの設定ファイルを作ろう > と、その前に、コマンドから起動するエディタの設定をしておきます。 > > ## CotEditorの設定 > >> いきなりvimエディタを使うのが難しい方のために、CotEditorを使ってファイルを編集するようなチュートリアルになっています。vim使いの方などは ```open -a CotEditor``` の部分を ```vim``` などと読み替えていただいて構いません。 > > CotEditorを使う方は文字コードの設定を完了しておいてください。 > > CotEditorを起動する > "CotEditor" > "環境設定" > "フォーマット" タブ > > "エンコーディング" の "既存ファイル" の設定を "自動認識" から "Unicode (UTF-8)" に変更し、CotEditorを終了する。 ターミナルの設定をさらに細かくするために、次の二つのファイルを、ホームディレクトリに作ります。ホームディレクトリとは/Users/あなたのアカウント/のことです。 * .bash_profile * .bashrc まず、ターミナルを開きます。(すでに開いていると思いますが。)上のファイルを作るために次のコマンドを順に実行してください。よく分からない人はターミナルにコピペしてください。ちなみに上のファイルは不可視ファイルなのでFinderでホームディレクトリを開いても見ることができません。 ``` cd touch .bash_profile touch .bashrc ``` .png) 次に、.bash_profileをエディタで開きます。開くために次のコマンドを実行してください。 ``` open -a CotEditor .bash_profile ``` .bash_profileに次の行を追加して保存します。これは、.bashrcがあればそれを読み込むという処理です。PHPで言えば ```if(file_exists('.bashrc')) include '.bashrc';``` です。保存したらエディタを閉じます。 ```txt:.bash_profile if [ -f ~/.bashrc ]; then . ~/.bashrc fi ``` 今度は、.bashrcの設定するためにファイルを開きます。 ``` open -a CotEditor .bashrc ``` アプリケーションやコマンドへのショートカットコマンドを設定していきます。私の設定は下のようになっています。自分の好みに合わせて設定してください。よく分からなければ、そのままコピペでもいいと思います。保存したらエディタを閉じます。 ```txt:.bashrc # Application Shortcuts alias cot='open -g -a CotEditor' # ターミナルからCotEditorを開く alias coda='open -g -a Coda' # ターミナルからCodaを開く alias safari='open -g -a safari' # ターミナルからSafariを開く alias console='open -a console' # ターミナルからコンソールを開く # for shortcut alias up='cd ..' alias upp='cd ../..' alias uppp='cd ../../..' alias ls='ls -GwF' alias la='ls -alh' alias t='tar zxvf' alias t-='tar xvf -' alias b='bzip2 -dc' alias dh='df -h' alias vi='vim' alias v='vim' alias sr='screen -d -R' alias grep='grep --exclude=*.svn*' alias greprn='grep -rn --exclude=*.svn*' alias bye='sudo shutdown -h now' # for svn alias st='svn st' alias stu='svn st -u' alias sd='svn di' alias sdi='svn di' alias sad='svn add' alias sup='svn up' alias sci='svn ci' alias scim='svn ci -m' # for git alias gst='git status' alias gci='git commit' alias gdi='git diff' alias gdc='git diff --cached' alias gad='git add' ``` 設定を書き込んだばかりだと、ターミナルに反映されないので、ターミナルを再起動します(ターミナルを再起動するのが面倒な人は ```bash``` コマンドを実行してください)。上の設定をコピペした場合、Safariをとじた状態で、safariとだけタイプしてエンターを押してみます。そこでSafariが起動すれば、設定が反映されていることになります。 ## プロンプトの書式をカスタマイズしよう プロンプトとは、ターミナルの行頭に出ている.png)の部分を言います。デフォルトでも全く問題はありません。ただ、ちょっと白黒で寂しいですし、行の前後にスペースがあったほうが見やすいので、カスタマイズしてしまいます。 再び、.bashrcを開いて、編集します。 ``` open -a CotEditor .bashrc ``` ``` PS1="\n\`if [ \$? = 0 ]; then echo \[\e[32m\]^_^\[\e[0m\]; else echo \[\e[31m\]O_O\[\e[0m\]; fi\` (\h:\w)\n* " ``` 保存したら、ターミナルを再起動(または ```bash``` コマンド実行)します。プロンプトが下のようになっていれば設定成功です。 .png) ## 自分用シェルスクリプト置き場を作ろう /Users/あなたの名前/bin をシェルスクリプト置き場としてディレクトリを作ります。 ``` mkdir ~/bin ``` ディレクトリを作ったら.bashrcを修正してパスを通します。 ``` open -a CotEditor .bashrc ``` 下記を.bashrcに追加します ```text:.bashrc export PATH=~/bin:$PATH ``` 保存したら、ターミナルを再起動(または ```bash``` コマンド実行)します。 # Homebrewのダウンロード Homebrewは、Unix向けに開発されたオープンソース・ソフトウェアを手軽にインストールすることができるMacOSX用のパッケージです。「Unixで使えるあのコマンド、Macでも使いたい!」というときに簡単にコマンドをインストールすることができます。 * [Homebrew](http://mxcl.github.com/homebrew/) ターミナルで下のコマンドを実行してインストールします: ``` /usr/bin/ruby -e "$(/usr/bin/curl -fksSL https://raw.github.com/mxcl/homebrew/master/Library/Contributions/install_homebrew.rb)" ``` Homebrewのしきたりとして、インストール直後や問題が起こった時に ```brew doctor``` コマンドを叩いて状況を確認するというものがあります: ``` brew doctor ``` ```html:結果例 Warning: You have no /usr/bin/cc. This means you probably can't build *anything*. You need to install the Command Line Tools for Xcode. You can either download this from http://connect.apple.com or install them from inside Xcode's Download preferences. Homebrew does not require all of Xcode! You only need the Command Line Tools package! Warning: Git could not be found in your PATH. Homebrew uses Git for several internal functions, and some formulae use Git checkouts instead of stable tarballs. You may want to install Git: brew install git Warning: Your Xcode is configured with an invalid path. You should change it to the correct path. Please note that there is no correct path at this time if you have *only* installed the Command Line Tools for Xcode. If your Xcode is pre-4.3 or you installed the whole of Xcode 4.3 then one of these is (probably) what you want: sudo xcode-select -switch /Developer sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer DO NOT SET / OR EVERYTHING BREAKS! ``` ```/usr/bin/cc``` が無いといけないらしいので、Xcodeで *Command Line Tools* を入れます: > Xcode > Preferences > Downloads で Command Line Tools を Install する。 Xcodeのパスを設定する: ``` sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer ``` brew 本体のアップデートを行う: ``` brew update ``` Homebrewでインストールしたunixツールが使えるようにパスを通します: ``` open -a CotEditor ~/.bashrc ``` Homebrew用に ```/usr/local/sbin``` と ```/usr/local/bin``` へのパスを通すために、.bashrc に下記の2行を追加します。 ```txt:.bashrc export PATH=/usr/local/sbin:$PATH # for Homebrew export PATH=/usr/local/bin:$PATH # for Homebrew ``` ## 便利なコマンドを入れておこう 以上で HomeBrew の設定は完了です。ためしに、便利なコマンドを入れてみましょう。 [treeコマンド](http://x68000.q-e-d.net/~68user/unix/pickup?tree)や[wgetコマンド](http://x68000.q-e-d.net/~68user/unix/pickup?keyword=wget&target=command)など、あると便利なコマンドを入れておきます。ターミナルから次のコマンドを実行することでインストールできます。 ``` brew install tree brew install wget ``` # Apache + MySQL + PHP のセットアップ MacビルトインのApacheを使ってもいいですが、Macのアップデートで変更があるといやなので、HomeBrewで別途システムとは独立した形でApacheをインストールします。また、PHPは5.2, 5.3, 5.4の環境を切り替えられるように、php-buildとphp-versionをインストールします。 > Apache + MySQL + PHP 環境に特にこだわりがない方、とりあえず手っ取り早く入れたい方は、[MAMP](http://www.mamp.info/en/index.html) をインストールしてこのセクションは読み飛ばしてください。WindowsではXAMPPが有名ですが、MAMPもXAMPPのようなアプリです。 ## Apacheをインストールする Apache 2.2を扱っているFormulaを登録する: ``` brew tap Homebrew/dupes ``` httpdがインストール可能か確認する: ``` brew search httpd ``` httpdをインストールする: ``` brew install httpd ``` ## MySQLをインストールする mysqlをインストールする: ``` brew install mysql ``` mysqlのデータベースをセットアップする: ``` mysql_install_db --verbose -user=$(whoami) --basedir="$(brew --prefix mysql)" --datadir=/usr/local/var/mysql --tmpdir=/tmp ``` ## Apacheを起動してみる apachectlはApacheを制御するコマンドです。apachectlがどこにあるか確認してみます。 ``` which apachectl ``` 上の結果が、/usr/local/sbin/apachectl であればOKです。そうでなければ、/usr/local/sbin と /usr/local/bin へパスが通っていない可能性があります。.bashrcの内容を確認してください。 Apacheを起動する: ``` sudo apachectl start ``` パスワードを聞かれるので自分のアカウントのパスワードを入力します。ちなみに、```sudo``` というのは管理者(root)として実行するという意味です。Macでは1024以下のポートを開くにはroot権限が必要になります。ApacheはHTTPなので80番ですね。 うまく起動できたか、[http://localhost](http://localhost)にアクセスしてみて確認します。「It works!」と出れば成功です。 次に `httpd.conf` の設定を変更します: ``` open -a CotEditor /usr/local/etc/apache2/httpd.conf ``` ### 1. サーバー名を設定する ``` #ServerName www.example.com:80 ``` ↓ ``` ServerName localhost:80 ``` ### 2. 実行ユーザを自分にする ``` User daemon Group daemon ``` ↓ ``` User suin Group staff ``` `suin` の部分は自分のユーザ名にしてください。自分のユーザ名は `whoami` コマンドで確認できます。 ### 3. アクセス拒否を取り払う: ``` <Directory /> Options FollowSymLinks # AllowOverride None AllowOverride All # Order deny,allow # Deny from all </Directory> ``` `Order deny,allow` と `Deny from all` をコメントアウトしてください。 .htaccessを使うことがあると思うので、 `AllowOverride None` を `AllowOverride All` に変更します。 ### 4. ディレクトリにアクセスしたときに `index.php` があれば実行するようにする: ``` <IfModule dir_module> DirectoryIndex index.html </IfModule> ``` ↓ ``` <IfModule dir_module> DirectoryIndex index.php index.html </IfModule> ``` ### 5. Virtual Hostを有効化する: httpd-vhostsがコメントアウトされているので、コメントアウトを解除してください。 ``` Include /usr/local/etc/apache2/extra/httpd-vhosts.conf ``` 保存したら Apache を再起動します: ``` sudo apachectl restart ``` うまく再起動できれば設定ファイル変更OKです。 もし、起動に失敗したら、`apachectl configtest` コマンドで設定ファルの書き方に問題がないか調べてください。 ``` apachectl configtest ``` ## MySQLサーバを起動してみる 続いてMySQLサーバを起動してみます。 ``` mysql.server start ``` ```txt:出力例 Starting MySQL .. SUCCESS! ``` Apacheを起動するときは```sudo```で実行する必要がありましたが、MySQLはデフォルト3306番ポートで起動するので、rootとして実行する必要はありません。 クライアントでつないでみる: ``` mysql -u root ``` ```txt:出力例 Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 4 Server version: 5.5.20 Source distribution Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. ``` 試しに、データベース一覧を出すクエリを実行してみます: ``` mysql> SHOW DATABASES; ``` ```txt:結果例 +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | test | +--------------------+ 4 rows in set (0.00 sec) ``` mysqlクライアントを終了します: ``` mysql> quit ``` ## PHPインストールの準備 PHP関連のツールを扱っているFormulaを登録する: ``` brew tap josegonzalez/php ``` php-build, php-versionをインストールする: ``` brew install php-build brew install php-version ``` .bashrcにphp-buildのパスを追加する: ```txt:.bashrc export PHP_VERSIONS=${HOME}/local/php/versions [ -f $(brew --prefix php-version)/php-version.sh ] && source $(brew --prefix php-version)/php-version.sh && php-version 5.4.0 >/dev/null ``` PHPのコンパイルに必要なライブラリをインストールする: ``` brew install re2c brew install jpeg brew install libpng brew install mcrypt ``` PHPのインストール先ディレクトリを作成する: ``` mkdir -p $HOME/local/php/versions ``` ## PHP5.4.0をインストールする PHP5.4のconfigureパラメータを修正して、Apacheモジュールを生成するようにする: ``` open -a CotEditor /usr/local/share/php-build/definitions/5.4.0 ``` ```txt:/usr/local/share/php-build/definitions/5.4.0 configure_option "--with-apxs2" "/usr/local/sbin/apxs" install_package "http://www.php.net/distributions/php-5.4.0.tar.bz2" install_pyrus install_xdebug_master ``` PHP5.4をコンパイル&インストールする: ``` php-build 5.4.0 $HOME/local/php/versions/5.4.0 ``` ApacheのPHPモジュールを退避する: ``` mv /usr/local/opt/httpd/libexec/libphp5.so $HOME/local/php/versions/5.4.0/libphp5.so ``` ## PHP5.3.10をインストールする PHP5.3のconfigureパラメータを修正して、Apacheモジュールを生成するようにする: ``` open -a CotEditor /usr/local/share/php-build/definitions/5.3.10 ``` ```txt:/usr/local/share/php-build/definitions/5.3.10 configure_option "--with-apxs2" "/usr/local/sbin/apxs" configure_option -D "--enable-fpm" install_package "http://www.php.net/distributions/php-5.3.10.tar.bz2" install_pyrus install_xdebug "2.1.3" ``` ※ 5.3.10 では "--enable-fpm" を無効化しないと「You've configured multiple SAPIs to be build. You can build only one SAPI module and CLI binary at the same time.」というエラーが出てしまうので、`configure_option -D "--enable-fpm"` が必要になる。 PHP5.3をコンパイル&インストールする: ``` php-build 5.3.10 $HOME/local/php/versions/5.3.10 ``` ApacheのPHPモジュールを退避する: ``` mv /usr/local/opt/httpd/libexec/libphp5.so $HOME/local/php/versions/5.3.10/libphp5.so ``` ## PHP5.2.17をインストールする PHP5.2のconfigureパラメータを修正して、Apacheモジュールを生成するようにする: ``` open -a CotEditor /usr/local/share/php-build/definitions/5.2.17 ``` ```txt:/usr/local/share/php-build/definitions/5.2.17 configure_option "--with-apxs2" "/usr/local/sbin/apxs" configure_option "--enable-fastcgi" configure_option -R "--with-mysql" configure_option -R "--with-mysqli" configure_option -R "--with-pdo-mysql" with_pear install_package "http://museum.php.net/php5/php-5.2.17.tar.bz2" install_xdebug "2.1.3" ``` PHP5.2をコンパイル&インストールする: ``` php-build 5.2.17 $HOME/local/php/versions/5.2.17 ``` ApacheのPHPモジュールを退避する: ``` mv /usr/local/opt/httpd/libexec/libphp5.so $HOME/local/php/versions/5.2.17/libphp5.so ``` mime.typesにPHPを登録する: ``` open -a CotEditor /usr/local/etc/apache2/mime.types ``` ```txt:/usr/local/etc/apache2/mime.types application/x-httpd-php php ``` ## php-versionの動作確認 php-versionは先程インストールした、PHP 5.2, 5.3, 5.4 の環境をコマンド一発で切り替えられるツールです。 php-versionでPHP 5.4.0にスイッチするのを試してみます: ``` php-version 5.4.0 ``` ```txt:出力例 SWITCHED PHP VERSION TO: 5.4.0 NEW PHP ROOT DIRECTORY : /Users/suin/local/php/versions/5.4.0 ``` 実際にどのPHPが有効になっているか確認します: ``` php -v ``` ```txt:出力例 PHP 5.4.0 (cli) (built: Apr 12 2012 11:55:21) Copyright (c) 1997-2012 The PHP Group Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend Technologies with Xdebug v2.2.0rc2-dev, Copyright (c) 2002-2012, by Derick Rethans ``` ## ApacheのPHPモジュール切り替え用シェルスクリプトを作る php-versionで切り替えられるのは、ターミナルで実行する php のバージョンのみです。そこで、ApacheのPHPのバージョンを切り替えるためのスクリプトを作ります。 ``` touch ~/bin/apache-php-version chmod +x ~/bin/apache-php-version open -a CotEditor ~/bin/apache-php-version ``` 下記の内容を apache-php-version に保存します。 ```sh:~/bin/apache-php-version #!/usr/bin/env bash PROGRAM_APPNAME=$(basename $0) PROGRAM_VERSION="1.0.0" APACHE_LIBEXEC_DIR="${APACHE_PATH}/libexec" show_help() { echo "${PROGRAM_APPNAME} ${PROGRAM_VERSION}" >&2 echo "" >&2 echo "Usage : ${PROGRAM_APPNAME} <version>" >&2 echo "Example: ${PROGRAM_APPNAME} 5.4.0RC6" >&2 exit 1 } check_php_home() { if [[ ! -d ${PHP_HOME} ]] then echo "Sorry, but ${PROGRAM_APPNAME} requires that \$PHP_HOME is set and points to an existing directory." >&2 exit 1 fi } check_apache_path() { if [[ ! -d ${APACHE_PATH} ]] then echo "Sorry, but ${PROGRAM_APPNAME} requires that \$APACHE_PATH is set and points to an existing directory." >&2 exit 1 fi } set_up_apache_module_path() { apache_module_path=$PHP_HOME/$PHP_VERSION/libphp5.so } check_apache_module_path() { if [[ ! -f ${apache_module_path} ]] then echo "Sorry, apache module not found: $apache_module_path" exit 1 fi } check_apache_libexec_dir() { if [[ ! -d ${APACHE_LIBEXEC_DIR} ]] then echo "Directory not found: $APACHE_LIBEXEC_DIR" exit 1 fi } replace_apache_module() { echo "Copy $apache_module_path to $APACHE_LIBEXEC_DIR/libphp5.so" cp $apache_module_path $APACHE_LIBEXEC_DIR/libphp5.so } restart_apache() { echo "Restarting apache..." sudo apachectl restart } [ $# != 1 ] && show_help PHP_VERSION=$1 check_php_home check_apache_path set_up_apache_module_path check_apache_module_path check_apache_libexec_dir replace_apache_module restart_apache ``` 次に、`apache-php-version` のためにApacheへのパスを通して、シェルを再起動します: ```bash:~/.bashrc export APACHE_PATH=$(brew --prefix)/opt/httpd ``` 切り替わるか試してみる: まず、切り替わっているのが分かるように```phpinfo()```を書いたファイルをドキュメントルートに作っておきます。 ``` touch /usr/local/opt/httpd/share/apache2/htdocs/phpinfo.php open -a CotEditor /usr/local/opt/httpd/share/apache2/htdocs/phpinfo.php ``` ```php:/usr/local/opt/httpd/share/apache2/htdocs/phpinfo.php <?php phpinfo(); ``` 切り替えを実行する: ``` apache-php-version 5.4.0 ``` ```txt:出力例 Copy /Users/suin/local/php/versions/5.4.0/libphp5.so to /usr/local/Cellar/httpd/2.2.22/libexec/libphp5.so Restarting apache... Password: httpd: Could not reliably determine the server's fully qualified domain name, using suinair2.local for ServerName ``` ブラウザで [http://localhost/phpinfo.php](http://localhost/phpinfo.php) にアクセスします。phpinfoのバージョンが5.4.0になっているでしょうか? ``` apache-php-version 5.3.10 ``` や ``` apache-php-version 5.2.17 ``` も試してみてください。 # SSHのセットアップ ## 秘密鍵/公開鍵を作ろう SSH秘密鍵・公開鍵を持ってない人はこの際作っておきましょう。 ``` ssh-keygen ``` ```txt:出力例 Generating public/private rsa key pair. Enter file in which to save the key (/Users/suin/.ssh/id_rsa): <= Enter押す Created directory '/Users/suin/.ssh'. Enter passphrase (empty for no passphrase): <= 秘密鍵のパスワードを決める Enter same passphrase again: <= 秘密鍵のパスワード確認 Your identification has been saved in /Users/suin/.ssh/id_rsa. Your public key has been saved in /Users/suin/.ssh/id_rsa.pub. The key fingerprint is: 99:04:e5:25:18:f9:3a:79:19:4c:ef:6e:a0:ee:e0:12 suin@suinair2.local The key's randomart image is: +--[ RSA 2048]----+ | o=o . | | oo.o | | +o. | | .+o. | | oS+ | | E + + . | | .. + o | | .. .. o | | ..oo . | +-----------------+ ``` /Users/(あなたの名前)/.ssh というディレクトリが作られ、そこに秘密鍵 ```id_rsa``` と公開鍵 ```id_rsa.pub``` が作られます。公開鍵はサーバに送る用です。秘密鍵はサーバに送ったり誰かに貸したりしないようにします。 . ## SSHのログイン設定ファイルを作っておこう ポート番号を変更しているサーバや鍵認証のサーバ、ドメインが長いサーバを使っていると、毎回```ssh -p 12345 -i ~/.ssh/id_rsa username@example.com```のような長いコマンドを打たなければならず不便ですね。そんなときは、```~/.ssh/config```ファイルに、サーバごとのログイン情報を書いておくとコマンドが短くなり利便性が増します。 ``` cd ~/.ssh touch config open -a CotEditor config ``` ~/.ssh/configファイルの書き方については[~/.ssh/config で簡単に複数ホストへのSSH接続を管理する](http://d.hatena.ne.jp/superbrothers/20090730/1248971671)や[.ssh/configの書き方](http://d.hatena.ne.jp/nauthiz/20100919/1284892108)などを参考にしてください。 # おわりに 以上で、とりあえず「直ぐにすること」は終えられたんじゃないかと思います。もう少し踏み込むと、PHPやApache、MySQLの設定、PHPUnitなどツール類のインストールなどやることは出てくるかと思います。これらの解説については他に詳しい記事があると思うので、そちらに譲ることにします。 PHPer向けということもあって、RubyやPython、Node.jsなどの環境構築には触れませんでしたが、他言語でもPHPerにとって有用なツールは沢山あります。是非、[【まとめ】これ知らないプログラマって損してんなって思う汎用的なツール 100超](http://qiita.com/items/f570f057a0ff927b47dc)をご覧になって、このまとめにあるツール名をキーワードに他言語のツールも探してみてください。 |
|
| 274位 |
|
|||
|
13:13:21 |
(peroli Inc. 所属) |
|
先日とある方と開発ワークフローについてお話していて初めて知ったのですが``git-commit``には``--allow-empty``という空の(親コミットと差分がない)コミットを作成できるらしいですね。
僕が今関わっているプロジェクトでは WIP PR を用いたワークフローを取り入れているのですが、この``git commit --allow-empty``を用いるともう一段階快適なワークフローになるかと思ったのでメモがてら書き留めておきます。 ## WIP PR って何? Work In Progress Pull Request の略です。 Github に Pull Request (以下、PR と表記)という機能があるのは皆さんご存知だと思います(知らない方はググってください)し、業務で取り入れている方も多いと思いますが、それを作業途中の状態で出すことを WIP PR と呼びます。 作業途中のトピックブランチを PR することによって - 早期にコードレビューのプロセスをまわせるので仕様誤認や設計不備があった場合の修正コストが比較的低い - 修正コストが低いため、レビュワーも気兼ねなく指摘することができる - 実装完了後のコードレビューだと「ここはもう少しこうしたほうがいいけど、スケジュールも詰まってるしすぐに問題になることはなさそうだから今回はこれでいいや…」となりがちです - 作業に着手した段階で PR が作成されるので進捗を追いやすい / 他のメンバーが何をやっているか可視化される といったメリットがあります。 これについては以前 LT をしたことがありますので(ほとんど同じことしか言っていませんが)そのときの資料をはっておきます。 <iframe src="http://www.slideshare.net/slideshow/embed_code/28061575" width="427" height="356" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px 1px 0; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="https://www.slideshare.net/asuenami/wip-pr" title="Wip prをやってみた" target="_blank">Wip prをやってみた</a> </strong> from <strong><a href="http://www.slideshare.net/asuenami" target="_blank">Akira Suenami</a></strong> </div> ## WIP PR に感じていたモヤモヤ感 WIP PR の説明で「作業に着手した段階で PR が作成される」と書きましたが、これはすごく厳密には正しくなくて実際には「作業に着手した後最初のコミット作成された段階で PR が作成される」が正しいです。コミットがなければ PR は作成できませんので。 これが結構今までモヤモヤしていたことで、 - 最初のコミットまで時間がかかる(詳細設計が必要、等)場合、なかなか PR が作成されない - (少なくとも最初の)コミットに関連付ける Issue が別途必要になる - PR の番号を関連付けたいけど、最初のコミット時点では PR が存在していないためそれはできない という問題がありました。 もしすべてのコミットが PR に紐づいていればそれ以外の Issue って必要ないなぁと思っていて、PR にコードが関連付いているので議論がしやすいですし、仕様の確認等も WIP PR が作成された後はそちらで行われ、もともとの Issue は形骸化してしまってました。 ## 空のコミットで WIP PR を作成する 上記のようなモヤモヤが先日のお話の中で解決してしまいました。たった一言「あれ?Gitって --allow-empty オプションってありましたよね?」と。 空のコミットで WIP PR を作成すればそれ以降のコミットに WIP PR の番号を関連付けることができるため、事実上すべてのコミットが PR に関連付くことになります。これはあとからコミットログを追うことになった場合に大変便利ですね! まだ実プロジェクトで導入はしてないのですがぜひ試してみたいと思います。 |
|
| 275位 |
|
|||
|
23:17:55 |
|
|
2015/05/12追記 : [Kobito for Windows](http://kobito.qiita.com/win)が出ました。
# Windowsでも使えるMarkdownエディタ KobitoとかMouとかいい感じのMarkdownエディタはなぜかWindows対応してない!なので、WindowsユーザーでMarkdownを編集するのに良いエディタがなくて困っているという人は多いんじゃないでしょうか? しかし先日、[Moongiftで紹介](https://www.moongift.jp/2014/03/haroopad-%E9%96%8B%E7%99%BA%E8%80%85%E3%81%AB%E3%81%B4%E3%81%A3%E3%81%9F%E3%82%8A%E3%81%AAmarkdown%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF/)されていた[Haroopad](http://pad.haroopress.com/)というエディタがなかなかいい感じでした。 # Haroopadとは  Haroopadは、[node-webkit](https://github.com/rogerwang/node-webkit)というNode.js + WebKitのクロスプラットフォームなUIキットを使って作られたMarkDownエディタです。 QiitaやGithubと同じように > \`\`\`js > console.log("hello"); > \`\`\` みたいに記述すれば、コードのシンタックスハイライトも使えます。つかえる言語は[このあたり](http://highlightjs.org/static/test.html)を見れば載ってます。 ダウンロードは[このページ](http://pad.haroopress.com/user.html)からできます。 海外製のエディタは、日本語がうまく使えないものも多いですがHaroopadは日本語にも十分に対応しています。 # Haroopadを日本語化する Haroopadをインストールした後 ```File > Prefarence``` で設定画面を開く。すると、自動的にリソースファイルのアップデートが開始して、アップデートの終了後、再起動をすればUIが日本語化される。…はずですがうちのWindows8.1ではうまくいきませんでした。 なので、直接Githubからリソースファイルをダウンロードしてきます。 https://github.com/rhiokim/haroopad-locales このページの右側のDownload Zipからダウンロードできます。 ダウンロードしたZipを解答して、```(HarooPadのインストールフォルダ)/Libraries/.locales```に丸ごと上書きしてしまいましょう。 その後、Haroopadを再起動すればUIが日本語化されます。 # エディタ側のフォントを変更する デフォルトだと、漢字のフォントがなんかちょっと読みづらいです。CSSに変更を加えて、好きなフォントに変更してしまいましょう。 ファイル > 設定 > エディタ > defaultをEdit cssの```editor{}```のところを以下のように編集してしまいましょう。 ```css editor { font-family: 'Bitstream Vera Sans Mono', 'Courier New', Courier, 'MS Gothic', 'Osaka-Mono', 'TakaoGothic', 'Hiragino Kaku Gothic ProN', 'メイリオ', monospace !important; } ``` ここで、重要なのは最後に```!important```をつけることです。これをつけないとフォントの設定が反映されません。 # ビューア側のフォントを変更する ビューア側の日本語フォントも読みづらいですね。こちらも変えてしまいましょう。 ファイル > 設定 > ビューワ > defaultをEdit ```css >* { font-family:'Helvetica Neue', Helvetica, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif; } code { font-family: 'Bitstream Vera Sans Mono', Courier, monospace; font-weight: normal; } ``` codeには等幅、それ以外にはプロポーショナルフォントを指定しました。 ここまで、設定しておけば日本語も問題なく使えるはずです。 バージョン0.11.1ということで、まだまだ発展途上ですがかなり期待が持てるエディタではないでしょうか |
|
| 276位 |
|
|||
|
13:49:49 |
(Increments inc. 所属) |
|
QiitaとKobitoで画像アップロードができるようになりました。
その後ろ側をちょっぴり公開します。 件名からも分かるように、背後ではAWSのS3を画像ストレージに採用しています。 - [画像アップロード機能をリリースしました - The Official Qiita Blog](http://blog.qiita.com/post/50993800271/qiita-image-uploading)  - [Kobito v1.6.1リリース: ドラッグ&ドロップやスクリーンショット撮影で簡単に画像を添付できるようになりました! - The Official Qiita Blog](http://blog.qiita.com/post/51270885648/kobito-v1-6-1-image-uploading)  #### 用語統一 **サーバ** はQiitaのサーバのことを指すことにします。(つまり、S3ではない、ということ) また **クライアント** は各ユーザのブラウザのことを指します。 # 要件 画像アップロード機能を実装するにあたっていくつかの要求がありました。 1. 成りすましを防げる 2. アップロードされたファイルを管理できる 3. 自分達のサーバに負荷をかけたくない 4. 変な画像のアップロードを阻止したい QiitaユーザがQiitaないしQiita:Teamへ記事を投稿するために提供する機能なので、例えば他のサービスからのアップロードなど、意図しない用途での使用を防げなければなりません。 またコスト的な問題から容量無制限で機能を提供することはできません。画像以外のファイルや大き過ぎるファイル、過剰なアップロードを排除する仕組みが必要です。 画像をアップロードする際に自前のサーバに中継させることもできますがサーバに負荷がかかるので、クライアントが直接S3へアップロードしたいです。 画像アップロード機能を実装するなら、だいたいどこでも同じような要求になるのではないでしょうか? ## S3で解決できた要求 ### 成りすましを防げる S3にファイルをアップロードするにはサーバが発行するポリシーとシグニチャが必要です。そしてこれを適切に発行するにはAWSのシークレットキーが必須なので、(シークレットキーが外部に流出しない限りは)サーバを経由せずに画像をアップロードすることは実質的に不可能です。 サーバではユーザの認証やファイルの簡単なチェックを行うことができます。ポリシーを厳格に定めることでユーザによるファイル偽装も(ある程度は)弾くこともできます。 ### アップロードされたファイルを管理できる 画像アップロードするにはサーバを経由しなければならないので、サーバ側でアップロードを許可したユーザとそのパスを紐付けて覚えておけば、S3上にアップロードされたファイルが何時誰によってアップロードされたのかを管理することができます。 ### 自分達のサーバに負荷をかけたくない ブラウザからS3へ直接ファイルをアップロードすることが可能です。 ## S3で解決できない要求 ### 変な画像のアップロードを阻止したい 残念ながら今回の構成では画像の中身までチェックすることはできません。 取れる対策としては定期的にクローラを走らせて中身を見るなどですがコスト的に見合わないので、 - 規約で縛る(もともとQiitaは技術情報とは無関係の投稿は禁止されてます) - 万が一変な画像をアップロードされても直ぐに削除できて誰が投稿したかが分かるようにしておく つまり運用でカバーする、という感じです。(他のメジャーなサービスもだいたい同じですね) # 概略図 Qiitaの画像アップロード機能の概略は次の画像のような感じになっています。  1. 選択されたファイルの情報をサーバに送る このときサーバには画像の種類と大きさだけを送るので、負荷はかからない。 2. サーバがクライアントの認証及びS3へアクセスするためのトークンを発行する アップロードに必要な、複製不可能な情報(ポリシーとシグニチャ)をその場で作ってクライアントに返す。 3. サーバからもらったポリシーとシグニチャと一緒に選択された画像ファイルをS3に送る サーバから返された情報を元に画像をS3に直接アップロード。その際S3はユーザのポリシーが有効なものであるか確かめて、ポリシーに違反していなければ画像を受け取る。 4. アップロードされたファイルのURLを挿入する。 # コード ## サーバ 上の画像でいうところの (2) のRubyでのサンプル実装です。 `policy_document`がポリシーの実態で、ここで色々な条件を指定することができます。ここでは画像形式と画像サイズの改ざんをチェックする設定だけを抜き出しています。その他に設定可能な項目は色々あります。詳しくは公式ドキュメントを参照してください。 ```ruby S3_BUCKET = 'xxx' AWS_SECRET_KEY = 'xxx' AWS_ACCESS_KEY_ID = 'xxx' # アクセスしてきたクライアントにS3へアップロードするためのpolicyと # それをハッシュ化したsignatureを返す。 def policies # 0. セッションの確認など # 1. 画像情報をバリデーションする # ファイルサイズは大き過ぎないか、種類は適切か、など。 # 2. policyを作る # policyで許可した内容しかクライアントはアップロードできなくなる。 key = '/path/to/file' # アップロード先のパス policy_document = { expiration: (Time.now + 1.minute).utc, # このポリシーは1分間のみ有効 conditions: [ # アップロード先のS3バケットを指定する { bucket: S3_BUCKET }, # ファイルのS3上でのパスを指定する。 # ワイルドカードも指定できるが、完全一致させておく。 { key: key }, # オプション。クライアントから送られてきたものそのままにする。 { 'Content-Type' => params[:content_type] }, # オプション。アップロード可能なファイルのサイズを範囲で指定できる。 # クライアントから送られてきたファイルサイズをそのまま指定することで # 1バイトでも大きさの異なるファイルは拒否できる。 ['content-length-range', params[:size], params[:size]] ] }.to_json policy = Base64.encode64(policy_document).gsub("\n", '') # 3. signatureを作る # AWSのシークレットキーとpolicyからsignatureを作る。 # signatureはシークレットキーを知っている人しか計算できないので # クライアントがpolicyを改ざんしてもAmazon側がそれを検知できる。 signature = Base64.encode64( OpenSSL::HMAC.digest( OpenSSL::Digest::Digest.new('sha1'), AWS_SECRET_KEY, policy)).gsub("\n", '') # 4. アップロードに必要な情報をクライアントに返す return { url: "https://#{S3_BUCKET}.s3.amazonaws.com/", form: { 'AWSAccessKeyId' => AWS_ACCESS_KEY_ID, signature: signature, policy: policy, key: key } } end ``` `content-length-range`を範囲指定しないことで、例えばサーバには10KBとして申請しておいて、実際は1MBの画像をS3にアップロードするなどの偽装を行えないようにしています。Qiitaではアップロード制限をかけているので、このような実装にしました。 ## クライアント ```javascript var fileInput = $('input[type="file"]'); fileInput.on('change', function (e) { var file = e.target.files[0]; // 0. 事前にファイルの大きさや種類をチェックする // 1. サーバからpolicyとsignatureをもらう // 上図でいう(1)に対応 $.ajax({ url: '/policies', type: 'POST' dataType: 'json' data: { // サーバにこれからアップロードするファイルの情報を渡す。 size: file.size, // ファイルの大きさ content_type: file.type // ファイルの形式 } }).done(function (data) { // 2. サーバが返した情報をそのまま使ってFormDataを作る var name, fd = new FormData(); for (name in data.form) if (data.form.hasOwnProperty(name)) { fd.append(name, data.form[name]); } fd.append('file', file); // ファイルも忘れずに添付する // 送信 // 上図でいう(3)に対応 var xhr = new XMLHttpRequest(); xhr.open('POST', data.url, true) xhr.send(fd); }) }); ``` ここで味噌なのはサーバから返したデータをそのままコピペしている点です。画像アップロードはKobitoからも行えるようになっていますが、Webサービスとして提供しているQiitaとは異なり、デスクトップアプリケーションであるKobitoは何か問題があっても直ぐにはコードを変更できません。ここでやっているようにすることで、将来的にS3の仕様が万が一変更になっても、サーバ側で対応するだけで仕様変更に対応することができて嬉しいですね。 ## 参考サイト - [Browser Uploads to S3 using HTML POST Forms](http://aws.amazon.com/articles/1434/) - [S3へ直接ファイルをアップロードする。Node.js版 - Qiita](http://qiita.com/ms2sato/items/f73a02f2ac14361247c3) - [S3に直接ファイルアップロード(jquery+PHP版) - Qiita](http://qiita.com/supertaihei02/items/a4f663d837e51f2f72b0) |
|
| 277位 |
|
|||
|
11:41:55 |
|
|
PythonからTwitter APIへのアクセス。Python2, 3両対応。
[python-twitter](https://code.google.com/p/python-twitter/) というのもあるけど、基本はHTTP Requestを送ってJSONを得るだけなので、わざわざTwitter専用のライブラリを使うことも無いと思って自分で書いてみた。OAuth認証だけは別途ライブラリ使う。 #OAuthのライブラリ Twitterへのアクセスには **OAuth認証** というのが必要。 認証方式は非常にややこしいので、凡人はライブラリを使う。 今回は「人間のためのOAuth」をもって自認する [Requests-OAuthlib](https://github.com/requests/requests-oauthlib) を使う。他のライブラリがどんな生物を想定しているのかは知らない。 #アプリケーション登録 Twitter APIにアクセスするにはまずアプリケーション登録が必要。これを済ませると * Consumer Key * Consumer Secret という2つの鍵が発行される。さらに、そのアプリのユーザー1人ごとに * Access Token * Access Token Secret という2つの鍵が発行される。Twitter APIへのアクセスにはこの4つの鍵が必要。 取得の仕方は[このへん](http://website-planner.com/twitter%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E4%BD%9C%E6%88%90%EF%BC%88consumer-key%E3%80%81consumer-secret%E3%80%81access-token%E3%80%81access-token-secret/)を参照。 アプリケーション登録自体は無料だが、2014年2月頃から携帯番号の登録が必要になった模様。詳細は知らないのでggr。 # ツイートを投稿 鍵が取得できたら、さっそくコードを書く。 以下 `CK, CS, AT, AS` の部分を適宜自分の鍵に直すこと。 ```py:tweet.py #!/usr/bin/env python # -*- coding: utf-8 -*- from requests_oauthlib import OAuth1Session CK = 'XXXXXXXXXXXXXXXXXXXXXX' # Consumer Key CS = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # Consumer Secret AT = 'XXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # Access Token AS = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # Accesss Token Secert # ツイート投稿用のURL url = "https://api.twitter.com/1.1/statuses/update.json" # ツイート本文 params = {"status": "Hello, World!"} # OAuth認証で POST method で投稿 twitter = OAuth1Session(CK, CS, AT, AS) req = twitter.post(url, params = params) # レスポンスを確認 if req.status_code == 200: print ("OK") else: print ("Error: %d" % req.status_code) ``` 基本はこんな感じ。 画像をつける場合は投稿部分をこのように書き換える。 ```py:tweet_with_image.py # ツイート本文 files = { "status": "Hello, World!", "media[]": open("image.png", "rb") } # OAuth認証で POST method で投稿 twitter = OAuth1Session(CK, CS, AT, AS) req = twitter.post(url, files = files) ``` **※2015/04/27 追記** 画像投稿の仕様が変更になった模様。こちらを参照→ [Python で画像付きツイート ](http://qiita.com/yubais/items/864eedc8dccd7adaea5d) # タイムラインを取得 URLが変わるのとPOSTがGETになる以外に大きな違いは無い。 戻り値はJSONで、ツイート20個がlist形式になっている。ここでは本文のみを表示するが、他にもユーザー情報やツイート時刻、Fav数など多くのメタ情報を含む。 ```py:timeline.py #!/usr/bin/env python # -*- coding: utf-8 -*- from requests_oauthlib import OAuth1Session import json CK = 'XXXXXXXXXXXXXXXXXXXXXX' # Consumer Key CS = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # Consumer Secret AT = 'XXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # Access Token AS = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # Accesss Token Secert # タイムライン取得用のURL url = "https://api.twitter.com/1.1/statuses/home_timeline.json" # とくにパラメータは無い params = {} # OAuth で GET twitter = OAuth1Session(CK, CS, AT, AS) req = twitter.get(url, params = params) if req.status_code == 200: # レスポンスはJSON形式なので parse する timeline = json.loads(req.text) # 各ツイートの本文を表示 for tweet in timeline: print(tweet["text"]) else: # エラーの場合 print ("Error: %d" % req.status_code) ``` パラメータは無くても良いが、取得するツイートIDの範囲(時刻順に付与されている)やツイート数(標準20個、最大200個)などが設定できる。 # その他のAPI あとはURLとparamsを書き換えればほぼ何でも出来る。 提供されているAPIの一覧はこちら。(英語) https://dev.twitter.com/docs/api/1.1 「フォローを外す」のAPIが `friendships/destroy` なのは何度見ても吹く。 # 悪名高いAPI制限 TwitterはAPIを太っ腹に公開してくれているので、機能的にはほぼ公式クライアントと同じことが出来る。ただしリクエスト回数に制限がある。とくにHome Timelineは **15分に15回まで** とやたら厳しいので、数秒ごとにタイムラインを更新することは出来ない。 API残数と次回更新の時刻はリクエストのヘッダに格納されている。 ```py # OAuth で GET twitter = OAuth1Session(CK, CS, AT, AS) req = twitter.get(url, params = params) if req.status_code == 200: # API残り limit = req.headers['x-rate-limit-remaining'] # API制限の更新時刻 (UNIX time) reset = req.headers['x-rate-limit-reset'] print ("API remain: " + limit) print ("API reset: " + reset) ``` API制限を超越する方法としては * API制限はアプリごとに課されるため、内部で複アプリにする * Homeに比べるとList Timelineの制限はゆるいので、全部のフォローを1個のリストに登録して見た目上Homeにする * ユーザーストリームを使う といったものがある。 # このあと何ができるか * 検索APIを使って特定キーワードのツイートを蓄積する * user_timeline で特定ユーザーのツイートを集めて並べて愉しむ * crontab で定期ツイート投稿してbotを作る * [bottle.py](http://bottlepy.org/docs/dev/index.html) のような簡易HTTPサーバーを使ってWebクライアントにする 可能性は無限大。 |
|
| 278位 |
|
|||
|
02:42:10 |
|
|
[CocoaPods](http://cocoapods.org/)とはターミナルからコマンドを打ってiOSやOS Xのライブラリを導入できるツール。 利用するライブラリのことをPodと読んでいるのでRubyGems≒CocoaPodsと考えるとわかりやすいかも。 ## CocoaPodsのインストール OS Xに入っているRubyGemsを使ってセットアップできる。 ( rbenvを使ってセットアップするメモ書きました http://qiita.com/items/53753cb824f47c4a48df ) http://rubygems.org/gems/cocoapods ```bash:Terminal sudo gem install cocoapods pod setup ``` 当然CocoaPods自体の更新もRubyGemsで行える。 ```bash:Terminal sudo gem update cocoapods ``` ## Podの利用 Xcodeプロジェクトファイル(MyPorjectName.xcodeproj)と同じ階層で ```ruby:Podfile platform :ios,'5.0' pod 'JSONKit', '~> 1.4' pod 'Reachability', '~> 3.0.0' ``` のようにファイル名**Podfile**というテキストファイルを置いてそのディレクトリで```pod install```を実行するとPodが使えるようになる。その際に必要なframeworkやリンクオプションを追加して依存関係を解決してくれ、Xcodeプロジェクトの設定が変更され、Xcodeワークスペースが用意される。通常はCocoaPodsが作成してくれるXcodeワークスペースを利用するのが良い。 ```bash:Terminal open MyPorjectName.xcworkspace ``` 仕組みとしてはPod.aというスタティックライブラリプロジェクトをワークスペースに追加して、そこにソースファイルをまとめている模様。 どういうことを実際に行なっているかは [Creating a project that uses CocoaPods](https://github.com/CocoaPods/CocoaPods/wiki/Creating-a-project-that-uses-CocoaPods) にあるので自前のワークスペースに設定するときやうまく動かないときに参考になる。 ``[!] xxxx is not compatible with iOS 4.3.``とエラーがおきる場合は、以下のようにして明示的にiOSバージョンをPodfileに指定する必要がある。 ```Podfile platform :ios,'5.0' ``` ヘッダファイルはHeader Search Pathに"${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/JSONKit" のように追加されるので```import "JSONKit.h"``` ```import "JSONKit/JSONKit.h"```のどちらでも記述できるが後者の方が安全か。異なるPodでヘッダファイル名が重複した場合の動きは未調査。 リンクがうまくいかない場合などはPROJECTの方に設定が反映されているのでTARGETのOther Linker Flagsに$(inherited)が入っているかなどをチェックする必要がある。 ### Subspecの利用 PodによってはモジュールごとにSubspecという単位で分けている場合がある。例えばShareKitを使いたいけど全部はいらなくて特定のサービスだけ使いたいというときはPodfileを以下のように書ける。 ```Podfile pod 'ShareKit/Facebook' pod 'ShareKit/Twitter' ``` **CocoaPods 0.17.0からSubspecがMainSpecを継承しなくなったのでSubspecだけを使うときに依存するファイルが足りないことがある** ### CocoaPods利用時のリポジトリ構成 Bundlerの思想を受け継いでいるならPodfileとPodfile.lockだけリポジトリに入れておいて、他の開発者にはpod installでPodsディレクトリとワークスペースを作成してもらえばいいから、特にワークスペースの設定をいじっていないなら ```.gitignore Pods/ MyPorjectName.xcworkspace ``` でいいんじゃないかと思っているんだけどそれを裏付けるドキュメントはオフィシャルWikiからは見つけられなかった。 ## Podの作成 CocoaPodsにライブラリ定義の生成(pod spec create)や検証(pod spec lint)を行うコマンドが用意されている ```bash: pod spec create MyPod vi MyPod.podspec pod spec lint MyPod.podspec ``` 書き方は[仕様](http://docs.cocoapods.org/specification.html)があるけど、[既存Pod](https://github.com/CocoaPods/Specs)のpodspecを見たりすると勉強になる。 ディレクトリ構成の推奨 https://github.com/CocoaPods/CocoaPods/wiki/Creating-and-maintaining-a-pod ```/path/to/Pods/NAME . ├── Classes └── ios └── osx ├── Resources ├── Project └── Podfile ├── LICENSE ├── Readme.markdown └── NAME.podspec ``` ただこの形になっていないPodが多数存在する。(現状がPod以前のライブラリをかき集めている状態のせいかも) 個人的には以下のような構成にしてる。 ``` MyPodName ├── MyPodName └── {*.h,*.m} ├── MyPodNameDemo ├── MyPodNameDemo.xcodeproj └── Podfile └── MyPodName.podspec ``` デモプロジェクトからもPodを使って確認する。以下のようにpathオプションでPodを指定する。(MyPodName/MyPodNameDemo/PodfileからMyPodNameに依存) **CocoaPods 0.19からlocalオプションはpathオプションに名前変更[#971](https://github.com/CocoaPods/CocoaPods/issues/971)** ```ruby:MyPodNameDemo/Podfile platform :ios,'5.0' pod 'MyPodName', :path => '..' ``` なおPodfile、podspecファイルを編集する場合はエディタでRubyと認識させるといい。 ### ローカルで自分のPodを自分の複数のプロジェクトで共有する 再利用可能な自分のPodを複数のプロジェクトから参照する場合、相対パスだと面倒なのでどこかにPodプロジェクトをまとめるディレクトリを作って以下のようにすると参照しやすい。 ```ruby:Podfile pod 'MyPodName', :path => '~/code/Pods/MyPodName' ``` ## Podの配布 Pod公式リポジトリに公開しなくても ```ruby:Podfile pod 'TTTFormatterKit', :git => 'https://github.com/gowalla/AFNetworking.git' ``` のようにしてSCM経由で利用してもらうこともできる。Forkしたライブラリを利用するときに有効。 ## 参考 * [CocoaPods ではじめる Objective-C ライブラリ管理 (2)](http://d.hatena.ne.jp/Watson/20111205/mac_dev_jp_advent_calendar_cocoapods02) * [CocoaPods wiki](https://github.com/CocoaPods/CocoaPods/wiki) * [Creating and maintaining a pod](https://github.com/CocoaPods/CocoaPods/wiki/Creating-and-maintaining-a-pod) |
|
| 279位 |
|
|||
|
14:26:20 |
|
|
個人的に使用頻度の高いショートカット。winとmacで標準に設定されているショートカットキーを表記しています。OSのショートカットと被っていたりすることもあるので注意。名称とカテゴリはkeymap設定に準じています。 ##Editor Actions ###Complete Current Statement おそらく最もお世話になるショートカット。現在のステートメントをカカッと終了させてくれる。行末にセミコロンを追加してくれるだけでなく`function Hoge`まで書いてコレすると`function Hoge(){}`まで補完してフォーマットも整えてくれる。 win:`Ctrl + Shift + Enter` mac:`⇧⌘⏎` ##View ###Recent Files 過去に開いたファイルやウィンドウの履歴。さっき編集してたファイルとか間違えて閉じたときとかするりと戻れる。あと検索できる。 win:`Ctrl + E` mac:`⌘E` ##Navigate ###Navigate Class クラス名で検索。ネームスペースでも探せるしスペース区切りで絞り込みできる。 win:`Ctrl + N` mac:`⌘O` ###Navigate File ファイル名で検索。 win:`Ctrl + Shift + N` mac:`⇧⌘O` ###Navigate Symbol シンボル(変数名、メソッド名、定数名etc)で検索。 win:`Ctrl + Alt + Shift + N` mac:`⌥⌘O` ###Navigate Line 行番号を指定して移動 win:`Ctrl + G` mac:`⌘L` ###Declaration 定義元 win:`Ctrl + B` mac:`⌘B` ##Bookmark ###Toggle Bookmark 現在の行に0番〜9番のブックマークをつける/外す win:`Ctrl + Shift + 0` 〜 `Ctrl + Shift + 9` mac:`^⇧0` 〜 `^⇧9` ###Go to Bookmark 指定したブックマークへジャンプ win:`Ctrl + 0` 〜 `Ctrl + 9` mac:`^0` 〜 `^9` ##Run ###Run Run/Debug Configrationsで設定されたものを実行する。テストの実行とか。 win:`Shift + F10` mac:`^R` ### Run Context configration テスト書いてるところで実行すると現在フォーカスしているメソッドだけ走らせられますphpunitのfilterを勝手に設定してくれる感じ win:`Ctrl + Shift + F10` mac:`^⇧R` ##Code ###Reformat Code コードの自動整形。phpstromはコーディング規約の設定がプロジェクト毎にかなり細かくできるので素晴らしいと思います(小並感 win:`Ctrl + Alt + L` mac:`⌥⌘L` ###Generate 生成のコンテクストメニュー。getter/setterやphpdocもいい感じに生成できます。 win:`Alt + Insert` mac:`^⏎` ##Other ###Show intention Actions 電球のマークをクリックした時のコンテクストメニューを出すショートカット。 win:`Alt + Enter` mac:`⌥⏎` ---- キー移動系は書いてませんが、標準でもemacsライクなキーバインド使えるみたいです。ぼくはviのキーバインドが使えるプラグイン入れてます。 |
|
| 280位 |
|
|||
|
23:14:34 |
(ACCESS CO., LTD 所属) |
|
あらまし
======= 大きな作業をする場合、こまめにローカルレポジトリのブランチにコミットして、何かあったときにすぐに戻せるようにしたくなります。 また、パフォーマンス改善など、実験や研究の色合いの強い作業は、試行錯誤しながらブランチに"とりあえず"保存しつつ、「あっちのほうが良かったかな〜」と思ったときに取り出せるようにしておきたくなるものです。 また、ローカルレポジトリだけでなく、リモートレポジトリに置いたほうがチームみんなで共有できたりしていろいろ便利です。 ですが、最終成果物はなるべく少ないコミットにしないと、マージが大変です。 **メインブランチにこんなコミットが入るとゲンナリしますよね?** ```bash $ git log --oneline bcdef12 Revert foo abcdef0 Add foo cdef123 Refactor bar again def1234 Refactor bar ef12345 Implement baz .... ``` こんなかんじにスパっと決めてからレビューなりマージなりしたいものです。 **特にマージは、コミットが無駄に多いとやたら衝突して手間はかかるやる気は下がる、いいことなし!!** ```bash $ git log --oneline 9876543 Refactor bar 210fedc Implement baz .... ``` なので、 * 手元では **作業を細かく分けてコミット** しながら、 * ときに **リモートブランチに保存** し、 * 最後に **成果物を細かくまとめる** ワークスタイルを紹介します。 ※ リモートブランチはある程度自由にコミットしても良い、という前提で書いてます。 時間のない人用まとめ ================= 以下、書いたはいいけど長ったらしいので短くまとめ。 ```bash git checkout -b foobar git commit -m "hoge" git commit -m "fuga" git push origin HEAD:foobar-WIP # ローカルfoobar のまま、リモートのfoobar-WIPとしてpush git commit -m "hoga" git rebase -i FETCH_HEAD # コミット "hoge" "fuga" "hoga" をまとめられる ``` 時間のある人用 ============ まずは、メインブランチからブランチを切り、作業を進めます。 ```bash $ git checkout master -b feature-1234 $ git branch * feature-1234 master $ git push origin feature-1234 # stuff $ git commit -m "Implement baz" # stuff $ git commit -m "Refactor bar" ``` リモートへの一時的な作業状態の保存 ------------------------------ このあたりで、「ちょっと手戻りするかも...」と思ったら、 **以下の方法でリモートリポジトリに保存** します。 ```bash $ git push origin HEAD:feature-1234-WIP # Work In Progress $ git branch * feature-1234 master ``` **`HEAD:remote-branch`とするのがミソです** 。 これをすることで、 **ローカルレポジトリのブランチを`feature-1234`から変えずに、リモートレポジトリの`feature-1234-WIP`として保存できます** 。 また、`feature-1234`として`git push`しないことで、あとで作業成果をまとめるときに楽できます。 ローカルの変更をまとめる --------------------- 作業を進めましょう。 ```bash $ git commit -m "Refactor bar again" # stuff $ git commit -m "Add foo" # stuff $ git commit -m "Revert foo" $ git log --oneline bcdef12 Revert foo abcdef0 Add foo cdef123 Refactor bar again def1234 Refactor bar ef12345 Implement baz ... ``` いいところまで実装できたので、 **`git rebase -i`で作業成果をまとめましょう。** どのハッシュを指定するか **悩む必要はありません**。 何も考えずに **`FETCH_HEAD`** を指定しましょう。 `FETCH_HEAD`は、最後にリモートレポジトリに`git push`した変更を示しています 。 `FETCH_HEAD`から始めれば`git push`するときに **Fast-Forwardできないエラーに遭遇せず** 、かつ **まとめることのできる変更すべてを列挙することができます** 。 ```bash $ git rebase -i FETCH_HEAD ``` このコマンドで、最後にリモートブランチと同期した直後からのコミットがエディタに出てきます。 ```bash pick 461a05e Implement baz pick 439994c Refactor bar pick 3e5b24b Refactor bar again pick 314c11c Add foo pick 9aa0335 Revert foo # Rebase 3d7e901..9aa0335 onto 3d7e901 # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out ``` さて、あとは毎回丁寧に書いてある下の説明書き通りにやりましょう ```diff pick 461a05e Implement baz # そのまま pick 439994c Refactor bar # そのまま +fixup 3e5b24b Refactor bar again # fixup で前の変更とくっつける -pick 3e5b24b Refactor bar again -pick 314c11c Add foo # いらないので捨てる -pick 9aa0335 Revert foo # 同じく ``` ファイルを保存すると書いたとおりに各変更が処理されていきます。 ログを見て意図通りにまとめられているのを確認したら、成果物としてブランチに`git push`します。 ```bash $ git log --oneline 98d701e Refactor bar 461a05e Implement baz ... $ git push origin feature-1234 ``` おしまい。 参考など ======== 以前、自身で書いた記事。 * [作業中のブランチとは別のリモートブランチにとりあえず作業状態を保存しておく。](http://qiita.com/aKenjiKato/items/deb556d2de732890839e "作業中のブランチとは別のリモートブランチにとりあえず作業状態を保存しておく。") `FETCH_HEAD`について * [Git - git-fetch Documentation](http://git-scm.com/docs/git-fetch "Git - git-fetch Documentation") Help me! ======== 記事を書いているときに気づいたんですが、 現在のブランチ **だけ** を表示してくれる方法ってなんかないですかね。 文中では`git branch`使ってるんですけどしっくりこない。 なにかいいのがあったらコメントください。 あと間違いあったら遠慮無く編集リクエストください。 |
|
| 281位 |
|
|||
|
08:44:00 |
|
|
あるテーブルに対して複数のレコードを同時に登録したいことがあります。
このときに登録する数だけINSERTを発行するのはあまり効率がよくありません。 そこでBULK INSERTと言われる単発のSQLで一括登録する方法がよく使われます。 ## BULK INSERTとは 単純なbooksというテーブルを使い、BULK INSERTはどういうものか確認する。 ```sql:books.sql CREATE TABLE `books` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ``` 複数のINSERTを発行する。 ```sql:insert.sql INSERT INTO books (columns) VALUES (values); INSERT INTO books (columns) VALUES (values); INSERT INTO books (columns) VALUES (values); ``` BULK INSERTで一括登録する。 ```sql:bulkinsert.sql INSERT INTO books (columns) VALUES (values), (values), (values); ``` それぞれのSQLでこのような違いがありますが、登録されるレコード結果は同じになります。 ## RailsでBULK INSERTを使う RailsのActiveRecordでBULK INSERTを使うには、**activerecord-import**という大変便利なgemがあるので利用させていただきます。 [activerecord-import](https://github.com/zdennis/activerecord-import) 使い方はとても簡単で、登録するレコード(model)のインスタンスをすべて配列にいれて、クラスメソッドのimportにそれを渡すだけです。 ```ruby:activerecord-import.rb books = [] 10.times do |i| books << Book.new(:name => "book #{i}") end Book.import books ``` これで10回分のINSERT文が1つにまとめられます。 ## パフォーマンスを検証する 1度に登録する数を100レコードにし、それを100回繰り返す処理で計測してみる。 Railsの通常処理でINSERTを繰り返すパターン。 ```ruby:insert.rb 100.times do |i| 100.times do |_i| Book.create! :name => "book_#{i}_#{_i}" end end ``` ```sql:insert.sql BEGIN; INSERT INTO books (...) VALUES (...); COMMIT; BEGIN; INSERT INTO books (...) VALUES (...); COMMIT; BEGIN; INSERT INTO books (...) VALUES (...); COMMIT; ... ``` activerecord-importを使って、BULK INSERTするパターン。 ```ruby:bulkinsert.rb 100.times do |i| books = [] 100.times do |_i| books << Book.new(:name => "book_#{i}_#{_i}") end Book.import books end ``` ```sql:bulkinsert.sql INSERT INTO `books` (...) VALUES (...), (...), (...), ...; ``` Railsの通常パターンでは毎回COMMITされてしまうので、TRANSACTIONを使ったパターンも追加する。 ```ruby:transaction.rb 100.times do |i| ActiveRecord::Base.transaction do 100.times do |_i| Book.create! :name => "book_#{i}_#{_i}" end end end ``` ```sql:transaction.sql BEGIN INSERT INTO books (...) VALUES (...); INSERT INTO books (...) VALUES (...); INSERT INTO books (...) VALUES (...); ... COMMIT ``` ### 計測結果 | Insert | BulkInsert | Transaction | |:---------------:|:---------------:|:---------------:| | 10.6876sec | 4.0554sec | 7.1316sec | それぞれ7回計測し、1番速いものと遅いものを除外した5回平均のデータ。 **実行環境** MacBookPro, Core i7 2.9GHz, SSD256GB, Mem8GB 単純なテーブルでの比較ですが、BULK INSERTを使うとパフォーマンスは大きく向上しました。 複数レコードを登録するときは、ぜひBULK INSERTで処理しましょう! |
|
| 282位 |
|
|||
|
07:20:11 |
|
|
# unite.vim とは vim 用の統合ユーザインターフェース。 様々なデータを共通のインタフェースを用いて操作できる。 unite.vim のインターフェースを学習するだけで色々なデータを簡単に操作できるようになり、また異なる種類のデータを同時に操作できる。 例えば、ノーマルモードで以下のコマンドを実行すると、カレントディレクトリとバッファの一覧を同時に表示できる。 :Unite file buffer # 導入方法 unite.vim は GitHub で公開されている。 [Shougo/unite.vim](https://github.com/Shougo/unite.vim) NeoBundle を導入済みの場合は、まずは以下を .vimrc に記述する。 NeoBundle 'Shougo/unite.vim' そして、ノーマルモードで以下のコマンドを実行すると、 unite.vim をインストールできる。 :NeoBundleInstall # 使い方 ## 操作の流れ 1. データの種類(Source)を選択 2. データを選択 3. Action を選択 ## 操作方法 1. `:Unite {source}` を実行し、 unite を起動する 2. データの一覧からデータを選択する(カーソルをデータに合わせる) 3. Action を選択する * `<Tab>` を押すと、 Action の一覧が出力される。その一覧から実行したい Action を選択する。 * `<Tab>` の代わりに `<Enter>` を押すと、 default のアクションをすぐに実行できるので便利。 ## 実行例 例えば、 source として *file* を指定する場合は、以下のコマンドを実行する。 :Unite file 上記コマンドを実行すると、カレントディレクトリのディレクトリとファイルが一覧表示される。 この時に insert モードに入ることで、データの絞り込みを行えるので便利。 Source が file の場合は、 default のアクションは file の *open* なので、ファイル選択中に Enter キーを押すと選択中のファイルを開くことができる。 ## Source よく利用する source の一覧。 | source | 内容 | |-----------|------| | file | カレントディレクトリのファイルとディレクトリ一覧 | | file/new | ファイルの新規作成 | | file_mru | 最近開いたファイル(Most Recently Used)の一覧 | | buffer | buffer の一覧 | | register | register の一覧 | | source | unite の source の一覧 | ## insert モードでの操作 unite 起動時の insert モードでよく利用する操作の一覧。 | キー | 動作 | |------|------| | Ctrl + p | 上の候補に移動 | | Ctrl + n | 下の候補に移動 | | Enter | default の Action を実行 | | Tab | Action 選択画面に移行 | ## Tips * Unite の停止 * q で unite が停止する。 # .vimrc 以下は私の unite.vim の設定。 ```vim:.vimrc " Unite NeoBundle 'Shougo/unite.vim' let g:unite_enable_start_insert=1 let g:unite_source_history_yank_enable =1 let g:unite_source_file_mru_limit = 200 nnoremap <silent> ,uy :<C-u>Unite history/yank<CR> nnoremap <silent> ,ub :<C-u>Unite buffer<CR> nnoremap <silent> ,uf :<C-u>UniteWithBufferDir -buffer-name=files file<CR> nnoremap <silent> ,ur :<C-u>Unite -buffer-name=register register<CR> nnoremap <silent> ,uu :<C-u>Unite file_mru buffer<CR> ``` 上記設定をすると、 insert モードで unite が起動する。 また、ノーマルモードで `,uu` と入力するだけで source に file_mru と buffer が指定された状態で unite が起動するのでとても便利。 ノーマルモードで `,uy` と入力すると、 yank の履歴が表示される。 # 参考 * [unite.vim について語る - C++でゲームプログラミング](http://d.hatena.ne.jp/osyo-manga/20130307/1362621589) * unite.vim の概要、導入方法、操作方法、 tips について分かりやすく記述されている * おすすめのページ * [Vimでファイラー兼ランチャーなunite.vimプラグインを使う](http://blog.ruedap.com/entry/20110110/vim_unite_plugin) * このページに書かれている .vimrc はとても参考になりました |
|
| 283位 |
|
|||
|
20:23:26 |
(Freelancer 所属) |
|
iOS7で採用された**フラットデザインっぽいUIを、現行iOS(〜6.x)で実装する際に役立つライブラリ**(OSS)をまとめました。後半ではiOS7っぽく**下のビューをブラーかけて半透過表示**するライブラリや、**パララックス表示**するライブラリも紹介しています。
###[FlatUIKit](https://github.com/Grouper/FlatUIKit) フラットデザインなUIコンポーネント詰め合わせ。ソースをみると、ほとんどのコンポーネントが該当するUIKitコンポーネントのカテゴリやサブクラスとしてつくってあって、使いやすそうです。     ###[UI7Kit](https://github.com/youknowone/UI7Kit) iOS5, iOS6 で iOS7 の見た目を実現するUIKitのサブクラス集。  ###[iPhone Flat Design UI](http://www.appdesignvault.com/iphone-flat-ui-design-patterns/) フラットデザインのサンプル集的なXcodeプロジェクト。コンポーネント化されて使い回せるわけではありませんが、こう設定すればこういう見た目にできるのか、という参考になります。   ###[QBFlatButton](https://github.com/questbeat/QBFlatButton) フラットな見た目にできるUIButtonのサブクラス。  ###[Flat Pill Button](https://github.com/brianmichel/FlatPillButton)  ###[FlatWebView](https://github.com/aaronbrethorst/FlatWebView) UIWebViewの後ろのドロップシャドウをなくしたUIWebViewサブクラス。実装をみると、subviewsを辿ってUIImageViewをhiddenにしているようです。  ###[FlatButton](https://github.com/jessesquires/FlatButton)  ##おまけ1:iOS7ぽいビューを実現するUIライブラリ ###[DKLiveBlur](https://github.com/kronik/DKLiveBlur)  動的に下のビューをぼかして半透過っぽく表示してくれるビュー。ちゃんと**Accelerate.frameworkを使用**して実装されてるのでレスポンス面は期待してよさげ。 ###[CFIFrostedOverlayView](https://github.com/CodaFi/CFIFrostedOverlayView)  上記同様、superviewをブラーをかけて表示してくれるビュー。こっちはGPUImageを使用。 ###[Parallax](https://github.com/acoomans/Parallax)  パララックス表示するビュー。ただサンプルのアニメーションGIFにあまりパララックス感がないような。。(あとで動かしてみます) ##おまけ2:フラットUIデザイン素材 ###[Free iOS 7 UI Kit](http://medialoot.com/item/free-ios-7-ui-kit/) > Start designing iOS 7 apps today with this comprehensive GUI kit for Photoshop. Absolutely every element you see is 100% vector and completely editable!  |
|
| 284位 |
|
|||
|
12:01:02 |
|
|
* ただの、自己満足乱れ書き * ふむ、下々の文章でも見てやるかって人だけ見てくれればおk * 対象はvimはじめたいという人から中級者ぐらい ## vimでhtmlファイルを編集するときに知ってると気持ち良くなれるコマンド というか最低限覚えておかないと、コーディング時間が3倍以上になる系のコマンド。 ### アクション範囲ターゲットの3つの組み合わせコマンド 例)`cit`とタイプ ``` <div>text</div> => <div></div> ↑消えて挿入モードに移行 ``` * `cit`の`c`はアクション。`c`の場合は削除して挿入モードに変更。他のアクションに置き換える事も可。`d`(削除)`v`(選択)など。 * `cit`の`i`は範囲。`i`はタグの中身が範囲で`a`に置き換えるとタグ全体が範囲になる。上記の場合で`a`にすると`<div></div>`ごと消えて挿入モードになる。 * `cit`の`t`はターゲット。`t`の場合はタグに対して行う。他にも`'`や`"`、`<>`、`{}`、`()`と言ったような何かで挟むものに使える。`<>`のような左右で違う記号はどちらか一方で良い。例`ci<`。 その逆はプラグイン`surround.vim`で。(下の方で紹介) ### アクション+検索の組み合わせコマンド 例)`ct"`とタイプ  →  * `ct"`の`c`は削除して挿入モード。もちろん他のアクションなども使える。`d`や`v`など。 * `ct"`の`t`は次に挿入した文字の手前までカーソルを移動。`f`の場合は検索する文字も含める。 * `ct"`の`”`は検索する文字 ちなみに、`t`(文字単体)のかわりに`/`(文字列)使うのも結構強力。 例)`c/</div>`などするとカーソル位置から`</div>`まで削除して挿入モード。 ### ctrl+v(矩形範囲) 矩形範囲を使うといろいろと便利  →  →  1. ctrl+vで縦に選択 2. `I`をタイプし適当に文字列を挿入 3. `esc`するとあら不思議。すべてに文字列が入る。 `I`を他のアクションに変える事も可。例えば`d`とタイプしたら選択しているところを削除。 これを使えるようになると数行一気にインデントにしたり、コメントアウトしたり、いろいろと使える。 `2013/09/13 12:00追記`矩形範囲を使った行末の一括追加 一日一回以上使ってる気がしたので追記。  →  →  →  1. ctrl+vで縦に選択 2. `$`をタイプ 3. `A`をタイプ後、文字列挿入 4. `esc`するとあら不思議。すべての行末に文字列が入る。 ## 入れていると気持ち良くなれるプラグイン ### surround.vim DL => http://www.vim.org/scripts/script.php?script_id=1697 ``` unzip surround.zip cp ./surround/plugin/surround.vim ~/.vim/plugin/ ``` 使用例 ``` こんにちは ← vで選択後にS<div>と入力 <div>こんにちは</div> ``` タグ以外にも`'`や`"`、括弧系もできる。 逆ももちろんできる。 ``` <div>こんにちは</div> ← dstと入力 こんにちは ``` 削除して別のものに付け替える ``` <div>こんにちは</div> ← cst<span>と入力 <span>こんにちは</span> ``` 他にもあるが、主要なのはこの三つ。 ### emmet.vim (zen coding) DL => https://github.com/mattn/emmet-vim ``` unzip emmet-vim-master.zip cp ./emmet-vim/plugin/emmet.vim ~/.vim/plugin/ cp ./emmet-vim/autoload/emmet.vim ~/.vim/autoload/ cp -a ./emmet-vim/pautoload/emmet ~/.vim/autoload/ ``` 使用例 ``` html:5 ← 入力後に<ctrl+y>,と入力 <!DOCTYPE html> <html lang="en">←enをjaに変え方は一番したに記述 <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> </html> ``` ``` div>ul>li.class#id_$$*5 ← 入力後に<ctrl+y>,と入力 <div> <ul> <li id="id_01" class="class"></li> <li id="id_02" class="class"></li> <li id="id_03" class="class"></li> <li id="id_04" class="class"></li> <li id="id_05" class="class"></li> </ul> </div> ``` 説明 * `>`は下の階層 * `+`は同階層 * `*数値`は個数 * `$`は連番が入る `$`を並べた数の桁が使える * `.`はクラス名 `#`はID名 * `()`はブロックにできる 複雑な階層があるときなどに使用 > div>(ul>li*5)+span * `[]`はプロパティなど設定 > a[href=http// sytle=display:none;] → `<a href="http//" sytle="display:none;"></a>` ちなみに、`a:link`と書くと`<a href="http://"></a>`になるみたいな書き方も用意されているが、量が多いので割愛。 cssも用意されている(`pos:r`で`position:relative;`など)が量が多いので割愛。 `zen coding css 一覧`で先生に聞くか、ドキュメントを確認。 #### 設定 lang="en"を"ja"に変更 ``` sed -i -e "s/'lang': \"en\",/'lang': \"ja\",/g" ~/.vim/autoload/emmet.vim ``` `~/.vim/autoload/emmet.vim`の中にある'lang': "en"を'lang': "ja"に変更すればおk `2013/09/08 13:32追記`コメントにてご指摘を受けたので追記 ソースをわざわざいじらなくてもvimrcの設定で行けるみたいです。 ``` let g:user_emmet_settings = { \ 'lang' : 'ja' \ } ``` ### yankring.vim yankの履歴をさかのぼる(コピペの履歴をさかのぼる) DL => http://www.vim.org/scripts/script.php?script_id=1234 ``` unzip yankring_170.zip cp ./yankring_170/plugin/yankring.vim ~/.vim/plugin/ ``` 使用方法 `p`でペーストした後、`ctrl+n`で昔のyankにさかのぼれる。反対は`ctrl+p`。 ### 後あれば編集に役立つプラグイン(詳しくは先生に聞いて下さい) * NERD_tree.vim ディレクトリのtree表示 * changed.vim 最後に保存したときからどこの行に追加、削除、変更があったかを左側に表示 * closetag.vim 閉じタグを自動挿入 * mru.vim 開いたファイルの履歴を管理。NERD_treeを使っていればあまり使わない。 |
|
| 285位 |
|
|||
|
19:05:12 |
(株式会社トクバイ 所属) |
|
# はじめに 僕はWebフロントエンドはほぼやってないんだけど、Firefox OSとかもアレだって事でソレ系にも手を出すかとあれこれやってたらまぁWebのフロントエンド周りのツールが何か色々あってよくわかんねーしどうしよっかなと思ってた所[Yeoman](http://yeoman.io/)辺りが大分良さそうなんじゃないか程度にはわかって来たので手を出してみた次第。 # Yeomanってなに Webフロントエンドでよくある各種構成をコマンド一発で生成してやろうじゃん、というのがYeomanらしい。Yeomanは内部でyo, bower, gruntの3つのツールにわかれていて、Yeoman自身が作ってるのはyo。[bower](http://bower.io/)はTwitter社が、[Grunt](http://gruntjs.com/)はBocoup社がメインで作っている。 ワークフローとしてはこうらしい  yoでプロジェクトを作成し、bowerで依存性管理をし、gruntでビルド&テストをするという事だそうで。[volo](http://volojs.org/)はこれらをひとつでやるけど、Yeomanは各種ツールが合体して一連のフローを提供する感じっぽい。 # Yeomanを導入 ではYeomanを導入してプロジェクトを作ってみる。yoもbowerもgruntもnpm経由でインストールするので環境ない人は[この辺り](https://www.google.co.jp/search?q=node+npm+install)を参考に構築して下さい。ちなみにYeomanの1.0 BETAはWindowsではまだ動かないらしいのでMacかLinuxでやって下さい。 > npm install -g yo grunt-cli bower こんだけ。 # Yeomanでプロジェクトを作ってみる yoコマンドでプロジェクトを作る。yoコマンドを実行したディレクトリに色々生成されるのでmkdirでディレクトリを予め作っとく。 ```bash: mkdir sample cd sample yo webapp ``` すると以下の様なのが出るので選択する。jQueryなどのモダンなjsはデフォで入るぽい。Sass使う?とかRequireJS使う?とか聞かれる。 ```bash: _-----_ | | |--(o)--| .--------------------------. `---------´ | Welcome to Yeoman, | ( _´U`_ ) | ladies and gentlemen! | /___A___\ '__________________________' | ~ | __'.___.'__ ´ ` |° ´ Y ` Out of the box I include HTML5 Boilerplate, jQuery and Modernizr. Would you like to include Twitter Bootstrap for Sass? (Y/n) n Would you like to include RequireJS (for AMD support)? (Y/n) ``` とりあえずSassとかよーわからんので(知ってるけど)そこはnoにして、RequireJSについてはyesでやってみるとズギャーっと色々ダウンロードされる。詳しくは見てないけどpackage.jsonとcomponent.jsonを生成してnpm installとbower installをしているみたい。以下の様な構成でファイルが生成される。 ```bash: . ├── Gruntfile.js ├── app │ ├── 404.html │ ├── components │ ├── favicon.ico │ ├── images │ ├── index.html │ ├── robots.txt │ ├── scripts │ └── styles ├── component.json ├── node_modules │ ├── bower │ ├── grunt │ ├── grunt-bower-requirejs │ ├── grunt-concurrent │ ├── grunt-contrib-clean │ ├── grunt-contrib-coffee │ ├── grunt-contrib-compass │ ├── grunt-contrib-concat │ ├── grunt-contrib-connect │ ├── grunt-contrib-copy │ ├── grunt-contrib-cssmin │ ├── grunt-contrib-htmlmin │ ├── grunt-contrib-imagemin │ ├── grunt-contrib-jshint │ ├── grunt-contrib-livereload │ ├── grunt-contrib-uglify │ ├── grunt-mocha │ ├── grunt-open │ ├── grunt-regarde │ ├── grunt-requirejs │ ├── grunt-rev │ ├── grunt-svgmin │ ├── grunt-usemin │ └── matchdep ├── package.json └── test ├── index.html ├── lib └── spec ``` デフォでcompassが入るんでRubyとCompassは入れとかないといけないっぽい。compass入ってなければgruntで色々やる前に入れておく。 ```bash: sudo gem update --system sudo gem install compass ``` あとはgrunt buildだのgrunt testだのすればよい。 # 色んなプロジェクト npmで以下の様にgeneratorを探して入れたらyoで色んな種類のプロジェクトが作れるようです。AngularJSとかもある。結構色々出来そう。 ```bash: npm search yeoman-generator ft-wp Front-Trends Wordpress generator =hubertburdach 2013-03-30 21:08 yeoman-generator web app front-e generator-angular Yeoman generator for AngularJS =sindresorhus =addyosmani =paulirish =btford 2013-04-08 03:21 yeoman-gen generator-aura Yeoman generator for Aura.js =dotcypress 2013-03-03 15:58 yeoman-generator yeoman generato generator-backbone Yeoman generator for Backbone.js =sindresorhus =addyosmani =paulirish 2013-04-13 22:07 yeoman-generator s generator-backbone-amd generator-backbone-amd ====================== =abiee 2013-04-02 05:10 yeoman-generator yeoman generato generator-barista Yeoman generator for Barista =trimeego 2013-03-26 17:43 yeoman-generator yeoman generato generator-bbb Yeoman generator for Backbone Boilerplate =sindresorhus =addyosmani =paulirish 2013-04-13 23:29 yeoman-generator s generator-boilerplate Yeoman Generator for ... =addyosmani 2013-04-17 15:04 yeoman-generator generator-bookmarklet Bookmarklet Generator for Yeoman =passy 2013-03-01 23:25 yeoman-generator bookmarklet generator-bootstrap Yeoman generator for Twitter Bootstrap =sindresorhus =addyosmani =paulirish =btford 2013-04-07 17:04 yeoman-gen generator-buster generator-buster ================ =abiee 2013-04-02 05:05 yeoman-generator yeoman generato generator-chrome-extension Yeoman generator for Chrome Extensions =sindresorhus =ragingwind =addyosmani =paulirish 2013-04-11 22:13 yeoman generator-chromeapp Yeoman generator for Chrome App =sindresorhus =addyosmani =paulirish 2013-04-14 00:56 yeoman-generator s generator-cityjs Yeoman generator for CityJS =dotcypress 2013-04-04 12:28 yeoman-generator yeoman generato generator-closure Generator for Closure Library =thanpolas 2013-04-14 15:45 yeoman-generator yeoman generato generator-cucumber Yeoman generator for Cucumber.js =lkanio 2013-02-17 21:34 yeoman-generator yeoman generato generator-ember Yeoman generator for Ember =inkredabull =sindresorhus 2013-04-15 17:15 yeoman-generator scaffold ge generator-footguard Yeoman generator for single page application =mazerte 2013-03-15 13:23 yeoman-generator yeoman generato generator-foundation Yeoman generator for Stylus themes, especially zurb foundation CSS framework =brian.lai 2013-04-18 21:25 yeoman-generator yeoman gene generator-generator Generator Generator for Yeoman =passy 2013-04-18 21:58 yeoman-generator generator-genesis Yeoman Generator for Genesis Skeleton =ericclemmons 2013-03-17 03:28 yeoman-generator express skeleto generator-h5bp HTML5 Boilerplate generator =sindresorhus 2013-04-13 23:57 yeoman-generator html boilerplat generator-inuit inuit.css generator =kevva 2013-03-15 12:03 yeoman-generator inuit.css generator-jasmine Yeoman generator for Jasmine =sindresorhus =addyosmani =paulirish 2013-04-15 19:34 yeoman-generator s generator-karma Yeoman generator for Karma =sindresorhus =btford =paulirish =addyosmani 2013-03-21 07:17 yeoman-gen generator-lessapp Edited default Yeoman generator for scaffolding out a front-end web app with Twitter Bootstrap in LESS =robinpokorny 2013-04-15 16:04 generator-maria A generator for Yeoman =revathskumar 2013-04-15 15:04 yeoman-generator MariaJS Boilerp ``` ## AngularJSで作ってみる せっかくなんでAngularJSのプロジェクト作ってみる。 ```bash: npm install -g generator-angular ``` あとはyoでangularを指定。色々聞かれるんで全部yesにしてみた。 ```bash: yo angular Would you like to include Twitter Bootstrap? (Y/n) y If so, would you like to use Twitter Bootstrap for Compass (as opposed to vanilla CSS)? (Y/n) y Would you like to include angular-resource.js? (Y/n) y Would you like to include angular-cookies.js? (Y/n) y Would you like to include angular-sanitize.js? (Y/n) y ``` このgeneratorは勝手にbower installとかしてくれないらしいんで、自分でやる ```bash: bower install ``` すると以下の様になる ```bash: . ├── Gruntfile.js ├── app │ ├── 404.html │ ├── components │ │ ├── angular │ │ ├── angular-cookies │ │ ├── angular-mocks │ │ ├── angular-resource │ │ ├── angular-sanitize │ │ ├── angular-scenario │ │ ├── es5-shim │ │ └── json3 │ ├── favicon.ico │ ├── index.html │ ├── robots.txt │ ├── scripts │ │ ├── app.js │ │ └── controllers │ ├── styles │ │ ├── _compass_twitter_bootstrap.scss │ │ ├── _compass_twitter_bootstrap_awesome.scss │ │ ├── _compass_twitter_bootstrap_responsive.scss │ │ ├── compass_twitter_bootstrap │ │ ├── main.css │ │ └── main.scss │ └── views │ └── main.html ├── component.json ├── package.json └── test ├── runner.html └── spec └── controllers ``` index.htmlを開くと以下の様に表示されます。リスト部分がng-repeatで動的に挿入されてるっぽいですね。  # おわりに yoで良さげなプロジェクトテンプレを探して生成し、あとはcomponent.jsonやGruntfile.jsをいじって良い感じにしたらよさそう。generator-nodeとかgenerator-expressは無いのかと思ったらStackoverflowの[Yeoman and ExpressJS](http://stackoverflow.com/questions/13160599/yeoman-and-expressjs)によると「Yeomanはフロントエンド用のツールじゃん?だからフロントエンドを生成するじゃん?nodeはバックエンドじゃん?」という事らしい。ごもっともだ。ただ将来的には両方いける口になりたいようです。 AngularJSとかよーわからんしー、という人でもgeneratorで色々生成されるんでファイル見ながらふむふむ出来る。gruntとかまだ全然分かってないけど、generator周りも自分で作れそうだし、いいんじゃね。これ。Firefox OS系のアプリ作りでも十分使えると思う。ていうかgenerator-openwebapps作ろうかな |
|
| 286位 |
|
|||
|
00:49:15 |
|
|
iOS Advent Calender 2013 iOS second stage 4日目担当の@hkato193です。書籍「[iPhoneアプリ開発エキスパートガイド](http://www.amazon.co.jp/gp/product/4844333852?ie=UTF8&camp=1207&creative=8411&creativeASIN=4844333852&linkCode=shr&tag=ktkt02-22&qid=1385912013&sr=8-3)」や「[OS XとiOSのためのOpenCV環境構築ガイド](http://www.amazon.co.jp/gp/product/4877833099?ie=UTF8&camp=1207&creative=8411&creativeASIN=4877833099&linkCode=shr&tag=ktkt02-22&qid=1385912013&sr=8-4)」などのiPhoneアプリ開発関係の本を書いたりしています。アプリだと、プロアスリートが練習に使うスキップバックレコーダーを実現した「[PastVid](https://itunes.apple.com/jp/app/pastvid-guo-quwo-yingsubideo/id525981467?mt=8)」や、弾むボールでリズムを表現する「[Metronome Bounce](https://itunes.apple.com/jp/app/metronome-bounce-leshiku-danmumetoronomu/id490494675)」などを公開しています。
さて、iOS 7ではバーコードやQRコードなどの1次元/2次元コードをカメラで読み取ることができます。さらにQRコードは読み取りだけでなく、Core Imageを使ったコード作成も行えます。本記事では、読み取りと作成の方法をそれぞれ紹介します。 # 1. 読み取り編 1次元/2次元コードの読み取りは、AVFoundationフレームワークのキャプチャ出力に関連するクラス`AVCaptureMetadataOutput`クラスの一機能として標準提供されるようになりました。  コード認識の処理は、顔認識の方法と基本的に同じです。 1. キャプチャセッションの準備 1. キャプチャセッションのアウトプットに繋ぐ```AVCaptureMetadataOutput```クラスを作成 1. `setMetadataObjectsDelegate:queue:`メソッドを使って、識別結果を受けるデリゲートオブジェクトと、デリゲートメソッドを呼ぶワークキュー(GCDのシリアルキューがベター)を指定 1. キャプチャセッションのアウトプットに接続 1. アウトプットに接続後、識別したいコードの種類を```metadataObjectTypes```プロパティに指定 1. キャプチャセッションを開始 という手順です。顔認識の手順とは5番で指定する値が異なるだけですね。これによって識別処理がキャプチャセッションの開始と同時に始まり、フレーム内にコードがあると分かったタイミングで、指定したワークキュー上でデリゲートメソッドが呼ばれ、```AVMetadataMachineReadableCodeObject```オブジェクトの配列として読み取り結果が渡されます。あとは得られた情報を元にWEBサイトにジャンプするなり、メッセージを表示するなり、好きな処理を実行するだけです。 ## キャプチャセッションの準備 実際のコードを見てみましょう。最初は、キャプチャセッションを作成するときの`AVCaptureMetadataOutput`クラスの初期化処理です。 ```objc:QRコードとJANコードの2種類を識別するアウトプットの設定例 // メタデータ識別用のオブジェクトを作成 AVCaptureMetadataOutput *metaOutput = [[AVCaptureMetadataOutput alloc] init]; // 識別結果を伝えるデリゲートオブジェクトと処理を行うシリアルキューを指定 // ※認識したメタデータが順序正しくデリゲートメソッドに届くよう、 // ワークキューにはグローバルキューではなくシリアルキューを渡すこと [metaOutput setMetadataObjectsDelegate:self queue:dispatch_queue_create("myQueue.metadata", DISPATCH_QUEUE_SERIAL)]; // アウトプットに追加 [self.session addOutput:metaOutput]; // アウトプットに追加後、識別したいコード(QRコード、JANコード標準タイプ)を指定 // ※顔認識とコード認識の両方を同時に渡しても、どちらも正常に動作する metaOutput.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]; ←② ``` 注意点としては、`metadataObjectTypes`プロパティの指定は「 **キャプチャセッションに`addOutput:`した後** 」で、なおかつ「 **キャプチャセッションにキャプチャデバイスが`addInput:`されている状態** 」で行うようにしてください。冒頭の図だと、中央のキャプチャセッションを挟んだ左右のオブジェクトです(プレビューはなくても大丈夫です)。 > これは識別できるメタデータの種類はキャプチャデバイスに依存していて、使用するキャプチャデバイスが分からない状況ではメタデータの種類を指定しようがないためです。たとえばキャプチャデバイスにはカメラだけではなくマイクもあります。マイクだけがaddInput:されている状態ではQRコードの識別が出来るはずもありませんので、この状態でQRコードを指定しても何の意味もありません。 metadataObjectTypesプロパティに設定できるタイプと、コード上の表現は次のとおりです。iOS 7時点では、各種コードの中でも特によく使われる10種類をサポートしています。日本でのバーコードと言えばEAN-13や8で、二次元コードだとQRコードが一般的ですね。 | *コードの種類* | *定数*(```NSString*```) | |:---|:---| | UPC-E | `AVMetadataObjectTypeUPCECode` | | Code 39 | `AVMetadataObjectTypeCode39Code` | | Code 39 mod 43 | `AVMetadataObjectTypeCode39Mod43Code` | | EAN-13(JANコード標準タイプ) | `AVMetadataObjectTypeEAN13Code` | | EAN-8(JANコード短縮タイプ) | `AVMetadataObjectTypeEAN8Code` | | Code 93 | `AVMetadataObjectTypeCode93Code` | | Code 128 | `AVMetadataObjectTypeCode128Code` | | PDF417 | `AVMetadataObjectTypePDF417Code` | | QRコード | `AVMetadataObjectTypeQRCode` | | Aztec code | `AVMetadataObjectTypeAztecCode` | | (参考)人の顔 | `AVMetadataObjectTypeFace` | 認識させたいコードは同時に複数種類を登録でき、また画面内に複数個のコードがあった場合も同時(最大4個)に認識します。 ちなみに現在設定可能なメタデータの種類は、`AVCaptureMetadataOutput`クラスの`availableMetadataObjectTypes`プロパティを使って取得できるので、この中に含まれるタイプだけを指定するようにするとプログラマブルでベターです。 > iOS 6 SDKではデフォルトで`AVMetadataObjectTypeFace`が指定された状態になっていて、何もしなくても顔認識を開始できたのですが、iOS 7 SDKを使うとデフォルトで`metadataObjectTypes`プロパティは空の配列が設定されていて、`AVMetadataObjectTypeFace`は含まれていません。既存アプリをiOS 7に対応させる場合は、このデフォルト動作の違いにも注意が必要です。 ## コードの識別結果を取得 キャプチャデータ中のコードが識別できると、準備のときに`setMetadataObjectsDelegate:queue:`メソッドで登録したデリゲートオブジェクトの`captureOutput:didOutputMetadataObjects:fromConnection:`メソッドを呼びます。そのときのコードを見てみましょう。 以下のコードは、識別結果のうちQRコードとJANコードに注目して、QRコードの場合はURLとして開き、JANコードの場合はISBNコードかどうかを見極めた上でamazonのページを開く処理です。 ```objc:識別できたコードを元にWEBページを開く(書籍の場合はAmazonを開く) - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { // 認識されたメタデータは複数存在することもあるので、1つずつ調べる for (AVMetadataObject *data in metadataObjects) { // 一次元・二次元コード以外は無視する // ※人物顔の識別結果だった場合など if (![data isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) continue; // コード内の文字列を取得 NSString *strValue = [(AVMetadataMachineReadableCodeObject *)data stringValue]; // 何のタイプとして認識されたかを確認 if ([data.type isEqualToString:AVMetadataObjectTypeQRCode]) { // QRコードの場合、URLとしてmobileSafariを開く NSURL *url = [NSURL URLWithString:strValue]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url]; } } else if([data.type isEqualToString:AVMetadataObjectTypeEAN13Code]) { // JANコード(標準タイプ)の場合、ISBNかどうかを調べて // ASINコードに変換し、対応するAmazonのページを開く long long value = strValue.longLongValue; NSInteger prefix = value / 10000000000; if (prefix == 978 || prefix == 979) { // ISBNかどうかをチェック long long isbn9 = (value % 10000000000) / 10; long long sum = 0, tmp_isbn = isbn9; for (int i=10; i>0 && tmp_isbn>0; i--) { long long divisor = pow(10, i-2); sum += (tmp_isbn / divisor) * i; tmp_isbn %= divisor; } long long checkdigit = 11 - (sum % 11); // asinコードに変換 NSString *asin = [NSString stringWithFormat:@"http://amazon.jp/dp/%lld%@", isbn9, (checkdigit == 10)? @"X":[NSString stringWithFormat:@"%lld", checkdigit%11]]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:asin]]; // URLのオープン } } } } ``` コードの認識結果を扱う`AVMetadataMachineReadableCodeObject`クラスは`AVMetadataObject`クラスのサブクラスなので、`AVMetadataObject`クラスが持つ属性の他に「`corners`プロパティ」と「`stringValue`プロパティ」の2つを持っています。`stringValue`プロパティが格納されていた文字列を示し、`corners`プロパティの情報は、コードの四隅がどこにあるかを示す座標を、正しい向きでの左上から逆時計回りに格納した情報です。よく似たプロパティとして`bounds`プロパティが`AVMetadataObject`クラスにありますが、こちらは画面上のコードが内接する矩形を示した情報を表すためのプロパティで、意味合いが異なります。 > 1次元コードは2次元コードと異なり、キャプチャデータの読み取り範囲が画面中央の一部に限定されています。アプリを作る場合は、画面上に帯を示すUIをもうけるようにしておくとUXの向上に繋がります。 # 2. コード生成編 続いてQRコードの作成方法です。こちらはCore Imageの`CIQRCodeGenerator`フィルターを使って作成します。 > 実はiOS 6にもQRコード生成フィルターは用意されていてPassbookアプリでも使われていたのですが、Appleのプライベートフィルタとして分類されていたため、公式利用はできませんでした。 「格納したいデータ」と「誤り訂正レベル」の2つを設定するだけで、使用するQRコードのバージョンを自動的に判断し、出力画像には必要分のマージンも含めてくれる機能を持っています。 ## 作成方法 以下のコードは、文字列「Hello, iOS7 world!」を含めたQRコードを作成し、画面上の`UIImageView`に表示するまでの処理です。 ```objc:QRコードを作成し、画面上のUIImageViewに表示 // QRコード作成用のフィルターを作成・パラメータの初期化 CIFilter *ciFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; [ciFilter setDefaults]; // 格納する文字列をNSData形式(UTF-8でエンコード)で用意して設定 NSString *qrString = @"Hello, iOS7 world!"; NSData *data = [qrString dataUsingEncoding:NSUTF8StringEncoding]; [ciFilter setValue:data forKey:@"inputMessage"]; // 誤り訂正レベルを「L(低い)」に設定 [ciFilter setValue:@"L" forKey:@"inputCorrectionLevel"]; // Core Imageコンテキストを取得したらCGImage→UIImageと変換して描画 CIContext *ciContext = [CIContext contextWithOptions:nil]; CGImageRef cgimg = [ciContext createCGImage:[ciFilter outputImage] fromRect:[[ciFilter outputImage] extent]]; UIImage *image = [UIImage imageWithCGImage:cgimg scale:1.0f orientation:UIImageOrientationUp]; CGImageRelease(cgimg); // 画面のUIImageViewに表示 self.imageView.image = image; ``` CIQRCodeGeneratorフィルターは次に示す2つのパラメータを持っています。 - inputMessage:QRコードに格納するデータ。NSData形式 - inputCorrectionLevel:誤り訂正レベル。NSString形式 「`inputMessage`」パラメータに文字列「Hello, iOS7 world!」を設定していますが、`inputMessage`はNSData形式のデータを渡す必要があるため、いったんUTF-8でエンコードした`NSData`オブジェクトに変換してから設定しています。「inputCorrectionLevel」パラメータは、仕様と同じ「L」〜「H」のいずれか1文字をNSStringで設定します。Hに近づくほど冗長性が確保され、表面の一部が汚れて読み取れないようなときの耐性が上がります(その代わり、出来上がるQRコードは大きく、複雑になります)。 たったこれだけで、任意のデータを含むQRコードが作成できます。 ## 生成したQRコードを表示するときの落とし穴 さて、これを画面上のUIImageViewにそのまま表示すると2つの問題がある問題に気付きます。それは、Core Imageで生成されるQRコードの画像が非常に小さいことで、もう1つは小さい画像を`UIImageView`に拡大表示しても見にくいか、あるいは拡大表示してもぼけた状態で拡大されてしまうことです。  ← 画面だと見やすいけれど、実際は等倍表示で小さすぎる…  ← 拡大してもボケボケ… 前者のQRコードが小さい問題ですが、これは設定変更などでは解決できないので、使用するときに拡大して表示するなどしなければいけません。後者の「小さな画像を大きく表示するときのスケーリング結果が汚い」という問題は、おもに2つの方法で解決できます。1つが`CGContextSetInterpolationQuality()`関数を使いつつ`UIImage`をリサイズする方法で、もう1つが`UIImageView`のレイヤーにある`magnificationFilter`プロパティを使って、スケーリングの補間アルゴリズムを切り替える方法です。 それぞれは次のようなコードで実現します。先のサンプルコードで、UIImageViewに表示する直前部分にどちらかを記述することを想定しています。 ```objc:境界をボケさせずに描画する方法2つ // その1:UIImageを最近傍法を適用しつつリサイズする UIGraphicsBeginImageContext(CGSizeMake(300, 300)); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetInterpolationQuality(context, kCGInterpolationNone);←補間方法の指定 [image drawInRect:CGRectMake(0, 0, 300, 300)]; image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // その2:UIImageViewの描画方法を最近傍法に切り替える self.imageView.layer.magnificationFilter = kCAFilterNearest;←補間方法の指定 self.imageView.contentMode = UIViewContentModeScaleAspectFit; ``` このどちらかの対策を施すことによって、次のように鮮明な画像を任意サイズで表示できます。  ←くっきり! 便利ですね。 # 3. おわりに AVFoundationフレームワークもCore Imageフレームワークも、バージョンを重ねるたびに実現出来ることがどんどん増えている、とても強力なフレームワークです。ぜひいろいろな用途に活用してみてください。 明日以降の[iOS Second Stage Advent Calendar 2013](http://qiita.com/advent-calendar/2013/ios-2)もお楽しみに! # One more thing iOS 7 SDKの新機能を踏み込んで説明した書籍「[上を目指すプログラマーのためのiPhoneアプリ開発テクニック iOS 7編](http://www.amazon.co.jp/gp/product/4844335200?ie=UTF8&camp=1207&creative=8411&creativeASIN=4844335200&linkCode=shr&tag=ktkt02-22&qid=1385912013&sr=8-1)」が発売されることになりました!  (カバーデザインは変更される可能性があります) 「iOS 7 SDKって何ができるの?どう使いこなしたらいいの?」「iOS 7のアプリ作成で、どういう点に気をつけたらいいの?」などに応える、中級〜上級者でも満足できる内容に仕上がっています。 [Amazonではすでに予約が始まっています](http://www.amazon.co.jp/gp/product/4844335200?ie=UTF8&camp=1207&creative=8411&creativeASIN=4844335200&linkCode=shr&tag=ktkt02-22&qid=1385912013&sr=8-1)ので、ぜひぜひご覧くださいませ。 明日のiOS Advent Calender 2013 iOS second stageもお楽しみに! |
|
| 287位 |
|
|||
|
22:56:57 |
(Plaid Inc. 所属) |
|
`save`メソッドを呼び出してモデルのデータを保存するときに、状況によって実行するバリデーションを切り替えたい場合がある。 そんな時は、`validates`メソッドの`on`オプションが使える。 例えば、 ```ruby validates :username, presence: true, on: :create ``` とすれば、モデルのデータを最初に永続化するときにだけ、バリデーションが実行されるようになるし、 ```ruby validates :username, presence: true, on: :update ``` とすれば、モデルのデータを更新するときにだけ、バリデーションが実行されるようになる。 `on`オプションには任意のシンボルを指定することができて、`save`メソッドの`context`オプションを呼び出し時に指定することで、自由に実行するバリデーションを切り替えることができる。 例えば、 ```ruby validates :username, presence: true, on: :registration ``` としていた場合、 ```ruby @user.save(context: :registration) ``` のような形で`save`メソッドを呼び出した時にだけ、バリデーションが実行される。 `valid?`メソッドや`invalid?`メソッドで保存処理を行う前にデータのバリデーションを実行したい場合は、 ```ruby @user.valid?(:registration) ``` のように、単に引数としてシンボルを渡すだけでよい。 ちなみに、以下のように複数のバリデーション定義に同じ`on`オプションを指定する場合、 ```ruby validates :username, presence: true, on: :registration validates :email, presence: true, on: :registration ``` `with_options`メソッドを使って複数のバリデーション定義をひとつにまとめることができる。 こんな感じ。 ```ruby with_options on: :registration do |registration| registration.validates :username, presence: true registration.validates :email, presence: true end ``` 便利。 |
|
| 288位 |
|
|||
|
15:22:17 |
(Medley, Inc. 所属) |
|
何がツラいって一番ありがちな下記のような場合ですよね。
```javascript var a = {}, b = [], c = 'hoge', d = new String('hoge'); typeof a; // "object" typeof b; // "object" ← ファッ?! typeof c; // "string" typeof d; // "object" ← ファッ?! ``` *Object* と *Array* を区別してくれないとかないわー。`new String()`使うことないけど、区別しちゃうとかないわー。 ということで以前に自分が日本語訳した[JavaScript Garden](http://bonsaiden.github.io/JavaScript-Garden/ja/#types.typeof)にも書いてあった方法を使うと 判別が楽になるんじゃないかと。 ```javascript function is(type, obj) { var clas = Object.prototype.toString.call(obj).slice(8, -1); return obj !== undefined && obj !== null && clas === type; } is('String', 'hoge'); // true is('String', new String('hoge')); // true ``` `Object.prototype.toString`することによって`[[Class]]`という内部プロパティを取得して必要なものを切り出してっていう話です。 これで下記のようなタイプを取得してこれますので比較対象として引数に渡してあげましょうという感じです。 - String - Number - Boolean - Date - Error - Array - Function - RegExp - Object これで大体感覚通りの型判別ができると思います。 余談ですが、上記のサイトの中で唯一`typeof`が使える場面としてた下記のような未定義変数の判別だけ!と書いてあります。 ```javascript typeof hoge !== 'undefined'; // 未定義だとfalseに ``` ですよね…。 参照: http://bonsaiden.github.io/JavaScript-Garden/ja/#types.typeof |
|
| 289位 |
|
|||
|
13:48:03 |
(株式会社ずんシステム 所属) |
|
# SSLの認証局とか証明書とか勉強し始めはホント難いよね
このへんのSSL/TLSの仕組みって勉強し始めの頃は凄く難しく感じるのよね。分かりやすく解説してくれてるサイトってあんま見たこと無いし。 んで、 >>300,304 みたいなことは僕も昔考えたことあったわー、と懐かしみを覚えたのでレスってみた。 証明書を発行できるかどうかは証明書のフラグで決まっている、という >>303 の指摘も重要よね。 ## 以下2chスレより引用 丁寧過ぎると評判のレスをしてるID:UyEJo1f2が僕なわけだがw 2chだとそのうち倉庫に行っちゃうかもしれないのでここにメモ。 【認証局】SSLに関するスレ 2枚目【ぼろ儲け】 http://hayabusa6.2ch.net/test/read.cgi/mysv/1286532904/298-309 ``` 298 :DNS未登録さん:2013/05/31(金) 13:31:04.18 ID:??? 認証局を立ててぼろもうけしたいんですが、 親にしたい認証局で何を買ってくればいいんですか? 299 :DNS未登録さん:2013/05/31(金) 16:48:31.50 ID:ha9l9tT5 >>298 馬鹿なの? マジレスすると自分の作ったCA証明書を、 各種ブラウザやアプリやOSにデフォルトでインストールしてくれるよう 全世界に対して頑張って営業すればよい。 300 :DNS未登録さん:2013/06/02(日) 08:35:31.77 ID:??? (1) RapidSSLで安いサーバー証明書を1個買う (2) 自分で中間認証局を作り、RapidSSLのサーバー証明書を割り当てる (3) 自分で、自分用の他のサーバー証明書を量産する ってことはできるものでしょうか? 要するに、自分で運営しているWebサイト(ドメインがいろいろある)を(1)でまかなうケチケチ作戦です(・∀・) ttp://e-words.jp/w/E4B8ADE99693E8AA8DE8A8BCE5B180.html ttps://www.verisign.co.jp/basic/pki/trust/index_4.html 301 :DNS未登録さん:2013/06/02(日) 19:01:05.76 ID:??? できない。 あなたのサーバ用の証明書は、そのサーバ(ドメイン)でしか使えない。 中間認証局用のキーは別に存在する。 まえに中間認証局用のキーが漏れたことがあって、大量に証明書を偽装する事件があったが。 302 :DNS未登録さん:2013/06/02(日) 19:06:03.97 ID:??? あった、あった、結構最近だったw http://internet.watch.impress.co.jp/docs/news/20130107_580795.html 303 :DNS未登録さん:2013/06/03(月) 07:50:44.66 ID:??? >>300 証明書には「その証明書で他の証明書を発行していいか」 (中間証明書になってもいいか)というフラグがあり、 通常の証明書ではそれがfalseになっているので、無理。 具体的にはX509v3 Basic Constraints: CAの値。 もちろんそれも証明書の署名範囲内なので、無理矢理書き換えれば無効な証明書になる。 304 :DNS未登録さん:2013/06/04(火) 09:57:43.60 ID:??? >>301-303 参考になりました。どうもありがとうございます。 中間証明書というのがないと、自分で公的な中間認証局を設置できないんですね>< →自分で中間認証局を作りたい場合、中間証明書ってのは、販売されているものなのでしょうか?(聞いたことないです) 普通の用途とは違って、特別なかんじがするから、販売されていたとしても、条件が厳しい・値段が高そうですね? 305 :DNS未登録さん:2013/06/04(火) 10:50:28.97 ID:??? リセラーで調べたら 306 :DNS未登録さん:2013/06/04(火) 11:39:58.91 ID:UyEJo1f2 >>304 いまいち理解してないようだが、認証局作るだけならopenssl使って誰も普通に作れるよ。 で、自分で作った認証局でいくらでも証明書の発行するのも自由。 自分のブラウザに「自分の認証局の証明書をインストール」さえしておけば、 自分で発行した証明書もブラウザは正規の証明書として認識するようになる。 そういう意味では発行し放題。 でもその行為(=得体のしれない認証局の証明書をインストール)は、 要は自分のブラウザのセキュリティを下げてるってことなのよ。 だって、google や yahoo の偽物証明書が返ってきたとしてもエラーにならないから フィッシングやウィルスの仕込みだってやりたい放題になる。 それが自分にインストール済みの認証局が発行したものだったらブラウザは信じちゃうわけだからね。 なのでテスト目的で自分で作った自分が信頼する認証局を使って自分でやる分には問題ないし。 自分がホンの欠片も疑いを持たないような全幅の信頼を置いている友人が立てた認証局を信頼するのであればやってもよいことになる。 まぁ、俺なら例え友人や家族だろうと素人が立てた認証局なんてインストールしたくないわ。 要は認証局とか証明書っていうのは、信頼の連鎖なわけよ。 俺が信頼すると決めた奴の言うことは俺も100%信頼する、という、さ。 公的な認証局になるってことは他人のセキュリティを支配可能になるということとほぼ同じ。 だから国とか特別な公的な団体とか色んな人間に日々監査され信頼された大企業とか、 特別に「信頼出来る組織」が運用してる認証局しか基本使えないし、使わないの。 OSやブラウザにデフォルトでインストールされている認証局ってのはそういう存在なの。 これだけ説明すれば個人が公的な認証局になるなんてことはほぼ不可能だと分かるだろ? 307 :DNS未登録さん:2013/06/04(火) 11:47:16.27 ID:??? 丁寧すぎワロタw 308 :DNS未登録さん:2013/06/04(火) 11:56:02.85 ID:UyEJo1f2 ついでに言うと、 だったらその「公的に認められた認証局」の中に悪人がいたらどうすんの? って思うかもしれないね。 うん、勿論大変なことになる。 でももし一度でもそんなことをしたらその認証局は誰にも信頼されなくなるよね。 その瞬間から、世界中から「お前はもう信頼しねー」と思われ「あいつは信頼してはいけない」と言われる存在になってしまう。 現実にはそれによって何が起こるかというと、 全世界的にブラックリストな認証局として記録・公開される。 マトモなOSやブラウザなら、デフォルトインストールの認証局リストからそいつを排除するし。 その認証局の証明書をアンインストールするパッチを配布する。 そうなったらもうその認証局は実質的に存在できなくなるわけだ。 発行した証明書はブラウザで警告が表示されて誰も信じてくれない。 当然、証明書の発行を商売にしてたとしたら誰も買わなくなるから潰れる。 世界中から訴訟も起こされるかもしれない。 認証局自身も過ちを犯してしまったらそうなることが分かってるから、 全力で自分の組織を健全に保とうと努力するし、内部からも外部からも監査を徹底することになる。 それは監査を受けることはその組織にとって自分を守ることでもあるわけだ。 そういう目に見えない力が働くことによって、世の中の信頼の連鎖が今日も保たれてるのだよ。 分かった? 309 :DNS未登録さん:2013/06/04(火) 12:44:01.25 ID:??? 外部から不正侵入されて偽証明書を発行してしまったオランダの認証局は 信用を失ってその後破産しました。 ``` ## 追記:CA認定を受ける方法 実際の認定条件とかは知らなかったんだが、はてぶでコメントが付いてたので引用させていただく。 > id:masabossa CAで事業するならWebTrust for CAに認定されている必要がある。それがブラウザ等にルートCA証明書を搭載してもらうための標準的な条件となっている。日本国のGPKIも認定してもらっている→ https://www.gpki.go.jp |
|
| 290位 |
|
|||
|
13:10:47 |
|
|
まだあわてるような時間ではございません。 ## インデックス 1.コミットメッセージを間違えちゃった! -- _直前のコミットのやり直し_ 2.そろそろブランチを整理しなきゃ! -- _ブランチの名称変更と削除_ 3.間違ってコミットしちゃった! -- _コミットを取り消す3つのリセット_ 4.別のブランチをプッシュしちゃった! -- _リモートブランチのリセットと削除_ 5.コミットの順番を間違えちゃった! -- _コミットログの並べ替えと削除_ 6.コミット細かすぎィ! -- _コミットログの統合と編集_ 7.あのコミットさえあれば…! -- _他のブランチのコミットを適用する_ 8.やばい!ハードリセットしたら消えちゃった! -- _過去の状態の復元_ 9.masterにマージ後にバグ発生!どうする!? -- _コミットを打ち消すコミット_ ## 1.コミットメッセージを間違えちゃった! -- _直前のコミットのやり直し_ エディタが立ち上がるので編集しましょう。コミットIDは変わります。 ```bash $ git commit --amend ``` ## 2.そろそろブランチを整理しなきゃ! -- _ブランチの名称変更と削除_ バックアップ用にブランチを切って後で削除するという用途によく使っています。 #### ブランチ名変更 ```bash $ git branch -m <old_branch> <new_branch> ``` #### ブランチ削除 ```bash $ git branch -D <branch> ``` ## 3.間違ってコミットしちゃった! -- _コミットを取り消す3つのリセット_ リセットはワーキングディレクトリとステージの状態によって3種類あります。 #### ワーキングディレクトリはそのままでステージとコミットをリセット ```bash $ git reset <commit> ``` #### ワーキングディレクトリもステージもそのままでコミットだけリセット ```bash $ git reset --soft <commit> ``` #### ワーキングディレクトリ, ステージ, コミットの全てをリセット ```bash $ git reset --hard <commit> ``` ## 4.別のブランチをプッシュしちゃった! -- _リモートブランチのリセットと削除_ 共用リポジトリで使うと怒られたり泣かれたりするのでやめましょう。 そういうときは後述のrevertを使うと良いかもしれませんね。 #### リモートブランチのリセット ```bash $ git push -f <repository> <commit>:<branch> ``` #### リモートブランチの削除 ```bash $ git push <repository> :<branch> ``` もしくは、1.7.0以降ならこちらでもOKです。 ```bash $ git push --delete <repository> <branch> ``` ## 5.コミットの順番を間違えちゃった! -- _コミットログの並べ替えと削除_ エディタが立ち上がるので並べ替えたり削除したりしましょう。 push前にログを整理するのによく使われます。 ```bash $ git rebase -i <commit> ``` ## 6.コミット細かすぎィ! -- _コミットログの統合と編集_ まずはエディタを立ち上げます。 ```bash $ git rebase -i <commit> ``` 親コミットに統合したい子コミットの`pick`を`fixup`に書き換えます。 コミットメッセージを編集したいコミットは`pick`を`edit`にします。 ```bash pick xxxxxx parent fixup yyyyyy child edit zzzzzz hoge ``` ## 7.あのコミットさえあれば…! -- _他のブランチのコミットを適用する_ リベースだと都合が悪いときに使います。 ```bash $ git cherry-pick <commit> ``` ## 8.やばい!ハードリセットしたら消えちゃった! -- _過去の状態の復元_ 落ち着いてください。全ての操作は記録されています。 ログから親コミットを指定してハードリセットします。 ```bash $ git reflog zzzzzz HEAD@{0}: reset: moving to <commit> yyyyyy HEAD@{1}: foo xxxxxx HEAD@{2}: hoge $ git reset --hard HEAD@{1} ``` ## 9.masterにマージ後にバグ発生!どうする!? -- _コミットを打ち消すコミット_ revertして修正後に再度マージしましょう。 revertはコミットログに残ります。 ```bash $ git revert <commit> ``` |
|
| 291位 |
|
|||
|
14:56:51 |
|
|
これは[Git Advent Calendar / Jun.](http://qiita.com/advent-calendar/git)20日目の記事です。前回は、[fukajun](http://qiita.com/users/fukajun)さんの[変更を一時的に退避!キメろgit stash](http://qiita.com/items/41288806e4733cb9c342)でした。
この記事ではgithubでpull requestを送る時に私が気をつけていることを共有したいと思います。 私が気をつけていることは次の3つです。 * masterにコミットしない * 簡潔なコミットメッセージを書く * コミットを1つにまとめる ##この話の前提 以降では、次の2つのリモートリポジトリが登録されている前提で説明します。 * upstream: fork元のリポジトリ * origin: upstreamからforkされた自分のリポジトリ ##masterにコミットしない 修正作業は必ず新しいブランチを作ってから行ない、masterブランチはあくまでupstreamの更新を取り込むためだけに使って下さい。 このルールを守らないでpull requestを送り続けているとupstreamの更新内容を取り込んだ時に謎のコンフリクトが発生したり、pull requestに謎のコミットが追加されるようになります(というか、なりました)。 このルールに従って修正作業を進めている場合、以下の手順でupstreamの更新内容を取り込むことが出来ます。(作業ブランチ: add-great-feature) ``` $git checkout master $git pull upstream master ``` これでupstreamの更新がローカルのmasterブランチに取り込まれました。次に、自分のリモートリポジトリをupstreamと同期します。 ``` $git push origin master ``` この状態でgithubでリポジトリの履歴をみると、元プロジェクトの履歴と同じになっていることが確認できます。次に、作業ブランチにupstreamの更新内容を反映します。 ``` $git checkout add-great-feature $git rebase origin master ``` 以上で、upstreamの更新内容を現在の作業ブランチに取り込むことができました。pull requestを送る前には必ずこの作業を行ってください。 ## 簡潔なコミットログを書く pull requestはfork元のプロジェクトメンバーへラブレターを送るようなものです。仮に貴方が受け取ったラブレターに"Some changes"とか"Fixed"とか書いてあったらどうしますか?私ならリジェクトします。そんな悲しいことにならないよう、pull requestを送る前にはかならずコミットメッセージを見直して修正内容が簡潔に説明されているかを確認して下さい。 もし、コミットメッセージを修正する必要がある場合は、 ``` $git commit --amend ``` とすることで修正することができます。 ## コミットを1つにまとめる プロジェクトの方針によるとは思うのですが、一般に他人の作業ログで自分の管理しているリポジトリの履歴が汚れることを好む開発者は少ないと思います。送られて来たpull requestのコミットメッセージに、"移動するので一旦コミット"とか書かれていたらどんなにすばらしい修正であっても受け入れがたいと思いませんか?重要なことなのでもう一度言いますが、 **pull requestはラブレター** なんです。だらだらと意味不明なコミットが続くのではなく、ビシッと1コミットでキメて下さい。 ``` $git rebase -i ``` このコマンドを実行すると、エディタが起動してコミットの一覧が表示されます。例えば、 ``` pick 9bb3a8c 移動するので一旦コミット pick 5e3b6cc 凄い機能の実装が完了した ``` という一覧が表示されます。このコミットを1つにまとめてコミットメッセージを書き直すには、 ``` reword 9bb3a8c 移動するので一旦コミット fixup 5e3b6cc 凄い機能の実装が完了した ``` とします。するとrebaseが開始され、今度はコミットメッセージの編集が始まるので簡潔なメッセージに修正して下さい。"rebase -i"の使い方すべてを書き下すには、この余白は狭すぎるので[Pro Git Book](http://git-scm.com/book/ja/ch6-4.html)を参照して下さい。 ## まとめ pull requestを送るのはなんだか敷居が高いと感じている人も多いかと思いますが、本物のラブレターを送るよりかは敷居が低いことは間違いありません。コードとコミットメッセージで世界中の開発者と語り合ってはいかがでしょうか? |
|
| 292位 |
|
|||
|
20:44:30 |
|
|
OnClickListenerの実装方法について煩悶としています。 どのように書くのが美しいのでしょうか。 ## Activity,Fragmentに実装する方法 Button等を表示するActivityやFragmentに実装させる方法があります。 ```java public class SampleFragment extends Fragment implements OnClickListener { // ... @Override public void onClick(View v) { // 押下時の処理 } } ``` 一般的だと思います。 よく見るコードですが、個人的にはイマイチだと思っています。 ### イマイチな点1: publicなonClick 個人的にpublicな`onClick()`というメソッドが作られてしまうことが気になります。 下記のようなことができてしまいます。 ```java SampleFragment fragment = new SampleFragment(); fragment.onClick(null); ``` やりませんけどね。 また、以下のようなこともできてしまいます。 ```java SampleFragment fragment = new SampleFragment(); View button = findViewById(R.id.button1); button.setOnClickListener(fragment); ``` やりませんよ? やりませんが、できてしまうということが気になります。 ### イマイチな点2: 1つしかないonClick この方法は、クリック時の処理を一箇所にまとめられるメリットがあるようです。可読性が高まるのでしょうか。 `switch`でViewのID毎に処理を振り分けるのが一般的だと思います。 ```java public void onClick(View v) { switch (v.getId()) { case R.id.button1: // ○○○ // □□□ break; case R.id.button2: // □□□ // ○○○ // ... break; case R.id.button3: // □□□ // ... // ○○○ break; } } ``` 美しくないですね。 可読性を意識される方は、こんないろんな処理がダラダラと続くようなコードを書いたりはしないはず。 ボタン毎にメソッドに切り出しましょう。 ```java private void onClickButton1() { // ○○○ // □□□ } private void onClickButton2() { // □□□ // ○○○ // ... } private void onClickButton3() { // □□□ // ... // ○○○ } public void onClick(View v) { switch (v.getId()) { case R.id.button1: onClickButton1(); break; case R.id.button2: onClickButton2(); break; case R.id.button3: onClickButton3(); break; } } ``` メソッドが小さくなって読みやすくなりました。 しかし、こうなると`onClick()`が大した仕事をしていません。 こんな`onClick()`が必要でしょうか。 ## 無名クラスとしてprivateフィールドに持つ方法 大した仕事をしない`onClick()`を使わないように、ボタン毎に`OnClickListener`を用意するようにしましょう。 ```java OnClickListener button1ClickListener = new OnClickListener() { @Override public void onClick(View v) { // ○○○ // □□□ } }; OnClickListener button2ClickListener = new OnClickListener() { @Override public void onClick(View v) { // □□□ // ○○○ // ... } }; OnClickListener button3ClickListener = new OnClickListener() { @Override public void onClick(View v) { // □□□ // ... // ○○○ } }; ``` クリック時の処理が必要なViewに対して、上記`OnClickListener`のインスタンスを渡してあげれば良いですね。 ```java view.findViewById(R.id.button1).setOnClickListener(button1ClickListener); view.findViewById(R.id.button2).setOnClickListener(button2ClickListener); view.findViewById(R.id.button3).setOnClickListener(button3ClickListener); ``` `onClick()`を消したので、Fragmentにimplementsさせていた`OnClickListener`も外しておきます。 ```java public class SampleFragment extends Fragment { // ... } ``` publicな`onClick()`がなくなりました。 Activity,FragmentにOnClickListenerを実装するよりは、正しい姿になったように思います。 ## 直接setOnClickListener()に無名クラスを渡す方法 privateフィールドにインスタンスを保持させても良いのですが、他で使うわけでもなし、無駄な気がします。 直接`setOnClickListener()`に渡してしまいましょう。 ```java view.findViewById(R.id.button1).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // ○○○ // □□□ } }); // ... 以下略 ``` これもサンプル等でよく見る形です。 無駄は削ぎ落とされてきましたが、ちょっと読みにくい気がします。 `setOnClickListener()`の中の記述量が多いからでしょうか。 以下のように一時変数に代入しておくと多少は読みやすくなるような気がします。 ```java OnClickListener button1ClickListener = new OnClickListener() { @Override public void onClick(View v) { // ○○○ // □□□ } }; view.findViewById(R.id.button1).setOnClickListener(button1ClickListener); // ... 以下略 ``` いかがでしょうか。 ## 専用のクラスに実装する方法 一時変数に代入したとしても、`setOnClickListener()`を行うメソッドにクリック時の処理という異物が紛れ込んでいることには変わりありません。 読みやすさという点では低下しているように感じます。 いっそクリック時の処理をクラスにしてしまいましょうか。 ```java class Button1ClickListener implements OnClickListener { @Override public void onClick(View v) { // ○○○ // □□□ } } ``` `OnClickListener`を設定するところは以下のようにすっきりします。 ```java view.findViewById(R.id.button1).setOnClickListener(new Button1ClickListener()); ``` すっきりはしますが…、ここまでやると大仰な気がしますね。 ここまでしなくても、処理を別の場所に書きたいだけであれば、「無名クラスとしてprivateフィールドに実装する方法」で十分に思います。 ## 結局 OnClickListenerの実装方法として、4つを挙げました。 1. Activity,Fragmentに実装する方法 2. 無名クラスとしてprivateフィールドに持つ方法 3. 直接setOnClickListener()に無名クラスを渡す方法 4. 専用のクラスに実装する方法 どの方法も一長一短あり、どれが一番良いとは決められません。 作成するアプリにもよるかもしれません。 私は2番目の「無名クラスとしてprivateフィールドに持つ方法」で実装することが多いです。 クラスとしての形、読みやすさのバランスが丁度良い気がしているからです。 みなさんはどのように実装しているでしょうか。 どのように実装されているか、理由付きで教えていただけると幸いです。 ## 追記 (2014/03/02) [amay077](http://qiita.com/amay077 "amay077 - Qiita")さんからコメントをいただきました。 上記3以外の方法では > 他所から参照される可能性を生みます。 だそうです。確かにそのとおりでした。 ### 正しくないコードが書けてしまう 1で関係ないViewの`setOnClickListener()`が呼べてしまったように、2,4でも同様のことができてしまいます。 ```java // 2でも↓こんなことが可能 view.findViewById(R.id.text5).setOnClickListener(button1ClickListener); // 4でも↓こんなことが可能 view.findViewById(R.id.check4).setOnClickListener(new Button1ClickListener()); ``` これは美しくないですね。 1と同様に、自分ひとりで開発していたのならば、こんなコードは書かないとは思いますが、間違えてしまう可能性はゼロではありません。 指摘していただいた点を踏まえると、「美しさ」は「3. 直接setOnClickListener()に無名クラスを渡す方法」が一番良いように思えます。 ### Androidのコードはどうなってる? Androidのソースコードではどのように実装されているのか以下のサイトで検索してみました。 [Androidソースコード検索サービス - Developer Collaboration Project](https://sites.google.com/site/devcollaboration/codesearch "Androidソースコード検索サービス - Developer Collaboration Project") キーワードは「setOnClickListener\\(」で検索しました。 検索結果をすべて確認したわけではないのですが、ざっと何ページか見てみただけでも「3. 直接setOnClickListener()に無名クラスを渡す方法」が圧倒的に多いことがわかります。 ときどき、「2. 無名クラスとしてprivateフィールドに持つ方法」が検索結果に現れ、ごく稀に「1. Activity,Fragmentに実装する方法」も使われているようです。 「4. 専用のクラスに実装する方法」は見つけられませんでした。 ### 結局どうなの 「3. 直接setOnClickListener()に無名クラスを渡す方法」が美しい理由を知ることができました。Androidのコードでも多数この方法で実装されているので、「3の方法が一番美しい」と決めつけてしまっても良いかも知れません(ただ、「それ、ループの中でも同じこと言えんの?」というツッコミはありそうです)。 他にもご意見ございましたら、是非お聞かせください。 よろしくお願いします。 ## 追記 (2014/03/03) [zaki50](http://qiita.com/zaki50 "zaki50 - Qiita")さんからもコメントをいただきました。 > 私は最近だと Android Annotations か Butter Knife をつかったプロジェクトばかりなので、 > Activity とかのクラスに @OnClick とか @Click なアノテーションつきのメソッドを用意するだけです。 私はどちらもちゃんと使ったことがなかったのですが、アノテーションをつけるだけでクリックイベントを処理できるというのは聞いたことがありました。 せっかくの機会なので試してみました。 ### AndroidAnnotations [AndroidAnnotations](http://androidannotations.org/ "AndroidAnnotations") 上記リンク先でもサンプルコードを見ることができますが、OnClickListenerに関する部分は以下のように書くことができます。 ```java @EActivity(R.layout.activity_main) public class MainActivity extends Activity { @Click void buton1() { // ○○○ // □□□ } @Click void button2(View v) { // □□□ // ○○○ // ... } @Click(R.id.button3) void hoge() { // □□□ // ... // ○○○ } } ``` すごいですね。 上記のコードだけで、`setOnClickListener()`相当の処理も含まれています。 ちょっと調べただけでも、以下のことができるようです。 - @Clickをつけたメソッドは、メソッド名と同じIDを持つViewのClickListenerとして扱われる(`R.id.button1`←→`button1()`) - `OnClickListener`と同じように引数にViewを取ることができる - @ClickにViewのIDを指定すると、任意のメソッド名をつけることもできる - メソッドをprivateにすることはできないっぽい 他にもできることがあるかもしれませんが、パッとわかったのはこれくらい。 ### Butter Knife [Butter Knife](http://jakewharton.github.io/butterknife/ "Butter Knife") 同様にButter Knifeを使った場合、以下のように書くことができます。 ```java public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.inject(this); } @OnClick(R.id.button1) void clickButton1() { // ○○○ // □□□ } @OnClick(R.id.button2) void clickButton2(Button button) { // □□□ // ○○○ // ... } @OnClick({ R.id.button3, R.id.button4 }) void clickOtherButton(Button button) { // □□□ // ... // ○○○ } } ``` こちらもすごいですね。 @OnClickにViewのIDを指定することは必須のようですが、AndroidAnnotationsとは違った便利さがありそうです。 - @OnClickにViewのIDを指定して利用する。メソッド名は任意の名前 - 引数を取ることができる上、キャストした状態で受け取ることができる - 複数のViewのクリックイベントをまとめて受けることができる - こちらもメソッドをprivateにはできないっぽい ### 試してみた感想 確かにこれらを使うとコードがすっきりします。 これらのライブラリは、メソッド名を自由に付けられたり、引数の有無を選択できたりと、美しいコードを書けるように作られているんだな、とも感じました。私もこれからのプロジェクトでは積極的に使っていきたいと思いました。 こういったライブラリはどれくらいの割合の人たちが利用しているんでしょう? また、先に挙げた4つの実装方法の選ばれている割合も気になります。 Qiitaにアンケートのような機能があったらよかったのに。 ## 追記 (2014/03/09) [shunsugai@github](http://qiita.com/shunsugai@github "shunsugai@github - Qiita")さんからコメントいただきました。 > layoutのxmlでandroid:onClick属性を使うという方法もあります。 確かにそんなやり方もありました。 shunsugaiさんも「あまり使いませんが…」と前置き付きで紹介くださいました。 せっかくなので使い方を書いておきます。 layout.xmlに以下のように`android:onClick`という項目を記述します。 ```xml:layout.xml <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="clickButton1" android:text="Button" /> ``` その上で、Activityに以下のように書きます。 ```:java public void clickButton1(View view) { // ... } ``` これだけでこのメソッドが呼ばれるようになります。 ActivityにOnClickListenerを実装する必要もありませんし、setOnClickListener()を呼ぶ必要もありません。 これだけ見ると便利そうな感じもしますが、これ、美しくないんですよね…。 - ActivityにOnClickListenerを実装する方法と同様に、publicなメソッドを実装しなければいけない。 - Fragmentにはこの方法が使えない。Activityにしか使えない(たぶん?)。 - メソッドを実装し忘れる(もしくは名前を間違える等)すると、実行時に例外が発生する。 - Activityのみを見たときに、メソッドがどこから呼ばれているのかわからない。 などなど。 今、使っている人はあまりいないんではないでしょうか。 せっかくコメントを頂いたので追記してみました。 初めからこれくらい網羅して「OnClickListener実装方法まとめ」というようなタイトルで公開した方が良かったかもしれませんね。 |
|
| 293位 |
|
|||
|
11:37:26 |
|
|
#<i class="fa fa-cubes" style="font-size:1em;"/>スタンダード
- **Android Support Library v4** - **android-support-v7-appcompat** - ~~JakeWharton/ActionBarSherlock · GitHub https://github.com/JakeWharton/ActionBarSherlock~~ - JakeWharton/Android-ViewPagerIndicator · GitHub https://github.com/JakeWharton/Android-ViewPagerIndicator #<i class="fa fa-cubes" style="font-size:1em;"/>UI ##<i class="fa fa-cube"></i>Menu - Creating a Navigation Drawer | Android Developers http://developer.android.com/training/implementing-navigation/nav-drawer.html - ~~jfeinstein10/SlidingMenu · GitHub https://github.com/jfeinstein10/SlidingMenu~~ - ~~SimonVT/android-menudrawer · GitHub https://github.com/SimonVT/android-menudrawer~~ - siyamed/android-satellite-menu · GitHub https://github.com/siyamed/android-satellite-menu - inmite/android-styled-dialogs https://github.com/inmite/android-styled-dialogs ##<i class="fa fa-cube"></i>List - ~~chrisbanes/Android-PullToRefresh · GitHub https://github.com/chrisbanes/Android-PullToRefresh~~ - ~~chrisbanes/ActionBar-PullToRefresh · GitHub https://github.com/chrisbanes/ActionBar-PullToRefresh~~ - tjerkw/Android-SlideExpandableListView · GitHub https://github.com/tjerkw/Android-SlideExpandableListView - android-amazing-listview - ListView with section headers and auto-loading of next pages (i.e. "Loading..." on the last item) - Google Project Hosting http://code.google.com/p/android-amazing-listview/ ##<i class="fa fa-cube"></i>Card - gabrielemariotti/cardslib https://github.com/gabrielemariotti/cardslib - Androguide/cardsui-for-android https://github.com/Androguide/cardsui-for-android ##<i class="fa fa-cube"></i>Animation - ~~**JakeWharton/NineOldAndroids** https://github.com/JakeWharton/NineOldAndroids~~ - [DEPRECATED] New applications should use minSdkVersion="14" or higher which has access to the platform animation APIs. - ListViewAnimations https://github.com/nhaarman/ListViewAnimations - daimajia/AndroidViewAnimations https://github.com/daimajia/AndroidViewAnimations ##<i class="fa fa-cube"></i>Notify and Progress - keyboardsurfer/Crouton https://github.com/keyboardsurfer/Crouton - JohnPersano/SuperToasts https://github.com/JohnPersano/SuperToasts - castorflex/SmoothProgressBar https://github.com/castorflex/SmoothProgressBar ##<i class="fa fa-cube"></i>~~ICS UI Backport~~ - ~~SimonVT/android-numberpicker https://github.com/SimonVT/android-numberpicker~~ - ~~SimonVT/android-datepicker https://github.com/SimonVT/android-datepicker~~ - ~~SimonVT/android-timepicker https://github.com/SimonVT/android-timepicker~~ - ~~SimonVT/android-calendarview https://github.com/SimonVT/android-calendarview~~ ##<i class="fa fa-cube"></i>misc - inmite/android-styled-dialogs https://github.com/inmite/android-styled-dialogs - johnkil/Android-ProgressFragment https://github.com/johnkil/Android-ProgressFragment - antonyt/InfiniteViewPager https://github.com/antonyt/InfiniteViewPager- - ~~yanzm/LoopViewPager https://github.com/yanzm/LoopViewPager~~ - maripo/MaripoWidgets https://github.com/maripo/MaripoWidgets -- AndroidのRadioGroupを改造し、子孫要素を全部グループ化できるようにした | コーヒーサーバは香炉である http://blog.maripo.org/2011/09/android-deep-radio-group/ - **mixi-inc/Android-Device-Compatibility** https://github.com/mixi-inc/Android-Device-Compatibility - PaoloRotolo/AppIntro https://github.com/PaoloRotolo/AppIntro - JakeWharton/timber https://github.com/JakeWharton/timber - A logger with a small, extensible API which provides utility on top of Android's normal Log class. ##<i class="fa fa-cube"></i>Utilities - **Butter Knife** http://jakewharton.github.io/butterknife/ - Annotate fields with @InjectView and a view ID for Butter Knife to find and automatically cast the corresponding view in your layout. - FRAGMENTARGS | HANNES DORFMANN http://hannesdorfmann.com/android/fragmentargs/ - Annotation Processor for setting arguments in android fragments https://github.com/sockeqwe/fragmentargs - inmite/android-validation-komensky https://github.com/inmite/android-validation-komensky - **hvisser / bundles** — Bitbucket https://bitbucket.org/hvisser/bundles - Maven Repository: com.neenbedankt.bundles http://mvnrepository.com/artifact/com.neenbedankt.bundles - zaki50/bundles_maven_repo https://github.com/zaki50/bundles_maven_repo ##<i class="fa fa-cube"></i>Material Design - ~~soarcn/BottomSheet https://github.com/soarcn/BottomSheet~~ - Bottom Sheets in Android Support Library 23.2 http://android-developers.blogspot.jp/2016/02/android-support-library-232.html - afollestad/material-dialogs https://github.com/afollestad/material-dialogs - tvbarthel/BlurDialogFragment https://github.com/tvbarthel/BlurDialogFragment #<i class="fa fa-cubes" style="font-size:1em;"/>通信系 - ~~Volley: Easy, Fast Networking for Android https://android.googlesource.com/platform/frameworks/volley~~ - **OkHttp** http://square.github.io/okhttp/ - **Retrofit** http://square.github.io/retrofit/ - **Picasso** http://square.github.io/picasso/ - ~~kevinsawicki/http-request https://github.com/kevinsawicki/http-request~~ - ~~loopj/android-async-http https://github.com/loopj/android-async-http~~ *※非推奨* - ~~loopj/android-smart-image-view https://github.com/loopj/android-smart-image-view~~ *※非推奨* - ~~koush/AndroidAsync https://github.com/koush/AndroidAsync~~ - ~~*AndroidAsync is a raw Socket, HTTP client/server, WebSocket, and Socket.IO library*~~ - ~~koush/ion https://github.com/koush/ion~~ - codegist/crest https://github.com/codegist/crest - Twitter4J http://twitter4j.org/ja/index.htm #<i class="fa fa-cubes" style="font-size:1em;"/>フレームワーク - AndroidAnnotations https://github.com/excilys/androidannotations -- How do you compare to RoboGuice? RoboGuice read those annotations at runtime, and inject the fields using reflection, which may impact the runtime performances. AndroidAnnotations generates the boilerplate code in subclasses at compile time, which obviously leads to no runtime impact on performances. - ~~Android Query - Google Project Hosting https://code.google.com/p/android-query/~~ - **ReactiveX/RxAndroid** https://github.com/ReactiveX/RxAndroid #<i class="fa fa-cubes" style="font-size:1em;"/>ユーティリティ - **ACRA** http://acra.ch/ - ~~**Otto** http://square.github.io/otto/~~ - [DEPRECATED] - Stetho http://facebook.github.io/stetho/ - **square/leakcanary** https://github.com/square/leakcanary - JakeWharton/DiskLruCache https://github.com/JakeWharton/DiskLruCache - Zxing http://code.google.com/p/zxing/ #<i class="fa fa-cubes" style="font-size:1em;"/>ユーティリティ (Java) - Apache Commons Lang http://commons.apache.org/lang/ - Apache Commons IO http://commons.apache.org/proper/commons-io/ - codegist/common https://github.com/codegist/common - square/okio https://github.com/square/okio - Gson http://code.google.com/p/google-gson/ - JDOM2 http://www.jdom.org/ - Apache Commons Collections - http://commons.apache.org/collections/ - javatuples - Main http://www.javatuples.org/ - **Joda-Time** - Home http://www.joda.org/joda-time/ - ThreeTen/threetenbp https://github.com/ThreeTen/threetenbp - Guava: Google Core Libraries for Java 1.6+ - http://code.google.com/p/guava-libraries/ > Java用ライブラリはサイズが大きいものがある。(*特にGuava) アプリケーションのサイズが大きくなるので注意しなければならない。また、com.sun パッケージや、awt/swingを使用しているライブラリは使用することが出来ない。 #<i class="fa fa-cubes" style="font-size:1em;"/>DI/モック - Guice http://code.google.com/p/google-guice/ - android-lifecyclecallbacks https://github.com/esmasui/underdevelopment/tree/master/android-lifecyclecallbacks - javax.inject - mockito http://code.google.com/p/mockito/ - Dagger http://square.github.io/dagger/ #<i class="fa fa-cubes" style="font-size:1em;"/>古いUI - 最近は使われない。 - ~~siyamed/GreenDroid https://github.com/siyamed/GreenDroid~~ - ~~yanzm/QuickActionLib https://github.com/yanzm/QuickActionLib~~ |
|
| 294位 |
|
|||
|
23:33:49 |
|
|
仕事でiPhoneアプリ開発をやるようになって半年が経ちました。面白いね!
その初期の頃に書いていたメモ書きが見つかったので、それを見返しながらまとめてみました。 新しく始める方のお役に立てれば! ##iPhoneアプリ開発の基本 ###Q. やり方が分からない A. とりあえず公式読むのがおすすめ。量が多くて躊躇するけど正しい。 最初に読むなら、以下のリンクの「初めての iOS アプリケーション」。 [日本語ドキュメント - Apple Developer](https://developer.apple.com/jp/devcenter/ios/library/japanese.html) それでも分からないって時は、困った時のドットインストール!これ以上に分かりやすいのはない! [iOSプログラミング入門 (全24回) - プログラミングならドットインストール](http://dotinstall.com/lessons/basic_ios) ###Q. StoryBoardの使い方が分からない A. ちょっと前までは、これについて詳しく書かれている本や記事がなくて困りましたが、最近はありそう。 これもドットインストールで見ると基本的な使い方は分かります。 仕事で使わなかったのでよく知らない。。 ###Q. StoryBoardって使わないの? A. うちの職場では使ってないです。xibでやってます。 凝ったアプリを作るには、あまり実用的ではない(らしい)というのと、 全画面が1つのファイルになるので、複数人で開発する時に競合が発生しやすい、という理由。 ###Q. xibって? A. StoryBoardが出来る前に使われていた、GUIで画面を作成するツール。 StoryBoardと違ってて画面遷移は担当しない。1画面1ファイル。 クラスを作る時に、「With XIB for user interface」というのを選べば自動的に作られる。 ###Q. GUIで作成した画面場の部品(ボタンとか)と、コードのひも付けはどうやってするの? A. クラス作る時に、「With XIB for user interface」を選んでいれば、画面のxibファイルとクラスは自動で関連づけされます。画面上の部品とコードとのひも付けは、画面上のボタンをcontrolを押しながら .h ファイルにドラッグする。それを押した時の処理のひも付けは、GUI上でも出来るし、コードでも書ける。詳しくは調べれば載ってると思います。自分はコードで書く派。 #####追記 > 画面上の部品とコードとのひも付けは、画面上のボタンをcontrolを押しながら .h ファイルにドラッグする コメントで教えて頂きましたが、.m にもドラッグ出来るそうです! 確かに部品のほとんどはpublicでなくていいものが多いので、.m にするケースが多そう。 ###Q. コードで書く場合、画面遷移ってどうやるの? A. 画面遷移は、基本的なパターンが3つあって、設計に応じてその中から使い分ける。 参考:[iOSアプリ画面遷移3本勝負 - たーせる日記](http://tercel-tech.hatenablog.com/entry/2012/11/13/004601) ###Q. 画面遷移時に実行したい処理はどこに書けばいいの? A. 以下のような順番でメソッドが実行されるから、そこに書く。 参考:[画面遷移時に呼ばれるメソッドの補足 - iPhoneアプリ開発まっしぐら★ - iPhoneアプリ開発グループ](http://iphone-dev.g.hatena.ne.jp/tokorom/20100110/1263150473) 上の記事はシンプルで分かりやすいけど、記事が古いから、iOS5から出来た viewWillLayoutSubviews と viewDidLayoutSubviews が載ってない。詳しくはこちら。アプリ起動した時に実行されるメソッドとかも説明されてますね。 参考:[iOS のイベント駆動をライフサイクルイベントとユーザアクションイベントにわけて理解する - A Day In The Life](http://d.hatena.ne.jp/glass-_-onion/20120405/1333611664) ###Q. 画面間のデータ共有はどうやるの? A. ややこしいから、最初は以下のページを参考にとりあえず動くものを作ってた。。 参考:[iOSゆとりプログラミングのススメ - 仕事人の開発日誌](http://d.hatena.ne.jp/omaemona01/20111204/1322961924) でもちゃんとやるなら、以下のページを参考にしましょう。 参考:[画面間でのデータの受け渡しに付いて: 永遠ログ](http://eien.seesaa.net/article/261740269.html) ###Q. コード書いたのに画面に反映されないんだけど? A. XCodeで [Product] > [Clean]すれば直るという可能性もありますが、自分がよくやっていたのは、部品を初期化する前に、色々設定していたというケースでした。 具体的にいうと、initWithNibName: 中で設置した Label や Button に文字を設定して、実行しても反映されない!というケース。それは、UIViewController のライフサイクルをもっと勉強しろという話なんだけど、viewDidLoad が実行された後にしないと LabelやButton などの部品は初期化されていないので、それに値を設定しても反映はされません。部品に初期値を設定する場合は、viewDidLoad でしましょう。 ##Objective-Cの基本 ###Q. プライベート変数やメソッドはどう書くの? A. <del>こことか参考になります。記事中にもありますが、Objective-Cの分からない事は公式ドキュメントの「Objective-Cによるプログラミング」を読むと良さそう。 参考:[イマドキっ子の Objective-C | cockscomb.info](http://cockscomb.info/objective-c_for_the_young_people_of_today/)</del> 追記(6/14):こっちの方が最近の記事なのでおすすめ。インスタンス変数の宣言の仕方って、色んな情報があって最初分からないですよね。 [Modern Objective-Cでのシンプルなプロパティ記述方式 #iPhone #Objective-C #iOS - Qiita [キータ]](http://qiita.com/items/8bcd083e59d68ad94927) ###Q. @property, @synthesize て何? A. 以下のページを参考。XCode4.4からは、@synthesize 省略可能。 参考:[Objective-Cの @property と @synthesize の組み合わせが何をやっているのかを解説 - 強火で進め](http://d.hatena.ne.jp/nakamura001/20101101/1288632739) ###Q. 定数定義ってどうやるの? A. #define か const で、 const が推奨されてるんですかね。 <del>自分は、画面に表示する文字列などは、ファイルをまとめて #defineで宣言してます。</del> 画面に表示する文字列をまとめてたのは、 Localizable.strings でした…! 参考:[Selection 9: Objective-cにおける文字列定数について](http://d.hatena.ne.jp/dice-t/20101216/1292478934) 参考:[よく使う数値や文字列をhファイルにまとめる。 - Object for cutie](http://d.hatena.ne.jp/tanaponchikidun/20120405/1333633708) |
|
| 295位 |
|
|||
|
00:22:31 |
(Freelance 所属) |
|
Ruby on Rails Advent Calendar 12日目。
[Rails開発で有用な便利Gem一覧 - Qiita [キータ]](http://qiita.com/shu_0115/items/2378ed4549542c68ef46) ↑去年のAdventCalendarで書いた上記の記事の2013年版です。 去年と比べるとRails3系で使っていたGemが順当にRails4に対応して、継続して使っている印象です。 ※2013/12/12時点 ### DBアダプタ * [sqlite3](https://github.com/luislavena/sqlite3-ruby) * [pg](https://bitbucket.org/ged/ruby-pg/wiki/Home) * [mysql2](https://github.com/brianmario/mysql2) 定番。Rails4になってもおなじみ。 ### ログイン認証 * [omniauth](https://github.com/intridea/omniauth) * [omniauth-twitter](https://github.com/arunagw/omniauth-twitter) * [omniauth-facebook](https://github.com/mkdynamic/omniauth-facebook) * [omniauth-github](https://github.com/trevorturk/omniauth-github) * [omniauth-identity](https://github.com/intridea/omniauth-identity) ↓自作したomniauth用のscaffoldを作るGemに組み込んで使っています。 [shu0115/minimum-omniauth-scaffold](https://github.com/shu0115/minimum-omniauth-scaffold) ### 定数/設定値管理 * [rails_config](https://github.com/railsjedi/rails_config) * [figaro](https://github.com/laserlemon/figaro) * [settingslogic](https://github.com/binarylogic/settingslogic) だいたいrails_configを使ってます。 ### ページネーター * [kaminari](https://github.com/amatsuda/kaminari) これはもうデファクトですね。 ### ユーティリティ * [action_args](https://github.com/asakusarb/action_args) * [html5_validators](https://github.com/amatsuda/html5_validators) これも自分が作るRailsアプリではほぼ毎回使ってます。 ### デバッグ * [tapp](https://github.com/esminc/tapp) * [awesome_print](https://github.com/michaeldv/awesome_print) * [rails-flog](https://github.com/pinzolo/rails-flog) * [pry-rails](https://github.com/rweng/pry-rails) * [better_errors](https://github.com/charliesome/better_errors) * [binding_of_caller](https://github.com/banister/binding_of_caller) * [quiet_assets](https://github.com/evrone/quiet_assets) 7日目のAdvent Calendarで紹介されている`rails-flog`も試しに使ってみてます。 [tail -f pinzo.log: Railsのログに出力されるパラメータとSQLを整形するgemを作った](http://blog.mkt-sys.jp/2013/12/rails-flog.html) ### アプリケーションサーバ * [puma](https://github.com/macournoyer/thin) * [unicorn](https://github.com/defunkt/unicorn) * [passenger](https://github.com/phusion/passenger) プロダクション環境では`unicorn`、ローカルの開発環境や、Herokuでちょっと試す時などは`puma`を使ってます。 ### Markdown * [redcarpet](https://github.com/vmg/redcarpet) * [rubychan/coderay](https://github.com/rubychan/coderay) * [redcarpet_filename_extension](https://github.com/naoty/redcarpet_filename_extension) 最近、`redcarpet`と`coderay`でMarkdownとシンタックスハイライトを実装しました。 [Markdown/シンタックスハイライト導入 - redcarpet/coderay - Qiita [キータ]](http://qiita.com/shu_0115/items/476a51cb4751515f3ac2) ### 画像管理 * [carrierwave](https://github.com/jnicklas/carrierwave) * [paperclip](https://github.com/thoughtbot/paperclip) * [mini_magick](https://github.com/probablycorey/mini_magick) 最近はほぼ`carrierwave`+`mini_magick`の構成で使ってます。 ### ジョブキュー処理 * [resque](https://github.com/defunkt/resque) * [delayed_job](https://github.com/collectiveidea/delayed_job) * [sidekiq](https://github.com/mperham/sidekiq) 参考: [ジョブキュー処理のResqueとDelayed Jobの使い分けの方針などはありますか? - QA@IT](http://qa.atmarkit.co.jp/q/2406) Rails4.0で見送られたRailsデフォルトのキューの仕組みはおそらくRails4.1に入るのではないでしょうか。 ### Twitter/Facebook API操作 * [twitter](https://github.com/sferik/twitter) * [fb_graph](https://github.com/nov/fb_graph) ### エラーハンドリング * [smartinez87/exception_notification](https://github.com/smartinez87/exception_notification) 参考:[Rails で捕捉されない例外が発生したらメールを送る #Ruby #Rails #rack #AdventCalendar - Qiita](http://qiita.com/items/adb89ede814b86e9c04a) ### 管理画面 * [rails_admin](https://github.com/sferik/rails_admin) * [active_admin](https://github.com/gregbell/active_admin) ### モバイル * [jpmobile/jpmobile](https://github.com/jpmobile/jpmobile) * [tscolari/mobylette](https://github.com/tscolari/mobylette) 以上です。 この他にも、たくさん便利なGemがあると思うので、何か良いGemがあったら是非、教えてください。 |
|
| 296位 |
|
|||
|
00:47:25 |
(Gunosy 所属) |
|
Machine Learning Advent Calendar向けの記事です。
普段はGunosyという会社で社長業をしながら社長をしています。 ## ざっくりいうと 結論だけ知りたい人はここだけ - 広告における機械学習の応用の多くはCTR予測や運用の最適化のため(クエリー予測とか)の予測問題 - 今後は「CVRの予測」や「アクティブなユーザーの予測」がホットな話題になる(加えてその運用をどう最適化するかといった話題も) - 現在は検索エンジンの応用例が多い。今後はディスプレイ広告やタイムライン広告への応用が増えていく - 個人のユーザー属性を集めることが今まで以上にメディアのビジネス的に重要になる - 広告や推薦エンジンに限らずドメイン知識は非常に重要。ドメイン知識と機械学習の知識を持ったエンジニアが意思決定に携わる会社は今後大きくのびる(と思う) ## 広告について 最近はもっぱら広告の開発をしており、広告分野でも機械学習をつかってイノベーションを起こせないかと頭を悩ませてます。アドサーバー楽しいよ。 まずは広告の基本知識を簡単におさらいします。 #### 用語 この記事にでてくる用語です。 - CPM(eCPM) - Cost Per Mille 1000PVあたりの単価。例えばCPM1000円といった場合、1PV広告が表示されると1円のコストがかかることになる。 - CPC - Cost Per Click 1クリックあたりの単価。例えばCPC60円といった場合、広告が1クリックされる毎に60円のコストがかかることになる。 - CPI - Cost Per Install 1インストールあたりの単価。例えばCPI500円といった場合、広告から1インストールされる毎に500円のコストがかかることになる。 - LTV - Life Time Value 1ユーザーがもたらす収益。LTV10000円のとき、ユーザーはそのサービスにおいて何らかの形で10000円の収益を生み出す。 - CTR - Click Through Rate クリック率のこと - CVR - Conversion Rate コンバージョンレートのこと。例えば広告をクリックしたユーザーが10人いてうち1人がコンバージョンした場合CVRは10%ということになる - 入札型広告(運用型広告) オークション形式で広告枠を買う形式の広告のこと。GoogleやFacebook、大手アドネットワークもこの形式を採用している。CPCだけの入札もあれば、CPC、CPM、CPIをすべて混ぜた入札などもある。後者の場合、予測CTRや予測CVRを出すことでオークションを行う #### 広告において広告主およびメディアは何を考えているか。何を最適化すればいいのか 広告主は広告を出すことで顧客を獲得したいと考えています。メディアは広告枠を売ることで収益化したいと考えています。ので広告周りの研究や開発ではかならず「広告主の視点」と「メディアの視点」が存在します。今風にいうならDSPとSSPとかですかね。要は広告主はうまく運用して効率よく質の良い顧客を手に入れたい。メディア側はそういった広告主の要望を読み取りつつ、ユーザーにとっても心地がよく、しかも収益性の高い広告商品を作りたいと考えています。 さて、次に具体的にみてみます。多くの広告商品で入札単位になったり、効果指標となるのが先ほどあげたCPM、CPC、CPIやLTVです。かつては広告枠をimpression(CPM)で売るのが主流でしたが、GoogleやFacebookの登場以来、CPC広告やCPI広告といった広告が増えてきました。まずはこれの意味を考えてみましょう。 ``` 広告を見る(CPM) -> 広告をクリックする(CPC) -> サービスに登録する(CPI) -> サービスを利用し、取引が発生する(LTV) ``` これは広告をみた後のユーザーの行動と課金ポイントになります。これを広告主とメディアの関係でみると、どちらがリスクをとるか(うまく予測できるか)で入札の単位が決まるということになります。 どういう意味かというと、CPMで広告を買うとします。この際広告主が考えることは広告を見た人のうち何人が広告をクリックし、そのうち何人がサービスに登録し、そのうち何人がどれくらいサービスを利用し、収益を生み出すか を予測して、広告予算を算出するはずです。 これはつまり広告主が「CTRを予測し、CVRを予測し、その後のLTVも予測している」という状態です。一方メディアの仕事は「インプレションを集める」です。 次にCPC広告を考えてみましょう。この場合広告主の仕事は「CVRと、その後のLTVの予測」であり、メディアは「インプレッションを集める、CTRを予測する」となります。(CTRの予測をミスるとメディア側が損をするからです) これがCPI広告となると、広告主の仕事は「LTVの予測」だけとなり、つまりこれは広告主が自分の事業のコア岳に集中できる状態となります。メディア側にはさらに「CVRの予測」という仕事が加わります。つまりCPC広告やCPI広告の増加は「メディアがよりリスクをとるようになっている」という状態であり、メディアにとって「CTRの予測」や「CVRの予測」が重要になってきています。 広告主にとっては先ほどの流れで右へいけば行くほどうれしい状態になります。予測する変数がへっていき、自分たちのコアな部分に集中できるからです。現在のネット広告市場の場合、トラッキングツールの普及もありCPI広告がかなり好まれているように思えます。ただ実際のメニュー数はそんなに多くなく、中心はCPC広告になっています。 これが現在の広告の流れです。今後トラッキングツールのさらなる普及や発達により、よりLTVに対して最適な配信を標榜するメディア(や代理店)が出てくると思われます。CPIが同じでも、メディアによって継続率がちがったり、課金率が違ったりすることは非常によくおこります。 メディアによっては自分たちのメディアが非常に価値が高く、質の高いユーザーや広告主に対して相性の良いユーザーをおくることでLTVを高めていることを証明できれば、ほかのメディアよりも高いCPIを許容してくれると考えるでしょう。そのため今後はただインストールさせる、ただ登録させるだけでなくその後の継続率や課金率、課金額の高さ間で考慮した商品も出てくるのではないでしょうか。また広告主側の視点で見るとそういった多様な商品が出てくるなかで、どのような配分で予算を消化すればよいか。どういった運用をすればいいかといった問題も出てきます。 こうなってくると機械学習が広告業界において非常に重要になってくると僕は考えています。ユーザーの行動履歴を集め、CTRを予測する、CVRを予測する、その後のLTVを予測するというタスクはメディアにとって非常に重要になります。またそういった商品が様々に出てくる中で、広告主がどう運用すればよいのか(最適なクエリーの予測や、ボリュームの予測、予算の最適配分etc)といった問題は機械学習が得意な分野であるからです。 ## 実際の研究事例 実際どういった研究があるのか調べてみました。 www2013のResearch TrackにSearch Advertisingというものがありました。のでどんな論文があるかを軽く見ると、 - Ad Impression Forecasting for Sponsored Search 広告主が、ある単価で入札したとき、どれくらいのuserに広告が見られるかの予測。ベイジアンネットを使ってる。Bingのデータで実験。Microsoftの中の人の研究 - Multi-Label Learning with Millions of Labels: Recommending Advertiser Bid Phrases for Web Pages 広告主がどのクエリーをえらぶべきか。web pageにでてくる言葉をNLP的に分析する。Multi-label Random Forestsを使ってる。Microsoftの中の人の研究 - Predicting Advertiser Bidding Behaviors in Sponsored Search by Rationality Modeling 広告主がオークションの単価をどうかえるかの予測。広告主の行動をモデリングし、検索エンジンの正確な収益予測ができるように。Microsoft Research - A Predictive Model for Advertiser Value-Per-Click in Sponsored Search どのキーワードが広告の価値を高めたか。keyword value prediction。線形モデルでどうたらこうたら。唯一の大学の研究 といった感じでした。メディア側より、広告主側の研究が多いですね。(そしてほとんどがMicrosoftの中の人の研究。Facebookも研究所作ったしこうなってくのかな) メディア側はアルゴリズムとかを公開したくないからなかなか論文としては公開しにくい?のでしょうかね。 またwww2012ではAdvertising on the WebというSessionがあります。こちらは論文名だけ - On Revenue in the Generalized Second Price Auction - Handling Forecast Errors While Bidding for Display Advertising - Optimizing Budget Allocation Among Channels and Influencers - Risk-Aware Revenue Maximization in Display Advertising - Targeting Converters for New Campaigns Through Factor Models - How Effective Is Targeted Advertising? How Effective Is Targeted Advertising?だけは読んだのですが、targeted adは過大評価されているので正当に評価しましょうというYahooのデータを使った論文でした。なかなか面白かったです。 そしてこうしてみるとやはりdata is kingというか、個人の属性情報なり、メディアにたまっていくログ(検索ログや、実際の広告のクリックログとか)をしっかり集めて、分析し、改善・反映するってサイクルをまわされると、ちょっとやそっとでは追いつけないような差になっていきますね。 またGoogle Scholarで「advertising predict ctr」などで検索してみると - Predicting clicks: estimating the click-through rate for new ads(www2007) - A novel click model and its applications to online advertising(WSDM2010) - Temporal analytics on big data for web advertising(IEEE2012) などなど面白そうな論文が結構でてきました。(ちなみにこの3つも全部Microsoft。すごいですねMicrosoft) ここら辺、気になるので、きちんと中身もちょくちょく読んで紹介できたらなと思います。 と、現状searchに対する広告の研究だったり応用が多いですが、今後RTBやDSP、SSPの普及でディスプレイ広告やタイムライン広告にもこういった研究の波が押し寄せるのではないでしょうか。 また公開はされてないですが、GoogleやFacebookなどメディア側の視点で様々なシグナルや機械学習のアルゴリズムを利用し、CTRの予測やCVRの予測で収益をふやし、かつユーザーにとって心地よい広告を作るといった努力を現在進行形でやっているのだろうなあと思ってます。ここの分野はかなり改善の余地がありおもしろいですね。 ## 最後に 今回は最近の仕事とあいまって、広告と機械学習について書きました。推薦エンジンや広告などを作ってみて思うのは、「ドメイン知識」の重要さです。間違った方向に学習させても何にもならないからです。 機械学習では未だどの方向に学習すべきかの仮説を立てる部分において人間のセンス、ドメイン知識が非常に重要だと感じます。 推薦エンジンや広告にかぎらず、今後様々な分野で機械学習が応用され、ホットであることは間違いないでしょう。その際思うのはドメイン知識を持ちかつ機械学習の専門知識を持つエンジニアがどこまで意思決定に関われるかがすごく大事だなあということです。 まとまってないですが以上で。 |
|
| 297位 |
|
|||
|
11:38:33 |
|
|
すでにリポジトリにignoreファイルを追加していると、 キャッシュが残っており.gitignoreファイルの内容が反映されない。 ```Bash:.gitignore git rm -r --cached . git add . git commit -m ".gitignore is now working" git push origin master ``` |
|
| 298位 |
|
|||
|
16:18:40 |
(oshiire 所属) |
|
Vagrant では、一つの BASE BOX イメージから、複数の VMを起動することができるようになってえらくステキです。
勿論、複数のイメージファイルを指定することもできますけれども、BASE BOX は素の状態にしておいて、各サーバは Chef の Role を使って識別して、構成できるようにするのが宗教的理由により優れていると判断しましたので、そのように構成してみようと至ったわけです。 今は、たまたま、ZABBIX2.2の環境を構成しようとしているのですが、ZABBIX の Webフロントエンドと、ZABBIX本体(+DB)を別々のサーバにして構成してみようというところから、この考えに至ったというか、今朝、[入門Chef Solo - Infrastructure as Code](http://www.amazon.co.jp/dp/B00BSPH158/)を読んでいたら、「Vagrant で、複数のVMを管理できるのか!!( ゚д゚ )クワッ!! Vagrant やべぇ!!」となったので、試してみただけです。 宗教的とかいってゴメンなさい、思いつきです。 # 1.正攻法で複数のVMをVagrantで制御する方法 まず、まっとうに、複数の BOXを元にして、それぞれの VMを起動する方法です。 めでたいことに Vagrantのdocumentとして[MULTI-MACHINE](http://docs.vagrantup.com/v2/multi-machine/index.html)のところに、その記述があります。 ```ruby:複数マシンの定義 Vagrant.configure("2") do |config| config.vm.provision "shell", inline: "echo Hello" config.vm.define "web" do |web| web.vm.box = "apache" end config.vm.define "db" do |db| db.vm.box = "mysql" end end ``` 複数定義しているところは、`config.vm.define` 部分ですね。それぞれ、`web` と `db` と関連づけられた VMを指定するようにしてあります。対象は次のとおりです。分かりやすいですね。 | 定義名 | 対象VM | |---:|:---| |web|apache| |db|mysql| このように指定することで、複数の VMを vagrantで制御できるようになります。 この時、`vagrant up` すると、全ての VMが起動します。 vagrant は `command` + `定義名` で個別の制御が可能です。従って、`vagrant up web` とすれば、`apache` VM のみを、`vagrant up db` とすれば `mysql` VM のみを起動することができます。`ssh` や `halt`も同様です。 ## sshの定義 みなさまご存じの通り、vagrant では `vagrant ssh-config` にて、ssh の構成を自動的に作ってくれる、便利な機能がありやんす。 これのお陰で、`~/.ssh/config` が溢れんばかりになっているクズもいることでしょうが、これほど便利な機能もありません。コレのために vagrant を使っていると言っても過言ではありません。ウソです。 さて、このように複数VMの場合ではどうすれば良いか。 同じです。 `vagrant ssh-config web` とすれば、おめでたい感じで出てきますが、`--host` をつけると、対象の定義名を変更して割り当てることもできます。 例えば、`vagrant ssh-config web --host web.oshiire.to` なんていう風にすれば、このようになります。 ```bash:~/.ssh/config Host web.oshiire.to HostName 127.0.0.1 User vagrant Port 2222 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile /Users/sho/.vagrant.d/insecure_private_key IdentitiesOnly yes LogLevel FATAL ``` (Mavericks使用:`sho`ユーザの場合) このようなおめでたい定義を、ssh コマンドで利用できるよう、`vagrant ssh-config web --host web.oshiire.to >> ~/.ssh/config` としておくと、`knife` をぶん回すときにも、`knife solo cook web.oshiire.to` なんて感じで使うことができて便利です。やったね、たえちゃん。 # 2.一つのboxから、複数の VMをVagrant で制御する方法 今日のメインはコレです。 実際に構成ファイルを見てもらった方が良いでしょう。どうぞ。 ```ruby:1BOXで複数VMの定義 VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "debian-7.4_c64" config.vm.define :web do | web | end config.vm.define :zabbix do | zabbix | end ``` もろに我が家での構成ファイルですが、`config.vm.define` の外で、`config.vm.box` を指定することで、この vm.boxを元に、必要なVMの数だけ VMがコピーされて利用できるようになります。 今回は 2つだけですが、4個も 10個も書けば、その分の VMが起動されて、あなたのマシンのリソースがなくなって大変なことになるでしょう。ぜひ、お試しください。 ##VM間通信をできるようにするには ぶっちゃけ、ここまでの話はどうでも良かったんです。そこら中に書いてあるので。 私がはまったのは、この先です。困ったことに、private_network 同士のVM間で通信できないのです。 イロイロと資料を見る限り、`xx.vm.network` で `hostonly` のネットワークを作れば、VM間での通信ができると、入門Chef にも多くのblogでも散見されました。しかし、うまくいかない。 どうしたもんかと、結局、Vagrant本家の[PRIVATE NETWORKS](http://docs.vagrantup.com/v2/networking/private_network.html)を見てみると、書いてあるじゃないですか。`virtualbox__intnet: true` しろよって。 ただ、これだけじゃあかんのですわ。 `virtualbox__` (ご丁寧に、安駄婆(あんだばあ)は二つって注釈付) prefix のものは、その指定した内容を、vitualbox provider へ送ることができるらしいです。んで、private な内部ネットワークを作る場合、virtualbox では、その `内部ネットワーク名` を指定する必要があるんです。で、その同一のネットワークでは疎通が可能になる「みたい」です。 従って、VM同士でネットワークをつなげたい場合、`virtualbox__intnet: "xxxx"`と共通の名前を付けましょうというのが、今日の一大事です。覚えて帰りましょう。 結果、我が家では次のように指定しています。みなさまも、どうぞご健勝ください。 ```ruby:virtualbox__intnet VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # All Vagrant configuration is done here. The most common configuration # options are documented and commented below. For a complete reference, # please see the online documentation at vagrantup.com. # Every Vagrant virtual environment requires a box to build off of. config.vm.box = "debian-7.4_c64" config.vm.define :web do | web | web.vm.hostname = "web" web.vm.network :private_network, ip: "192.168.33.10", virtualbox__intnet: "intnet" end config.vm.define :zabbix do | zabbix | zabbix.vm.hostname = "zabbix" zabbix.vm.network :private_network, ip: "192.168.33.20", virtualbox__intnet: "intnet" end ``` ついでなので、各VM に `xxx.vm.hostname` を使って、host名も定義しています。これで host名も変更されてステキです。どうぞ、ご利用ください。 ※ 当環境は vagrant 1.4.3 + virtualbox 4.3.6 においてで、今後のversionによっては変更がなされるやも知れません。 |
|
| 299位 |
|
|||
|
19:14:15 |
|
|
=============================================
ナイーブベイズ分類器、あるいは単純ベイズ分類器という分類器について解説したいと思います。 何それ?という方。まずはわけがわからないとしてもWikipediaのエントリを見てみましょう。 http://ja.wikipedia.org/wiki/単純ベイズ分類器 上の説明でよくわかったという方はこれ以上先に進む必要はありません。 ナイーブベイズ分類器は、一言でいうと、 分類問題ってベイズの定理を使えば解けるんじゃね? というものです。入力 $X$ が与えられた時に出力 $Y$ が得られる確率 $P(Y|X)$ は以下の等式で表す事が出来ます: $$ P(Y|X) = \frac{P(Y) P(X|Y)}{P(X)} $$ これがベイズの定理です。 $P(Y)$ は事前分布と呼ばれ、 $P(X|Y)$ は尤度とか条件付き確率とか呼ばれます。例を挙げると、 * X=受信メール, Y={迷惑メールである, 迷惑メールでない} * X=今日のテレビ番組, Y={見たい, そこそこ見たい, 見たくない} * X=仕事休んだ, Y={病気です, ずる休みです, 日曜だと思ってました} のように、Xには何らかの起こった事象や入力データを、Yにはそこから推論したい事柄などを当てはめるわけです。 ナイーブベイズ分類器は上の式の右辺を求めるわけですが、右辺を完璧に求めたいわけではなく、あるXに対して 最も確からしい、つまり最も値が大きくなるYを求めます。この時、Xはもう固定されて変数ではなく定数なので、次の 右辺に比例する式を求めれば十分です: $$ P(Y|X) \propto P(Y) P(X|Y) $$ 分母の $P(X)$ がなくなりました。最終的にはこの式の右辺を求めて、右辺が最大になるYを答えとして求める事になります。 このままでは単なるベイズ分類器です。ナイーブベイズ分類器は確率分布 $P(X|Y)$ をシンプルな分布に限定します。 いま、 Xはなんかよくわからんものですが、これを多次元変数であるとしましょう: $$ X = \\{x_1, x_2, x_3, \dots, x_N \\} $$ なので、みなさんは自分で解きたい問題を頑張って多次元変数にする必要があります。X=俺の今の気持ち、とかしても無理です。 それはさておき、 $P(X|Y)$ を以下の形に限定します: $$ P(X|Y) = \prod_{i=1}^N P(x_i|Y) $$ これがナイーブベイズ分類器です。以上終わり。 ・・・とこれで終わってしまうと味気なさすぎるので、もうちょっと突っ込んでいきます。 ナイーブベイズを解く ------------------ 今、学習データとしてD個の正解を今持っているとします: $$ \\{ (S, T) \\} = (X=S_1, Y=T_1), (X=S_2, Y=T_2), ..., (X=S_D, Y=T_D) $$ $(S_j, T_j)$ は入力と出力の正解データのペアを表しています。また、取りうるYの値は離散値で、1, 2, ..., Kのうちのどれかだとします。ちゃんと書くと、 $$ Y \in \\{ 1, 2, \dots, K \\} $$ です。 正解データはそれぞれ独立していて、データの順番は関係ないものとします。 独立していない場合というのは、例えば時系列データ等で、 $T_2$ や $S_2$ が $T_1$ や $S_1$ に依存して してしまうようなものです。 具体的な例を挙げると、昨日の試合でボロ負けした場合、チームメンバーの闘志に火がついてしまって、実は今日の試合結果に影響してしまう、といったようなことです。 さて、求める確率分布はこれら正解データをちゃんと再現出来ていなければなりません。 その一つのやり方として、(S, T)のデータを全部当てはめてみたら確率が一番大きくなっているものを選びます。 すなわち、 > $S_1$ だった時 $T_1$ で、かつ $S_2$ だった時 $T_2$ で、かつ ... で、かつ $S_D$ だった時 $T_D$ である確率を最大化する 先ほどのベイズモデルをちょっと具体的にして、 $$ P(Y; \Theta, \Phi) P(X; \Theta|Y) $$ とします。ここで $\Theta = \\{ \theta_1, \theta_2, ..., \theta_M \\}$、$\Phi = \\{ \phi_1, \phi_2, ..., \phi_L \\}$ は確率分布を特徴付けるパラメータです。この式を使うと、最大化する確率は $$ M(\Theta, \Phi) = \prod_{j=1}^D P(T_j; \Theta, \Phi) P(S_j; \Theta|T_j) $$ と書くことが出来ます。この時点で、変数はもはや $\Theta$ と $\Phi$ だけです。つまり、やるべきことはMを最大にする $\Theta$ と $\Phi$ を求める事です。 この式はもう少しだけ整理することが出来ます。 $T_j$ は1からKのどれかなので、正解データを綺麗に1から順番になるように並び替えましょう。正解データは独立なのでいくら並び替えても文句はないはずです。Y=kの正解データの個数を $Q_k$ と書くと、 $$ \begin{align\*} M(\Theta, \Phi) &= P(1; \Theta, \Phi) P(S_1; \Theta|1) \times P(1; \Theta, \Phi) P(S_2; \Theta|1) \times \cdots \times P(1; \Theta, \Phi) P(S_{Q_0}; \Theta|1) \\\\ & \times P(2; \Theta, \Phi) P(S_{Q_0 + 1}; \Theta|2) \cdots \\\\ &= \prod_{k=1}^K P(k; \Theta, \Phi) \prod_{i = 1}^{Q_k} P(S_{ki}; \Theta|k) \end{align\*} $$ ここで、 $S_{ki}$ はY=kになるSのi番目、という意味です。 これでより見通しがよくなりました。 Mが最大になる必要条件は?それは、微分して0になることです: $$ \begin{align\*} 0 &= \frac{\partial}{\partial \Phi} M(\Theta, \Phi) \\\\ 0 &= \frac{\partial}{\partial \Theta} M(\Theta, \Phi) \end{align\*} $$ 一般化したのでとりあえずこれ以上先には進めません。次にいくつかの具体例でこの条件を解いてみます。 ガウスモデル ----------- $P(x_i|Y)$ にガウス分布を仮定したモデルです: $$ P(X|Y) = \prod_i P(x_i|Y) = \prod_i \frac{1}{\sqrt{2 \pi \sigma_Y^2}} \exp \left( - \frac{1}{2 \sigma_Y^2} (x_i - \mu_{Yi})^2 \right) $$ 一つのYにつき、一組の $(\mu_Y, \sigma_Y)$ でモデル化しています。 $\mu_Y$はN次元の量である事に注意です。 ではガウスモデルを解いてみます。一例として、事前分布 $P(Y)$ は一様分布(定数)だとします。この時、変数 $\Phi$ に相当するものは無いので考える必要はありません。変数 $\Theta$ に対応するのは $(\mu_Y, \sigma_Y)$ です。最大化すべきMは、 $$ \begin{align\*} M(\Theta) &= \prod_{k=1}^K \text{Const} \prod_{q_k = 1}^{Q_k} P(S_{ki}; \Theta|k) \\\\ M(\vec{\mu}, \vec{\sigma}) & \propto \prod_{k=1}^K \prod_{i = 1}^{Q_k} \prod_{j = 1}^N \frac{1}{\sqrt{2 \pi \sigma_k^2}} \exp \left( - \frac{1}{2 \sigma_k^2} (s_{kij} - \mu_{kj})^2 \right) \end{align\*} $$ です。小文字sがえらい事になっていますが、"Y=kになる正解データのi番目の入力データのj番目の要素"です。 $\vec{\mu}$ のある一つの要素 $\mu_{lm}$ について微分したものが0になる条件は、 $$ \begin{align\*} 0 &= \frac{\partial M}{\partial \mu_{lm}} \\\\ 0 &= \prod_{j \ne m} \prod_{k \ne l} \prod_{i = 1}^{Q_k} \exp \left( - \frac{1}{2 \sigma_k^2} (s_{kij} - \mu_{kj})^2 \right) \frac{\partial}{\partial \mu_{lm}} \prod_{i = 1}^{Q_l} \exp \left( - \frac{1}{2 \sigma_l^2} (s_{lim} - \mu_{lm})^2 \right) \\\\ 0 &= \frac{\partial}{\partial \mu_{lm}} \exp \left( - \frac{1}{2 \sigma_l^2} \sum_{i = 1}^{Q_l} (s_{lim} - \mu_{lm})^2 \right) \\\\ 0 &= \exp \left( - \frac{1}{2 \sigma_l^2} \sum_{i = 1}^{Q_l} (s_{lim} - \mu_{lm})^2 \right) \frac{\partial}{\partial \mu_{lm}} \left( - \frac{1}{2 \sigma_l^2} \sum_{i = 1}^{Q_l} (s_{lim} - \mu_{lm})^2 \right) \\\\ 0 &= \sum_{i = 1}^{Q_l} (s_{lim} - \mu_{lm}) \exp \left( - \frac{1}{2 \sigma_l^2} \sum_{i = 1}^{Q_l} (s_{lim} - \mu_{lm})^2 \right) \\\\ 0 &= \left( \sum_{i = 1}^{Q_l} s_{lim} - Q_l \mu_{lm} \right) \exp \left( - \frac{1}{2 \sigma_l^2} \sum_{i = 1}^{Q_l} (s_{lim} - \mu_{lm})^2 \right) \end{align\*} $$ $\mu_{lm}$ が無限大、という解は置いておいて、 $$ \mu_{lm} = \frac{1}{Q_l} \sum_{i = 1}^{Q_l} s_{lim} $$ が得られました。直感的に当たり前な感じでいい感じですね! 次に、ある $\\sigma_l$ について微分したものが0になる条件は、 $$ \begin{align\*} 0 &= \frac{\partial M}{\partial \sigma_l} \\\\ 0 &= \frac{\partial}{\partial \sigma_l} \prod_{k=1}^K (2 \pi \sigma_k^2)^{- \frac{NQ_k}{2}} \exp \left( - \frac{1}{2 \sigma_k^2} \sum_{i = 1}^{Q_k} \sum_{j = 1}^N (s_{kij} - \mu_{kj})^2 \right) \\\\ 0 &= \frac{\partial}{\partial \sigma_l} \sigma_l^{- NQ_l} \exp \left( - \frac{1}{2 \sigma_l^2} \sum_{i = 1}^{Q_l} \sum_{j = 1}^N (s_{lij} - \mu_{lj})^2 \right) \\\\ 0 &= - NQ_l \sigma_l^{- NQ_l - 1} \exp \left( - \frac{1}{2 \sigma_l^2} \sum_{i = 1}^{Q_l} \sum_{j = 1}^N (s_{lij} - \mu_{lj})^2 \right) + \sigma_l^{- NQ_l - 3} \sum_{i = 1}^{Q_l} \sum_{j = 1}^N (s_{lij} - \mu_{lj})^2 \exp \left( - \frac{1}{2 \sigma_l^2} \sum_{i = 1}^{Q_l} \sum_{j = 1}^N (s_{lij} - \mu_{lj})^2 \right) \\\\ 0 &= - \sigma_l^{- NQ_l - 1} \exp \left( - \frac{1}{2 \sigma_l^2} \sum_{i = 1}^{Q_l} \sum_{j = 1}^N (s_{lij} - \mu_{lj})^2 \right) \left( NQ_l - \sigma_l^{-2} \sum_{i = 1}^{Q_l} \sum_{j = 1}^N (s_{lij} - \mu_{lj})^2 \right) \\\\ \end{align\*} $$ $\sigma_l$ が無限大、という解は置いておいて、 $$ \sigma_l^2 = \frac{1}{NQ_l} \sum_{i = 1}^{Q_l} \sum_{j = 1}^N (s_{lij} - \mu_{lj})^2 \\\\ $$ が得られました。こちらも直感的に当たり前な感じでいいですね! 得られた $\vec{\mu}$ 、 $\vec{\sigma}$ を $P(X|Y)$ の式に入れてあげればモデルの完成です。 ベルヌーイ分布 ------------- 各 $x_i$ が二値でしか表されない時に用いられる最もシンプルなモデルです。値はなんでもいいんですが、例えば0か1しかない、つまり $$ {}^\forall i, \quad x_i \in \\{ 0, 1 \\} $$ な状況において、次のような式で表されます: $$ P(x_i|Y) = \delta(1, x_i) p_i + \delta(0, x_i)(1 - p_i) $$ ここで、 $\delta(\cdot)$ はデルタ関数です。 $p_i$ は $x_i=1$ となる確率そのものを表している変数です。 またも、事前分布 $P(Y)$ は一様分布(定数)だとします。この時、変数 $\Phi$ に相当するものは無いので考える必要はありません。変数 $\Theta$ に対応するのは $p_i$ です。最大化すべきMは、 $$ \begin{align\*} M(\Theta) &= \prod_{k=1}^K \text{Const} \prod_{q_k = 1}^{Q_k} P(S_{ki}; \Theta|k) \\\\ M(\vec{p}) & \propto \prod_{k=1}^K \prod_{i = 1}^{Q_k} \prod_{j = 1}^N \left( \delta(1, s_{kij}) p_{kj} + \delta(0, s_{kij})(1 - p_{kj}) \right) \end{align\*} $$ です。$p_{kj}$ はY=kの時の $x_j$ の確率そのものを表しています。 $\vec{p}$ のある一つの要素 $p_{lm}$ について微分したものが0になる条件は、 $$ \begin{align\*} 0 &= \frac{\partial M}{\partial p_{lm}} \\\\ 0 &= \frac{\partial}{\partial p_{lm}} \prod_{i = 1}^{Q_l} \left( \delta(1, s_{lim}) p_{lm} + \delta(0, s_{lim})(1 - p_{lm}) \right) \\\\ 0 &= \sum_{u=1}^{Q_l} \frac{\partial}{\partial p_{lm}} \left( \delta(1, s_{lum}) p_{lm} + \delta(0, s_{lum})(1 - p_{lm}) \right) \prod_{i \ne u}^{Q_l} \left( \delta(1, s_{lim}) p_{lm} + \delta(0, s_{lim})(1 - p_{lm}) \right) \\\\ 0 &= \sum_{u=1}^{Q_l} \left( \delta(1, s_{lum}) - \delta(0, s_{lum}) \right) \prod_{i \ne u}^{Q_l} \left( \delta(1, s_{lim}) p_{lm} + \delta(0, s_{lim})(1 - p_{lm}) \right) \\\\ 0 &= \left( \sum_{u=1}^{Q_l} \frac{\delta(1, s_{lum}) - \delta(0, s_{lum})}{\delta(1, s_{lum}) p_{lm} + \delta(0, s_{lum})(1 - p_{lm})} \right) \prod_{i = 1}^{Q_l} \left( \delta(1, s_{lim}) p_{lm} + \delta(0, s_{lim})(1 - p_{lm}) \right) \\\\ \end{align\*} $$ 最初のカッコの中を0にすればいいので、 $$ \begin{align\*} 0 &= \sum_{u=1}^{Q_l} \frac{\delta(1, s_{lum}) - \delta(0, s_{lum})}{\delta(1, s_{lum}) p_{lm} + \delta(0, s_{lum})(1 - p_{lm})} \\\\ 0 &= \sum_{u=1}^{Q_l} \frac{\delta(1, s_{lum})}{\delta(1, s_{lum}) p_{lm} + \delta(0, s_{lum})(1 - p_{lm})} - \sum_{u=1}^{Q_l} \frac{\delta(0, s_{lum})}{\delta(1, s_{lum}) p_{lm} + \delta(0, s_{lum})(1 - p_{lm})} \\\\ 0 &= \sum_{u=1}^{Q_l} \frac{\delta(1, s_{lum})}{\delta(1, s_{lum}) p_{lm}} - \sum_{u=1}^{Q_l} \frac{\delta(0, s_{lum})}{\delta(0, s_{lum})(1 - p_{lm})} \\\\ 0 &= \sum_{u=1}^{Q_l} \delta(1, s_{lum}) \frac{1}{p_{lm}} - \sum_{u=1}^{Q_l} \delta(0, s_{lum}) \frac{1}{1 - p_{lm}} \\\\ \end{align\*} $$ これで、確率の比 $$ \begin{align\*} \frac{\sum_{u=1}^{Q_l} \delta(1, s_{lum})}{\sum_{u=1}^{Q_l} \delta(0, s_{lum})} &= \frac{p_{lm}}{1 - p_{lm}} \end{align\*} $$ が得られます。 $p_{lm}$ は、 $$ Q_l = \sum_{u=1}^{Q_l} \delta(0, s_{lum}) + \sum_{u=1}^{Q_l} \delta(1, s_{lum}) $$ という関係を使って、 $$ \begin{align\*} p_{lm} \sum_{u=1}^{Q_l} \delta(0, s_{lum}) &= (1 - p_{lm}) \sum_{u=1}^{Q_l} \delta(1, s_{lum}) \\\\ p_{lm} &= \frac{\sum_{u=1}^{Q_l} \delta(1, s_{lum})}{\sum_{u=1}^{Q_l} \delta(0, s_{lum}) + \sum_{u=1}^{Q_l} \delta(1, s_{lum})} \\\\ p_{lm} &= \frac{1}{Q_l} \sum_{u=1}^{Q_l} \delta(1, s_{lum}) \end{align\*} $$ で得ることができます。この式の右辺は $s_{lum} = 1$となる正解データの個数を全体で割ったものです。 当たり前だと思われている事実をちゃんと導けましたね! 多項分布モデルと文書分類 ----------------------- この節では、文書分類を具体例に多項分布のモデルを解いてみます。 ナイーブベイズがよく使われる例に文書分類タスクがあります。文書分類タスクというのは、文書が与えられた時に例えばそのカテゴリやトピック、タグのようなものを割り当てるというタスクです。別に文書と言っていますがメールだったりツイートだったり、なんかまとまったテキストの事です。迷惑メールの自動振り分けなんかもこれで、迷惑メールであるかそうでないかの2つのカテゴリを割り当てる事に対応します。 文書分類タスクをナイーブベイズで解く際に用いられる最も単純なモデルは多項分布モデルです。このモデルは文書を確率で表現する際に、 1. 文書は単語の並びである 2. 文書中、ある単語が現れる確率は他の単語に依存しない 3. 文書中、ある単語がi番目に現れる確率はiに依存しない という条件を課す事で得られます。この条件でモデルを作ってみましょう。事前分布 $P(Y)$ はひとまず置いておきます。 条件付き確率 $P(X|Y)$ は、Xを単語の並び $X = \\{w_1, w_2, \dots, w_N \\}$ ( $w_i$ はある単語)として $$ P(X|Y) = \prod_{i=1}^N P(w_i|Y) $$ と書けます。条件2があるので確率が単語毎にバラバラになりますね。次に、条件3があるので、 $P(w_i|Y)$ は文書中の出現位置iに依存しません。なので、単語 $w_i$ を50音順にソートしてしまいましょう。例えば、”この りんご は おいしい りんご だ”という文は”おいしい この だ は りんご りんご”になります。単語にIDを1から順に振って、 $P(\text{ID}|Y) = p_{\text{ID}}$ とすると、 $$ P(X|Y) = \prod_{i=1}^V p_i^{c_i} $$ ここで、Vは単語数(単語IDの上限)、 $c_i$ は単語ID i番の単語の文書X中での出現数になります。 実は、この節のタイトルは多項分布モデルとなっていますが、厳密な意味では多項分布の形にはなっていません。原因は、文書を多次元変数として捉えるときに、今回の様に一次元系列として捉えるか、単語の出現頻度を文書の表現として捉えるかの立場の違いによります。 私は今回のように問題を捉える方が直感的で理解しやすいと思います。なのでここではbag of wordsとか一切出てきません。 ### 事前分布を一様として解いてみる まずは一様な事前分布を仮定して解いてみます。最大化すべき関数Mは $$ \begin{align\*} M(\Theta, \Phi) &= \prod_{k=1}^K P(k; \Theta, \Phi) \prod_{i = 1}^{Q_k} P(S_{ki}; \Theta|k) \\\\ & \propto \prod_{k=1}^K \prod_{i = 1}^{Q_k} \prod_{j=1}^V p_{kj}^{c_{kij}} \\\\ \end{align\*} $$ です。事前分布は定数項なのでカットします。また、 $S_{ki}$ に対応するのは $p_{kj}$ と $c_{kij}$ です。 $p_{kj}$ はカテゴリがkである文書が持つ、単語IDがjの単語の確率です。 $c_{kij}$ はカテゴリがkである文書の、i番目の正解データの、単語IDがjの単語が出てくる数、です。 ちょっと見通しが悪いので、iに関する積をまとめて指数の肩に載せましょう: $$ \begin{align\*} M(\vec{p}) &\propto \prod_{k=1}^K \prod_{j=1}^V p_{kj}^{\sum_{i = 1}^{Q_k} c_{kij}} \\\\ \end{align\*} $$ ここで、 $$ \tau_{kj} = \sum_{i = 1}^{Q_k} c_{kij} $$ はカテゴリがkである正解データに含まれる単語ID jの単語の総数、です。 $$ \begin{align\*} M(\vec{p}) &\propto \prod_{k=1}^K \prod_{j=1}^V p_{kj}^{\tau_{kj}} \\\\ \end{align\*} $$ さて、Mを $\vec{p}$ のある一つの要素 $p_{lm}$ で微分して極値を求めましょう。ただし、今回は $p_{kj}$ に以下の条件が付きます: $$ 1 = \sum_{j = 1}^V p_{kj} $$ この条件をみたすようにMを最大化するためにラグランジュの未定定数法を使います。 $$ \begin{align\*} 0 &= \frac{\partial}{\partial p_{lm}} \left( \prod_{k=1}^K \prod_{j=1}^V p_{kj}^{\tau_{kj}} - \beta \left(1 - \sum_{j = 1}^V p_{kj} \right) \right) \\\\ 0 &= \tau_{lm} p_{lm}^{\tau_{lm} - 1} \prod_{k \ne l}^K \prod_{j \ne m}^V p_{kj}^{\tau_{kj}} - \beta \\\\ 0 &= \tau_{lm} p_{lm}^{- 1} \prod_{k=1}^K \prod_{j=1}^V p_{kj}^{\tau_{kj}} - \beta \\\\ p_{lm} &= \frac{\tau_{lm}}{\beta} \prod_{k=1}^K \prod_{j=1}^V p_{kj}^{\tau_{kj}} \\\\ \end{align\*} $$ ここで、 $$ 1 = \sum_{j = 1}^V p_{kj} $$ の条件を使って、両辺和を取ると、 $$ \begin{align\*} 1 &= \sum_{m=1}^V \frac{\tau_{lm}}{\beta} \prod_{k=1}^K \prod_{j=1}^V p_{kj}^{\tau_{kj}} \\\\ \beta &= \sum_{m=1}^V \tau_{lm} \prod_{k=1}^K \prod_{j=1}^V p_{kj}^{\tau_{kj}} \end{align\*} $$ これを $\beta$ に代入してあげれば、 $$ \begin{align\*} p_{lm} &= \frac{\tau_{lm}}{\beta} \prod_{k=1}^K \prod_{j=1}^V p_{kj}^{\tau_{kj}} \\\\ &= \frac{\tau_{lm}}{\sum_{m=1}^V \tau_{lm}} \end{align\*} $$ もう一度説明すると、 $\tau_{lm}$ は、カテゴリがlである正解データに含まれる単語ID mの単語の総数、です。 ### 頻度0問題とディリクレ事前分布 上で解いた分布には実用上大きな問題があります。それは、 * 正解データに一つも含まれない単語(未知語)があると確率が0になってしまう というものです。式中では $\tau_{lm}$ に相当します。せっかくなので、この問題に事前分布を設定することによって対処します。業界では、この事を”事前知識を与える”等と言ったりします。 多項分布モデルでよく使われる事前分布はディリクレ分布です。ディリクレ分布というのは下の形のような分布です: $$ \begin{align\*} & P(Y) = P(\vec{p}; \vec{\alpha}) = \frac{1}{Z({\vec{\alpha}})} \prod_{i=1}^V p_i^{\alpha_i - 1} \\\\ & Z(\vec{\alpha}) = \frac{\prod_{i=1}^V \Gamma(\alpha_i)}{\Gamma(\sum_{i=1}^V \alpha_i)} \\\\ & 1 = \sum_{i=1}^V p_i \end{align\*} $$ $\Gamma$はガンマ関数です。 これをいまの問題設定に当てはめてみます。この分布はこのままだと連続的に変化するベクトル $\vec{p}$ に対する分布になっているので、 離散的な、すなわち、空間上のあるK点でだけ値を持つような分布に限定します。 $$ \int d\vec{p} = \int \left( \sum_{k=1}^K \delta(p, \vec{p_k}) \right) d\vec{p} $$ 要は確率の定義域を変えます。すると、 $P(Y)$ のあるY=kでの確率は以下の様に書けるはずです: $$ \begin{align\*} & P(\vec{p}; \vec{\alpha}) = \frac{1}{Z({\vec{\alpha}})} \prod_{i=1}^V p_{ki}^{\alpha_i - 1} \\\\ & Z(\vec{\alpha}) = \sum_{k=1}^K \prod_{i=1}^V p_{ki}^{\alpha_i - 1} \\\\ & 1 = \sum_{i=1}^V p_{ki} \end{align\*} $$ 2つ目の式、kに関する和が確率全部足したら1、という条件を満たすための定数項を表しています。この関数 $Z$ は分配関数とも呼ばれています(今回はこのあたりには突っ込んでいきません)。 この事前分布を持つような多項分布モデルにおけるMは次のようになります: $$ \begin{align\*} M(\vec{p}; \vec{\alpha}) &= \prod_{k=1}^K P(k; \Theta, \Phi) \prod_{i = 1}^{Q_k} P(S_{ki}; \Theta|k) \\\\ & \propto \frac{1}{Z({\vec{\alpha}})} \prod_{k=1}^K \prod_{i=1}^V p_{ki}^{\alpha_i - 1} \prod_{j=1}^V p_{kj}^{\tau_{kj}} \\\\ &= \frac{1}{Z({\vec{\alpha}})} \prod_{k=1}^K \prod_{j=1}^V p_{kj}^{\tau_{kj} + \alpha_j - 1} \\\\ \end{align\*} $$ 事前分布が一様の場合と比べてほとんど形が一緒ですね。定数項のZを除くと $\tau_{kj}$ が $\tau_{kj} + \alpha_j - 1$ に変わっただけです。つまり、 $p_{lm}$ は $$ \begin{align\*} p_{lm} &= \frac{\tau_{lm} + \alpha_m - 1}{\sum_{m=1}^V (\tau_{lm} + \alpha_m - 1) } \end{align\*} $$ である、という事です。これでディリクレ事前分布を仮定した多項分布モデルが解けました。 で、この $\alpha_i$ って一体どうやって決めればいいのでしょうか?ひとつの方法は、”適当に決める”です。いや、本当です。 例えば、 $\alpha_m = \alpha + 1$ ( $\alpha$ は定数)とおいて、 $$ \begin{align\*} p_{lm} &= \frac{\tau_{lm} + \alpha}{\sum_{m=1}^V \tau_{lm} + V \alpha} \end{align\*} $$ とする事ができます。これは加算スムージングと呼ばれている方法になります。あとは $\alpha = 1$ なりなんなり適当に設定してください。 周辺尤度最大化法と呼ばれる方法もあります。周辺尤度は入力データの出現確率 $P(S_1, S_2, \dots, S_D)$ を最大にするようにパラメータを調整するというものです。 $S_i$ は正解の入力データです。それではやってみましょう。周辺尤度を $L$ とします。 $$ \begin{align\*} L &= \prod_{i=1}^D P(S_i) \\\\ &= \prod_{i=1}^D \int P(S_i|Y) P(Y) dY \\\\ &= \prod_{i=1}^D \int \left( \prod_{j=1}^V p_j^{c_ij} \frac{1}{Z({\vec{\alpha}})} \prod_{u=1}^V p_u^{\alpha_u - 1} \right) d \vec{p} \\\\ &= \left( \frac{\prod_{i=1}^V \Gamma(\alpha_i)}{\Gamma(\sum_{i=1}^V \alpha_i)} \right)^{-D} \prod_{i=1}^D \int \left( \prod_{u=1}^V p_u^{c_{iu} + \alpha_u - 1} \right) d\vec{p} \\\\ &= \left( \frac{\prod_{i=1}^V \Gamma(\alpha_i)}{\Gamma(\sum_{i=1}^V \alpha_i)} \right)^{-D} \prod_{i=1}^D \left( \frac{\prod_{j=1}^V \Gamma(c_{ij} + \alpha_j)}{\Gamma(\sum_{j=1}^V (c_{ij} + \alpha_j))} \right) \\\\ &= \prod_{i=1}^D \frac{\prod_{j=1}^V \Gamma(c_{ij} + \alpha_j) \Gamma(\alpha_j)}{\Gamma(\sum_{j=1}^V (c_{ij} + \alpha_j)) \Gamma(\sum_{j=1}^V \alpha_j)} \end{align\*} $$ 途中でディリクレ分布の正規化に関する等式 $$ \frac{\prod_{i=1}^V \Gamma(\alpha_i)}{\Gamma(\sum_{i=1}^V \alpha_i)} = \int \prod_{i=1}^V p_i^{\alpha_i - 1} d\vec{p} $$ を使いました。あとは $\alpha$を色々動かして、最大値を探せばOKです。多分この式は解析的に解けず、数値的に解くことになります。 おわりに -------- いかがでしたでしょうか?なるべく途中式を書いていたので数式アレルギーの人はきつかったかもしれません。 しかし、ナイーブベイズ分類器は文書分類の文脈での解説は良くありますが、解の導出を含めた詳細な日本語の解説があまり見当たらなかったので書いてみました。ナイーブベイズ分類器は、分布を相関の無い独立なものに限定したベイズモデルである事を理解できれば、自分で色々いじることも可能です。 |
|
| 300位 |
|
|||
|
01:24:54 |
|
|
## 1. AppDelegateの参照 AppDelegate≒グローバルとして使う場合にとても便利です。 ゆとり向けです。あまり使う頻度が高いと設計ができてないということなので要注意かも。 ```objectivec: #define APP_DELEGATE ((AppDelegate*)[[UIApplication sharedApplication] delegate]) ``` ## 2. テストの時に便利なBundleの参照 ユニットテストを書いてみたけどmainBundleがうまく取れなくってこける、という時のために。bundleForClass:[self class]を使うことで対応するバンドルを取得するようにします。 ```objectivec: #define BUNDLE [NSBundle bundleForClass:[self class]] ``` ## 3. 画面サイズ 480とか320とかは今時直書きしないと思いますが^^ IB使わずに配置すると画面全体のサイズを頻繁に参照することになるので、ショートカットを定義しています。 ```objectivec: #define SCREEN_BOUNDS ([UIScreen mainScreen].bounds) ``` ## 4. ログ出力 printfデバッグ厨には必須ですね。行数とメソッド情報加えてログ表示します。 ```objectivec: #define LOG(A, ...) NSLog(@"DEBUG: %s:%d:%@", __PRETTY_FUNCTION__,__LINE__,[NSString stringWithFormat:A, ## __VA_ARGS__]); ``` ## 5. UIColorを簡単に書く これもIB使わない時に便利。 ```objectivec: #define RGB(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1] #define RGBA(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:(a)] ``` ## 6. max, min NSObjCRuntime.hに定義されているので個別の定義は不要ですが、地味に便利ですね。 ```objectivec: #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) ``` ## 7. iPhone, iPad の確認 ユニバーサルアプリを作るときにあると便利。 ```objectivec: #define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) #define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ``` ## 8. Retina判定 Retina Displayを判定します。あんまり使わないかも。。 ```ObjectiveC: #define IS_RETINA ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [UIScreen mainScreen].scale > 1) ``` ## 9. 言語取得 現在の言語を取得します。 ```Objective-C: #define LANGUAGE ([NSLocale preferredLanguages][0]) ``` ## 10. よく使う標準コントロールのサイズ UIStatusBar, UITabBar, UINavigationBar, UIToolbarの高さを定義しています。ランドスケープも対応する場合は別途調整が必要です。 ```Objective-C: #define STATUSBAR_H 20 #define TABBAR_H 48 #define NAVBAR_H 44 #define TOOLBAR_H 44 ``` |
|
| 301位 |
|
|||
|
13:46:57 |
(KLab Inc. 所属) |
|
PyFes 2012.11 発表資料です。
システムコールに焦点を当てて、 meinheld のアーキテクチャを紹介します。 アーキテクチャを説明するために Pure Python でサンプル実装を書いていますが、ちゃんと動くし、HTTPリクエストのパースを端折っているので 10000req/sec 以上出ます。 イベントドリブンのコードでフローが判りにくい場合は `python -mtrace -t --ignore-module socket webserver1.py` などのようにトレースしながら実行するといいでしょう。 ## 前提 今日は、シンプルなレスポンスを返すだけの条件でをひたすら req/sec を追求する話をします。 たとえば、 nginx の lua モジュールで "hello" と返すだけとかです。 静的ファイルを配信するサーバーとかだともっと別のことも考えないといけません。 ## HTTPのおさらい 詳しくは rfc2616 を見てね Python でHTTPパースするとシステムコールじゃなくてそっちがボトルネックになっちゃうのでこの記事ではまともにHTTPパースしてません。 ### HTTPリクエスト HTTPリクエストはこんな形をしています。 ``` GET / HTTP/1.1 Host: localhost ``` ``` POST /post HTTP/1.1 Host: localhost Content-Type: application/x-www-form-urlencoded Content-Length: 7 foo=bar ``` 1行目は request-line で、 ``method URI HTTP-version`` の形をしています。URIはホストを含めた絶対URIの場合と、ホストを含めない絶対パスの場合がありますが、絶対パスの方が一般的です。 2行目から空行までが request-header です。各行は ``field-name: field-value`` の形をしています。 field-name は大文字小文字を区別しません。 request-line から request-header とそれに続く空行まで、改行は CR LF になってます。Windowsでよく見る改行コードですね。 method が POST 等のときは、空行のあとに message-body がつきます。 messeage-body がどんな種類のデータなのかは `Content-Type` ヘッダで、その大きさは `Content-Length` ヘッダで指定します。 `Content-Length` を省略することもできるのですが、その時のことについては後述します。 サーバーが VirtualHost を使っているかもしれないので、request-line が絶対URIじゃない場合は "Host" ヘッダをつけてホスト名を指定してあげます。 ### HTTPレスポンス HTTPレスポンスはこんな形をしています。 ``` HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 5 Hello ``` 1行目が status-line になっている以外は、HTTPリクエストとだいたい同じです。 status line は `http-version status-code reason-phrase` の形になっています。 "200 OK" とか "404 Not Found" とかいうアレですね。 ## Webサーバーの基本 Web サーバーは、HTTPリクエストを受け取ってHTTPレスポンスを返すTCPサーバーです。 1. TCPポートを bind して、 listen する. 2. accpet してクライアントからの新しい接続を受け付ける 3. recv して HTTP リクエストを受け取る. 4. リクエストを処理する 5. send して HTTP レスポンスを返す. 6. close して TCP 接続を切る. 3 の recv の代わりに read, readv, recvmsg を使うことがあります。 5 の send の代わりに write, writev, writemsg を使うことがあります。 今回は req/sec を突き詰めるので、 4 は無視します。 本当は Keep-Alive にも対応しないといけないんですが、それも今回は無視します。 ここまでを Python で書くとこんなかんじです。(リクエストはパースしていません) ```python:webserver1.py import socket def server(): # 1: bind & listen server = socket.socket() server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('', 8000)) server.listen(100) while 1: # 2: accept new connection con, _ = server.accept() # 3: read request con.recv(32*1024) # 4: process request and make response # 5: send response con.sendall(b"""HTTP/1.1 200 OK\r Content-Type: text/plain\r Content-Length: 5\r \r hello""") # 6: close connection con.close() if __name__ == '__main__': server() ``` ## 並行処理 さっきのサンプルコードは、同時に1つのクライアントとしか通信できません。 並行して複数の接続を扱うためには幾つかの基本的な方法と、それを組み合わせた無数の方法があります。 基本的な方法は次のようなものがあります。 ### accept() したあとの処理を別のスレッド or プロセスで行う ワーカーモデルと呼びます。 ワーカースレッド/プロセスの数を動的に調整しやすいのがメリットですが、acceptしたスレッド/プロセスからワーカースレッド/プロセスに接続を引き渡す処理が必要になります。 コンテキストスイッチの負荷もかかります。 ### スレッド or プロセスで accept() から close() までを行う prefork モデルと呼びます。 accept() から close() までの処理がシンプルなままなので、うまくいけば最大の性能がでます。 ただし、スレッド/プロセス数が少ないとそれ以上の並列数を扱えず、逆に多いとコンテキストスイッチの負荷がかかります。 ### epoll, select, kqueue などで多重化する イベントドリブンモデルと呼びます。 accept できるようになったら accept して、 recv できるようになったら recv して、 send できるようになったら send します。 コンテキストスイッチが不要ですが、それぞれ専用のシステムコールの呼び出しが必要になるので、そのオーバーヘッドがかかります ## 最速のアーキテクチャ(異論は認める) 本当に hello を返すだけのサーバーなら、単純な prefork モデルでコア数だけプロセスを作るのが最強です。 ```python:webserver2.py import multiprocessing import socket import time def worker(sock): while 1: con, _ = sock.accept() con.recv(32 * 1024) con.sendall(b"""HTTP/1.1 200 OK\r Content-Type: text/plain\r Content-Length: 5\r \r hello""") con.close() def server(): sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('', 8000)) sock.listen(100) ncpu = multiprocessing.cpu_count() procs = [] for i in range(ncpu): proc = multiprocessing.Process(target=worker, args=(sock,)) proc.start() procs.append(proc) while 1: time.sleep(0.5) for proc in procs: proc.terminate() proc.join(1) if __name__ == '__main__': server() ``` gunicorn の sync worker がこのアーキテクチャのはず。 ## 最強のアーキテクチャ(異論はry) 先ほどの prefork サーバーは hello というだけなら最速ですが、実用すると問題になってくるのが、リクエストの受信やレスポンスの送信に時間がかかるとそのプロセスは次のリクエストを処理できなくなって、CPUが遊んでしまうことです。 なので gunicorn の sync worker を使うときは、フロントに nginx とかを置いて静的ファイルはそっちで配信したり、リクエストやレスポンスのバッファリングをすることが推奨されてます。 でも2段構成にしたらそれだけで速度半減です。 そこで、各プロセスで epoll などを使ったイベントドリブンモデルを使って時間のかかる送受信処理も実行できるようにしてしまいます。 ## 速いイベントドリブンプログラム 次のコードみたいなイベントドリブンにすると、 accept, read, write 全てにイベントドリブンのためのシステムコールがくっつくので、オーバーヘッドが増えて hello が遅くなってしまいます。 ```python:webserver4.py import socket import select read_waits = {} write_waits = {} def wait_read(con, callback): read_waits[con.fileno()] = callback def wait_write(con, callback): write_waits[con.fileno()] = callback def evloop(): while 1: rs, ws, xs = select.select(read_waits.keys(), write_waits.keys(), []) for rfd in rs: read_waits.pop(rfd)() for wfd in ws: write_waits.pop(wfd)() class Server(object): def __init__(self, con): self.con = con def start(self): wait_read(self.con, self.on_acceptable) def on_acceptable(self): con, _ = self.con.accept() con.setblocking(0) Client(con) wait_read(self.con, self.on_acceptable) class Client(object): def __init__(self, con): self.con = con wait_read(con, self.on_readable) def on_readable(self): data = self.con.recv(32 * 1024) self.buf = b"""HTTP/1.1 200 OK\r Content-Type: text/plain\r Content-Length: 6\r \r hello """ wait_write(self.con, self.on_writable) def on_writable(self): wrote = self.con.send(self.buf) self.buf = self.buf[wrote:] if self.buf: wait_write(self.con, self.on_writable) else: self.con.close() def serve(): sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('', 8000)) sock.listen(128) server = Server(sock) server.start() evloop() if __name__ == '__main__': serve() ``` オーバーヘッドを減らすために、 wait_read, wait_write を減らせるところを探していきます。 まず、OSは accept() を呼ばなくても backlog (listen の引数に指定した数) までは自動でTCPの接続を開始しています。(SYN に対して ACK/SYN を返します) なので、アプリから accept() した時点で、実際にはTCPの接続は開始されていて、クライアントからのリクエストも受信済みかもしれません。なので、 accept() したら wait_read() せずにすぐに recv() しましょう。 read() が終わったらレスポンスの送信ですが、これも最初はソケットバッファが空のはずなのですぐに送信できます。wait_write()するのをやめましょう。 ```python:webserver5.py import socket import select read_waits = {} write_waits = {} def wait_read(con, callback): read_waits[con.fileno()] = callback def wait_write(con, callback): write_waits[con.fileno()] = callback def evloop(): while 1: rs, ws, xs = select.select(read_waits.keys(), write_waits.keys(), []) for rfd in rs: read_waits.pop(rfd)() for wfd in ws: write_waits.pop(wfd)() class Server(object): def __init__(self, con): self.con = con def start(self): wait_read(self.con, self.on_acceptable) def on_acceptable(self): try: while 1: con, _ = self.con.accept() con.setblocking(0) Client(con) except IOError: wait_read(self.con, self.on_acceptable) class Client(object): def __init__(self, con): self.con = con self.on_readable() def on_readable(self): data = self.con.recv(32 * 1024) if not data: wait_read(self.con, self.on_readable) return self.buf = b"""HTTP/1.1 200 OK\r Content-Type: text/plain\r Content-Length: 6\r \r hello """ self.on_writable() def on_writable(self): wrote = self.con.send(self.buf) self.buf = self.buf[wrote:] if self.buf: wait_write(self.con, self.on_writable) else: self.con.close() def serve(): sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setblocking(0) sock.bind(('', 8000)) sock.listen(128) server = Server(sock) server.start() evloop() if __name__ == '__main__': serve() ``` この方式には、 prefork との相性も抜群です。 accept したあと、できる限りの処理をするまで次を accept しないので、本当に仕事が無い他のプロセスに accept を譲ることができます。 また、 thundering hard 問題も軽減できます。 thundering hard 問題とは、 prefork 方式で複数プロセスが accept() を呼び出した時、1つのクライアントが接続しただけで全部のプロセスが起き上がり、そのうち1つだけが成功するというものです。accept()が失敗したプロセスは完全に起こされ損です。安眠妨害です。1コアマシンで100プロセスとかのサーバーでこれやられたら溜まったもんじゃないです。 accept() に関しては、最近の Linux は接続が来た時に1つのプロセスのacceptだけが返るようになったので、 thundering hard 問題は完全に解決されました。しかし、 select() してから accept() する場合、この問題が再発してしまいます。 プロセス数をCPUコア数までにして、acceptの前のselectを本当に暇な時にだけ行うようにすることで、「selectしたけどacceptできない」という現象が本当にCPUがヒマなときだけしか発生しないようにできます。 これで、 accept()〜close() に必要なシステムコールが prefork に比べて setblocking(0) が増えるだけになります。ちなみに、最近の Linux だと accept4 ってシステムコールがあって accept と同時に setblocking(0) をすることもできたりします。 ## 俺はユーザー空間をやめるぞ!ジョジョォ! ここまでの話は、あくまでもユーザー空間での最速・最強です。 Webサーバーをカーネル空間で実装したら、システムコールを発行する必要無いです。 https://github.com/KLab/recaro |
|
| 302位 |
|
|||
|
11:27:39 |
(Freelancer 所属) |
|
先週末に開催された『[もくもく開発会@鎌倉 #2 by Qiita](http://atnd.org/events/43284)』にて、 **iOS 7 で新たに加えられたAPI** の使い方等を勉強しつつ、簡単なサンプルを集めたアプリをつくってGitHubにアップしました。
[https://github.com/shu223/iOS7-Sampler](https://github.com/shu223/iOS7-Sampler)  今のところ21個のサンプルが入っています。何か調べた折に随時追加していきます。 以下サンプルのリストです。 ##Dynamic Behaviors iOS7では UIView サブクラスに簡単に物理演算を適用することができるようになりました。 本サンプルでは、UIDynamicAnimator, UIGravityBehavior, UICollisionBehavior, UIDynamicItemBehavior を用いて **重力と衝突をシミュレート** しています。  関連:[UIKit で物理演算エンジンを使用する](http://d.hatena.ne.jp/shu223/20130501/1367368335) ##Speech Synthesis AVSpeechSynthesizer, AVSpeechUtterance を用いた音声合成のサンプル。 **任意の文字列を iOS が読み上げ** てくれます。Macのsayコマンドのようなもの。  関連:[フリーの iOS 向け音声認識/音声合成ライブラリ『OpenEars』の使い方](http://d.hatena.ne.jp/shu223/20130810/1376105883) ##Custom Transition UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegateを用いた **カスタムトランジション** (画面遷移)のサンプル。 トランジションエフェクトは下記のOSSを入れています。 ### [HUAnimator](https://github.com/cinkster/HUAnimator) 細い線に分解されて遷移するエフェクト。  ### [ZBCustomTransitions](https://github.com/zonble/ZBCustomTransitions) ブロックに分解されガラガラと崩れ落ちて遷移するエフェクト。  ##3D マップ MKMapView の camera プロパティ (MKMapCamera という新クラス)を用いた **3D 地図表示** のサンプル。  関連:[Google Maps SDK for iOSの導入手順](http://qiita.com/shu223/items/bfb5ef3e45682c2bb763) ##iBeacon iBeacon を 2台のiPhone(iPad)同士で試せるようにしたサンプル。  一方をビーコン(Peripheral)、もう一方を監視側(Central)として使用します。 ###関連 [ビーコン的な演出をする](http://qiita.com/shu223/items/5018cf5e452c4fa0d002) ##120fps ビデオ録画(SLO-MO) AVFoundationでスローモーション動画撮影を行うサンプル。iPhone5sでは120fpsでの撮影が可能です。  ###作例  <p><a href="http://vimeo.com/82064431">See the 120fps Slo-Mo video in Vimeo 120fps.</a></p> ###関連 [AVFoundationで120fpsスローモーション動画撮影を実装する](http://d.hatena.ne.jp/shu223/20131217/1387257304) ##笑顔検出 CIDetectorに新たに追加された `CIDetectorSmile` と、CIFeature に追加されたプロパティ `bounds`, `hasSmile` を用いた **笑顔検出** のサンプル。  ##画像フィルタ CIFilter に追加されたフィルタのサンプル  下記の画像処理ををピッカーで選択してかけられるようになっています。 - CILinearToSRGBToneCurve - CIPhotoEffectChrome - CIPhotoEffectFade - CIPhotoEffectInstant - CIPhotoEffectMono - CIPhotoEffectNoir - CIPhotoEffectProcess - CIPhotoEffectTonal - CIPhotoEffectTransfer - CISRGBToneCurveToLinear - CIVignetteEffect ##Sprite Kit Sprite Kit の SKView, SKScene, SKSpriteNode, SKActionを用いて **キャラのスプライトを生成し、アニメーションさせる** サンプル。 ほぼCocos2Dと同じようなAPIです。  ##Map Directions MKDirections, MKDirectionsResponse, MKPolylineRenderer 等を用いた **経路探索とルートの描画** サンプル。  ##Motion Effects (Parallax) UIMotionEffect を用いた **パララックスエフェクト(視差効果)** のサンプル。  ##Multipeer Connectivity **Multipeer Connectivity Framework で Peer-to-Peer 通信を行う** サンプル。P2P通信には Wi-Fi または Bluetooth LE を使用します。  ##AirDrop / Flickr / Vimeo / ReadingList **新たに追加された UIActivityType (UIActivityTypeAirDrop, UIActivityTypePostToFlickr, UIActivityTypePostToVimeo, UIActivityTypeAddToReadingList)のサンプル** 。 AirDropはデバイスが対応している必要があり、Flickr, Vimeo は Settings でアカウントを設定している必要があります。  ##QR Code Reader 新たに追加された `AVMetadataObjectTypeQRCode` を利用してQRコードを読み取るサンプル。 参考:[iOS 7でバーコード・QRコードを読み取る方法と生成する方法(おまけもあるよ)](http://qiita.com/hkato193/items/c36a940c2929a124e416) ##QR Code Generator CIFilter の CIQRCodeGenerator を用いた **QRコード生成** のサンプル。  ##歩数カウントと移動状態 Core Motion に新たに追加された CMStepCounter と CMMotionActivityManager を用いて、 **歩数のカウント** と **移動状態(歩いている/走っている/車に乗っている)をトラッキング** するサンプルです。  本機能を使用するにはM7コプロセッサが必要なので、2013年10月現在出ているiOSデバイスでは **iPhone5sでのみ動作** します。 ##Static Map Snapshots MKMapSnapshotOptions, MKMapSnapshotter を用いた **地図のスナップショットを取得する** サンプル。  ##Safari Reading List **新たに追加された Safari Services framework** を使用して **Safari の Reading List にWebサイトを追加** するサンプル。 (※スクショは地味なので省略します) ##New Fonts **iOS 7 で新たに追加された全フォント36種類** をフォント名と共に確認できます。  関連:[iOS 7 で使えるフォント名一覧](http://d.hatena.ne.jp/shu223/20131017/1381992664) ##ばね風アニメーション ばね風のタイミングカーブでアニメーションさせることのできるUIViewのメソッドの挙動を確認できるサンプル。  関連:[ばねっぽいアニメーションを実現するUIViewの新メソッド](http://qiita.com/shu223/items/bce33f6ab448c90e4d2b] ##UIWebViewのページネーション UIWebViewの新機能、paginationMode のサンプル。Webページを書籍のように pagination して表示してくれます。  関連:[UIWebViewの新機能:UIWebPagination](http://qiita.com/shu223/items/3f2141f8708dacb02d50) |
|
| 303位 |
|
|||
|
17:13:22 |
(Increments inc. 所属) |
|
「最近Qiitaを検索結果でよく見るようになったなぁ」と感じる人はいませんか?
SEOを頑張ってるWebサービスは数多存在しますが、Qiitaもその中の1つです。 SEOっていうとやれキーワードがどうだこうだ、サイト内リンクがどうだこうだ、という話になってしまいがちですが(そういうのが不要と言っているわけではないですよ)、今回はQiitaがSEOの一環として行なっている数多の取り組みの中で、比較的Web上での言及数の少ない印象のある、HTMLのマークアップについて、外に出せる範囲で、前編と後編に分けて解説したいと思います。 [後編はこちら](http://qiita.com/yuku_t/items/3740cc9a5f8503693bd2) ## 免責事項的な SEOというのは完全に結果論でしか語ることのできないものです。また時と共に効果的とされるテクニックは変わっていきます。この記事はQiitaの中の人が「効果あるんちゃうか」と試行錯誤しながらあれこれ行なっていることの一部を紹介するものであって、その効果を保証するものではありません。「ふーんそんなことやってるんだ」程度の気分で読んでください。 # アウトラインとは何か アウトラインは(大雑把に言えば)機械から見たあなたのページの構造です。 つまりアウトラインが美しいページは、検索エンジンにより正しく解釈してもらいやすい、と言えそうです。適切に解釈してもらえれば、より適切なユーザに提示してもらえるようになりますね。 もし、なんでもかんでも検索ページに載りさえすればいい、と考えているのだとしたら、恐らくそれは間違いです。少なくともGoogleはユーザが検索結果をクリックしてから検索ページへ戻ってくる時間を計測してるはずです。不適当な相手に表示さると、それがまわりまわって検索順位の低下という形で帰ってくる可能性があるわけです。 # 何はともあれアウトラインを確認してみる - [HTML5 Outliner](http://gsnedders.html5.org/outliner/) アウトラインを解析できるサイトです。HTML直接渡すだけでなく、URLを指定して解析させることもできます。 試しに気になるサイトのアウトラインを確認してみましょう。例えばナナピはアウトラインが綺麗です。 ## アウトラインの決まり方 解説しているサイトはたくさんあるので、適当にググってください。大雑把に言えば `section` や `h1` 要素が存在すると新しくアウトラインが作られます。 詳しい仕様は下記で読めます。 - [見出しとセクション - セクション - HTML要素 - HTML5 タグリファレンス - HTML5.JP](http://www.html5.jp/tag/elements/headings-and-sections.html#outlines) # アウトラインが綺麗なHTMLを書くコツ アウトラインを意識しようにもデザインの関係もあってなかなか綺麗にできないかも知れません。 そういう場合は先にアウトラインを決めてしまってから、デザイン用の `div` や `span` 要素を付け足していく、という手順を踏むといいです。 ## まず最初に綺麗なアウトラインを作ってしまう HTML5にしろ、それ以前にしろ、アウトラインに影響を与えるタグと与えないタグというのは明確です。なので、まず先にアウトラインに影響を与える要素だけを並べて、理想的なアウトラインを書いてみましょう。 例えばこんな感じに書いたとします。(便宜上 `body` タグとかは省略してます) ```html <h1>サイト名</h1> <section> <h1>タイトル</h1> <section> <h1>見出し</h1> <h2>見出し2</h2> </section> </section> <section> <h1>コメント</h1> <section> <!-- コメント --> </section> </section> <section> <h1>投稿者情報</h1> </section> ``` できあがったHTMLのアウトラインを確認してみます。先ほどのサイトを開いて作ったHTMLをコピペして解析してみます。上記のHTMLの場合は次のように返されます > 1. サイト名 > 1. タイトル > 1. 見出し > 2. 見出し2 > 2. コメント > 1. *Untitled Section* > 3. 投稿者情報 おかしいなと思う箇所があったら試行錯誤をして綺麗に整えましょう。もちろんこのあとのデザインのことも考えて、要素の順番を入れ替えるとかはしても良いと思います。 ## schema.org そうしたら次は(余裕があれば)schema.orgに従ったマークアップを行なっていきます。これも細々したデザインの調整を行う前にやってしまう方がいいと感じています。 schema.orgについて詳しくは次回の [schema.orgに準拠してクローラと会話しよう](http://qiita.com/yuku_t/items/3740cc9a5f8503693bd2) を参照してください。 ## アウトラインに影響しないタグを書いていく そうしたらコンテンツに肉をつけて行きましょう。ここから先で書いていくのは全てアウトラインに影響しない要素なので、先ほど作ったアウトラインは綺麗なまま保たれるはずです。 アウトラインが綺麗になっていると `section` タグなどにそのままCSSを当てれば、わざわざ `div` とかいれなくても大丈夫、という場合もあるかと思います。 しかし、そういう場合でも `section` にCSSを当てずに、それをラップする `div` を作ってそれにclassやidを振って、CSSのルールを書いた方がいいでしょう。 何故かというと、アウトラインに比べてデザインの方が寿命が短いからです。 `section` にCSSを書いていると、アウトラインと実際の見た目の結合度が高くなります。その結果として、ちょっとしたデザイン変更のときについでにアウトラインが崩れる、みたいなことが起こったりします。 # まとめ - アウトラインは機械からどう見えるかを示すものです。検索エンジンに正しく理解してもらうためにも、余裕があるなら綺麗にしましょう。 - アウトラインは一番最初に整えましょう。 # QiitaのSEO事情 1. HTMLのアウトライン意識してますか? 2. [schema.orgに準拠してクローラと会話しよう](http://qiita.com/yuku_t/items/3740cc9a5f8503693bd2) |
|
| 304位 |
|
|||
|
12:54:32 |
|
|
## Pretty Pull Requests (Github)
[Chromeウェブストア - Pretty Pull Requests (Github)](https://chrome.google.com/webstore/detail/pretty-pull-requests-gith/ljnjpkadhhcdniohpfilddnhahoigdec) Pull Request を読むときに便利なエクステンション。 ファイルをアコーディオンで開閉したり、追加された行または削除された行のみを表示することができる。 長い PR を読むときに特に便利。 ## GitHub Wiki Search [Chromeウェブストア - GitHub Wiki Search](https://chrome.google.com/webstore/detail/github-wiki-search/gdifdhnjmjaidbajhapmbcbnoocoeooc) GitHub Wiki を検索するためのエクステンション。シンプルで使いやすい。 検索語をハイライトしてくれたり、検索中はプログレスバーがでたりと気が効いている。 これを使わずに検索しようとすると GitHub Wiki の git リポジトリをクローンして、 git grep するはめになる。 ## GitHub.Expandinizr [Chromeウェブストア - GitHub.Expandinizr](https://chrome.google.com/webstore/detail/githubexpandinizr/cbehdjjcilgnejbpnjhobkiiggkedfib) GitHub の横幅を広くしてくれるエクステンション。 大きいディスプレイを使っていると GitHub の横幅の狭さがイライラしてくるので、このエクステンションは地味に便利。 表示が崩れるときはブラウザのズーム比率を少し変えて調節するといい。 ## Emoty [Chromeウェブストア - Emoty](https://chrome.google.com/webstore/detail/emoty/kgljgnbhcigffgoifhjbbifhbdkapmgm) 絵文字選択を助けるエクステンション。 GitHub の絵文字の補完は便利だけど、一覧で絵文字を探したいときにこのエクステンションが便利。 [LGTM](https://chrome.google.com/webstore/detail/lgtm/ihckpnhmmfhihijdjnnjfjogoajgdklf) 似たようなのりで LGTM の画像をランダムに拾ってくるエクステンションもレビューのときに使ってみると面白い。 ## Night Reading Mode [Chromeウェブストア - Night Reading Mode](https://chrome.google.com/webstore/detail/night-reading-mode/chcciiimjmlgcoabgfdhkfjdcgfpgndi) ブラウザを色反転するエクステンション。 コードを読むとき白背景は目に悪いのでこのエクステンションで色反転して黒背景にしよう。 ドメイン毎に反転するかどうか選べるので使い勝手が良い。 背景色や文字色だけを暗くするエクステンションもあるが、僕の感覚としてはページまるごと色反転した方が読みやすい。 |
|
| 305位 |
|
|||
|
20:16:19 |
|
|
配列から特定のデータを取り出す時や、いじるときに以下のようにループを回す場面は多々あります。 ```objectivec:example1 for (int i = 0; i < array.count; i++) { ; } ``` ```objectivec:example2 for (id object in array) { ; } ``` example1ではループの際に毎回array.countが呼び出されて無駄だし、条件式などあるのでバグも生みやすい。 example2ではexample1のiに相当する配列のインデックスを求めるためには、変数を別途作ってインクリメントする必要があり面倒です。 そんな話をしていたところ、友人にNSArrayにはenumerateObjectsUsingBlock:なる便利なメソッドが存在することを教えてもらったのでご紹介します。 ## enumerateObjectsUsingBlock:を使う enumerateObjectsUsingBlock:はNSArrayに用意されているインスタンスメソッドで、iOS 4.0以上で使用することができます。 [NSArray Class Reference - enumerateObjectsUsingBlock:](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html#//apple_ref/occ/instm/NSArray/enumerateObjectsUsingBlock:) ```objectivec:定義 - (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block ``` * block - enumerateObjectsUsingBlock:はBlockの中に配列にしたい操作を記述します。Blockの中がfor(;;){ hoge }のhogeに相当します。 * obj - 配列から取り出された要素です。 * idx - 配列の要素のインデックスです。example1のiに相当します。 * stop - 停止フラグです。stopにYES(BOOL)を入れるとその時点で停止します。forのbreakに相当します。 ```objectivec:使い方 /* NSStringの配列から@"c"が入っている要素番号を取得する */ NSArray *array = @[@"a", @"b", @"c", @"d"]; __block NSInteger result = -1; // オブジェクト型でない変数をBlock内で操作する場合は__block修飾子が必要 [array enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) { NSLog(@"%d: %@", idx, obj); if ([obj isEqualToString:@"c"]) { result = idx; *stop = YES; } }]; NSLog(@"result: %d", result); // 出力結果 0: a 1: b 2: c result: 2 ``` 使いやすいです。 ## enumerateObjectsWithOptions:usingBlock:を使うと要素を後ろから見れます 配列を後ろから見ていきたいときに便利です。 [NSArray Class Reference - enumerateObjectsWithOptions:usingBlock:](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html#//apple_ref/occ/instm/NSArray/enumerateObjectsWithOptions:usingBlock:) ```objectivec:定義 - (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block enum { NSEnumerationConcurrent = (1UL << 0), NSEnumerationReverse = (1UL << 1), }; typedef NSUInteger NSEnumerationOptions; ``` * opts - NSEnumerationReverse 後ろから見ていきます ```objectivec:使い方 NSArray *array = @[@"a", @"b", @"c", @"d"]; [array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) { NSLog(@"%d: %@", idx, obj); }]; // 出力結果 3: d 2: c 1: b 0: a ``` ## 処理の並列化もできます 上記の **(NSEnumerationOptions)opts** に **NSEnumerationConcurrent** を指定することで、並列処理を行うことができます。 ```objectivec:使い方 NSArray *array = @[@"a", @"b", @"c", @"d"]; [array enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) { NSLog(@"%d: %@", idx, obj); }]; // 出力結果 3: d 1: b 2: c 0: a ``` ## 速度はforと同等かそれ以上 気になる実行速度ですが、for(;;)やfor-inと同等か、それ以上みたいなので安心して使えます。 [objective c - When to use enumerateObjectsUsingBlock vs. for - Stack Overflow](http://stackoverflow.com/questions/4486622/when-to-use-enumerateobjectsusingblock-vs-for) ## まとめ 使わない理由が見つかりません! |
|
| 306位 |
|
|||
|
16:53:55 |
|
|
# 環境
* CentOS release 6.4 (Final) * iptables.i686 1.4.7-9.el6 # 方針 以下の大原則に従う設定を作ることとする。 * 自ホストからの通信は通してやる。 * 外からのpingは通してやる。 * 外からのSSHは通してやる。 * それ以外の外からの通信は受け付けない。 * 外へ出ていく通信はすべて通す。 # 手順 ## まず、外からのすべての通信を受け付けないようにする。 外からの通信(INPUT, FORWARD)をすべてDROPする。そのあとで通すものを書いていくホワイトリスト方式とする。 中から外へ出ていく通信はすべて通してやる。 ``` :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] ``` ## 確立済みの通信は通してやる。 どこから来たのか、どのポート宛てなのか、は気にせず、すでに確立済みなら通してやる。要するに、これに続く記述で通信を確立させるかどうかを設定してやる。 ``` -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT ``` ## pingは通してやる。 プロトコルがICMPなら通してやる。 ``` -A INPUT -p icmp -j ACCEPT ``` ## ループバックは通してやる。 自ホストからの通信は問答無用で全部通してやる。 ``` -A INPUT -i lo -j ACCEPT ``` ## SSHは通してやる。 他ホストからSSH接続はするので、宛先が22番ポートなら通してやる。当然、SSH側のセキュリティ(パスワードログインさせないとか、rootログインさせないとか)はそちらで別途設定すること。 ``` -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT ``` SSH以外のサービスのためのポートを開ける場合はこれを真似して追加していく。例えば、HTTP(80)ならこんな感じで。 ``` -A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT ``` ## 全体はこんな感じで。 ```:/etc/sysconfig/iptables *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT COMMIT ``` ## iptablesを再起動して設定を反映させる。 rootでね。 ``` /etc/init.d/iptables restart ``` |
|
| 307位 |
|
|||
|
01:44:32 |
(kayac.com 所属) |
|
**まずあれです、タイトルは盛りました。** 実装は3分じゃおわらないよ!! でも公開のための作業は3分くらいで行けるんじゃないかと思います。 作ったものを、公開したくてうずうずしている方は、ぜひ読んでみてください。 ## [1分目] npmアカウントを作ろう ### npmの公式ページから なにはともあれ、npmの開発者として登録しましょう。 https://npmjs.org/signup から、「Make it so.」しましょう。 特筆するようなことはないですが、すぐに使うので - username - password - email address は、ちゃんとメモしておきましょう。 ### npm adduser 登録ができたら、ターミナルから```npm adduser```を実行します。 すると、username・password・email addressを聞かれるので、 先ほど作ったアカウントのものを入力します。 これで```~/.npmrc```にtoken情報が書き込まれ、 このアカウントから、npmモジュールを公開する準備が整います。 ## [2分目] モジュールを実装する いよいよ実装です。とりあえず、プロジェクトルートを作りましょう。 ``` % mkdir my-module % cd my-module ``` そしてあとは、元気にnodeのコードを書いていけばいいんですが、 npmモジュールならではのルールがいくつかあるので、覚えておきましょう。 ### モジュールに必要なファイル - **package.json** すべてのnpmモジュールに必須です。 主にモジュールの名前や、依存関係などが書いてあります。 ゼロから作るときには、公式などから例をコピペしてもよいのですが、 ```npm init```を使うと、対話式で入力することができるので便利です。 - **app.js** Webアプリケーションなら、メインのサーバーのコードはapp.jsに書くことが多いです。 [express](http://npmjs.org/package/express)とかでもそうですね。 ちなみに```node app```でサーバー起動する感じ。 - **index.js** nodeでは、```require('./hoge')```と書けば、```./hoge/index.js```を読んでくれます。 てなわけで、ライブラリ的なモジュールを書く場合は、 index.jsを置いておけば、requireしやすくなります。 lib以下でコードの本体を書いて、 index.jsでそれをrequire、そのままexport、みたいな形のモジュールが多です。 ```javascript:index.js // index.jsの例 var myModule = require('./lib/myModule'); // (実際には ./lib/myModule/index.js を読む) // それをそのまま、このモジュールからexport module.exports = myModule; ``` **※2015/4/3 追記** requireの挙動はちゃんと確認したところ、 *「指定したモジュールの`package.json`の`main`プロパティで指定したファイルを読む」* ということになっていたので、ライブラリ的なモジュールを書く場合は、`index.js`(とか)を用意した上で、 `package.json`の`main`でも指定しておかないと、 **requireができなくなるので注意**しましょう。 `require('./lib/index.js')`と書いた場合など、ローカルのファイルやディレクトリをrequireする場合は気にしなくてよい。 - **bin/my-module** nodeを使ったコマンドラインツールを作りたい場合は、 bin以下にコマンドとして実行したいjsを置きます。 その上で、package.jsonにコマンドの場所を記述しておけば、 ```npm -g install```されたら、コマンドをインストールする、というモジュールになります。 ``` #!/usr/bin/env node console.log('print argv.'); console.log(process.argv); ``` ```json:package.json { "name": "my-module", "bin": "bin/my-module" } ``` なお、もっとちゃんとしたコマンドラインツールを作りたい場合、 [optimist](http://npmjs.org/package/optimist)とか[commander](http://npmjs.org/package/commander)とか、 引数・オプションのハンドリングをしてくれるライブラリを使った方がいいです。 - **grunt-plugin** gruntのpluginを書くときの話は、 [さっき色々書いた](http://qiita.com/items/5590e7e92b4f2bd81d04)ので、よろしければ読んでみてください。 ## [3分目] 調整と公開 ### package.jsonの調整 いよいよ、公開の準備です。 package.jsonの中身をいろいろ確認しましょう。 - **private** このプロパティが```true```になっていると、 モジュールの公開ができません。 誤って公開してしまうのを防ぐフラグなので、 この公開直前のタイミングでfalseにするのがいいと思います。 - **name** モジュールの名前です。 npmモジュールそれぞれのURLは```http://npmjs.org/package/:name```という形式になっているので、 他の誰のモジュールとも重複禁止です。 http://npmjs.org で、公開してみる前にその名前が使われていないか、 チェックするとよいです。 また、nameに使えるのは **小文字英数字とハイフンだけ**です。 CamelCaseとか"_"区切りの名前はナシなので注意しましょう。 - **version** 最初は無難に、0.0.1とかにしておけばいいと思います。 2度目以降(アップデート)の時には、前に公開したときより大きい数字になるようにしましょう。 - **main** このモジュールの主となるスクリプトファイルを指定します。 前の項でも触れましたが、 **requireできるモジュールを作る場合は必須**です。 grunt pluginの場合は、実は`require`される機会がないのでいらない。 - **dependencies** 依存npmモジュールです。 プロジェクトにnpmモジュールを追加するときは、 必ず```npm install --save```を使うべきだと思います。 そうすれば、常にdependenciesは最新の状態に保たれます。 ただそれでも念のため、 1度```node_modules```を削除して、再度```npm insatll```して、 モジュールが正しく動くかどうか、などチェックしておくと安心です。 ### npm publish いよいよ、公開です!! package.jsonの準備さえ整っていれば、 ```npm publish```を叩くだけで、作業は終了です。 すぐに別の場所で```npm install my-module```して、 動作確認したり、にやにやしたり、しておきましょう。 ## [その後] アップデート npmでは、公開のコマンドもアップデートのコマンドも、 どちらも```npm publish```で行うので、 公開とアップデートはほぼ動作が同じです。 さっきも書きましたが、versionを更新することだけは忘れずに。 ## まとめ - [package.jsonのDoc](https://npmjs.org/doc/json.html)を読めばだいたい大丈夫 |
|
| 308位 |
|
|||
|
17:40:25 |
|
|
これらを一定時間置きにツイートするbotも作ってみました。よかったらどうぞ。
アカウントは@st2botです。 【 Command + C 】 (行の操作) 選択しているものがあればそれをコピーする。 何も選択していない状態では行全体をコピーする。 【 Command + L 】 (行の操作) 行全体を選択する。 【 Ctrl + Shift + K 】 (行の操作) 行全体を削除する。 【 Command + Shift + D 】 (行の操作) 現在の行を複製する。 【 Command + Shift + N 】 (ウィンドウ) 新規ウィンドウを開く。 【 Command + Shift + W 】 (ウィンドウ) ウィンドウを閉じる。 【 Command + Alt + ↑ 】 (ウィンドウ) 関連づけられたファイル(ヘッダファイルなど)を開く。 【 Command + Alt + 1-4 】 (ウィンドウ) ウィンドウを横にn等分する。 【 Command + Alt + Shift + 2,3 】 (ウィンドウ) ウィンドウを縦にn等分する。 【 Command + Alt + 5 】 (ウィンドウ) ウィンドウを縦横2等分する。 【 Ctrl + 1-4 】 (ウィンドウ) フォーカスをn番目のウィンドウに移す。 【 Ctrl + Shift + 1-4 】 (ウィンドウ) 選択中のタブをグループnに移す。 【 Ctrl + 0 】 (ウィンドウ) フォーカスをサイドバーに移す。 【 Command + 1-0 】 (タブの操作) n番目のタブに移動する。 【 Command + Shift + [ ] 】 (タブの操作) 隣のタブに移動する。 Command + Alt + ←→と同じ。 【 Command + Alt + ← → 】 (タブの操作) 隣のタブに移動する。 Command + Shift + [ ]と同じ。 【 Ctrl + Tab 】 (タブの操作) スタック内の後ろのタブに移動する。 【 Ctrl + Shift + Tab 】 (タブの操作) スタック内の前のタブに移動する。 【 Command + K → Command + B 】 (タブの操作) サイドバーの表示、非表示を切り替え。 【 Command + Ctrl + F 】 (タブの操作) フルスクリーンモード、ウィンドウモードを切り替える。 【 Command + Ctrl + Shift + F 】 (タブの操作) Distraction Free Mode(テキスト以外を表示しないフルスクリーンモード)に切り替える。 【 Command + Y 】 (編集) 繰り返し。 それ以上復元する操作がないときは最後の操作を繰り返す。 【 Command + U 】 (編集) やり直し。 【 Command + Shift + U 】 (編集) カーソル移動、文字列選択なども含めた元に戻す、やり直し。 【 Ctrl + Alt + ↑ ↓ 】 (編集) 画面をスクロールする。 【 Ctrl + Space 】 (編集) 補完候補を表示する。 【 Command + Alt + O 】 (編集) 上書きモードに切り替える。 【 Alt + F2 】 (編集) コンテキストメニューを開く。(右クリック) 【 Ctrl + ←→ 】 (カーソル移動 選択) 単語単位でカーソルを移動する。 【 Ctrl + Shift + ←→ 】 (カーソル移動 選択) 選択しながら単語単位でカーソルを移動する。 【 Ctrl + Shift + ↑↓ 】 (カーソル移動 選択) カーソルを移動せずに隣の行に選択を増やす。 【 Command + L 】 (カーソル移動 選択) 行末まで選択する。 【 Command + D 】 (カーソル移動 選択) カレントワードを選択する。 すでに文字列を選択中の場合は文字列を検索して順次選択する。 【 Command + Shift + Space 】 (カーソル移動 選択) スコープ範囲全体を選択する。 【 Ctrl + Shift + M 】 (カーソル移動 選択) 括弧内を選択する。 【 Ctrl + M 】 (カーソル移動 選択) 括弧の始め、終わりまでカーソルを移動する。 【 Command + Shift + J 】 (カーソル移動 選択) インデントまで選択する。 【 Command + Shift + A 】 (カーソル移動 選択) タグまで選択する。 【 Command + L 】 (カーソル移動 選択) 選択部分から改行までを削除する。 【 Ctrl + Command + ↑↓ 】 (カーソル移動 選択) 選択行と戦後の行を入れ替える。 【 Ctrl + BackSpace or Delete 】 (カーソル移動 選択) 単語をカーソルまで削除する。 【 Command + Return 】 (カーソル移動 選択) 変換の確定などを行わずに改行を追加する。 【 Command + Shift + Return 】 (カーソル移動 選択) 一つ下の行に改行を追加する。 【 Command + P 】 (検索関連 表示) ファイル選択パレットを開く。(Command + Tと同じ。) 【 Command + T 】 (検索関連 表示) ファイル選択パレットを開く。(Command + Pと同じ。) 【 Command + Shift + P 】 (検索関連 表示) コマンドパレットを開く。 【 Command + Ctrl + P 】 (検索関連 表示) プロジェクト選択パレットを開く。 【 Command + R 】 (検索関連 表示) 関数検索パレットを開く。 【 Ctrl + G 】 (検索関連 表示) 行番号指定パレットを開く。 【 Command + Alt + F 】 (検索関連 表示) 一括置換ボックスを呼び出す。 【 Command + F 】 (検索関連 表示) 検索ボックスを呼び出す。 【 Command + Shift + F 】 (検索関連 表示) ファイルやフォルダを指定して検索するボックスを開く。 【 Command + I 】 (検索関連 表示) インクリメンタル検索ボックスを開く。 【 Command + Shift + I 】 (検索関連 表示) インクリメンタル検索パネルを開く。(遡って検索) 【 Command + G 】 (検索関連 操作) 次を検索する。 【 Command + Shift + G 】 (検索関連 操作) 前を検索する。 【 Return 】 (検索パネルでのReturnの扱い) 次を検索する。 【 Shift + Return 】 (検索パネルでのReturnの扱い) 前を検索する。 【 Alt + Return 】 (検索パネルでのReturnの扱い) 検索結果すべてを選択する。 【 Command + Alt + C 】 (検索パネルの設定) 大文字小文字の区別するかを切り替える。 【 Command + E 】 (カレントワードからの検索) カレントワードを検索する。 【 Alt + Command + G 】 (カレントワードからの検索) カレントワードで次を検索する。 【 Shift + Alt + Command + G 】 (カレントワードからの検索) カレントワードで前を検索する。 【 Ctrl + Command + G 】 (カレントワードからの検索) カレントワードですべてを選択する。 【 Command + K から Command + 1-9 】 (折りたたみ) レベルnで折りたたみする。 【 Command + K から Command + 0(zero) 】 (折りたたみ) 折りたたみをすべて解除する。 【 Command + K から Command + J 】 (折りたたみ) 折りたたみをすべて解除する。 【 Command + Alt + [ ] 】 (折りたたみ) 選択部分の折りたたみ、もしくは解除する。 【 Command + / 】 (折りたたみ) 選択している行をコメントアウトする。 【 Command + Alt + / 】 (折りたたみ) 選択場所の後ろにコメントを挿入する。 【 Command + [ ] 】 (折りたたみ) インデント・アンインデントを実行する。 【 Ctrl + Q 】 (マクロ) マクロの記録開始・終了する。 【 Ctrl + Shift + Q 】 (マクロ) 記録したマクロを実行する。 【 Command + F2 】 (マーク) ブックマークを追加する。 【 F2 】 (マーク) 次のブックマークに移動する。 【 Shift + F2 】 (マーク) 前のブックマークに移動する。 【 Command + K から Command + Space 】 (マーク) マークを設置する。 【 Commakd + K から Command + A 】 (マーク) マークまで選択する。 【 Command + K から Command + W 】 (マーク) マークまで削除する。 【 Command + K から Command + X 】 (マーク) マークをカレント行に移す。 【 Command + Shift + F2 】 (マーク) ブックマークをクリアする。 【 Command + K から Command + G 】 (マーク) マークおよびブックマークをクリアする。 【 Command + K から Command + U 】 (整形) 選択範囲をすべて大文字に変換する。 【 Command + K から Command + L 】 (整形) 選択範囲をすべて小文字に変換する。 【 F6 】 (整形) スペルチェック機能のオンオフを切り替える。 【 Ctrl + F6 】 (整形) 次のミススペルを選択する。 【 Ctrl + Shift + F6 】 (整形) 前のミススペルを選択する。 【 F5 】 (整形) 行をソートする。 【 Ctrl + F5 】 (整形) 大文字小文字を考慮して行をソートする。 |
|
| 309位 |
|
|||
|
12:49:59 |
(SAKURA Internet Inc. 所属) |
|
Ansibleのroleを実際に使ってみてわかったことを共有します。さらに便利に活用するtips等ありましたら、ぜひコメントをお願いします。
## Ansibleのroleは単にインクルードの単位 最初roleという名前を聞いたときは、webserverとかdbserverといった役割を設定するのだろうと予想しました。が、[Rolesのドキュメント](http://docs.ansible.com/playbooks_roles.html#roles)を読んで、playbookの一部をインクルードして再利用するための仕組みだということがわかりました。つまり、playbookを分割・構成するコンポーネントという意味合いです。 もともとroleはなくてincludeの仕組みだけありましたが、より便利にするためにroleという仕組みが追加されたという経緯のようです。 例えばnginx, mysqlといった単位でroleを定義するのが良いです。さらにmysql/clientのように階層的なrole名を用いることも出来ます。下記で詳しく説明します。 roleを使わなくてもincludeでtaskから別のtaskのファイルを読み込むことはできます。が、roleを分けるほうが以下の利点があるため、他のroleから利用しやすくなります。 - roleが別になるのでrole_varsの内容も分割できる - role間の依存関係を設定できる - role間の依存関係には実行条件も設定できる ## roleのディレクトリ・ファイル構成 Ansibleのroleは以下の様なディレクトリ・ファイル構成になっています。ここではmysqlというroleを例に説明します。 - roles/mysql/tasks/main.yml - タスク定義のメインファイル。内容が多くなってきたら分割してtasks/foo.ymlのようなファイルを作ってtasks/main.ymlからincludeすることもできます。 - roles/mysql/handlers/main.yml - notifyに対するハンドラ定義のメインファイル。もう少し詳しく言うと、ファイルを変更した場合にAnsibleの実行の最後にサービスを再起動やリロードさせるためのnotificationの仕組みで呼び出されるハンドラ定義のメインファイルです。notificationについて詳しくは[Running Operations On Change](http://www.ansibleworks.com/docs/playbooks.html#running-operations-on-change)を参照してください。 - roles/mysql/vars/main.yml - role単位の変数定義のメインファイル。 - roles/mysql/meta/main.yml - roleの依存関係の定義ファイル。 ## 階層的なrole名も使えますが注意が必要です Ansibleのroleにはmysqlといったシンプルな名前の他に、mysql/clientといった階層的な名前をつけることもできます。 ただし、roleの定義フォルダにはtasks, handlers, vars, metaというフォルダがあるので、これらの名前は避ける必要があります。また将来roleの定義フォルダとして追加されそうな名前も避けておいたほうが無難です。 ## roleのデフォルト変数 [Role Defaults Variables](http://docs.ansible.com/playbooks_roles.html#role-default-variables)によると、roleから別のroleをインクルードしたり依存関係を設定するとそちらのrole_varsに定義された変数がデフォルト値として設定されます。それを自分のrole_varsで上書き変更することも可能です。 この仕組を使うと場合によっては変数定義を一箇所にまとめることもできます。例えば、mysql/clientでクライアント・サーバ共通用のrpmをインストールし、mysql/serverではサーバ専用のrpmをインストールする場合、mysql/serverからmysql/clientに依存させておいて、rpmのバージョンの変数定義をmysql/clientのほうにまとめて書くといったことができます。 # role間の依存関係を設定できる 例えばmysql/serverがmysql/clientに依存する場合は以下のように書きます。 ```yaml:roles/mysql/server/meta/main.yml --- dependencies: - { role: mysql/client } ``` 依存関係を設定すると、依存先のroleが先に実行されるようになります。依存関係は再帰的に辿られます。また、依存関係で複数回参照されても通常は最初の1回だけが実行されます。allow_duplicatesキーをtrueに設定すれば毎回実行することも出来ます。 詳細は[Role Dependencies](http://docs.ansible.com/playbooks_roles.html#role-dependencies)にドキュメントがありますので参照してください。 ## role間の依存関係には実行条件も設定できる 依存関係の定義にはwhenキーで条件を設定することも出来ます。これを使えばOSのディストリビューションに応じて実行するサブroleを切り替えることが出来ます。例えばlxcがlxc/redhat, lxc/debianに依存している場合は以下のように書けます。 ```yaml:roles/lxc/meta/main.yml --- dependencies: - { role: lxc/redhat, when: "ansible_os_family == 'RedHat'" } - { role: lxc/debian, when: "ansible_os_family == 'Debian'" } ``` ## 実際のplaybookでの例 CentOS 6.4 x86_64用のroleの実例は拙作のplaybookを参照してください。まだまだ非互換な変更が入る予定なので、その点はご留意ください。 https://github.com/hnakamur/ansible-playbooks/tree/master/roles |
|
| 310位 |
|
|||
|
02:04:36 |
(フリーランス 所属) |
|
気がついたら[Chef](http://tsuchikazu.net/chef_solo_start/)やら[Puppet](http://gihyo.jp/admin/serial/01/puppet)やら[Ansible](http://apatheia.info/blog/2013/04/06/about-ansible/)は[DevOps](http://www.atmarkit.co.jp/ait/articles/1307/02/news002.html)と呼ばれるようになったんですね。
時代のちょっと先端に行ったと思ったらまた取り残されてる感じがなんともこの業界ならではかと。 で、話は変わって前から[Vagrant](http://www.vagrantup.com/)のbaseBoxをゼロから作りたいぞ!!このヤロー!!って思っていたら [Packer](http://www.packer.io/)というVagrantの作者が仮想イメージを作るツールを出していたので作って見た時のメモ ## 環境 * macosx10.8(ホストOS) ## 作る仮想インスタンスのOS * CentOS6.4-x86_64_minimal ## 事前準備に必要なもの * [homebrew](http://brew.sh/index_ja.html) ※入れていない方は何も考えずに```ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"```と打とう * [virtualbox](https://www.virtualbox.org/) ※入れていない方は[そうだvirtualboxを使ってみよう](http://qiita.com/ryurock/items/31029ea9ac3fcb34abdc)からvirtualboxの入手だけを試してください ## 参考にしたサイト * [[続報]PackerでVagrant用のBoxを作成する](http://www.ryuzee.com/contents/blog/6706) * [OSXでpackerでCentOS6.4のVirtualBox VMを作成する](http://qiita.com/hnakamur/items/da0ed14192a18405f704) ## とりあえずPackerを試したい方はこちら * [Packerをつかって3ステップでVagrantのBoxを作る](http://qiita.com/ryurock/items/e3635221e4717f2addfe) ## ソースコード [ryurock/versicolor](https://github.com/ryurock/versicolor/tree/a2f83daedf9d30b6faa2c83c7536700b9dc6be29) ## Packerのインストール OSXで試したので他のOSは本家の説明に譲ります [INSTALL PACKER](http://www.packer.io/intro/getting-started/setup.html) ```shell brew tap homebrew/binary brew install packer ``` ## Packerの実行ファイルを作成する ### 【注意】 packer1.4以前と以降で設定方法が変わりました。 各項目のtypeは * Packer1.4以前は```virtualbox``` * Packer1.4以降は```virtualbox-iso``` になります。 適当なファイル名.jsonでもいいみたいです ```packer.json { "builders": [ { "type": "virtualbox-iso", "vm_name": "box", "boot_wait": "10s", "disk_size": 512000, "guest_os_type": "RedHat_64", "iso_checksum": "4a5fa01c81cc300f4729136e28ebe600", "iso_checksum_type": "md5", "iso_url": "http://ftp.iij.ad.jp/pub/linux/centos/6.4/isos/x86_64/CentOS-6.4-x86_64-minimal.iso", "ssh_username": "vagrant", "ssh_password": "vagrant", "ssh_port": 22, "ssh_wait_timeout": "10000s", "shutdown_command": "echo '/sbin/halt -h -p' > shutdown.sh; echo 'vagrant'|sudo -S sh 'shutdown.sh'", "guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso", "virtualbox_version_file": ".vbox_version", "vboxmanage": [ [ "modifyvm", "{{.Name}}", "--memory", "2048" ], [ "modifyvm", "{{.Name}}", "--cpus", "2" ] ], "http_directory": "./packer/builders/", "boot_command": [ "<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg<enter><wait>" ] } ], "provisioners": [ { "type": "shell", "scripts": [ "packer/provisioners/base.sh", "packer/provisioners/vagrant.sh", "packer/provisioners/virtualbox.sh", "packer/provisioners/cleanup.sh" ], "override": { "virtualbox-iso": { "execute_command": "echo 'vagrant'|sudo -S sh '{{.Path}}'" } } } ], "post-processors": [ { "type": "vagrant", "output": "./packer/vagrant-boxes/CentOS-6.4-x86_64-minimal.box" } ] } ``` 何が書いてあるのかわからないとおもうので説明していきます ### builders ```packer.json(一部抜粋) "builders": [ { "type": "virtualbox-iso", "vm_name": "CentOS6.4-x86_minimal", . . . } ] ``` これは作る仮想インスタンスの設定を記載します。 | 項目名 | 説明 | |:-----------|------------:| | type | 何の仮想インスタンスを作成するかを指定する項目ですAWSのインスタンスとかも指定可能。今回はvirtualboxなので 「virtualbox-iso」を記載 | | vm_name | デフォルトのvm名になりますVagrantでもオーバーライドできるの記載しなくても問題ない | | boot_wait | 最初にVirtualboxが起動bootするまでの待ち時間 | | disk_size | Virtualboxのディスク容量の指定適当に多めで | |guest_os_type| CentOSx86系なら「RedHat_64」を指定。typeの一覧は```VBoxManage list ostypes``` でわかる| |iso_checksum| isoのchecksum。わからない場合は```md5 作るOS.iso```と叩けばわかる| |iso_url| isoのDL先のURL。何故かHTTPしかサポートしていなかった| |ssh_username| sshユーザー名。vagrantと連携したいならユーザー名はvagrantにしといたほうが楽かも| |ssh_password| sshユーザーのパスワード| |ssh_port| 何も考えずに22でいいでしょう| |ssh_wait_timeout| SSHの接続時間| | shutdown_command | シャットダウンコマンド | | guest_additions_path | Virtualboxのguest_additionsのパス | | virtualbox_version_file | VirtualBoxのバージョン番号なんでしょう | | vboxmanage | Virtualboxのインスタンスの設定。今回はメモリを2G、CPUCore数を2としている | |http_directory| インストール時にローカルにWebサーバをたててkickstartなどの設定情報を持っていくための設定です。kickstartって何?って方は[kickstartでCentOS6の自動インストール](http://wg.drive.ne.jp/miura/archives/2208)をみてください| |boot_command| boot時のkickstartを実行するときのks.cfgの場所を指定する| ### post-processors OSを作成した後に実行することを記載する項目です ```packer.json(一部抜粋) "post-processors": [ { "type": "vagrant", "output": "./packer/vagrant-boxes/CentOS-6.4-x86_64-minimal.box" } ] ``` | 項目名 | 説明 | |:-----------|------------:| | type | vagrantのボックス作る場合はvagrantで| | output | vagrantのボックス作った後の.boxの吐き出し場所の指定です| ## KickStartファイルを書く [OSXでpackerでCentOS6.4のVirtualBox VMを作成する](http://qiita.com/hnakamur/items/da0ed14192a18405f704)のまんまです。 まずはディレクトリを作ろう ```shell mkdir -r packer/vagrant-boxes packer/builders vim packer/builders/ks.cfg ``` ```shell:packer/builders/ks.cfg install cdrom lang en_US.UTF-8 keyboard us network --bootproto=dhcp rootpw vagrant firewall --enabled --service=ssh authconfig --enableshadow --passalgo=sha512 selinux --disabled timezone Asia/Tokyo bootloader --location=mbr text skipx zerombr clearpart --all --initlabel autopart auth --useshadow --enablemd5 firstboot --disabled reboot %packages --ignoremissing @core bzip2 kernel-devel kernel-headers -ipw2100-firmware -ipw2200-firmware -ivtv-firmware %end %post /usr/bin/yum -y install sudo /usr/sbin/groupadd -g 501 vagrant /usr/sbin/useradd vagrant -u 501 -g vagrant -G wheel echo "vagrant"|passwd --stdin vagrant echo "vagrant ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/vagrant chmod 0440 /etc/sudoers.d/vagrant %end ``` ### provisioners インスタンスが作成された後に実行するものです。KickStartで仮想インスタンスにプラスアルファの機能を追加する場合はprovisioners側で管理したほうがいいかと思います。provisionersのハッシュの値を入れれば任意のファイルを実行できます ```packer.json(一部抜粋) "provisioners": [ { "type": "shell", "scripts": [ "packer/provisioners/base.sh", "packer/provisioners/vagrant.sh", "packer/provisioners/virtualbox.sh", "packer/provisioners/cleanup.sh" ], "override": { "virtualbox-iso": { "execute_command": "echo 'vagrant'|sudo -S sh '{{.Path}}'" } } } ], ``` ## ビルドの実行 ```shell cd packer.jsonが置いてあるカレントディレクトリ packer build --only=virtualbox-iso packer.json # 古いpackerのバージョンの方は下記のコマンドで # packer build -only=virtualbox packer.json virtualbox output will be in this color. ==> virtualbox: Downloading or copying Guest additions checksums virtualbox: Downloading or copying: http://download.virtualbox.org/virtualbox/4.2.10/SHA256SUMS ==> virtualbox: Downloading or copying Guest additions virtualbox: Downloading or copying: http://download.virtualbox.org/virtualbox/4.2.10/VBoxGuestAdditions_4.2.10.iso ==> virtualbox: Downloading or copying ISO virtualbox: Downloading or copying: http://ftp.iij.ad.jp/pub/linux/centos/6.4/isos/x86_64/CentOS-6.4-x86_64-minimal.iso ==> virtualbox: Starting HTTP server on port 8081 ==> virtualbox: Creating virtual machine... ==> virtualbox: Creating hard drive... ==> virtualbox: Creating forwarded port mapping for SSH (host port 3213) ==> virtualbox: Executing custom VBoxManage commands... virtualbox: Executing: modifyvm CentOS6.4-x86_minimal --memory 2048 virtualbox: Executing: modifyvm CentOS6.4-x86_minimal --cpus 2 ==> virtualbox: Starting the virtual machine... ==> virtualbox: Waiting 10s for boot... ==> virtualbox: Typing the boot command... ==> virtualbox: Waiting for SSH to become available... ==> virtualbox: Connected to SSH! ==> virtualbox: Uploading VirtualBox version info (4.2.10) ==> virtualbox: Uploading VirtualBox guest additions ISO... ==> virtualbox: Gracefully halting virtual machine... virtualbox: virtualbox: virtualbox: Broadcast message from vagrant@localhost.localdomain virtualbox: (unknown) at 0:22 ... virtualbox: virtualbox: The system is going down for power off NOW! ==> virtualbox: Preparing to export machine... virtualbox: Deleting forwarded port mapping for SSH (host port 3213) ==> virtualbox: Exporting virtual machine... ==> virtualbox: Unregistering and deleting virtual machine... ==> virtualbox: Running post-processor: vagrant ==> virtualbox (vagrant): Creating Vagrant box for 'virtualbox' provider virtualbox (vagrant): Copying: output-virtualbox/packer-disk1.vmdk virtualbox (vagrant): Copying: output-virtualbox/packer.ovf virtualbox (vagrant): Renaming the OVF to box.ovf... virtualbox (vagrant): Compressing box... virtualbox (vagrant): Compressing: Vagrantfile virtualbox (vagrant): Compressing: box.ovf virtualbox (vagrant): Compressing: metadata.json virtualbox (vagrant): Compressing: packer-disk1.vmdk Build 'virtualbox' finished. ==> Builds finished. The artifacts of successful builds are: --> virtualbox: 'virtualbox' provider box: ./packer/vagrant-boxes/CentOS-6.4-x86_64-minimal.box ``` ## VagrantFileに追加 VagrantFileがない場合は作る ```shell cd packer.jsonが置いてあるカレントディレクトリ vagrat init ``` VagrantFileに書きましょ ```ruby:Vagrantfile # -*- mode: ruby -*- # vi: set ft=ruby : BOX_NAME = "CentOS6.4-x86-minimal" Vagrant.configure("2") do |config| config.vm.box = BOX_NAME config.vm.box_url = "./packer/vagrant-boxes/CentOS-6.4-x86_64-minimal.box" config.vm.network :private_network, ip: "192.168.56.101" config.vm.provider :virtualbox do |vb| vb.name = BOX_NAME vb.customize ["modifyvm", :id, "--memory", "2048"] vb.customize ["modifyvm", :id, "--cpus", "2"] end end ``` ## Vagrantで起動確認 ```shell vagrant up Bringing machine 'default' up with 'virtualbox' provider... [default] Box 'CentOS6.4-x86-minimal' was not found. Fetching box from specified URL for the provider 'virtualbox'. Note that if the URL does not have a box for this provider, you should interrupt Vagrant now and add the box yourself. Otherwise Vagrant will attempt to download the full box prior to discovering this error. Downloading or copying the box... Extracting box...e: 285M/s, Estimated time remaining: --:--:--) Successfully added box 'CentOS6.4-x86-minimal' with provider 'virtualbox'! [default] Importing base box 'CentOS6.4-x86-minimal'... [default] Matching MAC address for NAT networking... [default] Setting the name of the VM... [default] Clearing any previously set forwarded ports... [default] Creating shared folders metadata... [default] Clearing any previously set network interfaces... [default] Preparing network interfaces based on configuration... [default] Forwarding ports... [default] -- 22 => 2222 (adapter 1) [default] Running any VM customizations... [default] Booting VM... [default] Waiting for VM to boot. This can take a few minutes. ``` これでオレオレ.boxの出来上がりです!! |
|
| 311位 |
|
|||
|
23:57:32 |
|
|
# 追記
## 2015-01-27 最近 Qiita また活発に見始めたので思い出してきました。 今は NeoBundle も更新されて ```neobundle#rc()``` は非推奨になっているので、更新しています。 ## 2013-05-22 .vimrc のサンプルの設定が一部間違っていたのを修正しました。 if has('vim_starting') 内に call neobundle#rc(expand('~/.vim/bundle')) を記述していたのは誤りです。 # はじめに 「vim なんか使うかよ!」って人に、30分ぐらいで「使っていこうかな」となってもらえたので、その時におすすめした設定やプラグインを書いていこうと思います。 # この記事に書いてあること - インストールするプラグインの説明 - モダンな環境にするための、最小のセットアップ手順 # ターゲット この記事は以下のような人を対象にしています。 - 設定ファイルを書き換える時とかは vim 使ってるよ - ローカルのファイルを編集する時は Sublime Text かなー - でも ssh 先のファイルを編集したりする時辛いんだよね - vim 使ってる人ってカッコいいし、僕も使えるようになりたい # 前提 - vim が割と新しいこと - 対象の環境に git が導入されていること # プラグイン ## neobundle このプラグインが提供する機能は、いわゆるプラグインのパッケージマネージャです。 こちらは導入後、ほんの少し設定が必要なので後述します。 (他のプラグインを導入するのが非常に楽になるので、入れておいてもらいたいです。) ## ctrlp このプラグインは *必須* といっていいぐらい、導入すると効率が変わります。 提供する主な機能は Sublime Text, 各種 IDE でも存在している ```再帰的にファイルを列挙し、絞り込んで開く``` です。 とりあえず導入して Ctrl+p をノーマルモードで打鍵すれば使い方は分かるでしょう。 ## NERDTree このプラグインが提供する機能も、各種 IDE などに存在している ```ファイルエクスプローラ(左側に表示されるやつ)``` です。 ctrlp を入れておけばいらないんじゃないの?という感じもしますが、 NERDTree を入れると「ファイルの削除/追加/移動」などを直感的に行えます。 とりあえず導入して :NERDTreeToggle で開き、削除したいファイルの上で m を打鍵すれば使い方は分かるでしょう。 ## syntastic このプラグインが提供する機能も、各種 IDE にあるものです。 ```保存時にシンタックスチェックを自動で行う``` です。 こちらはとりあえず導入すれば勝手に動きだすので本当に初心者にもおすすめです。 # セットアップ とりあえず、今回は自分の環境と同じ状態を目指します。 必要な作業は4つあります。どれも簡単な作業です。 ## 1. ディレクトリの作成 下記のディレクトリ構造を用意してください。 ```:directory ~/ .vim/ bundle/ .vimrc ``` ## 2. neobundle.vim の導入 ~/.vim/bundle の下に ```git clone git://github.com/Shougo/neobundle.vim``` してください。 ## 3. .vimrc の編集 これは一旦、下記の内容を .vimrc の先頭にコピペしてください。 ```vim:.vimrc scriptencoding utf-8 set nocompatible if has('vim_starting') filetype plugin off filetype indent off execute 'set runtimepath+=' . expand('~/.vim/bundle/neobundle.vim') endif call neobundle#begin(expand('~/.vim/bundle')) NeoBundle 'git://github.com/kien/ctrlp.vim.git' NeoBundle 'git://github.com/Shougo/neobundle.vim.git' NeoBundle 'git://github.com/scrooloose/nerdtree.git' NeoBundle 'git://github.com/scrooloose/syntastic.git' call neobundle#end() syntax on filetype plugin on filetype indent on " SSH クライアントの設定によってはマウスが使える(putty だと最初からいける) set mouse=n ``` ※ その他の設定はお好みで! ## 4. neobundle.vim で、プラグインをインストール 上記の設定をすべて終えたら、一度 vim を立ち上げ直してください。 立ち上げ直したら ```:NeoBundleInstall``` を実行します。 実行が終わったら、もう一度 vim を立ち上げ直すと終了です! # チートシート(2つしかない) 作業ディレクトリにカレントディレクトリを変更し vim を起動してください。 また、キーの表記は vim に合わせて書きます。 ※ *<C-p>* と書いてある場合は *Ctrl + p* を表します。 ## ファイルを開きたい *<C-p>* キーを打鍵し、ファイル名を入力して、*<CR>* を打鍵して開けます。 ## ファイル/ディレクトリを操作したい *:NERDTreeToggle* コマンドを実行後、操作対象の上にカーソルを移動させます。 その後 *m* キーを打鍵すると、メニューが出てくるのでお好みの操作を選択します。 # 終わりに 自分は上記プラグインとは別の、類似の機能をもったプラグインを入れたりしてます。 ただ、高機能な分設定が複雑だったりするので、とっつきやすいものを選びました。 この設定を行って、気に入ったらどんどん他のプラグインも探してみてくださいね! |
|
| 312位 |
|
|||
|
08:14:01 |
(和雲株式会社 所属) |
|
[遺伝的アルゴリズム(GA)](http://ja.wikipedia.org/wiki/%E9%81%BA%E4%BC%9D%E7%9A%84%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0)でサーバの自動チューニングをします。
GAを機械学習を一つと書いてしまいましたが違うようなのでタイトルを変更させて頂きました。 遺伝的アルゴリズムについては↓の動画が分かりやすいです > http://www.youtube.com/watch?v=yZJ1V-zv_gU まずは通常の負荷テストができるところまで準備する必要があります。攻撃用のサーバをターゲットと(ネットワーク的に)近い場所に用意してください。負荷を掛ける側(Attacker)にも相応のスペックは必要です。 ストレスツールはコマンドラインから利用出来るものでしたらなんでもかまいません。`ab`(Apache Bench)などは最初から入っているので手軽ですが、今回は「[グリーン破壊](https://github.com/KLab/green-hakai/)」というソフトを利用しました(グリーン破壊のインストール方法は本家サイトに譲ります) 自動チューニングを行うにあたり、ターゲット(=チューニング対象のサーバ群)がChefで管理されていて、パラメータが属性になっている必要があります。例えば、`httpd.conf`テンプレート`httpd.conf.erb`が以下のようになっているとき、 ```erb:httpd.conf.erb ... <IfModule prefork.c> StartServers 8 MinSpareServers 10 MaxSpareServers 20 ServerLimit 255 MaxClients 255 MaxRequestsPerChild 1000 </IfModule> ... ``` この`MaxClients`をChefの属性(attribute)にするなら以下のようにします。 ```erb:httpd.conf.erb ... <IfModule prefork.c> StartServers 8 MinSpareServers 10 MaxSpareServers 20 ServerLimit 255 MaxClients <%= node["httpd"]["max_clients"] %> MaxRequestsPerChild 1000 </IfModule> ... ``` この状態で、`nodes/www-1.example.json`に以下のように書くことで、`www-1.example`サーバの`MaxClients`を制御することができます。 ```json: { "httpd" :{ "max_clients" :255 }, ... } ``` この`JSON`を書き換え、Chefを使ってターゲットにデプロイし、負荷テストを掛けてその結果から新しい試行パラメータをGAを用いて探索していくのが拙作の[gargor](https://github.com/tumf/gargor)です。 gargorをインストールします。(Ruby 1.9〜が必要です) ```bash: gem install gargor ``` Chefレポジトリの下に`gargor.rb`を置きます。内容を適宜書き換えてください。 ```rb:gargor.rb # 世代数: 1以上を指定してください max_generations 10 # 個体数: 1世代あたりの個体数 population 10 # エリート: 世代交代の時に適応値の高い個体をそのまま次世代に引き継ぐ数 elite 1 # 突然変異の確率: "0.01"で"1%" mutation 0.01 # ターゲットをChefで料理するコマンド %sには、ノード名が入る target_cooking_cmd "knife solo cook %s" # ターゲットのノード # 攻撃前に以下のノードすべてに対してtarget_cooking_cmdが実施される target_nodes ["www-1.example"] # 攻撃コマンド attack_cmd "ssh attacker.example ./bin/ghakai www-1.example.yml 2>/dev/null" # 攻撃結果の評価 # code: attack_cmdのプロセス終了コード(普通は0:成功) # out: attack_cmdの標準出力 # time: attack_cmdの実行時間 evaluate do |code,out,time| puts out fitness = 0 # 攻撃コマンドで使っている、グリーン破壊の標準出力から # FAILEDの値を抜き出し0以外は適応値0としている if time > 0 && code == 0 && /^FAILED (\d+)/ =~ out && $1 == "0" # 攻撃コマンドで使っている、グリーン破壊の標準出力から # 適応値に代用できそうなものを正規表現で取得する # request count:200, concurrenry:20, 45.060816 req/s if /, ([\.\d]+) req\/s/ =~ out fitness = $1.to_f end # 単純に実行時間で適応値を設定したいなら以下のようにしても良い # fitness = 1/time end # このブロックは必ず適応値を返すこと(整数or浮動小数) fitness end # パラメータ定義 # GAにより変動されるパラメータをここで定義する # # param 名前 do # json_file: 値を上書くJSONファイル nodes/*.jsonやroles/*.json # (注意!) gargorはこのjsonファイルを容赦なく書き換える # json_path: json_fileの中から書変える値の場所をJSONPath形式で指定する # mutaion: 突然変異時の値の設定 param "max_clients" do json_file "roles/www.json" json_path '$.httpd.max_clients' mutation rand(500)+10 end ``` グリーン破壊以外を使うときには、evaluateをカスタマイズする必要がありますが ```rb: # fitness = 1/time ``` この部分をコメントアウトを外すことで計測時間(の逆数)でざっくりスコアを決めることができます。 gargorは最大で`世代数×個体数`の数の負荷試験を行い良さそうなパラメータを採用していきます。ものすごく時間がかかるので、帰り際に仕掛けて翌朝、結果を確認するような感じがよいかと。 もちろんパラメータを複数設定することもできます。 GAなので気まぐれの要素があり良い結果が出るかはやってみないとわかりません。個体数や世代数を増やすと時間がかかりますが精度が上がります。また、`mutation`の書き方で精度も変わりますので、是非いろいろやってみてください。 gargorはできたてホヤホヤのソフトウェアなので、不具合や改良がありましたらgithubにてお願いします。pull-requestお待ちしています。 > https://github.com/tumf/gargor |
|
| 313位 |
|
|||
|
13:47:07 |
|
|
テストコードでは必須と言ってもいいくらいにお世話になっているモックライブラリ「Mockito」
最低限の使い方というか、実際よく使っているパターンを紹介します。 # モックって? モックライブラリを使ったことがない方は、モックするという事自体に馴染みがないと思います。 例えば、テスト対象のクラスAが別のクラスBに依存している場合に、クラスAのテストコードなのにクラスBを初期化する処理を長々と書いたりするのは余計な手間です。もしクラスBがビジネスロジックなら目も当てられないテストコードが出来上がります。 モックライブラリでは、クラスBをクラスB自身の実装に依存しないmock(ハリボテ)として生成し、更にそのメソッドの戻り値を任意に設定するという事ができます。これにより「クラスBがこういう状態でこういう値を戻す場合のクラスAのテスト」が容易に書けます。 そしてそのモックライブラリの注目株が **Mockito** です。 # mock Mockito#<b>mock</b>メソッドを使うと、そのクラスの実装に依存しないモックオブジェクトを生成します。 各メソッドの戻り値はモックオブジェクトになりますが、指定したメソッドの戻り値だけを任意に設定できます。 ## 任意の値を戻すようにモックする Hogeクラスをモックし、Hoge#getFooメソッドの戻り値を任意のFooにします。 Hogeが抽象クラスやインターフェースの場合でも同じようにモックはできます。 ```java: Hoge hoge = mock(Hoge.class); Foo foo = new Foo(); when(hoge.getFoo()).thenReturn(foo); assertThat(hoge.getFoo(), is(foo)); ``` ### 引数に応じて戻り値を分ける 引数を受け取るメソッドなら、引数に応じて戻り値を変えるようにモックできます。 引数には具体的な値を指定するか Mockito#<b>any〜</b> メソッドを使いましょう。 各プリミティブ型・ListやSetなどCollection系・可変長引数・任意のクラス、それぞれに対応した<b>any〜</b>メソッドがあるので使い分けてください。 ```java: Hoge hoge = mock(Hoge.class); when(hoge.getFuga(anyInt())).thenReturn(-1); // どんな数が来ても -1 を返す when(hoge.getFuga(1)).thenReturn(100); // ただし、1の時は100を返す when(hoge.getFuga(2)).thenReturn(null); // ただし、2の時はnullを返す assertThat(hoge.getFuga(1), is(100)); assertThat(hoge.getFuga(2), is(nullValue())); assertThat(hoge.getFuga(999), is(-1)); ``` ## ネストしたオブジェクトをモックしたい mockメソッドの第2引数では各種オプションを設定できます。 Mockito#<b>RETURNS_DEEP_STUBS</b>を使うと、Hoge#getFooの戻り値のFooの各メソッドもモックオブジェクトを返すようになります。 これを用いると、Hoge#getFooとFoo#getBarの実装を気にせずに、Foo#getBarの戻り値を任意に設定できます。 ```java: Hoge hoge = mock(Hoge.class, RETURNS_DEEP_STUBS); Bar bar = new Bar(); when(hoge.getFoo().getBar()).thenReturn(bar); assertThat(hoge.getFoo().getBar(), is(bar)); ``` ※再帰的な構造のオブジェクトをRETURNS_DEEP_STUBSでmockして再帰メソッドに食わせると、無限ループになります。思いの外やってしまうミスなので注意して下さい。 # spy Mockito#<b>spy</b>メソッドを使うと、任意のインスタンスの一部だけモックできます。 これは、完全なハリボテを作るmockとは異なり、対象のクラスの実装に依存しつつ、指定したメソッドだけをモックします。 ## doSomethingの中で呼ばれるsomeMethodだけをモックしたい spyで得たオブジェクトは、モックしたメソッドの他は元々の実装で動作します。 mockでモックオブジェクトを生成した場合と、メソッドのモックの仕方が異なるので注意が必要です。 例えば、Hoge#someMethodに依存するHoge#doSomethingをテストしたい時、以下のようにHoge#someMethodの戻り値を任意に設定します。 ```java: Hoge hoge = spy(new Hoge()); Piyo piyo = new Piyo(); doReturn(piyo).when(hoge).someMethod(any(Bar.class)); Result result = hoge.doSomething(); verify(hoge).someMethod(any(Bar.class)); // someMethodが呼ばれた事をassert ``` # privateなフィールドを任意に設定 ## dependerが依存しているdependeeフィールドをモックする Mockitoにはprivateなフィールドをリフレクションで任意に設定するユーティリティが付属しています。 ```java: Whitebox.setInternalState(depender, "dependee", dependee); ``` # その他 Mockitoはモックする他にもテストに有用な機能を持っています。 上でもちょっとだけ出てきましたが、メソッドがどのように呼び出されたかをテストするverifyなどです。 verifyでは、対象のメソッドが、どういう順序で、どういう引数で、何回呼び出されたか、といったことをテストできます。 |
|
| 314位 |
|
|||
|
17:13:35 |
(Increments inc. 所属) |
|
[前編](http://qiita.com/yuku_t/items/726a67d8ae74eea2540a)ではHTML5のアウトラインを綺麗にする、というお話でした。アウトラインを綺麗にすれば、検索エンジンにコンテンツの階層構造がどうなっているのか、正しく教えることができます。
けどそれだけでは1つ1つのまとまりが一体何を表しているのかが不明です。 schema.orgに準拠することで、そこに何が書かれているのか、を検索エンジンに教えることができます。 # schema.orgとは [schema.org](http://schema.org) はGoogle, Microsoft, Yahoo!などの検索エンジンベンダーが集まってマークアップ方式を標準化している組織とその標準化された仕様を文書化しているサイトです。 例えば「ここは著者のことを書いています」「これは著者の名前です」「この記事が公開されたのはこの日時です」などなど、HTMLの機能だけでは伝えきれない詳細な情報をクローラに伝えるための手段が色々定義されています。 ちなみにschema.orgで定義されている語彙は全てmicrodataという方式が使われています。microdataという書き方に、Googleなどがルールを与えたものがschema.orgなわけです。 ## なぜQiitaはschema.orgに準拠したか やるからには効果が無きゃ困る訳ですが、Googleが提供しているツールを観察してみる限り、少なくともGoogleはschema.orgに準拠したマークアップを理解していることが分かります。 前回の[QiitaのSEO事情 - HTMLのアウトライン意識してますか?](http://qiita.com/yuku_t/items/726a67d8ae74eea2540a)でも言及したように、正しく検索エンジンに解釈されることがまわりまわって検索順位の向上に寄与すると考えているため、schema.orgに準拠することにしました。 また、検索順位というのは相対評価で決まるものです。他のサイトがやっていないことをすることに価値があるんじゃないか、と考えたというのもあります。 # マークアップする では実際にschema.orgに準拠したマークアップを行なってましょう。 ## itemscopeとitemtype 例えばQiitaの記事には著者情報を表した箇所があります。ここには著者名と自己紹介が書かれています。 ```html <section> <h1>著者情報</h1> <span>yuku_t</span> <p>自己紹介</p> </section> ``` ここが著者に関することを書いていると明示するために、まず `itemscope` 属性をその情報を囲っている要素に加えます。 ```html <section itemscope> <h1>著者情報</h1> <span>yuku_t</span> <p>自己紹介</p> </section> ``` `itemscope` を加えることで `<section>…</section>` が何かしらの項目についてのものであることが表明されます。 これでは何に関する情報化が分かりません。これが何について表しているのか `itemtype` 属性を `itemscope` 属性の直後に置くことで表明できます。 ```html <section itemscope itemtype="http://schema.org/Person"> <h1>著者情報</h1> <span>yuku_t</span> <p>自己紹介</p> </section> ``` これでこれがschema.orgで定義されている `Person` について記述している箇所だということが表現できました。タイプはURLを使って指定します。今回の場合だと `http://schema.org/Person` です。 ## itemprop `Person` には名前や詳細などのプロパティを持ちます。これを指定するには `itemprop="name"` を名前をマークアップしている要素に加えます。(指定できるプロパティは `itemtype` で指定したURLで知ることができます。今回の場合なら http://schema.org/Person です。) ```html <section itemscope itemtype="http://schema.org/Person"> <h1>著者情報</h1> <span itemprop="name">yuku_t</span> <p itemprop="description">自己紹介</p> </section> ``` ## 入れ子にする プロパティの中にはそれ自体が一つの項目になれるものがあります。 QiitaではユーザはOrganizationに所属することができます。ある要素が他の項目を表すことを表明するには `itemscope` を `itemprop` の直後に置きます。 ```html <section itemscope itemtype="http://schema.org/Person"> <h1>著者情報</h1> <span itemprop="name">yuku_t</span> <p itemprop="description">自己紹介</p> <div itemprop="memberOf" itemscope itemtype="http://schema.org/Organization"> <span itemprop="name">Increments.inc</span> </div> </section> ``` ここでは `Person` の `memberOf` プロパティに `Organization` が指定されています。これでyuku_tという `name` の `Person` がIncrements.incという `name` の `Organization` の `memberOf` である、という情報を表すとができました。 `memberOf` に `Organization` を指定できることは http://schema.org/Person の `memberOf` のExpected Typeで知ることができます。 # itemtypeの種類 - [Full Hierarchy - schema.org](http://schema.org/docs/full.html) `itemtype` に指定できるタイプはクラス継承のように、階層構造になっています。一番上が `Thing` です。 先ほど `memberOf` には `Organization` を指定できると言いましたが、 `Organization` を継承したタイプ、例えば `Corporation` や `Hospital` など、も指定することが可能です。 実に様々なタイプが定義されているので、必要に応じて選択しましょう。 # プロパティが画面に現れない場合 デザインの関係上、必ずしもプロパティが画面に現れるとは限りません。例えばユーザアイコンは表示するけど、名前は表示しないような場合です。こういう場合は `meta` か `link` 要素を使います。値がURLなら `link` 単なる文字列なら `meta` になります。 ```html+erb <link itemprop="url" href="/yuku_t" /> <meta itemprop="name" content="yuku_t" /> ``` # マークアップ時に意識すること schema.orgの「[Get started with schema.org](http://schema.org/docs/gs.html)」というドキュメントに、[schema.orgに準拠してマークアップするときに注意すること](http://schema.org/docs/gs.html#schemaorg_expected)が書かれているので、軽く紹介します。 ## hiddenな文書をマークアップしない 一般的に適切にマークアップしているなら、多いければ多いほどよいと言えますが、それは見える要素のみです。言明はされていませんが、schema.orgはhiddenな文書にマークアップするとペナルティが課されると暗にほのめかしています。 ## Expected Typeの代わりにテキストでも問題ない `memberOf` のExpected Typeは `Organization` ですが、これは別に必須というわけではありません。 `itemscope` にせず、単なるテキストやURLでも問題ありません。 ## urlプロパティを使う 他のページへのリンクがあって、そのリンク先が1つの項目を表しているなら、そのリンクには `itemprop="url"` をつけましょう。勿論そのリンク自体が `itemscope` に包含されていなければなりません。 # マークアップを確認してみる schema.orgに対応したマークアップをしてみたら、Googleが専用のツールを提供してくれているので、それが正しく書けているのか確かめましょう。 - [Google Structured Data Testing Tool](http://www.google.com/webmasters/tools/richsnippets) 確認したいページのURLを指定するか、HTMLを貼り付けてるか、の2通りの検査方法があります。 - サンプル:[このページの解析結果](http://www.google.com/webmasters/tools/richsnippets?q=http%3A%2F%2Fqiita.com%2Fyuku_t%2Fitems%2F3740cc9a5f8503693bd2) 上記のリンク先をみると、検索スニペットのサンプルに続いて、Personがどうたらこうたら、Articleがどうたらこうたらと出てくると思います。このような記述が意図した通りに出現していればOKです。 ちなみに、開発時はHTMLを貼り付けて確認することになると思いますが、なぜかHTML貼り付けだと後述するパンくずが検索スニペットのサンプルで適切に反映されないです。訝しみつつそのままデプロイしてみたらパンくずとして認識するようになったので、パンくずを設定するときは注意してください。(単なる勘違いの可能性もあります) # おまけ:存在しないパンくずを認識させる Qiitaの投稿ページがGoogle検索にひっかかるとこんな感じになってますよね。  SEOに効果があるのか分かりませんが、検索結果にパンくずを表示させることができます。パンくずとは何か、というのは適当にググってください。あと、これを表示する方法は公式ドキュメント([リッチ スニペット - パンくずリスト - ウェブマスター ツール ヘルプ](https://support.google.com/webmasters/answer/185417?hl=ja))を参照してください。 QiitaはURL構造が階層構造になっているので、ページにはパンくずが表示されていませんが、検索結果にはそれを反映させるようにしています。同様にデザインにはパンくずがないけど検索結果には表示させたい、という場合は、前述の「プロパティが画面に現れない場合」と組み合わせて次のように書きます。 ```html: <span itemscope itemtype="http://data-vocabulary.org/Breadcrumb"> <link itemprop="url" href="/yuku_t" /> <meta itemprop="title" content="yuku_t" /> </span> <span itemscope itemtype="http://data-vocabulary.org/Breadcrumb"> <link itemprop="url" href="/yuku_t/items" /> <meta itemprop="title" content="items" /> </span> ``` ここで使っているのがschema.orgではなくdata-vocabulary.orgであることに注意が必要です。 data-vocabulary.orgはschema.orgの前身となった(?)サイトで、現在ではそのほとんどがschema.orgに引き継がれていますが、少なくともこの `Breadcrumb` typeに関しては、Googleの公式ドキュメントでもそうしているように、data-vocabulary.orgで定義されているものを使わなければなりません。 # まとめ - 結構面倒なので、検索流入が重要なページだけやれば十分だし、余裕が無いならやらなくてもいい。 - でもやったら何かいいことがありそうな予感する。とりあえず無駄にはならないはず。 # QiitaのSEO事情 1. [HTMLのアウトライン意識してますか?](http://qiita.com/yuku_t/items/726a67d8ae74eea2540a) 2. schema.orgに準拠してクローラと会話しよう |
|
| 315位 |
|
|||
|
00:20:12 |
|
|

会社でAngularJSをわっしわっし使ってみて、そろそろ某かの意見を言えるようになったのではないか?と思ったので、推薦記事を書いてみます。AngularJSマンセー! 一般のAngularJS解説記事はまぁ巷にそれなりに出回っていると思うので、僕の周りに蔓延る混沌Andoroidクラスタ向けの説明記事を書いてみようと思います。 というわけで、AngularJSをAndroid用語を駆使して説明します。正確さはあまり気にしません。 読者に要求されるもの * Androidについての基本的な知識 * JavaScriptやHTMLなどに対する基本的な知識 参考 [わかめのAngularJSはてブ](http://b.hatena.ne.jp/vvakame/AngularJS/) # AngularJSってなに? [AngularJS](http://angularjs.org/)はGoogleが作成しているWebアプリのクライアント側用フレームワークです。 遠い仲間として[Backbone.js](http://backbonejs.org/)が、近い仲間として[Knockout.js](http://knockoutjs.com/)や[Flight](http://twitter.github.com/flight/)があります。 (まぁわかめはこの中ではBackbone.jsしか使ったことがないんですが。) 近年のWebアプリ界隈の発展と注文の増加っぷりは目覚ましく、JavaScriptで実装する対象がどんどん拡大しているといえます。静的ページで対応できる要素なんてタカが知れているんだ…! 要するに、要求がリッチになってきているので、クライアント側も発展しないとつらぽよだということです。 そこでJavaScript用のフレームワークだ!フリーダムすぎるJavaScriptで徒手空拳とか全裸と絆創膏だけでシベリアに遊びに行くようなもんですよ。 # なぜAngularJSを選ぶべきなのか? ## サーバ側動的ページ生成とか古いですよ! ぶっちゃけ、今更新規にアプリ作るのにJSPとか使って、何か操作するたびサーバ側でページ生成して…みたいな作り方することってないですよね。サーバはJSON吐く機械。ユーザに対する見た目(UI)を組み立て制御するのはJavaScriptでやるのが定番です。 ## JavaScriptで何かするにはフレームワークが欲しいですよ! JavaScriptは大変自由に書くことのできる言語で一定のルールを設けずに書くとなかなかヒドイことになります。わかめがTG社に入社したばかりの約3年前は、高階関数っぽい書き方をした中二病感漂うヤツと、ちゃんとOOPっぽく書くヤツと、C言語のように構造体+関数で書くヤツが混在してたような記憶があります。それはもう酷い有様です。(俺がどれかはあえて述べない) そこで、何らかのフレームワークを与え定まった書き方ができるのはメリットです。これはAngularJSに限った話ではないですが。 ## pushStateを自然に使うのが簡単になるよ! githubでは、リポジトリ内で移動する時にはページを読みなおして移動するのではなく、必要な部分のみ更新していて、大変シームレスに移動することができます。なおかつ、ブラウザの戻るや進むもキチンと使えます。この機能をしっかり提供するためには、あるページが読み込まれた時に現在のURLから適切にページをJavaScriptで構築しなければなりません。これを自分で1から作るのは結構な苦労です。 AngularJSであれば、URLに対してどういう画面を表示するかを指定する簡単な方法が提供されています。 ## テストを書くのが楽だよ! あとはAngularJSはテストが楽なのも好ポイントです。DI(Dependency Injection 依存性注入)をベースに色々な部品間でデータの受け渡しをしているので、テストの時にはMockを利用するようにするのも簡単です。このお陰でAjaxが絡んだテストもやりやすくなっています。 ## プログラムとDOMの分離が行いやすいよ! さらに、通常のWebアプリではjQueryを使ってDOMをごしごし組み立てて追加して…といったように、見た目の操作にはプログラミングの知識が必須でした。ですが、AngularJSではDOMをプログラムからいじる場面が大変少なくなるので、プログラマの手を借りずにデザイナさんが独力でいじくれる範囲が他のフレームワークより広くなるのではないかと思います。想像ですが。 ## つまり 今までわかめが試した限りでは、画面内でのDOMの組み換えや動きなどの少ないアプリ、例えば業務系のアプリなどを手早く組み立てるのに向いているように思います。 # そしてAngularJSを選ぶと苦労しそうな例 逆に、AngularJSを使うと苦労しそうなアプリはどういうアプリかを考えてみます。申し訳ないですが、まだ説明していない用語も使って書くのでこの記事を最後まで読み終わった後にもう一度読むとよくわかるかもしれません。 ## 既存のアプリを後からAngularJS化するのはちょっと… まず、既存のアプリをAngularJSに当てはめるのはあまりオススメできません。AngularJSに限らずですが、フレームワークというのはその世界観に100%入り浸った時に最大の恩恵が受けられるものです。なので、アプリを開発する時にAngularJSを導入する!と決めてしまうのが一番良いでしょう。無理矢理途中からAngularJSに置き換えた場合、controllerとDOMとの結合が強すぎて上手くテストできない!とかデータ通信周りのテストがあああぁ!とか`$routeProvider`を使った振り分けがあぁぁぁ!みたいな問題が出てきそうです。 ## DOMをぐりぐりいじくり倒すようなアプリはちょっと… また、AngularJSでは`service`や`controller`ではDOMをいじくるようなことはしてはいけない事になっています。なので、`directive`を作成してDOMを操作するしかない…んですが、それがなかなかメンドクサイ。わかめは未だに作り方がピンと来ていません。逆に言うと、見た目や挙動に凝らなければDOM操作はかなり避けて作れる、ということでもあります。 そのため、例えばゲームのようなアプリや、DOMをぐりぐりいじくり回すようなアプリは苦手なのではないかと思います。 と、思ったら[1000人対戦ボンバーマンのBombermine](http://bombermine.com/)がどうやらAngularJS使ってるみたいなんですよね…ど、どういうことだってばよ…( ゚д゚) # AngularJSのコードを見てみる ## HTML+AngularJSしてみる [サンプル](http://jsfiddle.net/vvakame/uUEGD/) を開くと自分で編集して試すことができます。jsFiddle便利なので、海外では結構流行っているみたいです。 <iframe style="width: 100%; height: 200px" src="http://jsfiddle.net/vvakame/uUEGD/embedded/result,html/presentation/" allowfullscreen="allowfullscreen" frameborder="0"></iframe> まず最初のサンプルです。最初はHTMLを書くだけで出来る範囲で見てみましょう。 以下の抜き出しではjsFiddleと少し変えて、より普通のAngularJSっぽくしてあります。 ```html:sample.html <!DOCTYPE html> <html ng-app> <body> <input type="text" ng-model="data" placeholder="名前?"> <hr> {{data || 'vvakame'}} より愛をこめて </body> </html> ``` `ng-app`属性が付いているものがAngularJSの管理下になります。これはHTML中に通常1回だけ出てきます。 通常、`ng-` で始まる属性はAngularJSがデフォルトで用意している要素です。つまり、公式のAPIを見れば解説が載っています。ng-appについて知りたい場合はngAppで検索したほうが良いかもしれません。 次に出てくるのは`ng-model`です。`ng-model`の中にはAngular Expressionsと呼ばれる式を書くことができます。概ねJavaScriptと似たような構文です。ここでは、`data`という変数を参照しています。inputタグへの入力値が`data`に反映されるようになります。 その後にある{{}}の中もAngular Expressionsで、`data || 'vvakame'`が評価され、`data`の中身が表示されます。`data`が空の場合は後者の`'vvakame'`が表示されます。 レッスン! 試しに、"○○から××へ愛をこめて" の形式になるように遊んでみましょう。`data`の部分をfromにし、もう一つtoのinputも増やしてみると上手くいきそうです。 ## HTML+JavaScript+AngularJSしてみる [サンプル](http://jsfiddle.net/vvakame/njAjQ/) <iframe style="width: 100%; height: 150px" src="http://jsfiddle.net/vvakame/njAjQ/embedded/result,html,js/presentation/" allowfullscreen="allowfullscreen" frameborder="0"></iframe> 2番目のサンプルです。ちょっとJavaScriptを書いてみます。`controller`を導入してみます。 ここでは `function Controller($scope)` という関数を定義し、HTML側で`ng-controller`に関数名を指定しています。引数の`$scope`はAngularJSではよく見る名前の引数です。`$scope`にセットした内容は、HTML側から参照することができます。変数のスコープまるまるそのものですね。 入れ子にした場合どうなるとか、`$rootScope`と呼ばれる一番上位のスコープがあったりするのですが、この記事では割愛します。詳しくは[こちら](http://docs.angularjs.org/guide/scope)を参照してください。 AngularJSのDIは、名前に従ってDIされるので、`$scope`以外の名前にすると上手く動かなくなってしまいます。どういう原理でDIをしているかというと、関数をtoStringすると関数のソースが手に入るので正規表現で無理矢理仮引数の名前を解析してDIしてます。無茶苦茶です。その場合、minifyすると引数の名前が変わってぶっ壊れちゃうんですが、そのための書き方もしっかり用意されています。しかし、めんどくさいので本記事では取り上げません。(というか、あんまり研究してない) ```html:sample.html <div ng-app> <div ng-controller="Controller"> <input type="text" ng-model="data" placeholder="名前?"> <button ng-click="reset()">消す</button> <hr> {{data}} より愛をこめて </div> </div> ``` ```javascript:sample.js function Controller($scope) { $scope.data = "vvakame"; $scope.reset = function () { $scope.data = "謎の誰か"; }; } ``` `ng-controller`は、そのタグの配下を制御する`controller`を指定してやります。`controller`には関数の名前を指定します。Controller内部では、変数の初期設定や使える関数を定義しています。`$scope.reset`では`data`の値を書き換えています。そして、`ng-click`の中ではその関数を呼んでいます。 # AngularJSをAndroidで説明する では、AngularJSで出てくる主要な仕組みや要素についてこれから説明していきます。 ## module = AndroidManifest.xml や library project 相当 AngularJSで何かをする時に、`module`を作成することを避けて通ることはできません。 サンプルコードだと`module`を作っていない場合も多いですが、`module`はAndroidManifest.xmlやlibrary projectに相当する設定が書けるのでざくざく書いてみましょう。 [サンプル](http://jsfiddle.net/vvakame/D4pLq/) ```javascript:sample.js angular.module("sample", [], function() { alert("初期化!"); }); function Controller($scope) { $scope.data = "vvakame"; $scope.reset = function () { $scope.data = "謎の誰か"; }; } ``` ```html:sample.html <div ng-app="sample"> <div ng-controller="Controller"> <input type="text" ng-model="data" placeholder="名前?"> <button ng-click="reset()">消す</button> <hr> {{data}} より愛をこめて </div> </div> ``` このようにします。moduleを定義した後は、`ng-app`にmodule名を指定すると、そのmoduleの元で初期化を行います。 この例だと、どっちかというとカスタムしたandroid.app.Applicationっぽいですねw module で行うことが多いのは以下の設定です。 ### 依存するmoduleの指定 ざっくり例えると、使うlibrary projectを追加する感じ。例えばInAppBillingが使いたい時はlibrary projectを使ったりするように、他の人が作ったmoduleや自作のmoduleを追加することもできます。 [サンプル](http://jsfiddle.net/vvakame/ydxTq/) ### $routeProvider の設定 ざっくり例えると、Activityとそれに対するIntentFilterを書くのに近いです。どのURLが表示/遷移された時にどのを表示するかを指定します。 [サンプル](https://dl.dropbox.com/u/6581286/sample/AngularJS-routeProvider/index.html) pushStateを使うので、iframeだとなんか上手く動かないぽいのでjsFiddleは無しです。 [gist](https://gist.github.com/vvakame/5056731/) ```javascript:sample.js angular.module("myApp", [], function ($routeProvider, $locationProvider) { $routeProvider .when("/foo", { templateUrl: "/template/foo.html" }) .when("/bar", { templateUrl: "/template/bar.html" }) .otherwise({ templateUrl: "/template/main.html" }); $locationProvider.html5Mode(true); }); ``` ```html:sample.html <!DOCTYPE html> <html ng-app="myApp"> <head> <title>AngularJS + $rootProvider の説明</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="angular.js"></script> <script type="text/javascript" src="main.js"></script> </head> <body> <marquee>こんにちは〜</marquee> <div ng-view></div> <script type="text/ng-template" id="/template/foo.html"> ふー!<br> <a ng-href="/u/6581286/sample/AngularJS-routeProvider/index.html">メイン</a><br> <a ng-href="/bar">ばー!</a> </script> <script type="text/ng-template" id="/template/bar.html"> ばー!<br> <a ng-href="/u/6581286/sample/AngularJS-routeProvider/index.html">メイン</a><br> <a ng-href="/foo">ふー!</a> </script> <script type="text/ng-template" id="/template/main.html"> おいでませクッパ城<br> <a ng-href="/foo">ふー!</a><br> <a ng-href="/bar">ばー!</a> </script> </body> </html> ``` こんな感じです。リンクをクリックすると、ページの内容が差し替えられ、URLも書き換えられます。 `$rootProvider`を使って、どのURLのパターンの時に`ng-view`に何を当てはめるかを指定してやります。この例では見た目を差し替えているだけですが、通常は`controller`の指定をおこなったりもします。 実際使う時は、URLのパターンに`/resource/:resourceId`などと指定すると、`$routeParams`をDIで受け取った時に`$routeParams.resourceId`などで値を利用することもできます。RESTっぽい設計にするとき大変便利ですね。 ### directive の設定 ざっくり例えると、独自Viewを作成するだけしておいてlibrary projectに投げ込んでおく感じです。実際に使うのはまた別の話。 ### service の設定 ざっくり例えると、自作のSystemServiceを作成して登録する感じです。AndroidでいうService(バックグラウンドでほげほげしたい!)とは全く別のものです。どっちかというとContentProviderみたいなデータをやり取りするための口、に近いです。 ### filter の作成 ざっくり例えようにも、Androidにはあんまり該当するものがないんですよね…。android.widget.FilterやAdapterが近いかもしれないけど、もっと柔軟で汎用的な仕組みです。Arrayから該当する要素だけを抜き出したり、JSONに変換したり、色々なことが出来るので、フィルタというよりコンバータの方が適切な言い方かもしれないですね。 紛らわしいことに、`filter`という`filter`が存在して、これはまんまArrayから条件に当てはまる要素のみを抽出することができます。 ## directive = 独自View `directive`は、HTML上で見慣れないタグや属性があったらもう全部`directive`って事でいいんじゃないでしょうか? `ng-app`も`ng-model`も`ng-click`もアレもコレも実は全部`directive`として実装されているんだよ!ナ、ナンダッテー!! AngularJSでは、DOMを明示的にいじくる事はほとんどありません。むしろ、テストのやりやすさのために後述の`service`や`controller`中ではjQueryなどを使ってDOMをいじくるのは積極的に非推奨です。可能な限り行わないようにしましょう。 では、DOMをいじくりたい時、jQueryのDatePickerやGoogle MapsのWidgetを使いたい時、どうやってAngularJSの世界に組み込めば良いのか?そういうのはdirectiveを使って定義することになります。 [AngularUI](http://angular-ui.github.com/)などのサンプルをみてみると大変分かりやすいです。既存のjQuery pluginとかをAngularJSに組み込む時のサンプルにもなるでしょう。 [サンプル](http://jsfiddle.net/vvakame/v6xFE/) <iframe style="width: 100%; height: 100px" src="http://jsfiddle.net/vvakame/v6xFE/embedded/result,js,html/presentation/" allowfullscreen="allowfullscreen" frameborder="0"></iframe> ```javascript:sample.js angular.module('myApp', []) .directive('vvMarquee', function ($parse) { return { restrict: 'A', require: ['?ngModel'], replace: true, template: '<marquee/>', link: function (scope, element, attrs) { scope.$watch(attrs.ngModel, function (newVal) { var getter = $parse(attrs.ngModel); var model = getter(scope); element.html(model); }); } }; }); function Controller($scope) { $scope.data = "vvakame"; } ``` ```html:sample.html <div ng-app="myApp" ng-controller="Controller"> <div vv-marquee ng-model="data"></div> <input type="text" ng-model="data"> </div> ``` このような感じで、DOMをいじくったりできます。ここでは昔なつかしmarqueeタグを使ってみました。ここでは詳しい説明を省きます。ぶっちゃけ難しくてキッチリ説明できるほどわかめも理解できていないという説が有力です。 [もうちょいかっこいい例](https://gist.github.com/vvakame/4991980) ## service = Context#getSystemService(name)的な `service`は、わかめの理解ではサーバとAngularJSの世界を繋ぐためのノリのように使うのが良いようです。SystemServiceみたいとは一体なんだったのか…。事前にServiceの定義を用意しておいて、Fragmentなどから自在に呼び出してコキ使う…というのに向いています。 こうしておくと、AngularJSはDIをベースに成り立っているため、サーバとの接続部分だけをテストしたり、`controller`がデータを受け取って処理をしたり…という部分を個別にテストしやすくなり、テスタビリティが向上します。 [サンプル](http://jsfiddle.net/vvakame/bbTzH/) <iframe style="width: 100%; height: 400px" src="http://jsfiddle.net/vvakame/bbTzH/embedded/result,js,html/presentation/" allowfullscreen="allowfullscreen" frameborder="0"></iframe> ```javascript:sample.js angular.module("twitter", [], function () { }) .factory("twitterService", function ($http) { return new TwitterService($http); }); function TwitterService($http) { this.getList = function (word) { return $http({ method: "JSONP", url: "http://search.twitter.com/search.json?q=" + word + "&callback=JSON_CALLBACK" }); }; } function Controller($scope, twitterService) { twitterService.getList("AngularJS").success(function(data) { $scope.tweets = data.results; }); }; ``` このような感じで、twitterServiceを`service`として登録し、Controllerから利用しています。$httpの使い方の説明はめんどくさいので説明を省きますが、TwitterのSearch APIでツイートを検索し、取得しています。ここで、serviceを利用することでControllerからはAjaxを利用していることなどを隠蔽し、普通のメソッド呼び出しの体になっています。 TypeScriptを利用していることもあって、わかめはこの形式でserviceを利用しています。 ## filter = android.widget.Filter or Adapter `filter`は、主にデータの加工に利用したりします。Androidではありませんが、例えば `cat hoge.tx | grep android | wc -l` というコマンドを考えると、`cat hoge.txt`部分が加工元のデータ、`grep android`が1つ目のfilter、`wc -l`が2つ目のfilter、といった具合です。 [サンプル](http://jsfiddle.net/vvakame/UAhsW/) <iframe style="width: 100%; height: 150px" src="http://jsfiddle.net/vvakame/UAhsW/embedded/result,js,html/presentation/" allowfullscreen="allowfullscreen" frameborder="0"></iframe> ```javascript:sample.js angular.module("myApp", []) .filter("upper", function() { return function(input, options) { return input.map(function(data) { return { id: data.id, name: data.name.toUpperCase() }; }); }; }); function Controller($scope) { $scope.list = [ { id:1, name:"vvakame" }, { id:2, name:"grapswiz" }, { id:3, name:"u1aryz" }, { id:4, name:"eaglesakura" } ]; }; ``` ```html:sample.html <div ng-app="myApp" ng-controller="Controller"> <input type="text" ng-model="search" placeholder="絞込み"> <ul> <li ng-repeat="data in list | filter:search | upper"> No.{{data.id}} {{data.name}} </li> </ul> </div> ``` ここでは、AngularJSがデフォルトで持っている`filter filter`で絞込みを行った後、作った`upper filter`で名前を大文字に変換しています。 ## controller = Activity or Fragment or Adapter `controller`は、ActivityやFragmentにあたるもので、Viewに対して表示するデータをやりくりして`ListFragment#setAdapter`にAdapterをセットしてやる、的な感じです。`controller`ではAdapterなどの代わりに`$scope`を利用します。$scopeに対して行った操作は、Viewとの双方向のデータのやり取りに利用します。 また、ネストさせる事もできるので、このように他のControllerの一部を再利用することもできます。ただ、あまり分かりやすいコードでもないですし、使うチャンスは少ないように思います。 [サンプル](http://jsfiddle.net/vvakame/SzkwL/) <iframe style="width: 100%; height: 300px" src="http://jsfiddle.net/vvakame/SzkwL/embedded/result,js,html/presentation/" allowfullscreen="allowfullscreen" frameborder="0"></iframe> ```javascript:sample.js function SelectController($scope, $window) { $scope.selectedStateList = []; $scope.selectedDataList = []; $scope.changeSelection = function(index, data) { if ($scope.selectedStateList[index]) { $scope.selectedDataList[index] = data; } else { $scope.selectedDataList[index] = null; } }; $scope.show = function() { var message = "selected " + $scope.selectedDataList.join(","); $window.alert(message); $scope.message = message }; } function UserController($scope) { $scope.userList = [ "vvakame", "sys1yagi", "kojira", "neco" ]; } ``` ```html:sample.html <div ng-app> <div ng-controller="UserController"> ユーザ一覧 <div ng-repeat="user in userList"> {{user}} </div> </div> <div ng-controller="SelectController"> ユーザ選択 <div ng-controller="UserController"> <div ng-repeat="user in userList"> <input type="checkbox" ng-model="selectedStateList[$index]" ng-change="changeSelection($index, user)"> {{user}} </div> {{message}}<br> <button ng-click="show()">選択してるユーザを表示</button> </div> </div> </div> ``` ## HTML = layout xml HTMLはまんまHTMLの本来の役割通りの画面です。`directive`と同じく、だいたいViewだと思ってもよいでしょう。HTMLは`controller`からデータを与えてもらい、画面に反映する役割を持ちます。どういう構造で表示するかを書く、という面でlayout xmlと似ています。ですが、どの`controller`が制御するかなどを指定する役割もあるので、細かく違うところももちろんあります。 HTMLについてもここまでに色々とサンプルで出てきたのでもはや割愛します。 ## $window や $location とか unittestを行う際に実際のwindowやlocationなどを使ってしまうと、node.js環境で存在しなかったり、window.alertが呼ばれた時にポップアップが出てウザかったりするのでDIされた物を使うようにします。そのためにこれらが準備されています。 # AngularJSのもう少し詳しい情報 他に知ってたほうがいいことを概説してみます。 * [Form](http://docs.angularjs.org/guide/forms) 入力値のvalidationやエラーメッセージの表示なども手軽に行うことができます。 * [Expression](http://docs.angularjs.org/guide/expression) AngularJSでdirectiveなどに指定するExpressionについての解説です。JavaScriptっぽい構文ですが、完全に別物なのでうんうん唸って苦しんだりすることになります。なかなか覚えられなくて辛いです。 * [UnitTest](http://docs.angularjs.org/guide/dev_guide.unit-testing) 上でくどくどと言っていますが、AngularJSはDIで成り立っているので、テストもなかなか書きやすいです。ルールが飲み込めてくるまでは結構苦労するんですが…。Jasmineとかで適当にテストを書くことができます。ていうかこのドキュメントの最下部なんなの?マジぱないっす。 * [testacular](http://testacular.github.com/) そのうえで、よく使われているのがtestacularです。AngularJS自体もtestacularを利用してテストされています。PhantomJSやChrome、Firefox、Safari、IEなど複数のブラウザ上でテストを一括で走らせることができます。 * [E2E Test](http://docs.angularjs.org/guide/dev_guide.e2e-testing) End to End testを書くこともできます。結構こういうのって珍しい気がします。この辺りはまだちゃんと研究していないので[shogoggのブログ](http://shogogg.hatenablog.jp/entry/2013/01/25/005753)とかを参照してみるといいかもしれない。 # AngularJSとあまり関係のない情報 * [TypeScript](http://www.typescriptlang.org/) 型があるぞー!!JavaScriptとの互換性もかなりあるぞー!!やったーー!!! * [tsd](http://www.tsdpm.com/) TypeScript用の.d.tsファイルのダウンローダ的な感じ。 * [grunt](http://gruntjs.com/) 色々なタスクを行わせることができます。shell script書くより、色々な人が作ったタスクを組み合わせたほうが楽です。JavaScriptで設定ファイル書けますし。どっかのm○nとかいうXML編集させられるクソツールよりいいと思います。 * [bower](http://twitter.github.com/bower/) npmのクライアント版です。 # 参考情報 という名の愚痴 ## 本家ドキュメント [ここ](http://angularjs.org/) ぶっちゃけ、ドキュメントとしては量が少ないです。かなり大きなフレームワークですがそれに対して量が十分ではありません。なので、AngularJSを使い始めて、その世界観に慣れるまでに結構時間がかかります。わかめもなんやかやで2週間くらいかかった気がします。 また、本家ドキュメントとは直接関係はないのですが、Googleとかで検索した結果の資料として、1.0以前の内容はアテにならない場合が多いため、昔の情報はあまり信用しないほうがいいかもしれません。 ## テストの書き方 わかるまでが大変なのが、serviceやcontrollerのテストです。 そろそろサンプル作成するのが大変になってきたので、今書いているプロジェクトから抜粋します。言語はTypeScriptです。ご容赦ください…!! 普段はTypeScript+Jasmine+Testacularでテストを作って流して、ってしています。 ```typescript:test.ts describe("Serviceの", ()=> { var $injector:ng.auto.IInjectorService; beforeEach(()=> { $injector = angular.injector(['ngMock']); }); describe("Sample.Serviceの", ()=> { var $httpBackend:ng.IHttpBackendService; var service:Sample.Service; beforeEach(()=> { $httpBackend = $injector.get("$httpBackend"); service = $injector.instantiate(Sample.Service, { $routeParams: { hoge: "fuga" } }); }); it("getメソッドのテスト", ()=> { $httpBackend.expect("POST", null).respond(200, {"hoge": "fuga"}); var promise = service.get(); var model; promise.success((data)=> model = data); $httpBackend.flush(); expect(model).toBeDefined(); }); }); }); describe("Controllerの", ()=> { var $injector:ng.auto.IInjectorService; beforeEach(()=> { $injector = angular.injector(['ngMock']); }); describe("Sample.Controllerの", ()=> { var $scope:Sample.Scope; var $controller:ng.IControllerService; var $httpBackend:ng.IHttpBackendService; var locals:any; var makeController = (specificLocals:any = locals):Sample.Controller => { return $controller(Sample.Controller, specificLocals); }; beforeEach(()=> { $httpBackend = $injector.get("$httpBackend"); $controller = $injector.get("$controller"); $scope = <any> $injector.get("$rootScope").$new(); var sampleService = $injector.instantiate(Sample.Service, {}); locals = { $scope: $scope, sampleService: sampleService }; }); it("Controllerの作成", ()=> { var controller = makeController(); expect(controller).not.toBeNull(); }); it("actionメソッドのテスト", ()=> { var controller = makeController(); $httpBackend.expect("POST", null).respond(200, {"hoge": "fuga"}); $scope.action(); $httpBackend.flush(); expect($scope.result).toBeTruthy(); }); }); }); ``` ## プロジェクト構成 [普段使ってる設定](https://gist.github.com/vvakame/5053711) ## 日本語の情報 [AngularJSドキュメント日本語訳プロジェクト](https://github.com/angularjs-jp/angular.js/wiki) [AngularJS メーリングリスト]() 後半の失速っぷりハンパナイネ! |
|
| 316位 |
|
|||
|
16:34:00 |
|
|
基本的な設定方法や使用方法は書いていません。[CapybaraのREADME](https://github.com/jnicklas/capybara/blob/master/README.md)を読みましょう。
## Driverはどれ使えばいいのか (参照: [READMEs Drivers section](https://github.com/jnicklas/capybara/blob/5d99d97775015cd5add209837307782f84a7a2a2/README.md#drivers)) 種類としては以下の3種類のDriverがあります | Driver名 | JSが実行出来るか | 外部HTTP通信が出来るか | headlessかどうか | |:------------------------------------:|:----------------:|:----------------------:|:----------------:| | RackTest | N | N | Y | | Selenium | Y | Y | N | | Capybara-webkit, Poltergeist | Y | Y | Y | ### defaultは? defaultではRackTestが使用されていて、高速だしRubyで書かれているのでRuby以外に依存してるソフトウェアが無くて良いのですが、JSが実行出来ませんし外部APIとかも叩けません。 個人的な意見としてはJS実行、外部APIを叩くことが必要でなければRackTestのままでいいと思います。 ### JS実行や外部APIを叩きたければ? こうなるとheadlessではないSelenuimか、headless driverであるCapybara-webkitやPoltergeistになってきます。 まず、headlessではないdriverを選んでしまうとテスト実行毎にブラウザが立ち上がってしまいます。これは鬱陶しいのでメインで使うには不適当です。 ということでheadless driverであるCapybara-webkitやPoltergeistになってきます。 最近までは[Capybara-webkit](https://github.com/thoughtbot/capybara-webkit)を使うのが多かった気がするのですが、最近は[Poltergeist](https://github.com/jonleighton/poltergeist)を採用するケースが多い気がします。 理由としては、GitHubの社員が自社内で使用してると思われるRailsアプリの[PR](https://github.com/github/swordfish/pull/32)の中で言ってる通り、 - QT, xvfbに依存していない - 安定している - 開発がより盛ん - より速い 特にQTに依存していないというのは個人的に大きいと思っていて、これのお陰でCI上での実行のしやすがあがりますし、 QTをコンパイルする時間がなくなるのでビルド全体の時間も短縮されます。 下で説明しますがheadlessなのにinspectorも起動出来たり、機能も豊富なのも良いですね。 ## デバッグのやりかた ここからは基本的にPoltergeist driverを使用してること前提に話します。 僕が今までcapybaraのテストを書いていて困った事は主に2つあって、 1. 動作の書き方がわからない(e.x. どういうコードを書いたら特定のボタンが押せるのか 2. テスト上で実行した時だけ機能の挙動が違う でした、1.は基本的にググって実際に書いてみてやり方を見つけるしかないです。 2.は厄介でして、これは基本的にテスト環境だと違う値のもの(定数だったり設定の値)が影響を及ぼして結果的に違う挙動を引き起こしてることが多いです。 どの値のものが影響を与えてるかは、挙動元のコードを辿ってみていくしか無い気がします。 たどっていう過程で以下の欲求が出てくると思います、それぞれ方法を説明します。 - ある状態での画面の状態を知りたい - JS側のデバッグをしたい ### ある状態での画面の状態が知りたい headless driverを使用しているのでボタンを押した後状態とかテストを実行してるだけでは中々分かりにくいです 例えば以下の様なテストがあったとして ```ruby describe 'Sign up feature' do it 'works' do visit root_path click_on 'Sign up' click_on 'hoge' end end ``` よく起こるのが - "Sign up"押したら、 - root_pathから新規登録ページに飛んで、 - 新規登録ページにある"hoge"をクリックしたいのに、 - "hoge"が見つかりませんとエラーが出る みたいなケースです。headless driverを使ってるのでSign upをクリックしたあと起こってることが分かりにくいです。 そんな時は以下のようにしてスクリーンショットを撮りましょう ```ruby … visit root_path click_on 'Sign up' page.save_screenshot('/path/to/file.png') click_on 'hoge' end end ``` `page.save_screenshot`の説明は[ここ](https://github.com/jonleighton/poltergeist#taking-screenshots-with-some-extensions)。 ### JS側のデバッグをしたい サーバー側のデバッグは普通に[pry](https://github.com/pry/pry)を使用した`binding.pry`等でデバッグが可能なのですが、JSはそうにもいきません。 ただJS側でデバッグしたいことは起こるのでそういう時はPoltergeistが提供している[Remote debugging機能](https://github.com/jonleighton/poltergeist#remote-debugging-experimental)を使用します。 この機能を使うには、先ほどのリンクにも記載されてる通り、driverを設定する際、 ```ruby Capybara.register_driver :poltergeist do |app| Capybara::Poltergeist::Driver.new(app) end ``` を ```ruby Capybara.register_driver :poltergeist do |app| Capybara::Poltergeist::Driver.new(app, inspector: true) end ``` に変えるだけで出来ます。 これによって、例えば先ほどのテストで、以下のように書けば ```ruby describe 'Sign up feature' do it 'works' do visit root_path page.driver.debug click_on 'Sign up' click_on 'hoge' end end ``` `page.driver.debug`の時点で以下のようにwebkit inspectorが起動し、"Elements"でhtml DOMを確認したり、"Console"でJSを実行したり出来ます。  普段ブラウザ上で行ってるデバッグが可能になります、headless browserなのに :) |
|
| 317位 |
|
|||
|
18:14:40 |
(Increments 所属) |
|
あるファイルに大量のコンフリクトが発生し解決が面倒なとき、パッチを使ってファイルに1コミットずつ変更を適用する方法を示す。この方法のメリットは:
- ファイルへの変更を1コミットずつ適用・コンフリクト解決することができる - それぞれのコミットを適用する前に、コミットをパッチファイルの形で編集できる - 注目するファイル以外への変更をいったん無視し、そのファイルに関係する変更に集中できる の3点である。複数コミットの変更が混ざった大量のコンフリクトマーカーを手作業で消すような状況に陥ったとき、この方法を使えばいくぶんかは楽にマージ作業を進められる。 # 概要 マージ中に特定のファイルに大量のコンフリクトが起きたら、マージを中止する。一時作業用ブランチを作り、そのファイルに1コミットずつパッチを当てて編集する。パッチを当て終わったらマージをやり直し、コンフリクト解決作業中に、コンフリクトしたファイルを一時作業用ブランチからチェックアウトしたファイルで置き換える。 # やりかた 次のようにマージを実行したとき、 Foo.h, Foo.m に多くのコンフリクトが発生したとする。 ``` $ git checkout master $ git merge dev # Foo.h, Foo.m に大量のコンフリクトが発生 # 他のファイルには比較的簡単なコンフリクトが発生 ``` まず merge を中止する。 ``` $ git merge --abort ``` 現在のブランチから新しい作業用ブランチを切り、そのブランチに移る。 ``` $ git checkout -b work ``` 取り込もうとしているブランチから Foo.h, Foo.m に関係するコミットだけをパッチファイルに書き出す。 (補足:以下のコマンドで、 `..dev` は「`dev` から到達可能だが `HEAD` から到達可能でないコミット」、言い換えれば「両者が分岐した以降、 `dev` にのみ含まれるコミット」を表す。 `--histogram` は diff アルゴリズムを histogram diff に切り替える。場合によるが、デフォルトのアルゴリズムより読みやすいパッチを生成する) ``` $ git format-patch --histogram ..dev -- Foo.h Foo.m 0001-Edit-Foo.patch 0002-Another-change-to-Foo.patch ``` 必要なら適用する前にパッチを手作業で編集する。ここで不要な変更を取り除くなどしておく。 ``` $ vim *.patch ``` `git apply -3` でパッチを順に適用する。1つ前のステップでパッチの行数が変わるような編集を行ったなら、`git apply` に `--recount` オプションを与える。作業用ブランチは後で削除するのでコミットメッセージは適当でよい。(補足: `git apply` に `-3` オプションを与えると 3-way merge を実行する。 `--recount` オプションを与えると、パッチに含まれる行数のヒントを無視する) ``` $ git apply -3 0001-Edit-Foo.patch # コンフリクトを解決 $ git commit -m "Tmp 1" $ git apply -3 --recount 0002-Another-change-to-Foo.patch # コンフリクトを解決 $ git commit -m "Tmp 2" ```` すべてのパッチを適用した時点で、作業用ブランチ内の Foo.h, Foo.m は必要な変更をすべて取り込んだ状態になる。 パッチファイルを削除してから、先ほど中止したマージをやり直す。 ``` $ rm *.patch $ git checkout master $ git merge dev # ここではまだ Foo.h, Foo.m にコンフリクトが発生する ``` 作業用ブランチからコンフリクト解決済みの Foo.h, Foo.m をチェックアウトする。チェックアウトしたら作業用ブランチは削除してよい。 ``` $ git checkout work -- Foo.h Foo.m $ git add Foo.h Foo.m $ git branch -D work ``` 適宜その他のコンフリクトを解決し、マージを完了する。 ``` # コンフリクトを解決 $ git add -u $ git commit ``` # Tips 上では「現在のブランチから作業用ブランチを切り、その上に取り込もうとしているブランチの変更を適用」するとしたが、変更の量によっては「取り込もうとしているブランチから作業用ブランチを切り、その上に現在のブランチの変更を適用」してもよい。注目するファイルに対して変更量が多い・変更の内容が複雑なブランチをベースに作業用ブランチを作ると、取り込むべきパッチの量が減り、作業が楽になる。 |
|
| 318位 |
|
|||
|
15:39:34 |
(Increments inc. 所属) |
|

続編 [JavaScript - Qiitaのtextarea自動補完がOSSになりました](http://qiita.com/yuku_t/items/7ddcc842d3d8f6a9fcd5) GitHubのコメントでは@と入力するとカーソルの下に入力補完が出現する。さらっとやっているが、実はこれが結構難しい。なぜ難しいのかというと、JavaScriptではカーソルが何文字目にいるかは分かるが、 **カーソルのXY座標を取得するAPIが存在しない** からだ。カーソル位置が分からなければ、適切な位置に補完候補を表示することができない。では一体どうすればいいのか? [今回Qiitaではコメント欄でのメンションの補完機能を実装した。](http://blog.qiita.com/post/48026202087/mention-autocomplete)本稿では前述の問題を解決するために用いたテクニックを解説する。 ちなみにこのメンション補完機能はチーム用プライベートQiitaである「[Qiita:Team](https://teams.qiita.com/)」でも勿論使える。現在絶賛無料トライアル実施中なので、興味を持たれた方はそちらも使ってみて欲しい。 # 要約 textarea内でのカーソル位置を計算するには... - ダミーのdiv要素を画面外に作り、textarea要素のスタイルを模倣する - 先頭からカーソルまでの文字列をコピーし、div要素に挿入する - 末尾にspan要素を追加し、そのspan要素のdiv要素内での相対位置を計算する ただしこれはカーソル位置を計算する唯一の方法ではなく、他にも方法は考えられる。 ## この記事を読むと分かること - textarea内でのカーソルの相対位置を計算する方法 ## この記事を読んでも分からないこと - 補完メニューを良い感じの場所に設置する方法 - 補完メニューの実装方法 # カーソルのXY座標を計算する カーソルの下に補完メニューを表示するには、カーソルのXY座標が分からなければならない。だが、冒頭でも述べたようにJavaScriptにはカーソルのXY座標を取得するAPIが存在しない。そのため、何かしらの方法を用いて自ら計算方法を実装しなければならない。 結論から言えば、カーソル位置はtextareaのスタイルと、カーソルの前にどのような文字列が存在するかによって決定される。つまり、これらを完全に模倣してやればいいわけだ。 ## カーソルが何文字目にいるか取得する カーソルが何文字目にいるかを取得するAPIは、モダンなブラウザであればtextareaの`selectionStart`もしくは`selectionEnd`で取得できる。これらは基本的に同じ値が格納されており、ドラッグして範囲を選択した場合のみ値が変わる。名前からも分かるように`selectionStart`から`selectionEnd`までがドラッグされているわけだ。 ```javascript:textarea内でカーソルが何文字目にいるか var textarea = document.getElementsByTagName('textarea')[0], start = textarea.selectionStart; ``` `selectionEnd`が実装されていないようなブラウザもサポートするには、もっと込み入った処理が必要になる。この点については[StackOverflowのこの解答](http://stackoverflow.com/a/263796)が詳しい。 ## 先頭からカーソル位置までの文字列を取得する カーソルが何文字目にいるかを取得する`getCaret`関数が実装できたとする。先頭からカーソル位置までの文字列を取得するコードは次のようになる。 ```javascript:textareaの先頭からカーソル位置までの文字列を取得する // 先頭からカーソル位置までを取得する var text = textarea.value.substring(0, getCaret(textarea)); ``` これでカーソル位置の計算に必要なtextarea要素内の文字列を取り出すことができるようになった。 ## textareaを模倣したdiv要素を用意する 次に先ほど取得した文字列を入れるための容れ物になるdiv要素を準備する。このとき、この要素がtextareaと同様のスタイルになっていなければ正しくカーソル位置を計算できないことは言うまでもない。 textarea要素からコピーするスタイルは次のものだ(抜けがある可能性もある)。逆に言えば、これらの値が一致していれば中身の文字列の大きさや間隔などが一致することを意味する。 - border-bottom-width - border-left-width - border-right-width - border-top-width - font-family - font-size - font-style - font-variant - font-weight - height - letter-spacing - word-spacing - line-height - padding-bottom - padding-left - padding-right - padding-top - text-decoration - width 加えてdiv要素を画面外に描画しなければならない。これらを実装すると次のようになる。 ```javascript:textareaのスタイルを模倣したdiv要素を作る var i, div = document.createElement('div'), list = ['border-bottom-width', 'border-left-width', 'border-right-width', 'border-top-width', 'font-family', 'font-size', 'font-style', 'font-variant', 'font-weight', 'height', 'letter-spacing', 'word-spacing', 'line-height', 'padding-bottom', 'padding-left', 'padding-right', 'padding-top', 'text-decoration', 'width']; // 画面外に配置する div.style.position = 'absolute'; div.style.top = 0; div.style.left = -9999; // textareaのスタイルをコピーする for (i = 0; i < list.length; i++) { div.style[list[i]] = textarea.style[list[i]]; } // divを画面に挿入する document.body.appendChild(div); ``` ## カーソルまでの文字列とspanを末尾に入れてspanの位置を計算する textareaと同じスタイルのdivが用意できた。あとはカーソルまでの文字列をその中に入れ、それに続くかたちでspanを入れて、その位置を取得するだけだ。 ```javascript:カーソルの位置を計算する var span = document.createElement('span'); // spanに大きさをもたせるために適当な文字列を挿入 span.innerHTML = ' '; // 文字列を挿入 div.textContent = text; // スクロール位置を調整 div.scrollTop = div.scrollHeight; // spanを挿入 div.appendChild(span); // spanの位置を取得。簡単のためにjQueryを使っている var position = $(span).position(); // divの左上から見たときの相対位置 position.top; position.left; ``` ここで得られた`position`はspanの親要素であるdivからの相対的な位置になっている。そしてこの値はtextareaの左上から見たカーソルの相対位置と等しい。 # textareaの補完メニューを実装するには これでカーソルの位置は取得できるようになった。だが道はまだ半ばだ。実際に動作する補完メニューを実装するにはこのあとさらに次のようなステップを踏むことになる。 1. textareaの内容から検索文字列を検出する 2. それに対して検索を実行する 3. 補完メニューをカーソルの下に表示する 4. 検索結果を補完メニューに表示する 5. 補完メニューをクリック可能にする 6. 選択された値を用いてtextareaを更新する Qiitaの場合はこれらに加えてさらに - コードの中だったら補完メニューを表示しない - キーボードでメニューを選択可能 - 再利用性を高めるためにBackboneのViewのためのMixinとして実装 などを行なっている。 これら残りの工程についてここでは時間の関係上解説しないが、本稿の反響が良ければ書くかも知れない。「残りについても解説記事希望!」という場合は是非ともその旨を、このページの下にあるコメント欄で私にメンションを飛ばして知らせて欲しい。 |
|
| 319位 |
|
|||
|
11:29:36 |
(SAKURA Internet Inc. 所属) |
|
# 2014-09-08 追記
[White Tiger - 英語版Windows 7 Ultimateを日本語化する方法]( http://www7b.biglobe.ne.jp/~whitetiger/win/win7_001.html )にスクリーンショット入りの親切な記事があったので、お好みでそちらをご参照ください。 # 概要 [Modern.IE](http://www.modern.ie/)のサイトで配布されているIEテスト用のWindows VMは英語環境になっています。これに日本語ランゲージパックをインストールして、日本語の表示と入力をできるようにするための手順のメモです。ついでにタイムゾーンも日本に変更します。 試したVMはIE8 Win7です。環境はOS X 10.8.5上のVirtualBox 4.3.6です。 # タイムゾーンを日本に変更。 1. スタートメニューの[Control Panel]をクリックします。 1. [Clock, Language, and Region]セクションのリンクをクリックします。 1. [Date and Time]セクションの[Change the time zone]リンクをクリックします。 1. [Date and Time]ダイアログが開いたら[Change time zone...]ボタンを押します。 1. [Time Zone Settings]ダイアログが開いたら[Timezone]ドロップダウンで[(UTC+09:00) Osaka, Sapporo, Tokyo]を選択し[OK]ボタンを押してダイアログを閉じます。 1. [Date and Time]ダイアログで[OK]ボタンを押してダイアログを閉じます。 # 日本語ランゲージパックのインストール 1. スタートメニューの[Control Panel]をクリックします。 1. [System and Security]をクリックします。 1. [Windows Update]セクションの[Check for updates]をクリックします。 1. [Windows Update]のページに切り替わったら[Check for updates]ボタンを押し、更新チェックが終わるのを待ちます。 1. まだ[Install updates]ボタンは押さないでください!今押すとIE10がインストールされてしまいます。[19 important updates are available]のリンクをクリックします。 1. [Internet Explorer 10 for Windows 7]のチェックを外します。 1. 画面左の[Optional]タブをクリックしてタブを切り替えて、[Japanese Language Pack - Windows 7 Service Pack (KB2483139)]にチェックをつけて[OK]ボタンを押します。 1. Windows Updateの画面に戻ったら[Install Updates]ボタンを押します。 1. インストールが終わったら[Restart now]ボタンを押します。 # 日本語環境に設定変更 1. 再起動したら、[Start]→[Control Panel]とクリックしてコントロールパネルを開きます。 1. [Clock, Language, and Region]セクションの[Change display language]をクリックします。[Region and Language]ダイアログが開きます。 1. ダイアログ内の[Choose a display language]のドロップダウンで[日本語]を選択します。 1. [Change keyboards…]ボタンを押し、新たに開いたダイアログで[Default input language]で[Japanese (Japan) - Microsoft IME]を選択して[OK]ボタンを押してダイアログを閉じます。 1. [Region and Language]ダイアログの[Location]タブに切り替えて[Current location]ドロップダウンで[Japan]を選択します。 1. [Formats]タブに切り替え[Format]ドロップダウンで[Japanese (Japan)]を選択します。 1. [Region and Language]ダイアログで[OK]ボタンを押して設定変更を反映します。 1. [You must log off for display language changes to take effect]というダイアログが表示されたら、[Log off now]ボタンを押します。 1. ログイン画面に戻ったらパスワードにPassw0rd!と入力してログインします。デスクトップのごみ箱アイコンのラベルが「ごみ箱」と日本語になっていればOKです。 英語キーボードのMacBookの場合はalt+`を押すたびに半角全角がトグルで切り替わります。IEの検索画面で日本語を入力してみたスクリーンショットを添付します。  |
|
| 320位 |
|
|||
|
17:11:54 |
|
|
Railsの環境のよって変わる設定値をどうするか問題については、rails_configとかsettingslogicなどのgemがよく紹介されているけど、今回はdotenvというgemを紹介したいと思います。
http://rubygems.org/gems/dotenv これは、仕組みとしてはとても簡単なもので、カレントディレクトリに .env という名前で環境変数を書き込んでおくと、自動的に ENV の中にその値を追加してくれるというものです。 もともと foreman の機能でしたが、切り出されて別のgemになりました。 なので foreman や heroku を使っている人は知っているかと思います。 ```bash # .env AWS_ACCESS_KEY_ID="hogehoge" AWS_SECRET_ACCESS_KEY="mogamoga" ``` ```rb puts ENV["AWS_ACCESS_KEY_ID"] # => nil Dotenv.load puts ENV["AWS_ACCESS_KEY_ID"] # => "hogehoge" ``` Railsの場合は、dotenv-railsを使います。自動でDotenv.loadしてくれます。 ```rb gem 'dotenv-rails' ``` この.envファイルはVCSにコミットせず、それぞれの開発環境で設定します。 なぜこれが便利なのかというと、設定値へは常に ENV を使ってアクセスすることができるからです。 それが元々環境変数として設定されているのか、.env で設定されているのかは意識する必要がありません。 本番サーバでは環境変数として設定されていて、開発環境ではほかのプロジェクトと競合するなどの理由でグローバルに設定できない設定値も、プロジェクトごとにローカルに設定することができます。 さらに、/bin/dotenv というコマンドを使うと、ruby でないコマンドでも.envの設定を引き継いで実行することができます。 #### ところで [Repro株式会社](https://repro.io/) では一緒に切磋琢磨できる仲間を募集しています! ぜひ[こちら](https://www.wantedly.com/companies/repro/projects)をごらんください! |
|
| 321位 |
|
|||
|
00:22:56 |
|
|
これまでAngularJSでアプリを作ってきた中で、いくつかパフォーマンスの問題に遭遇しました。
それらの問題は、AngularJSの仕組みを十分に理解できていないために、よくないコードを書いてしまって発生しているものでした。 というわけで、AngularJSの内部構造を解説しつつ、パフォーマンスを改善するコードの書き方を紹介したいと思います。 # 計測できないものは改善できない パフォーマンス問題に取り組むには、ソースコード修正の前後でパフォーマンスを計測し、改善の効果を計測することが重要になります。 というわけでまずはツールの紹介です。 AngularJSでは、Batarangという便利なツール(Chrome Developer Toolsの拡張機能)が用意されています。 利用方法はとても簡単で、下記のChromeウェブストアからインストールして、Chrome Developer Toolsを起動し、AngularJSタブのEnableにチェックを入れるだけです。 * https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk  このツールで、`$scope`のツリー構造を見ることができたり、パフォーマンスの計測ができたり、モジュールの依存関係グラフが表示できたりします。 パフォーマンスのタブでは、処理時間のかかっている箇所が一目で分かるようになっています。  このツールはすごく簡単に使えますが結果がざっくりとしているので、より詳しく解析したければChrome Developer Toolsのプロファイリング機能などを使いましょう。 # 仕組みを知る パフォーマンス改善を行うためには、フレームワークの仕組みを知り、何がボトルネックになるのかを理解しておくことが重要です。 AngularJSでは双方向のデータバインディングの仕組みがとても便利ですが、パフォーマンスの影響が出やすい機能でもあるので、その仕組みを見てみましょう。 データバインディングの仕組みの実現方法はいくつかあります。 例えばKnockout.jsでは、データの値が変化した時にイベントリスナでViewに通知する方式をとっています。 一方、AngularJSでは、dirty-checkingと呼ばれる方式で実装されています。 これは、バインドしているすべての変数について、特定のタイミングで前回の値と今回の値を比較し、値に変化があればDOMを書き換えるという仕組みです。 ざっくりと以下のような流れで動いています。 1. HTMLを解析してディレクティブをコンパイルする際に、バインドされている変数を`$scope.$watch()`で登録します。 1. `$rootScope`から子のscopeを順にたどり、watchで登録されたすべての変数の変更チェックを行います。この処理のことを`$digest`ループと呼び、下記のようなタイミングで実行されます。(定期的なポーリングはしていません) * `$scope.$apply()`を呼んだ時 * DOMイベント(テキストボックスのonChange、ボタンのonClickなど)が発生した後 * `$http`や`$resource`でレスポンスが返ってきた後 * `$timeout`のイベントが発生した後 * `$location`でURLを変更した後 1. 変更チェックが完了したら、変更があった部分のDOM書き換えを行います。 dirty-checking方式は、このように多くのオブジェクトの比較処理を行う仕組みなので、イベントリスナ方式に比べるとパフォーマンスの問題が起こりやすそうですね。 でもこの実装方法を採用することで、PureなJavaScriptオブジェクトをModelとして利用できたり、監視対象のオブジェクト間の依存関係を難しく考える必要がないというメリットもあります。 ちなみに、将来的にはdirty-checking方式から`Object.observe`に置き変えることも考えられているようです。([参考](http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html )←ちょっと古い情報ですが) # 改善する ## `$digest`ループ内の処理はできるだけ少なく軽量に `$digest`ループでは、watchしている変数の数とその変数の比較処理時間がパフォーマンスに大きく影響します。 なので、watchする変数の数はできるだけ少なく、オブジェクトの比較処理はできるだけ軽くするのが基本です。 watchする変数の数の目安は2000個以下、比較処理時間の目安は25μsecだと言われています。([参考](http://stackoverflow.com/questions/9682092/databinding-in-angularjs)) 25μsec×2000回であれば50msec以内に処理を完了できるので、ユーザーの体感としては十分に速いと感じられるからだそうです。 Batarangを使うと`$scope`のツリー構造が視覚化されるので、watchしている変数の数の目安になります。  オブジェクトの比較処理については通常はそれほど意識しなくても大丈夫です。 `$scope.$watch()`の第1引数には監視対象のプロパティ名を文字列で渡すことが多いですが、監視対象の値を返すfunctionを指定することもできます。この場合、functionの中では重たい処理を書かないようにしましょう。 また、`$scope.$apply()`は`$rootScope`からすべての子要素に対して変更チェックを行いますが、`$scope.$digest()`は現在のスコープからの子要素に対して変更チェックを行います。 手動で`$scope.$apply()`を呼び出すときは、`$scope.$digest()`で置き換えられないかどうか検討してみましょう。 ## フィルタでは重たい処理をしない 以前、markdownフォーマットの文字列をレンダリング用のHTMLに変換するようなフィルタを作成し、`ngRepeat`の中でそのフィルタを使うコードを書きました。 ~~~html <div ng-controller="MyController"> <input type="text" ng-model="content"> <button ng-click="addItem()">add</button> <div ng-repeat="item in items"> <div ng-html-bind="item.content | markdown"></div> </div> </div> ~~~ ~~~js angular.module('MyApp') .controller('MyController', function ($scope) { $scope.items = []; $scope.addItem = function() { $scope.items.push({content: $scope.content}); }; }); /* markdown変換用のフィルタ */ angular.module('MyApp') .filter('markdown', function () { return function (input) { return marked(input); }; }); ~~~ しかしこの実装では、テキストボックスに1文字入力するたびに、`items`リストの要素数分だけmarkdownフィルタが実行されてしまい、非常に重たくて使い物になりませんでした。 実はフィルタはレンダリングの時にだけ使うのではなく、`$digest`ループで比較処理を行う際にも呼び出されています。(値が変化した時には比較用とレンダリング用の2回フィルタが呼ばれます。) よってフィルタの処理はできるだけ軽くしなければなりません。 markdownをHTMLに変換するような重たい処理はフィルタで実装するのではなく、変換結果を`$scope`に持たせておくのがよいのでしょうね。 ## ngShow/ngHideとngIfの使い分け AngularJSでは、HTML要素の表示・非表示を切り替えるためのディレクティブとして、`ngShow` / `ngHide`と`ngIf`が用意されています。 これらはほぼ同じ機能なんですが、実装方法が異なります。 `ngShow` / `ngHide`はCSSで表示を切り替えていて、`ngIf`はDOMの追加と削除を行うようになっています。([参考](http://angularjsninja.com/blog/2013/08/27/ngshow-nghide-ngswitch/)) 例えば、以下のようにタイトルだけが一覧で表示されていて、タイトルをクリックすると詳細が開くようなUIを考えてみましょう。 ~~~html <div ng-repeat="item in items" ng-controller="MyController"> <a href="" ng-click="item.isOpen = !item.isOpen"> {{item.title}} </a> <div ng-show="item.isOpen"> <div> /* 生成するのが重たい要素 */ </div> </div> </div> ~~~ `ngShow`を利用すると、リストの要素が多いときに最初の読み込みが遅くなりますが、表示・非表示の切り替えは軽快になります。 逆に`ngShow`の代わりに`ngIf`を利用すると、最初の読み込みは軽快になりますが、表示・非表示の切り替えは少し遅くなります。 このような特性を理解して、状況に応じて使い分けるのがよいですね。 # まとめ AngularJSの内部構造を解説し、アプリを開発する際にパフォーマンス周りで気をつける点を紹介してみました。 僕もそれほど複雑なアプリを開発したわけじゃありませんが、上記の項目を気をつけていればパフォーマンスの問題で悩むこともほとんどありませんでした。自作のアプリをスマートフォンで動かしてみても、思っていた以上にサクサク動きましたし。 より詳しくパフォーマンス対策やAngularJSの内部構造を知りたい場合は、下記の書籍が参考になります。(今回の記事の内容もこの書籍を参考にしている箇所が多いです) * [Mastering Web Application Development with AngularJS](http://www.amazon.co.jp/Mastering-Web-Application-Development-AngularJS-ebook/dp/B00EQ67J30) さらにチューニングしたい場合の一例として、下記のコードは面白いです。 `$digest`ループの一部を上書きしてしまっているので、若干黒魔術ではありますが。 * [AngularJSのチューニング](http://wazanova.jp/post/66084016704/angularjs) * https://github.com/scalyr/angular/tree/master/src/js/directives # 参考 * [Databinding in angularjs](http://stackoverflow.com/questions/9682092/databinding-in-angularjs) * [Mastering Web Application Development with AngularJS](http://www.amazon.co.jp/Mastering-Web-Application-Development-AngularJS-ebook/dp/B00EQ67J30) * [次世代JavaScriptでデータバインディング: Object.observe() を試す](http://d.hatena.ne.jp/jovi0608/20121206/1354762082) |
|
| 322位 |
|
|||
|
16:08:55 |
(Simplearchitect 所属) |
|
1. Varnishとは
--- Varnishはリバースプロキシを提供するためのミドルウェアである。Varnishを導入することで、Readが多いアプリケーションサーバーの前に設置することで、レスポンスの向上や、アプリケーションサーバーの負荷軽減が見込まれる。また、キャッシュの破棄等も明確に設定できるのが嬉しい。 [Varnish](https://www.varnish-cache.org) 2.Varnishサーバーの構築 --- ### 2.1.Varnishのインストール CentOS系のAmazonLinuxを使ってVanish環境を構築してみる Red Hat Enterprise Linux 6.4 - ami-5769f956 (64-bit) / ami-bb68f8ba (32-bit) Red Hat Enterprise Linux version 6.4, EBS-boot. ``` $ sudo rpm --nosignature -i http://repo.varnish-cache.org/redhat/varnish-3.0/el6/noarch/varnish-release/varnish-release-3.0-1.el6.noarch.rpm $ sudo yum install varnish ``` [Varnishインストール手順](https://www.varnish-cache.org/installation/redhat) ### 2.2.転送先ホストの設定 ``` $ sudo vim /etc/varnish/default.vcl ``` 転送先を例えばyahooに設定してみた例は次の通り ``` backend default { .host = "www.yahoo.co.jp"; .port = "80"; } ``` ### 2.3.Varnishのポートの設定 ``` $ sudo vim /etc/sysconfig/varnish ``` ``` VARNISH_LISTEN_PORT=80 ``` ### 2.4.サービスの起動 ``` $ sudo service varnish start ``` ちなみに、AmazonEC2のRedHatの場合、セキュリティグループを設定しても、自前でiptablesを設定する必要がある ``` $ curl http://localhost:80 (Webブラウザからはアクセス不可だが、サーバー内から可能) $ sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT $ sudo sudo iptables --list $ sudo /sbin/service iptable save ``` これで動作する事を確認した。 3.キャッシュクリアの方法 --- [Cash Invalidation](https://www.varnish-software.com/static/book/Cache_invalidation.html) Varnishのキャッシュをクリアする方法は次の4つがある + ttlの設定時限設定 + purge オブジェクト1つに対するキャッシュクリア + ban 正規表現を使ったキャッシュクリアの予約(SmartBan) + set req_hash_always_miss = true あるURLに対するキャッシュのクリア 今回の実験では、キャッシュミスの方法、SmartBan(Banの正規表現)、ttlの時限設定を試してみた。 結論としては、一括でキャッシュをクリアできるのは、SmartBanのみであり、実施するとすればそれになる。それ以外の可能性だと、ttlの時限設定も良い。元々Varnishのデフォルトでは、キャッシュがクリアされるのが2分の時限設定がもうけられている。2分のタイムラグが許容されるなら、方式がカンタンになるのでよいと思われる。 ### 3.1.キャッシュミスの設定 最初にテストした方式は、キャッシュされているオブジェクトをその場でキャッシュクリアする方法である。ただし、この方法では、URLで指定したオブジェクトしか、キャッシュミスにすることができない。 /etc/varnish/default.vcl ``` backend default { .host = "127.0.0.1"; .port = "80"; } acl backend_apps { "127.0.0.1";} sub vcl_recv { if (client.ip ~ backend_apps && req.http.X-REFRESH) { set req.hash_always_miss = true; } } ``` 実行イメージ ※この時は、ローカルのvagrant環境に、CentOS64をぶち込んでテストしています。 ``` [vagrant@vagrant-centos64 www]$ cat index.html hello 9 [vagrant@vagrant-centos64 www]$ cat test.html test 7 [vagrant@vagrant-centos64 www]$ curl http://127.0.0.1:6081 hello 8 [vagrant@vagrant-centos64 www]$ curl http://127.0.0.1:6081/test.html test 6 [vagrant@vagrant-centos64 www]$ curl -H 'X-REFRESH: DOIT' http://127.0.0.1:6081/ hello 9 [vagrant@vagrant-centos64 www]$ curl http://127.0.0.1:6081 hello 9 [vagrant@vagrant-centos64 www]$ curl http://127.0.0.1:6081/test.html test 6 [vagrant@vagrant-centos64 www]$ curl -H 'X-REFRESH: DOIT' http://127.0.0.1:6081/test.html test 7 [vagrant@vagrant-centos64 www]$ curl http://127.0.0.1:6081 hello 9 [vagrant@vagrant-centos64 www]$ curl http://127.0.0.1:6081/test.html test 7 ``` この方式では今回の要求(サブディレクトリの下を一括してキャッシュクリア)を満たせない。対象の数が少ない場合はこれでもいいかもしれない ### 3.2.SmartBan方式 通常のBANや、Purge、キャッシュミスの方式は、ある特定のオブジェクトに対応するため、一括でキャッシュをクリアしたい用途には向いていない。そのような場合は、Smart Banを用いるのが良い BANの方式はキャッシュをすぐにクリアするのではなく、対象のキャッシュをBANにしておき、次にそのURLがリクエストされた際に、バックエンドのサーバーに読みにいくといった挙動をする。 #### 3.2.1. SmartBanの設定とテスト 次のファイルをApacheのwwwルートに配置する。そのファイルを取得した後に、キャッシュがかかっているのを確認したのち、キャッシュを破棄するコマンドをhttpで発行する 次のシェルで4つのファイルの取得をVarnishに依頼している ``` #!/bin/bash curl http://127.0.0.1:6081/index.html curl http://127.0.0.1:6081/test.html curl http://127.0.0.1:6081/sub/1.html curl http://127.0.0.1:6081/sub/2.html ``` 最初に、リクエストを送った後に、ファイルをの中身を書き換えるが、キャッシュされて、新しいものに反映されない(これは期待通りの動作) ``` [vagrant@vagrant-centos64 www]$ ./request.sh index.html ver 1.6 test.html ver 1.6 1.html ver 1.6 2.html ver 1.6 [vagrant@vagrant-centos64 www]$ sudo vi index.html [vagrant@vagrant-centos64 www]$ sudo vi test.html [vagrant@vagrant-centos64 www]$ sudo vi sub/1.html [vagrant@vagrant-centos64 www]$ sudo vi sub/2.html [vagrant@vagrant-centos64 www]$ ./request.sh index.html ver 1.6 test.html ver 1.6 1.html ver 1.6 2.html ver 1.6 [vagrant@vagrant-centos64 www]$ ./request.sh index.html ver 1.6 test.html ver 1.6 1.html ver 1.6 2.html ver 1.6 ``` varnishの設定ファイルには次のように記述されている。これは、BANというMethodのリクエストが送られてきた時に、/subディレクトリ以下の全てのhtmlと、/test.htmlのキャッシュをbanに入れる、つまり次回アクセスされたときに、バックエンドに取得するような設定になっている。 ``` $ cat /etc/varnish/default.vcl backend default { .host = "127.0.0.1"; .port = "80"; } sub vcl_recv { if (req.request == "BAN") { ban("req.url ~ ^/sub/.*.html"); ban("req.url ~ ^/test.html"); error 200 "Banned."; } } ``` #### 3.2.2.キャッシュの破棄のコマンド キャッシュの廃棄はdefault.vclの書き方次第だが、上記の書き方の場合、次のコマンドで、キャッシュが破棄される。(注:refresh.htmlは何でも良い)尚、banに関しては後で述べるvarnishadmで発行することも可能 ``` $ curl -X BAN http://127.0.0.1:6081/refresh.html ``` #### 3.2.3.キャッシュ破棄の確認 その後、アクセスすると、無事キャッシュが破棄されて、内容が反映される ``` [vagrant@vagrant-centos64 www]$ curl -X BAN http://127.0.0.1:6081/refresh.html <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title>200 Banned.</title> </head> <body> <h1>Error 200 Banned.</h1> <p>Banned.</p> <h3>Guru Meditation:</h3> <p>XID: 666764559</p> <hr> <p>Varnish cache server</p> </body> </html> ``` この様子は、varnishadmコマンドを実行していると、バックエンドのサーバーにBanが設定されているのが確認できる。(説明書には、varnishadminは、varnishdに-Tを設定しないとダメと書いてあったが、実際はデフォルトで使えた) 最初はbanのリストを表示しても、何も表示されない ``` $ sudo varnishadm varnish> ban.list 200 Present bans: ``` curlでコマンドを送ると次のようになる ``` varnish> ban.list 200 Present bans: 1394437574.186510 0 req.url ~ ^/test.html 1394437574.186499 0 req.url ~ ^/sub/.*.html ``` 実際にシェルで確認すると、ファイルの中身が最新になっていることが確認できた ``` [vagrant@vagrant-centos64 www]$ ./request.sh index.html ver 1.7 test.html ver 1.7 1.html ver 1.7 2.html ver 1.7 ``` ### 3.3.キャッシュオブジェクトのタイムアウト キャッシュミスを設定する方法、SmartBanを設定する方法を解説したが、その他にも、キャッシュしたオブジェクトのタイムアウトを設定する方法がある。Varnishのデフォルトのタイムアウトは、120秒なので、それを利用して、キャッシュのタイムアウトが有効である事を確認する。 #### 3.3.1.デフォルトタイムアウトの挙動 ``` [vagrant@vagrant-centos64 www]$ date Mon Mar 10 09:07:11 UTC 2014 vagrant@vagrant-centos64 www]$ ./request.sh index.html ver 1.7 test.html ver 1.7 1.html ver 1.7 2.html ver 1.7 [vagrant@vagrant-centos64 www]$ sudo vi index.html [vagrant@vagrant-centos64 www]$ ./request.sh index.html ver 1.7 test.html ver 1.7 1.html ver 1.7 2.html ver 1.7 ``` 途中で、ファイルを更新しているのだが、キャッシュヒットして、反映されない。2分待ってみる。何もしなくても、キャッシュが破棄されている。キャッシュのタイムアウトは別途設定可能である。 ``` vagrant@vagrant-centos64 www]$ ./request.sh index.html ver 1.8 test.html ver 1.7 1.html ver 1.7 2.html ver 1.7 [vagrant@vagrant-centos64 www]$ date Mon Mar 10 09:10:02 UTC 2014 ``` #### 3.3.2.ttlのデフォルト値の変更 キャッシュのデフォルト値の変更は、/etc/sysconfig/varnishの次の項目を設定すれば変更できる。実際にテストしてみたが、ちゃんと動作した。 ``` # Default TTL used when the backend does not specify one VARNISH_TTL=120 ``` 下の例では、VARNISH_TTLを300(5分)に設定し、元々のデフォルトの2分ではキャッシュクリアされていないが、5分後にはキャッシュがクリアされている事を確認できる。 ``` [vagrant@vagrant-centos64 www]$ ./request.sh index.html ver 1.8 test.html ver 1.7 1.html ver 1.7 2.html ver 1.7 [vagrant@vagrant-centos64 www]$ date Tue Mar 11 03:51:59 UTC 2014 [vagrant@vagrant-centos64 www]$ sudo vi index.html [vagrant@vagrant-centos64 www]$ sudo vi test.html [vagrant@vagrant-centos64 www]$ sudo vi sub/1.html [vagrant@vagrant-centos64 www]$ sudo vi sub/2.html [vagrant@vagrant-centos64 www]$ ./request.sh index.html ver 1.8 test.html ver 1.7 1.html ver 1.7 2.html ver 1.7 [vagrant@vagrant-centos64 www]$ ./request.sh index.html ver 1.8 test.html ver 1.7 1.html ver 1.7 2.html ver 1.7 [vagrant@vagrant-centos64 www]$ date Tue Mar 11 03:54:21 UTC 2014 [vagrant@vagrant-centos64 www]$ ./request.sh index.html ver 1.9 test.html ver 1.9 1.html ver 1.9 2.html ver 1.9 [vagrant@vagrant-centos64 www]$ date Tue Mar 11 03:58:52 UTC 2014 ``` #### 3.2.3.キャッシュ対象によるタイムアウトの変更 また、キャッシュの時間をコンテンツによって変化させたい場合も対応できる。この場合は、default.vclに記述すればよい。 詳細は、[Varnish Book:VCL Basics](https://www.varnish-software.com/static/book/VCL_Basics.html)を参考にすればよい。例えばこんな例が載っている.次の例は,jpgファイルを強制的に60sでタイムアウトさせる例。 ``` sub vcl_fetch { if (req.url ~ "\.jpg$") { set beresp.ttl = 60s; } } ``` ### 3.4. varnishlog Varnishは、マネージメントインスタンスと、チャイルドインスタンスから出来ている。コマンドには、varnishadmin, varnishlog, varnish… の3つがある。varnishadminはvarnishを操作可能になる。(Banの発行も可能) varnishlogは現在のリクエストの状態を見る事ができる。どのリクエストでバックエンドに行ったかを見る事ができる varnishlogの例 ``` [vagrant@vagrant-centos64 varnish]$ varnishlog -b -o -i TxURL 14 BackendOpen b default 127.0.0.1 60909 127.0.0.1 80 14 TxURL b /sub/1.html 14 BackendReuse b default 14 TxURL b /sub/2.html 14 BackendReuse b default 14 BackendClose b default 14 BackendOpen b default 127.0.0.1 60912 127.0.0.1 80 14 TxURL b /index.html 14 BackendReuse b default 14 TxURL b /test.html 14 BackendReuse b default 14 TxURL b /sub/1.html 14 BackendReuse b default 14 TxURL b /sub/2.html 14 BackendReuse b default 14 BackendClose b default 14 BackendOpen b default 127.0.0.1 60936 127.0.0.1 80 14 TxURL b /sub/1.html 14 BackendReuse b default 14 TxURL b /sub/2.html 14 BackendReuse b default 14 BackendClose b default 14 BackendOpen b default 127.0.0.1 60939 127.0.0.1 80 14 TxURL b /index.html 14 BackendReuse b default 14 TxURL b /test.html 14 BackendReuse b default ``` ### 3.5. varnishstat varnishのヘルスチェックができるvarnishstatは下記の通り。詳細の見方はGettingStaredの記事を参照。 ``` $ sudo varnishstat 0+01:54:13 Hitrate ratio: 6 6 6 Hitrate avg: 0.7500 0.7500 0.7500 36 0.00 0.01 client_conn - Client connections accepted 36 0.00 0.01 client_req - Client requests received 24 0.00 0.00 cache_hit - Cache hits 12 0.00 0.00 cache_miss - Cache misses 3 0.00 0.00 backend_conn - Backend conn. success 9 0.00 0.00 backend_reuse - Backend conn. reuses 2 0.00 0.00 backend_toolate - Backend conn. was closed 12 0.00 0.00 backend_recycle - Backend conn. recycles 12 0.00 0.00 fetch_length - Fetch with Length 10 . . n_sess_mem - N struct sess_mem 4 . . n_object - N struct object 6 . . n_objectcore - N struct objectcore 6 . . n_objecthead - N struct objecthead ``` 4.設計のTipsとアーキテクチャ --- ### 4.1.Varnishのアーキテクチャ ### 4.1.1.Varnishとプロセス Varnishは大きくわけると、Managementプロセスと、Child/cacheプロセスに分かれる。Child/cacheプロセスがキャッシュを行い、Managementプロセスがそれを管理する。varnishadmコマンドで管理コマンドを発行できる。反映は、Varnishを再起動する事無く行える。(尚、varnishadmはリモートからでも操作できるように設定可能) アーキテクチャの図は次のURLのProcess Architectureの項を参考にすると良い [Tuning The Varnish Book](https://www.varnish-software.com/static/book/Tuning.html) ### 4.1.1.VCL(Varnish Configuration Language) Varnishは、VCL(Varnish Configuration Language)というステートマシンがあり、それがコンセプトの中心になっている。それを操作するのは、defalt.vclだったりvarnishadmだったりする。 次のフロー(VCL request flow)を参考にするとよい [VCL Basics - The Varnish Book](https://www.varnish-software.com/static/book/VCL_Basics.html) このURLを参考にすると、default.vclの書き方は全て理解できる。基本的なラインとしては、リクエストされると、vcl_recvの状態に遷移する。例えば次のvcl_recvと比較してみよう。次のvcl_recvはVarnishのデフォルト実装である。上のURLの状態がメソッド名(vcl_recv)等で、return句で返却しているのが、次の状態(pipe/pass/lookup)である。returnで返すのは-1等の値ではなく、次の状態への状態遷移を記述する。 ループは書けないが、if文を書く事ができる。ifの中身は、次のような比較や正規表現もサポートされている このif文の分岐によって、クライアントから送られてきたHTTPヘッダ、デバイス種類、Methodを用いで分岐し、キャッシュをさけたり、キャッシュのタイムアウトを調整したり、BANにしたり等の様々の操作を行うことができる。 ``` sub vcl_recv { if (req.restarts == 0) { if (req.http.x-forwarded-for) { set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip; } else { set req.http.X-Forwarded-For = client.ip; } } if (req.request != "GET" && req.request != "HEAD" && req.request != "PUT" && req.request != "POST" && req.request != "TRACE" && req.request != "OPTIONS" && req.request != "DELETE") { /* Non-RFC2616 or CONNECT which is weird. */ return (pipe); } if (req.request != "GET" && req.request != "HEAD") { /* We only deal with GET and HEAD by default */ return (pass); } if (req.http.Authorization || req.http.Cookie) { /* Not cacheable by default */ return (pass); } return (lookup); } ``` ### 4.1.3. VCLで使えるfunction VCLで使えるfunctionは次の通り + regsub(str, regex, sub) + regsuball(str, regex, sub) + ban_url(regex) + ban(expression) + purge; + return(restart) + return() + hash_data() regsub/regsuballがURLの書き換え系、ban_url, ban, purgeがキャッシュのクリア系, return 次の状態遷移の為のfuntion. hash_dataはhash inputに文字を足す為のもの. 次のURLは、VCLのリファレンス [VCL](https://www.varnish-cache.org/docs/trunk/reference/vcl.html) ### 4.2. サイジング Varnishのサイジングとしては、キャッシュするオブジェクトにそれぞれ1kbのオーバーヘッドがかかる。例えば1,000,000オブジェクトあるとすると、1GBのメモリが必要になる。それ以外に100MB程度のメモリを消費する詳細は同じく次のURLに [Tuning The Varnish Book](https://www.varnish-software.com/static/book/Tuning.html) ちなみに上記のURLには、スレッド数等の他のパラメータも掲載されている。 ### 4.3. ログについて ログファイルはデフォルトでは書かれない。これは、ログのサイズがすぐふくれあがってしまうため。 次のURLのLog dataを参照 [Getting started - The Varnish Book](https://www.varnish-software.com/static/book/Getting_started.html) 5.参考資料 --- Varnish Bookというリソースが特に良いが、他の物も記述する ### 5.1.キャッシュの無効化の記事 これは一番詳しい記述 [Varnish Book: Cache invalidation](https://www.varnish-software.com/static/book/Cache_invalidation.html) 公式資料 [Purging and banning](https://www.varnish-cache.org/docs/3.0/tutorial/purging.html) [Bans and purges in Varnish](https://www.varnish-software.com/blog/bans-and-purges-varnish-30) curlのオプション解説 [curl the man page](http://curl.haxx.se/docs/manpage.html) ### 5.2.Varnishの全体像 一番まとまっているGetting Startedこれを見れば全貌がわかる。 ◎[Getting started](https://www.varnish-software.com/static/book/Getting_started.html) Varnish Command Line Interface コマンドラインインターフェイスは、varnishadmで使えるコマンド群 ◎VCLの解説。コレを読めばdefault.vclの意味が分かる [Varnish Book:VCL Basics](https://www.varnish-software.com/static/book/VCL_Basics.html) 公式資料 [Varnish CLI](https://www.varnish-cache.org/docs/trunk/reference/varnish-cli.html) ### 4.3.Varnishの設定 ◎チューニングの決定版。アーキテクチャも説明されている [Varnish Book: Tuning](https://www.varnish-software.com/static/book/Tuning.html) 公式のサイジング資料だが、上記のTuningに全て載っている[Sizing your cache](https://www.varnish-cache.org/docs/3.0/tutorial/sizing_your_cache.html) ◎varnishdの公式説明でフォルト値が見れるのがポイント。全部よまなくていいけど、デフォルト値がわかるのがよい [varnishd](https://www.varnish-cache.org/docs/3.0/reference/varnishd.html) 設定ファイルのサンプル [/etc/default/varnish](https://gist.github.com/reifman/4651558) |
|
| 323位 |
|
|||
|
19:46:22 |
|
|
CentOSのパッケージは枯れたものが多いので、外部リポジトリを追加することで新しいパッケージを利用できるようにします。 よく使うEPELリポジトリの追加方法を備忘録として記録します。 ## yumプラグインのインストール どのリポジトリを優先的に利用するか設定するためのパッケージをインストールします。 ```bash:Bash $ sudo yum -y install yum-priorities ``` ## 優先度の設定 各リポジトリにpriorityを設定し、Baseリポジトリの優先度を上げます。 数値が小さいほど優先的が高く、優先的に利用されます。 ```bash:Bash $ sudo vim /etc/yum.repos.d/CentOS-Base.repo ``` ```conf:CentOS-Base.repo [base] … priority=1 [updates] … priority=2 [extra] … priority=2 [centosplus] … priority=2 [contrib] … priotiry=2 ``` ## リポジトリの追加 EPELリポジトリを追加します。ここでは64bit版CentOS 6の場合を想定します。 32bit版CentOS 6の場合は http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/6/i386/ 64bit版CentOS 6の場合は http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/6/x86_64/ 32bit版CentOS 5の場合は http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/5/i386/ 64bit版CentOS 5の場合は http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/5/x86_64/ で、ページ内を`epel-release`で検索し、最新のバージョンを確認することをおすすめします。 ```bash:Bash $ wget http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm $ rpm -ivh epel-release-6-8.noarch.rpm ``` ## 追加したリポジトリの設定 追加したリポジトリを常時利用するかどうか、どの優先度で利用するかを設定します。 ```bash:Bash $ sudo vim /etc/yum.repos.d/epel.repo ``` ```conf:epel.repo [epel] … # 常時利用、基本リポジトリと同様に利用 enabled=1 priority=1 # 常時利用、基本リポジトリになければ利用 enabled=1 priority=2 # 一時利用 enabled=0 ``` 一時利用の設定にした場合は、以下のコマンドで一時的に有効にしてインストールします。 オプションが必要になりますが、基本リポジトリのパッケージか追加リポジトリのパッケージかが明確にわかります。 ```bash:Bash $ sudo yum install <package-name> --enablerepo=epel ``` ## 参考 - [CentOS 外部レポジトリの追加(EPEL)](http://www.tooyama.org/yum-addrepo-epel.html) - [CentOS に EPEL リポジトリを追加する - xykの日記](http://d.hatena.ne.jp/xyk/20120711/1341993199) |
|
| 324位 |
|
|||
|
23:30:17 |
(Increments Inc 所属) |
|
Gitにある程度慣れ,基本的な操作ができるようになるとより深く知りたくなってくると思いますが,そのときにググって分散した情報を読むのではなく,まとまったドキュメントを活用すると効率が良いです.
まずは中級者向け`git help`について. # `git help help` まずは`git help help`をして,よく使う`git help`についてより良く知っておきましょう.`git help`の中でおすすめオプションは以下の2つ. ## `-w` HTMLでのmanをブラウザで開く.長いmanを読むときなどは読みやすくて地味に便利 ## `-g` ガイド一覧.特定のgitコマンドに縛られないような内容を見ることができます. ## gitガイド ``` $ git help -g The common Git guides are: attributes Defining attributes per path glossary A Git glossary ignore Specifies intentionally untracked files to ignore modules Defining submodule properties revisions Specifying revisions and ranges for Git tutorial A tutorial introduction to Git (for version 1.5.1 or newer) workflows An overview of recommended workflows with Git 'git help -a' and 'git help -g' lists available subcommands and some concept guides. See 'git help <command>' or 'git help <concept>' to read about a specific subcommand or concept. ``` この中でも特に **glossary**(用語集)や **revisions**(リビジョン(範囲)の指定方法一覧)は一度見ておくと,helpで利用される基本用語の意味や曖昧になりがちなrevision指定について整理できるのでおすすめです.例えば`HEAD~`と`HEAD^`や`master..branch`と`master...branch`の違いなど [リリースされたばかりのv1.8.5](https://raw.github.com/git/git/master/Documentation/RelNotes/1.8.5.txt)で入った`@`の意味も`git help revisions`に書かれています(`HEAD`と同じ). (余談: `..`と`...`の使い分けの1つは[現在のブランチで加えた変更を見る - Qiita](http://qiita.com/yaotti/items/ab52a7e834422a6f9662)を参考に) (余談その2: [v1.8.5で入った`git cherry-pick -`のコミット](http://article.gmane.org/gmane.comp.version-control.git/233960/) ;)) # Pro Git Scott先生の書いた本.仕組みから各コマンドの使い方まで体系立ってまとめられているので一読をすすめます. 特にchapter 9はGitの内部構造についてまとまって書かれた貴重な日本語情報源です. [Pro Git日本語版](http://git-scm.com/book/ja) # [Git ML](http://git-scm.com/community) Gitの動きを知りたい場合. 全部読むのはかなり気合入れないとつらい(たぶん無理…). 全体の動きを知りたいなら[Hamanoさん](https://github.com/gitster)が週1ぐらいで書いている"What's cooking in git.git"を読むと近いうちにこんなものが入るんだなーというのがわかる.[archive](http://search.gmane.org/?query=What%27s+cooking+in+git.git&author=&group=gmane.comp.version-control.git&sort=date&DEFAULTOP=and&xP=what%27s%09Zcook%09git&xFILTERS=Gcomp.version-control.git-Ac-Ahamano-Ajunio---A) 以上,基本操作が出来るようになってからの理解を深めるのに役立つ情報源でした. |
|
| 325位 |
|
|||
|
00:39:35 |
|
|
**※ここで説明している方法でのChefのインストールはお勧めしません。**
**こちらを参考に入れてください。** http://qiita.com/hamichamp/items/d3ca7c365353285a564b # Vagrantで仮想環境を作る準備をしよう ## VirtualBoxをインストールしよう https://www.virtualbox.org/wiki/Downloads 今回はMacOSXで環境を作るので、Mac版をダウンロードします。 ## Vagrantをイントールしよう http://downloads.vagrantup.com/ ここから最新のものをダウンロードしましょう。 この記事を書いている段階では、v1.2.7でした。 今回はMacOSXで環境を作るので、Mac版をダウンロードします。 # Vagrantで仮想環境を作ってみよう ## 仮想環境を登録しよう 今回はCentOSをダウンロードします。 ``` $ vagrant box add centos http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130427.box ``` centosという名前でvagrantにboxが追加されたか確認しましょう。 ``` $ vagrant box list ``` ## 仮想環境を初期化しよう 初期化するとVagrantfileというファイルが作成されます。 適当なフォルダを生成し、そのフォルダで作業をしましょう ``` $ mkdir ~/vagrant $ cd ~/vagrant $ vagrant init centos ``` これで、~/vagrant フォルダにVagrantfileというファイルが作成されます。 次にこれを編集しましょう。 どんなエディタでも良いです。 ホストのMacから接続できるIPアドレスを設定します。 次がコメントアウトされているので、コメントを外します。 <pre> config.vm.network :private_network, ip: "192.168.33.10" </pre> ## 仮想環境を起動しよう ``` $ vagrant up ``` しばらくすると仮想環境でCentOSが起動します。 ## 仮想環境にアクセスしよう ``` $ vagrant ssh ``` 無事にアクセスできたでしょうか? これでベースとなる仮想環境ができました。 仮想環境での作業は後で行いますので、再びMaxOSXでの作業を続けましょう。 # Chefをインストールしよう **こちらを参考に入れてください。** http://qiita.com/hamichamp/items/d3ca7c365353285a564b **※以下は、元々書いていた方法ですが、この方法でのChefのインストールはお勧めしません。** > # MacOSXのRuby環境を整備しよう > MaxOSXには最初からRubyが入っているんですが、少し古いです。 > いろいろと面倒な事が起きるので、最新版をインストールしましょう。 > > ## Xcodeのインストール > App Storeからインストールしておきましょう。 > > ## Xcode Command Line Toolsのインストール > Xcodeを起動し、Preferencesを開き、Downloadsタブ「Components」から『Xommand Line Tools』をインストールしましょう。 > > ## Homebrewのインストール > コマンドプロンプトから、以下のコマンドを入力します。 > > ``` > ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)" > ``` > > 次に一応、以下のコマンドも入力してみます。 > > ``` > brew doctor > brew update > brew -v > ``` > > 私のバージョンは今のところこんな感じでした。 > > <pre> > Homebrew 0.9.4 > </pre> > > ## Ruby2.0をインストールするための準備 > Homebrewを使って、Ruby2.0をインストールしますが、その前に必要なものをインストールしましょう。 > 説明は省きます。 > > ### readline、openssl、rbenv、ruby-build をインストール > > ``` > $ brew install readline openssl rbenv ruby-build > $ brew link readline openssl --force > $ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile > $ . ~/.bash_profile > ``` > > ### curl-ca-bundle をインストール > ``` > $ brew install curl-ca-bundle > $ brew list curl-ca-bundle /usr/local/Cellar/curl-ca-bundle/1.87/share/ca-bundle.crt > $ cp /usr/local/Cellar/curl-ca-bundle/1.87/share/ca-bundle.crt /usr/local/etc/openssl/cert.pem > ``` > > ## Ruby2.0をインストールしよう > さて、いよいよRuby2.0をインストールしましょう。 > > ``` > $ CONFIGURE_OPTS="--with-openssl-dir=`brew --prefix openssl` --with-readline-dir=`brew --prefix readline`" rbenv install 2.0.0-p247 > $ rbenv rehash > $ rbenv global 2.0.0-p247 > ``` > > バージョンを確認しておきます。 > > ``` > $ rbenv version > $ ruby -v > ``` > > 2.0.0-p247が使えるようになったでしょうか? > > ### rbenv rehash を自動化 > この後、gemでいろんなものをインストールするんですが、きちんと使えるようにするために、おまじないを唱えておきましょう。 > > ``` > $ gem i rbenv-rehash > ``` > > これで、ひとまずMacOSXのRuby環境がずいぶんと改善されたと思います。 > > > # Chefをインストールしよう > > これからの作業がMaxOSXのRuby環境を整えた理由です。 > まずは、gemでChefをインストールします。 > > ``` > $ gem i chef --no-ri --no-rdoc > ``` > > Chefをインストールすると、knifeというChefの便利コマンドもインストールされます。 > knifeの設定もしておきましょう。 > > ``` > $ knife configure > ``` > > 実行すると、~/.chef/knife.rbにknifeの設定ファイルが保存されます。 > 質問事項は全てデフォルトでOKです。 > > ## knife-soloをインストールしよう > knife-soloはknifeのプラグインです。 > ここでは0.3.0を入れるようにしておきましょう。 > > ``` > $ gem i knife-solo --no-ri --no-rdoc --pre > ``` > > --preをつけないと0.2.0がインストールされたのですが、以下のような問題に苦しみました。 > http://tk0miya.hatenablog.com/entry/2013/04/18/011339 ## knife-soloでリポジトリを作ってみよう。 現在のフォルダの中にリポジトリを作成してみましょう。 ``` $ knife solo init chef-repo ``` chef-repoというフォルダが作成されましたか? 中にはいろんなものがありますね。 ## サードパーティのレシピを使ってみよう。 レシピの作り方なんてとりあえずわからないので、用意されているレシピをダウンロードしてつかってみましょう。 **※BerkshelfはChef Developer Kitをインストールすることで既にインストールされています。** **以下は元々の記事ですが、この方法でのインストールは不要です。** > まずは、Berkshelfというツールをインストールします。 > > ``` > $ gem i berkshelf --no-ri --no-rdoc > ``` 先ほど作ったchef-repoフォルダの中にBerksfileを作成します。 ``` $ cd chef-repo $ vim Berksfile ``` ダウンロードしてくるレシピを指定します。 どんなレシピがあるかは下記サイトをのぞいてみてください。 https://github.com/opscode-cookbooks ``` site :opscode cookbook 'yum' cookbook 'nginx' ``` Berksfileを保存したら、レシピをダウンロードしてきましょう。 ``` $ berks install --path cookbooks ``` berkshelf3になってから、コマンドが変わったようです。 ``` $ berks vendor cookbooks ``` chef-repoフォルダ内のcookbooksフォルダにレシピがダウンロードされたはずです。 ## 仮想環境へ設定をする前に 久しぶりに仮想環境が登場です。 仮想環境は立ち上がっていますか? 立ち上げ方を忘れちゃった人は、~/vagrantフォルダから下記のコマンドで立ち上がりますよ。 ``` $ vagrant up ``` 前回は下記コマンドで仮想環境にアクセスしたと思います。 ``` $ vagrant ssh ``` 便利になるように設定をしておきましょう。 ``` $ vagrant ssh-config --host centos >> ~/.ssh/config ``` これで、どのフォルダからでも以下のコマンドで仮想環境にアクセス可能になります。 ``` $ ssh centos ``` アクセスできることを確認したら、またMaxOSXから設定を行いましょう。 ## 仮想環境へ設定する準備 ### 仮想環境にchefをインストールします さっき作ったchef-repoフォルダに戻って、下記コマンドを実行しましょう。 ``` $ knife solo prepare centos ``` これで、仮想環境にchefがインストールされます。 最初に1回だけ実行すればOKです。 成功するとnodesフォルダの中にcentos.jsonというファイルが生成されるはずです。 作成されたcentos.jsonを編集しましょう。 ``` { "run_list":[ "yum::epel", "nginx" ] } ``` ## 仮想環境へ設定を反映させる 下記コマンドで、仮想環境に設定が反映されます。 ``` $ knife solo cook centos ``` ## Mac OS X側から仮想環境にアクセスしてみよう 192.168.33.10というIPアドレスで仮想環境にアクセスできるんですが、 このままでは、仮想環境がすべてのアクセスを拒否しています。 アクセスを受け付けるように仮想環境の設定を変更します。 ``` #Macのコンソール $ ssh centos #以下は仮想環境のコンソール $ sudo /sbin/iptables -F $ sudo /etc/init.d/iptables save ``` この状態でMac OS Xのブラウザからアクセスをしてみましょう。 続く・・・。 と思ったけど、やっぱり続きません! |
|
| 326位 |
|
|||
|
09:57:38 |
(アトラエ 所属) |
|
サイトが大きくなればなるほど未使用セレクタとか増えちゃってCSSで余計な容量食っちゃったりしませんか? そんな時に、ページごとに使っていないセレクタを見つけて削除したい。 いろいろ未使用セレクタを見つけるツールがあるみたいですが今回はChromeの開発ツールを使いました。 ##未使用CSSセレクタの見つけ方  Choromeの開発ツールの`Audits`をクリックし、 `Run`ボタンをクリック。  `Remove unused CSS rules`を開くと そのページで使われていないCSSセレクタが一覧で表示されます。 ##未使用CSSセレクタの削除 あとは以下の手順で削除する。 1. 他のページのviewやjavascriptでそのセレクタが使われていないか調査 2. 使われていない場合は削除する 自分の環境だとRails使っているので `grep`かけて他で使ってないかチェックしてから削除します。 ``` grep -r 'fb_hidden' app/views/ | grep -v svn grep -r 'fb_hidden' public/javascripts/ | grep -v svn ``` どちらでも使われていない場合はそのセレクタを削除する。 |
|
| 327位 |
|
|||
|
21:52:45 |
(Increments Inc 所属) |
|
[Git Advent Calendar / Jun.](http://qiita.com/advent-calendar/git) 29日目の記事です.28日目は[@uasi](http://qiita.com/users/uasi)さんの「[どこでも使える git diff と git apply](http://qiita.com/items/905376f02ff029fb23f8)」でした.
「間違ってマージしていないブランチを消した」「reset --hard HEAD^*で戻しすぎた」ということがたまにある. しかし`git reflog`を使うと(GCされていなければ)過去のあらゆるコミット履歴を見ることができ,git logやgit branchでは辿り着けない時点まで戻すことができる. ```sh $ git reset --hard HEAD^^ # HEAD^と指定するつもりが間違えた! $ git reflog f5cb888 HEAD@{0}: head^^: updating HEAD b0b8073 HEAD@{1}: merge @{-1}: Merge made by the 'recursive' strategy. fe3972d HEAD@{2}: checkout: moving from fix/some-bug to master $ git reset --hard HEAD@{1} # reset --hard HEAD^^する前に戻れる ``` 歴史を書き換え間違えたときのために覚えておくと便利! |
|
| 328位 |
|
|||
|
18:08:55 |
(フリーランス 所属) |
|
皆さんiOSでシングルトンは使っていますか?
シングルトンはいわゆるデザインパターンと呼ばれるもので、複数のクラス間の変数、オブジェクト、メソッドのやりとりを劇的に簡単にすることが可能です。 このように便利ですが今ひとつとっつきずらいiOSのシングルトンについてこの記事では少々解説したいと思います。 シングルトンを使用したクラスには、例えば以下のメリットが有ります。 ・複数のクラス間での変数やオブジェクトの共有が簡単にできる。 ・複数のクラス間でメソッドの共有ができる。 ・ViewControllerの機能を分担させることができる。 一方で、以下のようなデメリットも有ります。 ・不要になっても自動的に解放されない。 ・何度もインスタンス化することができない。 必要に応じて、通常のクラスとシングルトンを適用したクラスを使い分けるといいと思います。 シングルトンですが、シングルスレッドの場合は次のように書くことができます。 ```objc:SingletonSample.h @interface SingletonSample : NSObject + (SingletonSample *)sharedManager; @end ``` ```objc:SingletonSample.m @implementation SingletonSample static SingletonSample *sharedData_ = nil; + (SingletonSample *)sharedManager{ if (!sharedData_) { sharedData_ = [SingletonSample new]; } return sharedData_; } - (id)init { self = [super init]; if (self) { //Initialization } return self; } ``` 他のクラスからシングルトンのクラス内のプロパティやメソッドにアクセスするためには、以下のように記述します。 ```objc [SingletonSample sharedManager].(プロパティ名) [[SingletonSample sharedManager] (メソッド名)]; ``` シングルトンではインスタンスの初期化が最初の1回しか行われず、それ故に1つのインスタンスを共有できることがメリットですが、マルチスレッドの場合は複数のスレッドが同時に初期化を行ってしまう危険性があります。 そのため、以下のように@synchronizedを使ったスレッドセーフな記述をした方がベターかと思います。 ```objc:SingletonSample.m @implementation SingletonSample static SingletonSample *sharedData_ = nil; + (SingletonSample *)sharedManager{ @synchronized(self){ if (!sharedData_) { sharedData_ = [SingletonSample new]; } } return sharedData_; } - (id)init { self = [super init]; if (self) { //Initialization } return self; } ``` もしくは、以下の通りにdispatch_once_tを使ってアプリの起動中初期化処理は1回のみにするという手もあります。これもスレッドセーフです。 ```objc:SingletonSample.m @implementation SingletonSample static SingletonManager* sharedData_ = nil; + (SingletonSample*)sharedManager { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedData_ = [SingletonSample new]; }); return sharedData_; } ``` それでは、@synchronizedとdispatch_once_tを使った方法が全く一緒なのかというと、微妙に異なります。 前述の通り、dispatch_once_tはアプリの起動中1回しか起動できないので、一度sharedData_のメモリを解放してしまうともう初期化ができません。 @synchronizedの場合は、例えば同じシングルトンのクラスの中に、 ```objc:SingletonSample.m - (void)terminate{ _sharedData = nil; } ``` と書くことで_sharedDataを解放後に、再び初期化することができます。 メモリの解放は関数が終了した後に行われるようですので特に問題は無いようです。 クラス内の変数が多すぎて値を初期値に戻すのが大変な場合は、この手を使うのも有りかと思います。 という認識ですが、間違った内容があれば是非教えてくださいね。 |
|
| 329位 |
|
|||
|
13:46:12 |
(garbs 所属) |
|
多分、Gitを使い始めて詰まる(というか悩む)ところの大多数が、「コミットコメント」だと思う。
これについては色々な人が色々なことを書いていて、結構どれも正しいので、それらを参考にしてもらうのが良いと思う。 じゃあ俺はこれから何を書くのか、というと、 **こういう風にコメントするとチーム開発が捗るよ** というのを書こうと思う。 俺自身も出来てないことが多いので、自戒を込めて書くことにした。 ### ※注意! この記事は「ウチのチームで俺はこういうのを意識してコミットコメントを書いている」というものであって、 万人にドンピシャリと来るものではないというのを予めお伝えしておく。 ## コミットが何を変えるのかを明確にする たとえばこんなコミットコメントがあったとする。 ``` Fix some bugs ``` **アウト。ダンゴで試合終了。** "Fix some bugs"、直訳すると「いくつかバグ直したぜ!」である。どこの、どんなバグを、何個直したのかが全くもって不明瞭で、何を言いたいのか伝わらない。 ひとつのコミットで複数の変更をするというのも、個人的には納得がいかないが、それは以下のように書くことで、百歩譲って許す。 ``` fix some bugs - Typo - This feature is already gone. ``` じゃぁ、どうなってるのが(俺の)理想なのか。それは、 **1コミットにつき1つのバグを直す** である。上の例で言うなら、 ``` Typo ``` と、 ``` This feature is already gone ``` は、別物としてコミットする。そうすることで、そのコミットがプロダクトに対してどのように作用するのかが、タイトル1行読むだけで理解できるようになる。コミットコメントにストーリーが生まれるのだ。大変わかりやすいではないか。 あ、ちなみに個人的には、 ``` Fix failing specs ``` はアリ。なんでかというと、「死んでるspecを直した」っていうのはコード自体には影響しないし、特に価値を生み出してるわけでもないし、あと他に書きようがないから。 ## チームで言葉の意味を育てる 割と啓蒙活動的なところがあるが、結構そういうもんだと思う。 たとえば、[某弊社のプロダクト](https://forkwell.com)には時々、 ``` Oooops ``` とか、 ``` indent ``` とかっていう、一言で済ましちゃうコミットが発生したりする。 「いやいや全然内容わかんないじゃんなんだよこれ」 と思う人が大半だろう。だがこれらのコミット、うちのチームでは余裕で意味が通じる。 なぜかというと、「時々発生する=内容が推察しやすい」からだ。 `indent`って言われたら、「Hamlのインデント間違ってたんだろうなぁ」と思うし、`Oooops`って言われたら、「まぁ多分置換し忘れたんだろう」と、こんな感じだ。 究極的なことを言ってしまうと、俺はコミットコメントはそのリポジトリに関わる人に意味が通じるならOKと思っている。ので、上記したコメントは意味が通じているのでOKだ。 (もちろんそれがバグになる可能性も否定出来ないので、結局全コミット見返している) 言ってしまえば、定型文だ。「拝啓」とか「敬具」と同じレベルだ。 そして、この項についてはこれだけは理解してほしい。 **オープンソースにコミットするなら、そこの流儀を守って、コミットコメントもそこの例に習うべき。** 自分にしか通じないコミットコメントは、混乱しか生まない。 ## FIXMEを潰した!と書く [某弊社のプロダクト](https://forkwell.com)なんかのログでよく見られる光景に、「FIXMEを潰しといたよ」というものがある。 デザイナーと協業する上で、作ったデザインの中に、「こんな感じで動作するようにロジックをいれこんでください」という説明が、FIXMEを添えて埋め込まれているのだ。 なので、FIXMEを潰したぜ、という意味を込めて、FIXYOUと付けてコミットしている。例えば、 ```haml:show.html.haml -#FIXME 名前をデータベースから出すようにしてください = '馬場達郎' ``` というコードがあったとして(某弊社のプロダクトで実際にこんなんがあった)、これを修正したコミットは、 ``` FIXYOU; Show full name of the user from database ``` と、こんな感じになる。こうすればFIXMEと付けた人間は、「あ、直ったんだアレ」というのが一目で分かるし、周りの人間も「バグ修正的な?」というふうに、内容の想像が付きやすい。 ## コミットコメントは動詞(一人称の現在形)から始める ここまでの例で、 ``` Fixed a bug ``` というような、「過去形」が1つも無いという事実に気づいた人はいるだろうか。 実は某社の某プロダクトを開発している某チームは、コミットコメントでほとんど過去形を使わない(もちろん例外はある)。 なぜかというと、コミットする瞬間を起点にして、「今」「そのコミットが」「リポジトリに対してどんな変化を与えるのか」を記述しているからだ。 コミットコメントを以下のようにエア置換すると分かりやすい。 ``` (This commit) Fixes a bug ``` 理解しやすくなったはずだ。あくまでリポジトリにどんな変更を入れるか、なので、主体はコミットであるべきで、ここであえて「俺が直したんだぜ」とかそういうのは必要ない。そもそも誰がコミットしたのかなんてコミットのAuthorを見れば分かる。 ## emojiを上手く使う 例えば、「昔使ってたメソッドだけどもう使われてなくて、でも消すのを忘れてしまっていた」みたいなメソッドを消しました、というのはよくある話だと思う。 そんな時に便利なのが、 `:put_litter_in_its_place:` である。 といったふうに、いろいろなシーンでemojiを有効活用することで、ちょっと肩の力を抜いた感じのコミュニケーションを生むことが出来るのはオススメだ。 ちなみに以下のようなものが、うちのチームで良くあるemojiの使い方だ。 | commit comment | 意味 | |---|---| | :put_litter_in_its_place: | ゴミを捨てた(要らないメソッド・空行を消した)| | Typo :trollface: | このスペルミスはカッコ悪いよねぇ〜 | | cosme :lipstick: | お化粧(コードをちょっと綺麗に整えた) | ## 参考資料とか - [英語でコミットを書こう](https://speakerdeck.com/pwim/ying-yu-dekomitutowoshu-kou) - [英語コミットコメントに使えるオシャレフレーズ集](http://qiita.com/ken_c_lo/items/4cb49f0fb74e8778804d) 英語でコミットコメント、っていうときに参考になると思う。 「英語でコミットを書こう」は、社内の開発に限らない、オープンソース活動なんかでも使える、むしろオープンソースを意識した分かりやすいコメントを目指す時の、最初の一歩になる。 「英語コミットコメントに使えるオシャレフレーズ集」は、上にも書いた「チームで言葉の意味を育てる」で有効活用したり出来る。 ## 最後に ごちゃごちゃ色々書いたけど、最終的に大事なのは、やっぱり「人に伝えること」なので、独りよがりにならず、「 **相手がこのコミットを見た時にどう捉えるか** 」を意識して書けば、自然と良いコミットコメントになるのではないか。 コミットコメントはドヤるためにあるんじゃないんだよ! |
|
| 330位 |
|
|||
|
21:20:23 |
|
|
## lsofコマンドの使い方です。
lsofコマンドは、、、 PortやPID、プロセス名からファイルがオープンしている情報を表示するコマンド。 ## よく利用する場面 「netstat -an |grep LISTEN 」とかでListenしているPortを調べてそのPortが何のプロセスとかで動いているのかとかを確認するのに使っています! ## 使い方サンプル ```bash:ファイルを指定 % lsof /var/log/system.log COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME tail 97529 USERNAME 3r REG 1,2 498299 12085861 /private/var/log/system.log ``` /var/log/system.logはtailコマンドで開いていることがわかりますねー ---- ```bash:ポート指定 % lsof -i:80,443 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME Dropbox 376 USERNAME 14u IPv4 0x2b3f22aae05f3e3 0t0 TCP 192.168.100.102:58140->108.160.162.100:http (ESTABLISHED) Dropbox 376 USERNAME 19u IPv4 0x2b3f22ab4456703 0t0 TCP 192.168.100.102:54071->108.160.160.177:https (CLOSED) ``` portが80, 443で開いているプロセスを表示指定ます。 LISTENしているプロセスも表示されます。(DROPBOX って HTTPポート(80) を使って同期しているんですねー) #### 明示的にLISTENされているプロセスを調べたい場合 ```bash:redisのポート lsof -i:6379 |grep LISTEN # TCPとして指定する lsof -iTCP:6379 |grep LISTEN ``` --------------------- ```bash:プロセス名指定 % lsof -c ruby COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME ruby 18910 USERNAME cwd DIR 1,2 1190 2 / ruby 18910 USERNAME txt REG 1,2 2805460 1903306 /Users/USERNAME/.rbenv/versions/1.9.3-p385/bin/ruby ruby 18910 USERNAME txt REG 1,2 13384 1903333 /Users/USERNAME/.rbenv/versions/1.9.3-p385/lib/ruby/1.9.1/x86_64-darwin12.2.1/enc/encdb.bundle ruby 18910 USERNAME txt REG 1,2 13168 1903375 /Users/USERNAME/.rbenv/versions/1.9.3-p385/lib/ruby/1.9.1/x86_64-darwin ``` rubyプロセスが開いているファイルを表示していますねー ---- ```bash:PIDを指定 % lsof -p 376 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME Dropbox 376 USERNAME cwd DIR 1,2 2550 7815603 /Applications/Dropbox.app/Contents/Resources Dropbox 376 USERNAME txt REG 1,2 89808 7815600 /Applications/Dropbox.app/Contents/MacOS/Dropbox ``` PIDが376のプロセスが開いているファイルを表示 ---- ```bash:ユーザ名を指定 % lsof -u ユーザ名 ``` 指定したユーザが開いているプロセスを表示 |
|
| 331位 |
|
|||
|
16:27:26 |
(SonicGarden Inc. 所属) |
|
RubyはModuleで組み込まれたメソッドや、`method_missing`を利用した「ゴーストメソッド」など、自分の見ているメソッドがそのクラス以外のどこで定義されているのか、分かりにくいケースがよくあります。
特にJavaやC#から移ってきた僕のようなプログラマは、「メソッド = どこかのクラスで定義されているもの」という印象が強いので、「持ち主がよく分からないメソッド」の存在はデバッグ等で苦労させられます。 こういったケースでは、`Kernel#method`と`Method#source_location`を組み合わせることで、メソッドの定義場所を見つけることができます。 たとえば、rails consoleで`blank?`メソッドの定義場所を見つけたい場合は、こんな感じです。 ```` $ rails c > 'x'.method(:blank?).source_location => ["/Users/mypc/.rvm/gems/ruby-1.9.2-p320/gems/activesupport-3.2.3/lib/active_support/core_ext/object/blank.rb", 102] ```` ### 制限事項 * `Method#source_location`はRuby 1.9以上でしか使えません。 * irbやrails consoleで簡単にチェックできるメソッドならいいですが、gemの奥まった処理で使われているメソッドなどを調査しようと思うと、結構大変かもしれません。 ### 参考 * http://stackoverflow.com/questions/175655/how-to-find-where-a-method-is-defined-at-runtime ### 2014.12.10 追記 最近はこの方法ではなく、 **RubyMine** を使ってます。 RubyMine、便利! [RubyMineのコードジャンプ機能は本当にすごい!!困ったときはCommand+Bを押すべし!](http://qiita.com/jnchito/items/bcb2bb5311a44113fdd9) ### 2017.1.30 追記 定数(クラス名やモジュール名を含む)の定義場所を調べたい場合はこちらの記事を参考にしてください。 [Rubyでclassやconstの定義位置を調べる方法 \- Qiita](http://qiita.com/ksss/items/377eef080909564e4a25) |
|
| 332位 |
|
|||
|
10:07:58 |
(LIFULL Co., Ltd. 所属) |
|
Xcodeの便利ツールとかじゃなくて、Xcodeを触る上で知っておいたほうが有利なこと
これからも追加してく ## Double Click Navigation 誰か1度はやるかもしれない Xcodeを起動したらToolbarもAssistant Editorもない、この画面 まさに絶望、やる気をすべて奪う悪魔のウィンドウ  原因はProject Navigatorのファイルをダブルクリックで新しいウィンドウを開いたままXcodeを閉じてしまうこと。 解決には`Prefarence... > Navigation > Double Click Navigation`を`Uses Separate Tab`にすること これで、ファイルをダブルクリックしたときは、タブで表示される。 ## ショートカット 私がよく使うのだけ、他にもいくつかあるけど  ### 編集 `Esc` 補完機能の呼び出し `Cmd + /` コメントアウト(または解除 `Cmd + ¥` ブレークポイントの設置(または削除 ### ファイル操作 `Cmd + T` タブ `Cmd + Shft + [` 後ろのタブを開く `Cmd + Shft + ]` 前のタブを開く `Cmd + W` タブを閉じる `Cmd + Shft + F` ワークスペースから検索 `Cmd + G` 次の検索対象を開く `Cmd + Shft + G` 前の検索対象を開く `Cmd + Shft + Opt + F` ワークスペースから置換 ### ビルド `Cmd + R` 実行 `Cmd + B` ビルド `Cmd + .` 実行、ビルドを停止 `Cmd + Shft + K` クリーン ### その他 `Cmd + Shft + 2` Organizarを開く ## Function Menu ### #pragma mark - メソッドをグルーピングする時にコード内に`#pragma mark`と記述することができる また、`#pragma mark - `と記述することで、ラインを引く事ができる ```objective-c:AppDelegate.m #pragma mark グルーピングA - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { return YES; } - (void)applicationWillResignActive:(UIApplication *)application { } #pragma mark - グルーピングB - (void)applicationDidEnterBackground:(UIApplication *)application { } ``` 結果 > また、`#pragma mark`はXcode4.4からソースエディタジャンプができるので、次のようにClassNameを指定することで飛ぶ事ができるようになった ```objective-c:AppDelegate.m #pragma mark - ClassName - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { return YES; } ``` ### // TODO: コメントアウトに`TODO: `を書く事で、それを強調することができる ```AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // TODO: Override point for customization after application launch. return YES; } ``` 結果 > ### #warning ビルド時に警告として表示させる ```objective-c:AppDelagate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { #warning 警告ですよー return YES; } ``` 結果 > ### FIXME: !!!: ???: あんまりつかわない気がする |
|
| 333位 |
|
|||
|
09:22:11 |
(元KAYAC Inc. 所属) |
|
## 追記
たまにストックされるので追記です。 記事の内容が理解できたら、実際にコードを書く際には、 https://github.com/joeldev/JLRoutes などを利用したほうが幸せになれるかと思います。 ## URLSchemeとは URLSchemeとは、 __非常にざっくり__ 説明すると、皆さんには見慣れている ``` http://example.com/ ``` の`http`の部分です。 iOSアプリでは、このURLSchemeを独自に設定して、そのURLSchemeが呼ばれた時に自身のアプリを開く事ができます。設定方法などは今回説明しないので、適宜ググってください。 はじめから用意されているURLSchemeなどはこちらのドキュメントに書かれています。 [About Apple URL Schemes][1] ## URLSchemeで起動した後のお話 さて、独自に設定したURLSchemeが呼ばれると、そのURLSchemeに対応しているアプリが起動すると書きましたが、もう少しプログラムよりな話をすると`UIApplicationDelegate`の`application:openURL:sourceApplication:annotation`が呼ばれます。 実際のフローなどは以下のドキュメントの`Implementing Custom URL Schemes`の項に書いてあります。 [Advanced App Tricks][2] ここでURLをアプリ側で受け取る事ができますので、必要ならば渡されたURLを元に処理を行っていくことになります。(なにもしなくてもアプリは起動します) 実際にURLを受け取った部分のソースを上のドキュメントから拝借させて頂きますが、以下の様な感じになります。 ```objectivec - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { if ([[url scheme] isEqualToString:@"todolist"]) { ToDoItem *item = [[ToDoItem alloc] init]; NSString *taskName = [url query]; if (!taskName || ![self isValidTaskString:taskName]) { // must have a task name return NO; } taskName = [taskName stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; item.toDoTask = taskName; NSString *dateString = [url fragment]; if (!dateString || [dateString isEqualToString:@"today"]) { item.dateDue = [NSDate date]; } else { if (![self isValidDateString:dateString]) { return NO; } // format: yyyymmddhhmm (24-hour clock) NSString *curStr = [dateString substringWithRange:NSMakeRange(0, 4)]; NSInteger yeardigit = [curStr integerValue]; curStr = [dateString substringWithRange:NSMakeRange(4, 2)]; NSInteger monthdigit = [curStr integerValue]; curStr = [dateString substringWithRange:NSMakeRange(6, 2)]; NSInteger daydigit = [curStr integerValue]; curStr = [dateString substringWithRange:NSMakeRange(8, 2)]; NSInteger hourdigit = [curStr integerValue]; curStr = [dateString substringWithRange:NSMakeRange(10, 2)]; NSInteger minutedigit = [curStr integerValue]; NSDateComponents *dateComps = [[NSDateComponents alloc] init]; [dateComps setYear:yeardigit]; [dateComps setMonth:monthdigit]; [dateComps setDay:daydigit]; [dateComps setHour:hourdigit]; [dateComps setMinute:minutedigit]; NSCalendar *calendar = [s[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; NSDate *itemDate = [calendar dateFromComponents:dateComps]; if (!itemDate) { return NO; } item.dateDue = itemDate; } [(NSMutableArray *)self.list addObject:item]; return YES; } return NO; } ``` この例だとURLSchemeは`todolist`で、`todolist://?taskName#today`こんなURLを受け取ることを想定していて(多分)`taskName`というtodoを`today(今日)`の日付で作成して登録しています。 こんな感じで、受け取るURLによって様々な処理をさせることが出来ます。 この処理を書く部分ですが、例の様に`AppDelegate`にだらだら書いてしまいがちです。 処理の種類が少ないうちはそれでも対応できますが、種類が増えて分岐が増えてくるとあっという間にスパゲッティな感じになりそうです。`AppDelegate`はできればシンプルを保ちたいですね。 それでは __どうやってこの部分をシンプルに保つのか__ 。というのがこの記事のメインTopicです。 ## どのように処理をシンプルにするのか 偉そうに書いて居ますが、世の中のWEBアプリケーションフレームワークのURL設計を参考に真似すればいいだけです\(^o^)/ 記事の最後にサンプルプロジェクトを置いておくので、記事中の説明は必要箇所だけにします。 __あくまでも一例です。__ ## URLの設計をする URLを作る上でのルールですね、各アプリケーションで決めればいいと思いますが大体こんな感じで足りるのでは無いでしょうか。 `scheme://controller/action?query` 上記のURLはNSURLにすると ```objectivec scheme = [url scheme] controller = [url host] action = [url lastPathComponent] query = [url query] ``` こういう感じでそれぞれ受け取れるので楽です。 アプリ側は受け取ったcontrollerを元に、使用するcontrollerクラスを選び、そのcontrollerクラスにactionとqueryを渡します。各actionの挙動については各々のcontrollerに任せるという実装にしてみます。 ## 実際の処理の流れ まずは、URLを元にcontrollerを呼び出す処理をさせるクラスを作ります。ここでは半端なRailsの知識から`Routes`クラスと名づけましょう。(意味違ったらこっそり教えて下さい) 以降、説明の都合上Prefixがないクラスを作っていきますが、実際にはちゃんとつけてあげてください。 ### URLを受け取る処理 ```objectivec:Routes.h - (BOOL)openURL:(NSURL *)url; ``` こんな感じで`NSURL`を渡すと、そのURLを元に処理を試みて、その結果(成功or失敗)を`BOOL`で返します。 `BOOL`で返すようにするのは`UIApplicationDelegate`の`application:openURL:sourceApplication:annotation`の返り値も`BOOL`なので極限にシンプルにすると ```objectivec - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { return [routes openURL:url]; } ``` こんな感じで書けるからです。実際にはscheme名くらい見たほうが後々楽そうなので、 ```objectivec - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { if ([[url scheme] isEqualToString:@"myapp"]) { return [routes openURL:url]; } return NO; } ``` こんな感じの落とし所になるでしょうか。 ### controllerを呼び出す さて、URLのcontroller部は`NSURL`の`host`で受け取れることは前述のとおりですが、 そのcontroller文字列を元に対応するcontrollerクラスを呼び出さないとなりません。 ここのアプローチとしては ```objectivec NSString *controllerStr = [url host]; if (controllerStr isEqualToString:@"setting") { SettingController *controller = [[SettingController alloc] init]; // controllerに対して何か処理をする。 } ``` というのがパッと浮かびますが、コントローラーを追加するたびに分岐が増えてしまいますので、 controllerクラスのインタフェースを統一しつつ、文字列から直接`Class`を取得し`new`するアプローチを取ってみます。 まず、すべてのControllerクラスが実装しなくてはならないプロトコルを定義します。スーパークラスを用意して、すべてサブクラスにするやり方でもどっちでもいいです。 ```objectivec:ControllerProtocol.h @protocol ControllerProtocol <NSObject> - (BOOL)action:(NSString *)action query:(NSDictionary *)query; @end ``` `action:query`については後ほど説明します。 各Controllerはこのプロトコルを実装するようにします。 今回はAlertを表示するだけのAlertControllerを作ってみましょう。 ```objectivec:AlertController.h #import "ControllerProtocol.h" @interface AlertController : NSObject<ControllerProtocol> @end ``` ```objectivec:AlertController.m @implementation AlertController - (BOOL)action:(NSString *)action query:(NSDictionary *)query { //あとで return NO; } ``` 今は`action:query`を実装した、ということが大切です。 さて、これで`openURL`は以下のようにシンプルな状態に保つことがでみます。 ```objectivec:Routes.m - (BOOL)openURL:(NSURL *)url { NSString *controllerName = [url host]; // 先頭の一文字を大文字にする controllerName = [controllerName stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[controllerName substringToIndex:1] capitalizedString]]; // Classを文字列から取得 Class controllerClass = NSClassFromString([NSString stringWithFormat:@"%@Controller",controllerName]); // ClassがControllerProtocolを実装しているかチェック if ([controllerClass conformsToProtocol:@protocol(ControllerProtocol)]) { id<ControllerProtocol> controller = [[controllerClass alloc] init]; return [controller action:[url lastPathComponent] query:[self _dictionaryFromQueryString:[url query]]]; } return NO; } ``` `_dictionaryFromQueryString:`の部分はURLについてくる`title=titleString&message=messageString`などの`query`部分を後で利用しやすいように ```objectivec @{ @"title":@"titleString", @"message":@"messageString" } ``` といった`NSDictionary`に変換しているだけです。 他の処理内容はコメントで察してもらうとして、一箇所だけ。 ```objectivec // ClassがControllerProtocolを実装しているかチェック if ([controllerClass conformsToProtocol:@protocol(ControllerProtocol)]) { } ``` この部分は、呼びだそうとしているクラスが本当に`ControllerProtocol`を実装しているControllerクラスかどうかをチェックしています。 URLはアプリ外部から渡されるものなので、悪意のある文字列が渡される可能性があります。つまりセキュリティリスクになりうるので、このように外部から得たデータを使用する時は __特に注意が必要__ です。 セキュリティについては [Validating Input and Interprocess Communication][3] こちらのドキュメントも一読ください。 とりあえずこれで`myapp://alert/fuga`というURLを呼ぶと、AlertControllerが存在した場合、インスタンス化して`action:query`を呼び出します。actionには`@"fuga"`が入りますね。 Controllerを追加したい場合は`ControllerProtocol`を実装した`ControllerClass`をプロジェクトに追加すればいいだけです。 `appDelegate`を`if`祭りにしなくて済みます! ### controllerにactionを実装する。 さきほど、`AlertController`を作ったと思うので、その中に処理を書いてみます。 受け取った文字をUIAlertViewで表示するだけのシンプルなactionです。`show`アクションとしてみましょう。 このアプリのURLSchemeを`myapp`だとすると`AlertController`の`show`アクションを呼ぶためのURLは`myapp://alert/show`となり、`show`アクションではアラートのタイトルとメッセージも受け取りたいので最終的には ``` myapp://alert/show?title=titleStr&message=messageStr ``` この様になると良さそうです。(通常queryなどはURLエンコードが必要になるかと思います) 実際には以下のようにしました。 ```objectivec:AlertController.m - (BOOL)action:(NSString *)action query:(NSDictionary *)query { SEL sel = NSSelectorFromString([NSString stringWithFormat:@"%@Action:", action]); if ([self respondsToSelector:sel]) { BOOL (*actionImp)(id, SEL, NSDictionary *); actionImp = (BOOL (*)(id, SEL, NSDictionary *))[self methodForSelector:sel]; return actionImp(self, sel, query); } return NO; } - (BOOL)showAction:(NSDictionary *)query { if (query[@"title"] && query[@"message"]) { [[[UIAlertView alloc] initWithTitle:query[@"title"] message:query[@"message"] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]show]; return YES; } return NO; } ``` `action:query`内でactionを元に`SEL(@selector(showAction:))`を取得して、存在すればそれを実行しています。 `performSelector:withObject:`を使わずに関数を直に実行しているのは、戻り値としてBOOLが欲しいからです。 いまさらですが、`action:query`は各Controllerクラスで共通になりそうなので、ControllerProtocolではなくControllerBaseクラスを作ってそれを継承する方が良さそうでしたね。 そうすれば、各Controllerが`hogeAction:`を実装するだけで良くなり、もう少しシンプルになります。 話を戻しますが以上で`myapp://alert/show?title=title&message=message`が呼ばれた際に、`AlertContorller`の`showAction:`を呼ぶようにできました。 あとは同じようにControllerを増やしたり、Actionを増やしたりしていけばいいだけです。 __非常にシンプルですね!__ 以上、Custom URL Schemeの処理をシンプルに書く。でした! それではクリスマスに向けて、快適なURLScheme生活を送ってくださいね〜。 ## 注意点 - URLSchemeは他のアプリとかぶることが有ります。 - くれぐれもセキュリティには気をつけてください。 ##おまけ サンプルコードに以下のサンプルが入ってます。 カスタムURLSchemeを便利に使って行きましょう。 - ActionControllerのshowAction - アプリ内or外からURLSchemeでpresentViewControllerを出す - UIWebView内からURLSchemeでpresentViewControllerを出す 下の2つを補足すると、自分自身のURLSchemeを呼ぶこともできるので、 URLSchemeを利用してアプリ内で遷移させたり出来ます!ということです。 APIのレスポンスによって次に表示するページを切り替えたり、部分的にUIWebViewを利用したりするアプリで、 もしかしたら役に立つかもしれないですね!! ただし ```objectivec [[UIApplication sharedApplication] openURL:urlScheme]; ``` こんな感じで自分自身を呼ぶと実行までに時間がかかってしまうので(理由はドキュメント読むとわかる) ```objectivec [[Routes new] openURL:urlScheme]; ``` と、書いてしまうことをおすすめします。 UIWebView内からのリンクは仕方ないですが`<a href={urlscheme}>オリジナルのURLScheme</a>`と書くと`UIApplicationDelegate`の`application:openURL:sourceApplication:annotation`が呼ばれます。 --- ## サンプル [CustomURLSchemeSample][4] [1]: https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html [2]: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/AdvancedAppTricks/AdvancedAppTricks.html [3]: https://developer.apple.com/library/ios/documentation/Security/Conceptual/SecureCodingGuide/Articles/ValidatingInput.html [4]: https://github.com/naonya3/CustomURLSchemeSample/tree/master/CustomURLSchemeSample |
|
| 334位 |
|
|||
|
02:57:31 |
|
|
毎度コピペがめんどいのでまとめておく。
しょっちゅう自分で参照してるのでちょこちょこ編集する。 ## UITableViewController ### delegate storyboad上でもよい ```objc self.tableView.delegate = self; self.tableView.dataSource = self; ``` ### Cellまわり * Section数返し ```objc - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return 1; } ``` * Cell数返し ```objc - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return 1; } ``` * Cell返し ```objc - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString* CellIdentifier = @"Cell"; UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; /* //storyboadでひもづけしてないときに自己生成でリユースにひもづける UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if(!cell){ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; }*/ cell.clipsToBounds = YES;//frameサイズ外を描画しない return cell; } ``` * Cell高さ返し ```objc - (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 55; } ``` * Cell選択時 ```objc - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ [tableView deselectRowAtIndexPath:indexPath animated:YES]; // 選択状態の解除 UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath]; } ``` * Cellアクセサリボタン選択時 ```objc - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath{ UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath]; } ``` ### Headerまわり * UIView返し dequeueReusableCellWithIdentifierは使わないほうが良い。 segueで戻ってきた時リユースに失敗する。 xibからView返したほうが良い。 ```objc - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{ NSArray* views = [[NSBundle mainBundle] loadNibNamed:xibName owner:nil options:nil]; UIView* view = [views objectAtIndex:0]; return view; } ``` * header高さ返し ```objc - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ return 20; } ``` ## UITableViewCell ### きほん dequeueReusableCellWithIdentifierでセル生成時にindexPath.rowとtableviewの参照を渡しておくと後々色々しあわせ。 ### アクセサリのカスタマイズ accessoryViewにボタンをいれる。storyboadで紐付けてもよい。 アクセサリの押下を取る場合、accessoryViewに無理に紐付けなくても良い。 UIButtonにaddTargetを設定して押されたイベントを取得する。 押されたらTableViewControllerのaccessoryButtonTappedForRowWithIndexPath に通達。あたかもアクセサリが押されたようなイベント通達を偽装できる。 ```objc [button addTarget:self action:@selector(touchButton:event:) forControlEvents:UIControlEventTouchUpInside]; self.accessoryView = button;//なくてもよい ``` ```objc - (void)touchButton:(id)sender event:(id)event{ NSIndexPath * indexPath = [self.tableView indexPathForRowAtPoint: [[[event touchesForView: sender] anyObject] locationInView: self.tableView]]; if ( indexPath == nil ) { return; } [self.tableView.delegate tableView: self.tableView accessoryButtonTappedForRowWithIndexPath: indexPath]; } ``` ## 小ネタ ### 上下の余計なセル消し viewDidLoad時に高さ0のViewを当てれば良い。 大抵footerだけで事足りる。 ```objc UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; view.backgroundColor = [UIColor clearColor]; //[self.tableView setTableHeaderView:view]; [self.tableView setTableFooterView:view]; ``` ### reloadData と beginUpdates,endUpdates * 即時反映はreloadData、アニメーション絡む場合はbegenUpdates〜 #### reloadData 1. viewWillAppearに仕込む ```objc - (void)viewWillAppear:(BOOL)animated { [self.tableView reloadData]; [super viewWillAppear:animated]; } ``` #### beginUpdates,endUpdates 1. セルの削除、追加に使う ```objc [self.tableView beginUpdates]; NSIndexPath* path1 = [NSIndexPath indexPathForRow:0 inSection:0]; [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:path1] withRowAnimation:UITableViewRowAnimationTop]; [self.tableView endUpdates]; ``` ### ジェスチャー * pan等ジャスチャー時の反応にはreloadData、離してからはbeginUpdates〜 * cellForRowAtIndexPathはコストが高いのでなるべく使わない。 ### reloadData等更新したのに値が上手く反映されないとき ```objc -(void)reloadDataWithComplete:(void(^)(void))waitBlock { [self.tableView reloadData]; if(waitBlock){ waitBlock(); } } ``` ブロック構文入れたりすると解決したりする。 * 上記の現象の起因はスレッドによるところが大きい。 * 特定スレッドまで消化しないと値が反映されない。 * スレッド分岐された処理にてreloadDataすると、反映がすごい遅い場合がある。(フリーズ or レンダリングされないような現象) * BreakPointを置くと、どのスレッドで何やっているか分かるのでViewの更新はメインスレッドでやるようにするとよい。 ```objc dispatch_sync(dispatch_get_main_queue(), ^{ [self.tableView reloadData]; }); ``` ### TableViewの入れ子状態でのスクロール制御 * TableView(FrontTableView)の中のtableViewCellにTableView(DeepTableView)を入れた場合、FrontTableViewの操作でDeepTableViewが一切反応しなくなる。scrollEnabled=NOでも起こる。 * FrontTableViewのhitTestをオーバーライドしてDeepTableViewをhitTestして当たったらDeepTableViewを返すようにするとOKくさい。 * ScrollViewが入れ子だと何でも起こる。tableViewCellにUIWebViewを入れるなど。 ```objc - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *hitView = [super hitTest:point withEvent:event]; //対象のTableViewが入ったCellを取る UITableViewCell* cell = [self cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]]; //縦方向にcellのframe位置分ズレるので補正 CGPoint pos = CGPointMake(point.x, point.y - cell.frame.origin.y); UIView* view = [cell hitTest:pos withEvent:event]; if(view != nil) return cell.deepTableView; return hitView; } ``` ### 高速化のはなし * [なめらかに動作するUITableViewのつくりかた](http://labs.gree.jp/blog/2013/12/9669/) * [見積もりの高さでUITableViewを高速化する話](http://qiita.com/glayash/items/92863bedc734eaa8e7ed) * drawRectで文字列を描画:[[iOS]文字列を描画する](http://ameblo.jp/bitz/entry-11185128306.html) * UILabelなどの背景色の透明をなるべく使用しない ## まとめなど UITableViewControllerはやることほぼ全部管理できるせいでよく膨れ上がって後々管理しづらくなる。 値の出し入れ、segueの管理等… デザインの反映、アニメーション、ジェスチャ等入りだすと 子のTableView,TableViewCellのクラスを結局作ることになるので はじめから3種作って役割仕切ったほうが後々楽なパターンが多い。 あと、親子関係のデータ受け渡しがめんどくさいので、ちゃんとCoreDataつくる。 |
|
| 335位 |
|
|||
|
01:53:02 |
|
|
**追記:[Qiita「TwitterAPI Devise連携/グラフ可視化/データの効率的格納/API高速化」](http://qiita.com/items/ea2c5f7faec66347a10f) にて続きを書きました。**
qiitaにもある、「twitterでログイン」を実装します。 devise(railsのユーザー認証用gem)を使ってtwitter,facebookなどのOAuth認証。 今回はOAuth認証のみでdevise本来の追加認証はしません。tokenも使いません。 deviseの導入から解説していきます。 #環境 - ruby(1.9.3) - rails(3.2.3) - devise(2.0.4) - omniauth (1.1.0) - omniauth-oauth (1.0.1) - omniauth-oauth2 (1.0.2) - omniauth-facebook (1.3.0) - omniauth-twitter (0.0.9) #devise+omniauth認証 まずはdeviseの導入。https://github.com/plataformatec/devise を参考に。 ##1. Gem 以下をgemに追加 ```ruby:Gemfile gem 'devise' #omniauth gem 'omniauth-facebook' gem 'omniauth-twitter' ``` ##2. devise導入 ```console: $ bundle install $ rails g devise:install config/inistializer/devise.rb (設定ファイル)作成 $ rails g devise user 認証用モデル作成。今回はUserモデルにした。 $ rails g migration AddOmniToUser uid:integer migrationファイルにて、omniauth認証用カラム追加 ``` ##3. deviseのmigration設定 migrationファイルを編集します。Omniauthableのためのカラムを追加します。 今回はtrackable以外の機能は使用しないので、その他はコメントアウトしてください。使う場合はお好みで。 trackableはログイン回数などを記録する機能です。 **facebookはuidが非常に長いので、bigintにすることに注意してください!(3日はまりました)** ```ruby:db/migrate/*_devise_create_users.rb class DeviseCreateUsers < ActiveRecord::Migration def change create_table(:users) do |t| ## Trackable t.integer :sign_in_count, :default => 0 t.datetime :current_sign_in_at t.datetime :last_sign_in_at t.string :current_sign_in_ip t.string :last_sign_in_ip ##Omniauthable t.integer :uid, :limit => 8 #bigintにする t.string :name t.string :provider t.string :password t.timestamps end add_index :users, :uid, :unique => true ``` ```console: $ rake db:migrate ``` ##4. deviseのモデル編集 Userモデルを編集します。omniauthableにし、使わない機能をコメントアウトしましょう。 ```ruby:app/models/user.rb class User < ActiveRecord::Base devise :trackable, :omniauthable attr_accessible :name, :password, :uid, :provider end ``` ##5.developerサイトに登録 OAuth認証したいサイトにてアプリを作成し、IDとsecretを取得します。 twitter,facebookのdeveloperサイトでの作成法を解説。 サイトのURLを設定するので、development,production用に2つ作ると良いです。 (**ここで指定するURLが異なると動かない場合があります!**) http://developers.facebook.com/ 一番上のバーにある「アプリ」→右上「新しいアプリケーションを作成」 name,descriptionを記述。 herokuで作るかどうか選べますが、今回関係ないのでチェックしません。 アプリの個別ページに飛ぶので、 「アプリをFacebookに結合する方法を選択してください」でサイトのURLを記述します。 http://localhost:3000/ とでもしておきましょう。 App IDとApp Secretをメモっておきます。 https://dev.twitter.com/ ログイン後、右上のアイコンから「My Application」→「Create a New Application」で詳細を入力します。 name,descriptionを記述。websiteには実際に動かすURLのルートを。Callback URLにも同様にURLを記述。 Consumer key,Consumer secretをメモってください。 - 注意:http://localhost:3000/ だとエラー出ます。ドメインやIPを入れてください。(なお twitterの場合、facebookと異なり、ここのURL指定が適当でも動いたりします) - 注意:Callback URL(ログイン完了後のredirect URL)を記述しないと動きません。なぜかここで記述したURLにはredirectされないので、なんでもいいので記述を。 ##6. deviseのconfig編集 OAuth認証のために、deviseを設定します。 config/initializer/devise.rb に記述してもいいのですが、開発環境と本番環境を分けたいため、development.rbに記述します。 ```ruby:config/environments/development.rb . . . Devise.setup do |config|devise@example.com" config.omniauth :facebook, '{ID}', '{SECRET}', :scope => 'email,user_birthday', :display => 'popup' config.omniauth :twitter, '{ID}', '{SECRET}', :display => 'popup' end ``` facebookではメールと誕生日も取得できるようにしてありますが、不要な場合はscopeを消してください。 ##7. Userモデル再編集 各種関数を追加します。 ```ruby:app/models/user.rb . . . def self.new_with_session(params, session) super.tap do |user| if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"] user.email = data["email"] end end end def self.find_for_facebook_oauth(auth, signed_in_resource=nil) user = User.where(:provider => auth.provider, :uid => auth.uid).first unless user user = User.create(name:auth.extra.raw_info.name, provider:auth.provider, uid:auth.uid, # email:auth.info.email, #emailを取得したい場合は、migrationにemailを追加してください。 password:Devise.friendly_token[0,20] ) end user end def self.find_for_twitter_oauth(auth, signed_in_resource=nil) user = User.where(:provider => auth.provider, :uid => auth.uid).first unless user user = User.create(name:auth.info.nickname, provider:auth.provider, uid:auth.uid, # email:auth.extra.user_hash.email, #色々頑張りましたがtwitterではemail取得できません password:Devise.friendly_token[0,20] ) end user end ``` ##8. Controller設定 app/controllers/users/omniauth_callbacks_controller.rbを作成し、編集します。 ```ruby:app/controllers/users/omniauth_callbacks_controller.rb class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def facebook # You need to implement the method below in your model @user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user) if @user.persisted? flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook" sign_in_and_redirect @user, :event => :authentication else session["devise.facebook_data"] = request.env["omniauth.auth"] redirect_to new_user_registration_url end end def twitter # You need to implement the method below in your model @user = User.find_for_twitter_oauth(request.env["omniauth.auth"], current_user) if @user.persisted? flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Twitter" sign_in_and_redirect @user, :event => :authentication else session["devise.twitter_data"] = request.env["omniauth.auth"] redirect_to new_user_registration_url end end end ``` ##9. route定義 config/routes.rbを設定します。 ログイン後にはrootに飛ばされるので、root定義もすると良いです。 ```ruby:routes.rb root :to => 'yourapp#index' devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" } devise_scope :user do get 'sign_in', :to => 'devise/sessions#new', :as => :new_user_session get 'sign_out', :to => 'devise/sessions#destroy', :as => :destroy_user_session end . . . ``` ##10. 確認 viewを作り、サーバーをたちあげて確認。 ```app/views/yourapp/index.html.erb <ul class="nav"> <% if user_signed_in? %> <li><%= link_to 'ログアウト', destroy_user_session_path %></li> <% else %> <li><%= link_to "facebookでログイン", user_omniauth_authorize_path(:facebook) %></li> <li><%= link_to "twitterでログイン", user_omniauth_authorize_path(:twitter) %></li> <% end %> </ul> ``` Invalid URI error が出る場合は、twitterやfacebookのURL指定が間違っています。 facebookの場合は、設定変更の反映に時間がかかる場合があります。 どうしてもダメだったらfacebook上のアプリを作りなおしてください。 ##11. 最後に 長かったですがお疲れ様でした! 他のOAuth認証も、似たような形で追加してください。 |
|
| 336位 |
|
|||
|
10:44:50 |
(DTS corporation 所属) |
|
# 1. iOSのライセンス体系 iOSのライセンスには以下の3種類がある。  ※iDEPの取得方法については省略 https://developer.apple.com/jp/programs/ios/enterprise/ # 2. 配布方法の違い AdHocとIn-Houseの違いは以下の通り。 ## 評価用配布(AdHoc) - App Storeを介さずに、最大100台のデバイスまでアプリ配布可能。 - デバイス追加の都度、UDID登録、プロビジョニングファイル更新、リビルド等の作業が必要。 ## 組織内配布(In-House) - App Storeを介さずに、デバイス無制限でアプリ配布可能。 - AdHocで必要となる煩雑な作業(UDID管理等)が一切不要。 - **In-House版アプリが関係者以外にインストールされないよう厳重管理が必要。** - **In-House版アプリの利用者はiDEP契約主体法人の従業員に限る。** In-Houseは企業向けに強力な機能を備えている一方、配布にあたって厳格な管理が求められます。 故意か否かに関わらず、利用規約に反して契約主体法人以外にアプリ配布があった場合はiDEPライセンス取り消し等のペナルティが課されます。 **受託開発ではiDEP契約主体である自社以外に顧客企業へのアプリ配布が必要となる場合があるため、AdHocを採用します。** (または、顧客のAppleIDでIn-Houseで配布する形式になるかと思います) ### AdHocでの配布デバイス数制限について 上記のとおり、AdHocでは配布デバイス数に上限があります。 iDEPライセンスは企業につき1つのみとなるため、大量のデバイス登録が必要な場合はプロジェクト毎に別途Standardライセンスを取得の上でAdHocにてアプリ配布を行ってください。 # 3. 通常のAdHoc配布 通常、 AdHocによるアプリ配布は開発用端末にデバイスを1台ずつUSB接続して行います。 ただしこの方法は非常に手間がかかる上、開発ロケーションが離れている場合等に迅速なアプリ配布を行うことができないといった問題が伴います。  # 4. TestFlightを利用したAdHoc配布 AdHocによるアプリ配布をサポートするWebサービスとしてTestFlight( https://testflightapp.com )があります。 TestFlightを利用することで、Web経由で複数デバイスに対して一斉にAdHoc配布を行うことが可能になります。 加えてデバイスの一元管理・アクティビティ確認といった機能も備えているため、誰がどの端末でどのように配布アプリを使用しているかを容易に把握することも可能になります。  |
|
| 337位 |
|
|||
|
18:56:40 |
(Gunosy Inc 所属) |
|
Machine Learning Advent Calendarです。 普段はGunosyという会社で推薦システムを作ってます ## はじめに 推薦システムに関する最近の文献を読むと結構な割合で出てくるMatrix Factorizartion(MF)と呼ばれる手法があります。 ざっくり言うとこの手法は協調フィルタリングにおける次元削減を行うことでよりよい推薦を行おうという手法であり、 Netflix Prize(100万ドルの賞金が賭けられた推薦システムのコンテスト)で最も成果を上げたモデルの一つでもあります。 本記事ではこの手法を紹介していきます。 ## 協調フィルタリング まず協調フィルタリングについておさらいしましょう。 あるサービスで3人のユーザが5つのアイテムに対して5段階評価をしたとき、その評価値を以下のようにベクトルで表すことができます。 ```math \vec{user_{1}} = (4, 5, 0, 1, 0)^T \\ \vec{user_{2}} = (3, 0, 1, 5, 0)^T \\ \vec{user_{3}} = (4, 0, 0, 3, 5)^T \\ ``` 一つ目の要素が$ item_1$の評価であり、同じ次元の要素は同じアイテムにたいする評価を表します。 このとき$user_2$に近いのは$user_1$と$user_3$のどちらでしょうか? このような多次元空間のベクトル同士がどれだけ似ているかを測る指標は様々にありますが今回はコサイン類似度を用いることにしましょう。 コサイン類似度の定義は以下に示します。 ```math \cos( \vec{p}, \vec{q}) = \frac{\vec{p} \cdot \vec{q}}{|\vec{p}||\vec{q}|} \\ ``` pythonのscipyパッケージを用いてこれを計算しましょう ```python >>> from scipy.spatial.distance import cosine >>> import np >>> user1 = numpy.array([4,5,0,1,0]) >>> user2 = numpy.array([3,0,1,5,0]) >>> user3 = numpy.array([4,0,0,3,5]) >>> cosine(user1, user2) 0.55660554868629408 >>> cosine(user2, user3) 0.35457655095942753 ``` scipy.spatial.distance.cosineは距離を返す関数になっているので, 先ほど定義した値と違い$1- \cos$を返します。 なので値が小さいほど近くなるため, $user_1$より$user_3$のほうが$user_2$に似ていることになります。 なので$user_3$が評価していて$user_2$がまだ評価していない$item_5$を推薦すればいいのではないか、といった形になります。 ## なぜ次元削減をするのか? Matrix Factorizationは協調フィルタリングにおいて次元削減を実現する手法です。 ではなぜ次元削減が必要なのでしょうか? 先ほど示したユーザ3人、アイテム5つの例ではうまくいっているように見えます。 しかしこれはアイテムもユーザも少ないからです。実際のサービスを考えればアイテムは何万、何十万とあります。 それだけ次元が増えてしまうと機械学習では問題をうまく扱うのが困難になります。 これは次元の呪いと呼ばれる問題です。 そのためこのような高次元のデータを扱うために、高次元データの特徴をできるだけ保持したままデータを低次元データに変換するという対応がしばしば行われます。これが次元削減です。 ## Matrix Factorizationとは? $m$人のユーザと$n$個のアイテムを考えます。 先ほどの例では、ユーザは$n$次元のベクトルで表現されることになりますが、これを$m > k > 0$である$k$次元に次元削減して変換することを目指します。 これは評価値を表す$ m \times n $の行列$R$に対して これをユーザ要素を表す$k \times m$の行列$P$と$k \times n$の行列$Q$を考え以下のように近似します ```math R \approx P^TQ ``` 図で表すと以下のような形になります。  ここでユーザ$u$が評価したアイテム$i$の評価値は $\vec{p_{u}}^T\vec{q_{i}}$として表現することができます。 この各ユーザ、各アイテムに対する$\vec{p_u}$、$\vec{q_i}$を既知の評価値から学習するのがMatrix Factorizationです。 以下の式を満たすP, Qを訓練データから導きます ```math min_{P, Q} \sum_{(u, v) \in R} (r_{u,v} - \vec{p_u}^T\vec{q_i})^2 + \lambda ( ||\vec{p_u}||^{2}_{F} + ||\vec{q_i}||^{2}_{F} ) ``` 上の式を最適化するための更新式は以下のように求められることが知られています ```math e_{u, v} = r_{u, v} - \vec{p_u}^T \vec{q_i} \\ p`_{u,k} = p_{u, k} + 2 * \alpha e_{u, v} q_{k, v} \\ q`_{v,k} = q_{v, k} + 2 * \alpha e_{u, v} q_{u, k} \\ ``` なおこの近似式はNetflix Prize参加者のSimon Funkという人が提案したものなのですが、 普通にブログに投稿されてて、それが論文とかに引用されててなんかすごいです。 当時Netflix Prizeでは彼が公開したアルゴリズムを実装するところがまず入門編だったと言われています。 ## なぜMatrix Factorizationを用いるか? 次元削減をする方法としてはSVD(Singular Value Decomposition)と呼ばれる手法が広く知られており、情報検索などの分野で活用されています。 しかし推薦システムに適用した場合、通常の協調フィルタリングのモデルに比べて大きな改善はあまり起こらず、むしろ悪くなるケースもあることが報告されています。 これは次元削減の対象となる推薦システムの評価値行列の特性と関係があります。 推薦システムの評価値における0は一般的には「評価値が0」だったわけではなく「評価していない」ことを表す値です。 なのでその0という情報を保存しながら次元削減を行うと推薦の最適化にはあまり有効ではない形で変換されてしまうことになります そのため、値があるところのみで最適化していくMatrix Factorizationが推薦システムには有効であるとされています。 ## 実際にやってみた ```python import numpy def get_rating_error(r, p, q): return r - numpy.dot(p, q) def get_error(R, P, Q, beta): error = 0.0 for i in xrange(len(R)): for j in xrange(len(R[i])): if R[i][j] == 0: continue error += pow(get_rating_error(R[i][j], P[:,i], Q[:,j]), 2) error += beta/2.0 * (numpy.linalg.norm(P) + numpy.linalg.norm(Q)) return error def matrix_factorization(R, K, steps=5000, alpha=0.0002, beta=0.02, threshold=0.001): P = numpy.random.rand(K, len(R)) Q = numpy.random.rand(K, len(R[0])) for step in xrange(steps): for i in xrange(len(R)): for j in xrange(len(R[i])): if R[i][j] == 0: continue err = get_rating_error(R[i][j], P[:, i], Q[:, j]) for k in xrange(K): P[k][i] += alpha * (2 * err * Q[k][j]) Q[k][j] += alpha * (2 * err * P[k][i]) error = get_error(R, P, Q, beta) if error < threshold: break return P, Q R = numpy.array([ [5, 3, 0, 1], [4, 0, 0, 1], [1, 1, 0, 5], [1, 0, 0, 4], [0, 1, 5, 4], ] ) nP, nQ = matrix_factorization(R, 2) nR = numpy.dot(nP.T, nQ) ``` ```python >>> nR [[ 5.01206098 2.96974291 3.86684235 0.99604183] [ 3.9862704 2.37235361 3.27696605 0.99620086] [ 1.05492795 0.86913835 5.53828733 4.99229311] [ 0.97312539 0.77030044 4.5002216 3.98906219] [ 1.63962431 1.16127693 4.93822412 4.04436897]] ``` すでに値が入っているところは近似された値に、 0だったところには予測値が入っていますね! ## 蛇足 日本語でMarix Factorizationちゃんと説明したウェブのってあんまりないなーと思って自分で調べるついでに書いてみました。 でもKDD Cup07ぐらいのときはまだRegular-SVDとか呼ばれてていつの間にMatrix Factorizationなんて名前ついたんでしょう。結構謎。 SVDが効かなかったみたいなくだりはなるほどなーという感じでした。 ただSVDだったり、Random-Walkだったり次元削減だったりスパース対策だったりいろいろ手法はあるけど、比較してる文献ってあんまりなさそうな感じでした。知ってたらだれか教えてください 最近はNMF(non-Negative Matrix Factorization)とかが主流なんでその辺もおいおい調べて紹介したいです ## 参考 * Y.Koren, R.M. Bell and C.Volinsky, Matrix Faxctrization Techniques for Recommender Systems, Computer, 42(8):30-37, 2009. * Badru M. Sarwar, George Karpis, Joseph A. Konstan and John T. Riedl, Application of Dimensionality Redution in Recommender System — A Case Study, IN ACM Web KDD Workshop, 2000 * A. Paterek, Improving Regularized Singular Value Decomposition for Collaborative Filtering, Proc. KDD Cup and Workshop, 2007 * http://www.quuxlabs.com/blog/2010/09/matrix-factorization-a-simple-tutorial-and-implementation-in-python/ * http://stepped.dodgson.org/?date=20080814 |
|
| 338位 |
|
|||
|
23:40:38 |
(Increments Inc. 所属) |
|
この記事は[Ruby Advent Calendar 2013](http://qiita.com/advent-calendar/2013/ruby)の6日目の記事です。
昨日はShindo200さんの[Ruby で paiza.jp のオンラインハッカソン問題に挑戦するときに少し役に立ちそうなこと](http://qiita.com/Shindo200/items/a63df49f963f9e3cc441)でした。 ## 概要 Rubyのデファクトスタンダードなテストフレームワークと言えるRSpecですが、現在バージョン3.0のリリースへ向けて開発が進められており、先日2013年11月8日には3.0.0.beta1がリリースされました。 この記事ではRSpec 3における変更点と、RSpec 3へのアップグレード手順、また既存のspecを最新の記法に変換するツールを紹介します。 ### 追記 RSpec 3は2014年6月2日に正式リリースされました。この記事は2013年12月6日に書かれたものですが、正式版においても通用する内容になっています。 正式版における主要な変更点は、以下のページが参考になるでしょう。 * [Myron Marston » Notable Changes in RSpec 3](http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3) * [yujinakayama/transpec - Supported Conversions](https://github.com/yujinakayama/transpec#supported-conversions) ## RSpec 3での変更 RSpec 3は、2010年10月の2.0リリース以来3年ぶりのメジャーバージョンアップとなるため、多くのdeprecatedな機能が削除されます。 ### The Plan for RSpec 3 RSpec 3がどのようなものになるかは、RSpecプロジェクトのリードメンテナMyron Marston氏が今年2013年7月に記事を書いています。 * [Myron Marston » The Plan for RSpec 3](http://myronmars.to/n/dev-blog/2013/07/the-plan-for-rspec-3) * [RSpec 3に向けての計画(日本語訳) - 有頂天Ruby](http://nilp.hatenablog.com/entry/2013/07/16/131641) その後、基本的にはこの計画通りに開発は進んでおり、3.0.0.beta1ではこのうちの一部が実装されています。具体的には、 _What’s Being Removed_で挙げられている項目は全て実装(というか削除)が完了しています。また _What’s New_の項目に関しては以下の3つが実装されています。 * [Mocks: Test double interface verification](http://myronmars.to/n/dev-blog/2013/07/the-plan-for-rspec-3#mocks_test_double_interface_verification)(Mocks: テストダブルのインターフェースの検証) * [Core: DSL methods will yield the example](http://myronmars.to/n/dev-blog/2013/07/the-plan-for-rspec-3#core_dsl_methods_will_yield_the_example)(Core: DSLメソッドがexampleをyieldするようになる) * [Mocks: any_instance block implementations will yield the receiver](http://myronmars.to/n/dev-blog/2013/07/the-plan-for-rspec-3#mocks__block_implementations_will_yield_the_receiver)(Mocks: `any_instance`がブロック実装にreceiverをyieldするようになる) ### その後の変更 The Plan for RSpec 3はあくまでも7月時点での計画だったため、その後追加された変更があります。 #### `be_true`/`be_false`マッチャのリネーム `be_true`/`be_false`マッチャが、`be_truthy`/`be_falsey`(または`be_falsy`)にリネームされ、3.0では`be_true`/`be_false`は利用できなくなります。この変更は既に3.0.0.beta1に取り込まれています。 この理由ですが、 * `be_true`は、その名前にもかかわらず、テスト対象が真(`false`, `nil`以外)であればパスしており、`true`との同一性はテストされていなかった。 * `be_false`も同様に、テスト対象が偽(`false`, `nil`)であればパスしており、`false`との同一性はテストされていなかった。 といったように、名前と挙動が一致していませんでした。 今回の変更は、これまでの挙動に名前を合わせた形になります。実際のところ`be_true`/`be_false`はpredicate method(`?`で終わるメソッド)のテストに使われることが多く、それらは直接`if`文などの条件部に置かれることが多いため、この挙動が問題になるケースはあまりなかったように思います。 既存のspecをどうするべきかですが、この挙動を以前から知っていて敢えて使っていた、もしくは知らなかったけど特にそこまでの厳格さを求めない、ということであれば、`be_truthy`/`be_falsey`に書き換えれば良いでしょう。 一方、そんな挙動だったなんて知らなかった、これを機に厳格にテストしたい、という場合は`be true`/`be false`を利用することが推奨されています。これらは新しい記法ではなく、従来からある`be`マッチャと`true`/`false`リテラルの組み合わせです。`be`は`equal`マッチャのaliasであり、オブジェクトの同一性がテストされます。 #### `rspec-mocks`への一部のexpect記法の追加 should記法の、ハッシュを引数に取った`stub`と、`stub_chain`に対応するexpect記法が追加されます。 ```ruby # should記法 obj.stub(:foo => 1, :bar => 2) obj.stub_chain(:foo, :bar, :baz) # expect記法 allow(obj).to receive_messages(:foo => 1, :bar => 2) # 3.0.0.beta1から利用可能 allow(obj).to receive_message_chain(:foo, :bar, :baz) #3.0.0.beta2から利用可能 ``` 2012年7月に[RSpec 2.11で導入されたexpect記法](http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax)ですが、この時点では`rspec-expectations`のみへの導入でした。その1年後の2013年7月にRSpec 2.14で`rspec-mocks`にも[expect記法が導入](http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/)され、`expect(obj).to receive(:message)`や`allow(obj).to receive(:message)`といった記述が可能になりました。この記法は2.11での導入時ほど話題にならなかったため、知らない方も割といるのではないでしょうか。 しかしRSpec 2.14時点での`rspec-mocks`のexpect記法は、should記法すべてに対して代替手段が用意されている訳ではありませんでした。これは単なる実装漏れではなく、いわゆるcode smellがするため一旦導入を見送っていたなどの理由があったのですが、最終的には前述の2つについては代替記法が導入されることになります。ちなみに`unstub`は未だexpect記法は存在せず、RSpecコアチームの見解を見る限り、おそらく今後も導入されることは無いものと思われます。 #### ワンライナー`should`のexpect記法 ワンライナー`should`のexpect記法として`is_expected.to`が追加されます。 ```ruby it { should be_empty } it { is_expected.to be_empty } # 2.99.0.beta2から利用可能 ``` expect記法が導入された経緯として、既存のshould記法は`should`/`should_receive`/`stub`などのメソッドを`BasicObject`クラスにモンキーパッチで追加するため、delegate/proxyなオブジェクトで正常にテストができないという問題がありました。しかしワンライナー`should`は`BasicObject`クラスにモンキーパッチされたものではなく、その実体は`RSpec::Core::ExampleGroup#should`であり、前述の問題は発生しません。そのためワンライナー`should`はこれまでexpect記法が用意されておらず、`RSpec.configure`で[should記法が無効化](https://www.relishapp.com/rspec/rspec-expectations/v/3-0/docs/syntax-configuration#disable-should-syntax)されていても利用することができました。 しかしコードの見た目上expect記法と混在させた時に一貫性がなかったり、「ワンライナー`should`のexpect記法は?」といったユーザからの声が多かったため、`is_expected.to`が代替記法として追加されます。 ちなみにモンキーパッチ`should`はRSpec 3.0からdeprecated扱いになりますが(ただし[明示的にshould記法を利用する宣言](https://www.relishapp.com/rspec/rspec-expectations/v/3-0/docs/syntax-configuration#disable-expect-syntax)をすればdeprecation warningは表示されない)、ワンライナー`should`は3.0でも現役なため、`is_expected.to`が冗長だと感じる場合はワンライナー`should`を使い続けても問題ありません。 ## RSpec 3へのアップグレード ここでは既存のプロジェクトをRSpec 3にアップグレードする際の手順を解説します。 ベータ版ではありますが、今のところ大きなバグもなく、deprecatedな機能が一掃されたバージョンなので、今のうちにアップグレードしておくと正式リリースの際にスムーズな移行ができます。アグレッシブな方はこの機会にアップグレードしてしまいましょう。 ### RSpec 2.99 RSpec 2.99は、バージョン2系との後方互換性を保ちつつ、3.0で非互換になる全ての機能に対してwarningを表示する、アップグレードの通過点となるバージョンです。 既存のspecをまずバージョン2.99で実行することで、そのspecを3.0に対応させるにあたって必要な変更を知ることができます。3.0のchangelog全ての項目に目を通して、どれが自分のプロジェクトに影響を与えるかをいちいち調べる必要はありません。 ### Transpec しかし何を変更すれば良いかはわかっても、その書き換え作業は自分で行う必要があります。正直言ってこれはかなり面倒くさいし、世界中のRSpecユーザがそれぞれ正規表現のワンライナーなどでちまちま置換作業をするのは非合理的です。 という訳で、既存のspecを最新の記法に書き換えるツール、[Transpec](http://yujinakayama.me/transpec/)を作りました。Transpecを既存のプロジェクトに対して実行すると、静的解析と動的解析によってspecファイルを最新の記法に書き換えます。現時点で、RSpec 2.0から3.0にかけてdeprecatedになった大半の記法の変換をサポートしています。詳細は[README - Supported Conversions](https://github.com/yujinakayama/transpec#supported-conversions)を参照して下さい。 プロジェクトによってはTranspecによる変換だけでRSpec 3対応が完了する場合もあるでしょうし、追加で手作業が必要な場合もごくわずかな書き換えで済むかと思います。また、Transpecは[RSpecプロジェクトそのものでも利用されて](https://github.com/rspec/rspec-expectations/commit/f2121650e10464b8cb004e66d03bc77bc7746c6b)います。 ### 手順 RSpec公式のアップグレードガイドがあるので、この手順に従います。 * https://relishapp.com/rspec/docs/upgrade (rspec、rspec-rails共通) * https://www.relishapp.com/rspec/rspec-rails/docs/upgrade (rspec-rails固有の特記事項) 1. 現状のままspecを実行し、all greenなことを確認します。 2. `Gemfile`で`rspec`のバージョンに`~> 2.99`を指定し、`bundle update rspec`を実行します。 3. 再度specを実行し、all greenなことを確認します。RSpecは[Semantic Versioning](http://semver.org/)準拠で開発されているため、RSpec 2.0時代のspecであっても2.99で正常に動作するはずです。動作しないのであれば、それはRSpecのバグです。必要であればこの時点でコミットをします。 4. spec実行後、deprecation warningが表示されるので内容を確認します。これらが3.0対応にあたって変更が必要な点になります。 5. `gem install transpec`でTranspecをインストールします。これは日常的に使うツールではないので、通常はプロジェクトの`Gemfile`に追加する必要はありません。 6. プロジェクトのルートディレクトリで`transpec`を実行します。Transpecはデフォルトで可能な限り最新の推奨された記法に変換しますが、必要であればTranspecの[README](https://github.com/yujinakayama/transpec)を参照し、コマンドラインオプションで変換の挙動をカスタマイズして下さい。例えば`have(n).items`や`its`はRSpec 3.0で削除されますが、外部gem化された[rspec-collection_matchers](https://github.com/rspec/rspec-collection_matchers)や[rspec-its](https://github.com/rspec/rspec-its)を利用することで3.0以降も使い続けることができるため、もしその方針であればこれらの変換を`--keep have_items,its`オプションで無効化できます。 7. Transpecによる変換が完了したら、一旦コミットします(Transpecは[自動的にコミットメッセージを生成する](https://github.com/yujinakayama/transpec#basic-usage)のでそれを利用すると良いでしょう)。 8. 再度specを実行します。まだdeprecation warningが表示されるようであれば、手作業で対処します。all greenかつdeprecation warningがなくなったらコミットします。 9. `Gemfile`で、`rspec`のバージョンに`~> 3.0`を指定し、`bundle update rspec`します。 10. RSpec 3.0でspecを実行します。all greenなはずですが、もし失敗するのであればそれはRSpecのバグです。問題なければコミットします。 11. RSpec 3.0から新たにdeprecated扱いになる機能もあり、場合によってはwarningが表示されるかもしれません。また、2.99の時点では代替となる記法が存在せず、3.0になってからでないと変換ができないものもあります(前述の`receive_messages`など)。その場合は再度`transpec`を実行するとそれらを変換できます。 12. 再度specを実行します。問題なければコミットします。 13. 完了! ## おわりに 本記事で解説したRSpec 3での変更は確定した訳ではなく、今後も新たな変更がある可能性があります。ちなみにGitHubのRSpecのマイルストーンを見る限り、3.0.0.beta2は2013年内のリリースを目指している模様です。 |
|
| 339位 |
|
|||
|
10:29:53 |
|
|
コピペ
http://za.toypark.in/html/2007/06-29.html 以下引用 *** nohupは良く使っていた。最近になってdisownを知った。違いは、プロセスの起動前に使うのがnohup、起動後に使うのがdisown。これだけだと判りにくいので、例を考えてみる。プログラムを作って、起動させる。 ```sh $ hoge ``` 終わるまでに1分待ったのに終わらない、でもログアウトして帰宅したい。そんな時は、C-zで一時停止して、psとかjobsで一時停止したプロセスのPIDかジョブ番号を調べる。これを引数にしてdisownを走らせる。 ```sh $ jobs $ disown %1 ``` これで心おきなく帰宅できる。ログアウトしてもプロセスは死なず、プログラムhogeが完走するまでプロセスは生きつづける。 hogeが時間のかかるプログラムだということが判ったので、次に計算させるときはあらかじめログアウト後もプロセスを生かしておきたい。そんなばあいはnohupをつけてhogeを起動。 ```sh $ nohup hoge ``` >GNU Free Documentation License |
|
| 340位 |
|
|||
|
02:16:02 |
|
|
JavaScriptの勉強を兼ねて、Webサービスの作り方まとめです。
簡単なTodoリストを作ります。 # AngularJS+NodeJS(ExpressJS)+MongoDB Webサービスを作るには色々と手段はありますが、今回選んだのは上記の組み合わせ。 サーバからクライアントまで全部JavaScriptで書けるためです。 MongoDBのサイトでは、[MEANスタック](http://jp.blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and)などと呼ばれている模様です。 以下、meanstack-sampleと名付けて、動くようになるまでにやったことを順番に書いていきます。 1. [Yomanの設定](#yeoman) 2. [NodeJSでのREST](#rest) 3. [MongoDBのインストールとNodeJSとの接続](#mongo) 4. [AngularJSとRESTの連携](#angular) 5. [NodeJSでのOAuth認証](#oauth) 6. [まとめと参考](#matome) ## <a name="yeoman"></a>Yeomanの設定 [別エントリ参照](http://torub.github.io/blog/yeoman.html) ## <a name="rest"></a>NodeJS(+ExpressJS)でRESTもどきを作る まずはサーバ側が無いと動かないので、 Node.jsでRESTっぽく値を返してくれるものを準備します。 Node.jsのWebアプリケーションフレームワークであるExpressをインストールします。 詳しいガイドは[こちら](http://hideyukisaito.github.io/expressjs-doc_ja/guide/)。 ``` % npm install express ``` Node.js/Expressを使って以下をweb.jsとして作成します。 ```js var express = require('express'), http = require('http'), path = require('path'), db = require('./db.js'), application_root = __dirname; var app = express(); app.configure(function() { app.set('port', process.env.PORT || 3000); app.set('view engine', 'ejs'); // AngularJSのディレクトリを静的ファイルとして追加 app.use(express.static(path.join(application_root, "app"))); app.use(express.cookieParser()); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); }); app.get('/todo', db.read); // GETの処理 app.post('/todo', db.create); // POSTの処理 app.delete('/todo/:id', db.delete); // DELETEの処理 app.put('/todo/:id', db.update); // PUTの処理 // サーバ起動 http.createServer(app).listen(app.get('port'), function(){ console.log('Express server listening on port ' + app.get('port')); }); ``` URLを定めて、HTTPのGET、POST、DELETE、PUTを使うことで [CRUD](http://ja.wikipedia.org/wiki/CRUD)を実現します。 AngularJS側からHTTPメソッドを使うことで、簡単にデータ操作ができるようになります。 ## <a name="mongo"></a>MongoDBのインストールとNodeJSとの接続 サーバ側の仕組みが作れたので、次はDBと接続します。 同じJavaScriptで、ということでMongoDBを選んでみました。 MongoDBは[Wikipedia](http://ja.wikipedia.org/wiki/MongoDB)によると、 >オープンソースのドキュメント指向データベースである。C++言語で記述されており、開発とサポートはMongoDB Inc.によって行なわれている。MongoDBはRDBMSではなく、いわゆるNoSQLと呼ばれるデータベースに分類されるものである。 というもので、SELECT文等のSQLを使わず、JavaScriptのメソッドでデータ操作をします。 インストール等の準備は、[ここ](http://codezine.jp/article/detail/6982)を見ればできます。 Node.jsからMongoDBに接続するためには、色々なライブラリが存在しています。 ぐぐってみると、mongooseというものが有名なようですが、 今回はより単純な[mongodb](http://mongodb.github.com/node-mongodb-native/)というライブラリを使ってみます。 これは、ターミナルからMongoDBを使う場合とほぼ同じ感じで使えるみたいです。 ``` % npm install mongodb ``` でインストールして、web.js内で利用しているdb.jsで接続部分を作ります。 ```js var mongo = require('mongodb'); var Db = mongo.Db, BSON = mongo.BSONPure; var mongoUri = 'mongodb://localhost/contentdb'; // CRUDのC exports.create = function(req, res) { var content = JSON.parse(req.body.mydata); Db.connect(mongoUri, function(err, db) { db.collection('todolist', function(err, collection) { collection.insert(content, {safe: true}, function(err, result) { res.send(); db.close(); }); }); }); }; // CRUDのR exports.read = function(req, res) { Db.connect(mongoUri, function(err, db) { db.collection('todolist', function(err, collection) { collection.find({}).toArray(function(err, items) { res.send(items); db.close(); }); }); }); }; // CRUDのU exports.update = function(req, res) { var content = JSON.parse(req.body.mydata); var updatedata = {}; updatedata.data = content.data; updatedata.checked = content.checked; var id = req.params.id; Db.connect(mongoUri, function(err, db) { db.collection('todolist', function(err, collection) { collection.update({'_id':new BSON.ObjectID(id)}, updatedata, {upsert:true}, function(err, result) { res.send(); db.close(); }); }); }); }; // CRUDのD exports.delete = function(req, res) { var id = req.params.id; console.log(id); Db.connect(mongoUri, function(err, db) { // add Db.connect db.collection('todolist', function(err, collection) { collection.remove({'_id':new BSON.ObjectID(id)}, {safe:true}, function(err, result) { res.send(); db.close(); }); }); }); }; ``` 基本はDBに接続、コレクションを選択、コレクションを操作、DBクローズの流れで処理をします。 毎回DBに接続せずにコネクションを保持するやりかたもあると思いますが、 Heroku上で動かそうとしたら上手く行かず、この形式にしました。 ## <a name="angular"></a>AngularJSとRESTの連携 ここまでくればサーバ側は完成しているので、後はクライアント側をAngularで作っていきます。 yeomanを使ってyo angular後の状態として編集をしていきます。 app/views/main.htmlと、app/scripts/controllers/main.jsを修正します。 画面は簡単なTODOリストです。(何故かHTML内の中括弧が表示できないので、全角にしています。) ```html <div class="hero-unit"> <input type="text" ng-model="newtodo"><input type="button" value="add todo" ng-click="createTodo()"> <ul> <li ng-repeat="todo in todolist"> <input type="checkbox" ng-change="updateTodo(todo)" ng-model="todo.checked"> <span class="done-{{todo.checked}}">{{todo.data}}</span> <input type="button" value="×" ng-click="deleteTodo(todo)"> </li> </ul> </div> ``` ```js 'use strict'; angular.module('angularApp').controller('MainCtrl', function ($scope, $http) {//, $templateCache) { var url = '/todo'; $scope.todolist = []; $scope.getTodo = function() { $http.get(url).success(function(data) { $scope.todolist = data; }); }; $scope.createTodo = function() { var todo = {}; todo.checked = false; todo.data = this.newtodo; var senddata = 'mydata=' + JSON.stringify(todo); $http({ method: 'POST', url: url, data: senddata, headers: {'Content-Type': 'application/x-www-form-urlencoded'}, }).success(function(response) { $scope.getTodo(); }).error(function(response) { }); this.newtodo = ""; }; $scope.updateTodo = function(todo) { var senddata = 'mydata=' + JSON.stringify(todo); $http({ method: 'PUT', url: url + '/' + todo._id, data: senddata, headers: {'Content-Type': 'application/x-www-form-urlencoded'}, }).success(function(response) { $scope.getTodo(); }).error(function(response) { }); }; $scope.deleteTodo = function(todo) { var senddata = 'mydata=' + JSON.stringify(todo); console.log(senddata); $http({ method: 'DELETE', url: url + '/' + todo._id, data: senddata, headers: {'Content-Type': 'application/x-www-form-urlencoded'}, }).success(function(response) { $scope.getTodo(); }).error(function(response) { }); }; $scope.getTodo(); }); ``` ここまででひととおり動くようになったので、ここまでのソースを[github](https://github.com/torub/meanstack-sample/tree/708c5d94bdf40a784c41f7948c9f9a834c97b4c8)に置きました。 動かすと、以下のような画面になります。  ## <a name="oauth"></a>NodeJSでのOAuth認証 ここまで作成したものに認証機能を付けていきます。 修正箇所は主に以下の3点です。 - Node.jsのPassportライブラリを利用して、認証機能追加(web.js修正) - データベースの項目にuidを追加(db.js修正) - クライアント側のapp.jsを修正、login.html追加 ### web.js修正 Node.jsのPassportというライブラリを使うと、簡単に様々な認証機能を持たせることができます。今回は[Passportの公式サイト](http://passportjs.org/guide/twitter/)を参考に、Twitter認証を追加します。 新たにauthという関数を用意して、認証が通っているかを確認した上で、認証の上でDB操作を行う用に変更します。 なお、consumerKeyや、consumerSecretは、Twitterの[Developerのページ](https://dev.twitter.com/apps)から取得できます。 ```js /* passportの設定 */ var passport = require('passport'), TwitterStrategy = require('passport-twitter').Strategy; // Passport: TwitterのOAuth設定 passport.use(new TwitterStrategy({ consumerKey: "your key", consumerSecret: "your secret", callbackURL: "/auth/twitter/callback" }, function(token, tokenSecret, profile, done) { // ユーザIDを設定 profile.uid = profile.provider + profile.id; process.nextTick(function() { return done(null, profile); }); })); // Serialized and deserialized methods when got from session passport.serializeUser(function(user, done) { done(null, user); }); passport.deserializeUser(function(user, done) { done(null, user); }); // Define a middleware function to be used for every secured routes // 認証が通っていないところは401を返す var auth = function(req, res, next){ if (!req.isAuthenticated()) { res.send(401); } else { next(); } }; var app = express(); app.configure(function() { ... // OAuth認証用 app.use(express.session({ secret: 'hogehoge', cookie: {maxAge: 1000 * 60 * 60 * 24 * 30} })); app.use(passport.initialize()); // Add passport initialization app.use(passport.session()); // Add passport initialization app.use(app.router); }); // route to log in app.get("/auth/twitter", passport.authenticate('twitter')); app.get("/auth/twitter/callback", passport.authenticate('twitter', { successRedirect: '/', falureRedirect: '/login' })); app.get('/todo', auth, db.read); // GETの処理。要認証(auth関数の後にこれまでの処理実施) ... ``` ### db.jsの修正 ユーザ毎にデータを持つため、MongoDBのCRUD操作の際にuidを追加します。 ```js ... // CRUDのC // セッション内のUIDに紐付けてデータを作成する exports.create = function(req, res) { var uid = req.user.uid; var content = JSON.parse(req.body.mydata); content.uid = uid; Db.connect(mongoUri, function(err, db) { db.collection('todolist', function(err, collection) { collection.insert(content, {safe: true}, function(err, result) { res.send(); db.close(); }); }); }); }; ... ``` ### app.jsの修正(とlogin.html追加) web.jsのauth関数と連動して、認証されていない際に帰ってくる401エラーを検知する機能を実現します。 HTTPレスポンスを確認して、401エラーが含まれていればログインページに飛ばす処理を作ります。 [AngularJS公式の$httpページ](http://code.angularjs.org/1.0.8/docs/api/ng.$http)を参考に作りました。 が、使う前に「$q and deferred/promise APIs」の理解が必要と記載があり、そこが理解できていないため、 過不足があるかもしれません。 ```js ... // []で囲わないと、gruntでminifyした際にエラーとなる $httpProvider.responseInterceptors.push(['$q', '$location',function($q, $location) { return function(promise) { return promise.then(function(response) { // Success: 成功時はそのまま返す return response; }, function(response) { // Error: エラー時は401エラーならば/loginに遷移 if (response.status === 401) { $location.url('/login'); } return $q.reject(response); } ); }; }]); ... ``` ```html <div class="container"> <div class="page-header"> login </div> <div> <p><button class="btn-auth btn-twitter" onclick="location.href='/auth/twitter'"> Sign in with <b>Twitter</b></button></p> </div> </div> ``` ## <a name="matome"></a>まとめと参考 認証を追加したところまでのソースコードは[github](https://github.com/torub/meanstack-sample/tree/1c47d5b917da08590030757a547bd14b8f0ad351)に置いてあります。見た目もしょぼく、セキュリティ面でも怪しいところはありますが、一通り動作するはずです。 あとは最初から入れてあるBootStrapを使って見た目を直すとか、Herokuにアップしてみるとか、 Facebookでの認証も付けてみるとか、色々応用ができると思います。 ### 参考 - [DailyJSのAngularJS回](http://dailyjs.com/2013/04/11/angularjs-1/) - [日々之スクラッチ: Node.js + express + MongoDB on Heroku でアプリケーションを作成する](http://sctfarch.blogspot.jp/2012/03/nodejs-express-mongodb-on-heroku.html) - [チュートリアル: node.jsとHerokuでAngularJSアプリを作る Part I ](http://techblog.appirio.co.jp/2013/02/nodejsherokuangularjs-part-i.html) - [【Node.js】PassportでFacebookとTwitterのOAuth認証する方法 | creator note](http://creator.cotapon.org/articles/node-js/node_js-oauth-passport-facebook-twitter) - [SQL脳に優しいMongoDBクエリー入門 - taka512's blog](http://taka512.hatenablog.com/entry/20110220/1298195574) |
|
| 341位 |
|
|||
|
10:52:03 |
(Increments inc. 所属) |
|
Nodeの組み込みモジュール・サードパーティモジュール問わず広く使われるのが `EventEmitter` オブジェクト。
これはNodeで使われるデザインパターンの筆頭みたいなものなので、知っておかねばならない。 ドキュメント: [Events](http://nodejs.org/api/events.html) ブラウザ上のJavaScriptで `addEventListener` を使ってイベントドリブンの開発を行うが、Node上でそれを行うのための機能を提供するのが `EventEmitter`。 例えば次のように使う ```js var EventEmitter = require('events').EventEmitter; function asyncFunc() { var ev = new EventEmitter; console.log('in asyncFunc'); setTimeout(function () { ev.emit('done', 'foo', 'bar'); }, 1000); return ev; } var async = asyncFunc(); async.on('done', function(arg1, arg2) { console.log(arg1, arg2); }) ``` これを実行すると `in asyncFunc` と表示された1秒後に `foo bar` と表示される。 Nodeを使うと非同期処理が多くなるが、コールバックを受け取る設計では、複数の関数を実行したい時面倒くさい。 そこでコールバックを受け取る代わりに `EventEmitter` を返すようにする。一回だけしか実行されないなら `once` を使うといい。 ```js var ev = new EventEmitter; ev.on('data', function(data) {console.log('on', data);}); ev.once('data', function(data) {console.log('once', data);}); ev.emit('data', 1); // => on 1 // => once 1 ev.emit('data', 2); // => on 2 // once は実行されない ``` 組み込みモジュールで `EventEmitter` を使っているものは非常に多い。非同期処理がからむモジュールは基本的にみな使っていると言って過言でない。 サードパーティだと例えば [Socket.IO](http://socket.io) が同様のインタフェースを用いている。 |
|
| 342位 |
|
|||
|
02:42:11 |
|
|
[Unity Gems](http://unitygems.com)というウェブサイトがあって、初心者のためにより有用な情報を集積しようという目的で運営されている。 Unity Answersの常連が集まって、過去の投稿の中から選別されているだけあって、本当に優良なサイトで、私自身の技術翻訳ブログでもしばらくの間、トピックとして取り扱ってきた。 ちょうどシリーズが一つ終わったところなので考察を少ししてみたい: --------------- #Unity初心者がハマる11の「罠」~の考察 ##学習ステップ 目次: [Unity初心者がハマる11の「罠」~目次](http://gamesonytablet.blogspot.jp/2012/11/unity11_2.html) 1. [別スクリプトの変数やメソッドへの参照](http://gamesonytablet.blogspot.com/2012/11/unity.html) 2. [OnTriggerやOnCollisionの判定条件](http://gamesonytablet.blogspot.com/2012/11/unityontriggeroncollision.html) 3. [Inputの正しい用法](http://gamesonytablet.blogspot.com/2012/12/unityinput.html) 4. [未来事象の正しい文法](http://gamesonytablet.blogspot.com/2012/12/unity.html) 5. [ListやDictionariesで増減するオブジェクトコレクションを管理](http://gamesonytablet.blogspot.com/2012/12/unity_8031.html) 6. [Rigidbodyの正しい移動方法](http://gamesonytablet.blogspot.jp/2012/12/unityrigidbody.html) 7. [Character Controllerの正しい使い方](http://gamesonytablet.blogspot.com/2012/12/unitycharacter-controller.html) 8. [クォータニオンの修正方法](http://gamesonytablet.blogspot.com/2012/12/unity_7.html) 9. [C#とJavascriptを正しく連動させる](http://gamesonytablet.blogspot.com/2012/12/unitycjavascript.html) ##Unity初心者がハマる11の「罠」 ※上記1~9の中から抜粋。 ###1. 簡単なタイマー機能にyieldやcoroutineを使用しないこと ```C#:タイマーを作成するだけなら以下のコードで良い: float timer; int waitingTime; void Update(){ timer += Time.deltaTime; if(timer > waitingTime){ //Action timer = 0; } } ``` ###2. staticを変数に用いないこと >「staticを変数に用いないこと」がUnityで陥りやすい「罠」。ついつい変数を保有するスクリプトの参照方法が分からない場合に、簡単に参照出来る方法としてやってしまいがちだ。正確に理解していないかぎりやってはいけない典型パターンだ。staticな変数はグローバル変数の扱いであり使用すべき状況はかぎられていて、かつ上級者向けにシナリオといえる。複雑なゲームのプログラミングにおいて一度も使うことがなかったとしても全く困らないことが殆どだ。 ###3. CharacerControllerを格闘やゲームでの移動ではない目的で使用しないこと >物理(Physics)エンジンを使用したい場合はCharacter Controllerを使用しないこと。このケースではカスタムのスクリプトを作成する必要がある。 ###4. FixedUpdateの中でInputを使用しないこと >大体において、問題はInputを通して物理エンジンを作用させるときに問題が起きる。PhysicsはFixedUpdateに配置するものなので、そこにInputも配置すれば良いだろう、とおもって。そこが大きな勘違いなのである。 ```C#:誤った使用例 using UnityEngine; public class Test:MonoBehaviour{ void FixedUpdate(){ if(Input.GetKeyDown(KeyCode.Space)){ //Action } } } ``` ###5. Array, HashtableやArrayリストを間違った使用方法で使わないこと >Array、ArrayList、HashTableを用いないこと。.NETのジェネリックコレクションか組み込みされた配列を使用すべきである。古いコレクションでは多くのコードを記述しないと必要なオブジェクト型を得ることが出来ない一方で、ジェネリックコレクションはその問題を解決し、さらに必要であれば異なったオブジェクト型にも対応することも可能である ###6. Quaternionのx,y,z値を更新しないこと >クォータニオンのx、y、z、wパラメータは、インスペクタで表示されるオブジェクトの回転の値と関係がない。x、y、z、wは角度で格納はされている(角度のサイン値、コサイン値で格納)。クォータニオンのx、y、z、wは、本当に何をやっているのか理解していないかぎり、修正すべきではない。もし何かの回転を角度で修正したい場合は.eulerAnglesを使用して修正すべきである。 ###7. KinematicでないRigidbodyでオブジェクトを移動しないこと >物理エンジンを使用する場合、KinematicでないRigidbodyを移動する場合はAddForceなどを使用すること。 ###8. CollisionやTriggerの発生条件を勘違いしないこと >1. OnCollision、OnTriggerメソッドをふくむスクリプトの対象となるゲームオブジェクトにRigidbodyコンポーネントをアタッチすること >2. 衝突するオブジェクトのどちらかのRigidbodyはスリープになっていないことが必要 ###9. Java, Javascript一般のプログラミング手法をUnityに持ち込まないこと ※gamesonytabletブログでは未投稿 >UnityのJavascriptはJavascriptではない。多くの人はUnity Scriptと呼び区別する。UnityのJavascriptは、JavascriptというよりAction Scriptに近い.NET言語といえる。Javascriptのいくつかの機能はあるものの、大部分の機能がない。プロトタイプの継承よりも伝統的な継承が使われ、コンパイルの後に関数を加えることは出来ない、など違いはその他にも多くある。 >どんな状況であっても本当のJavascriptのチュートリアルを学習したり、本当のJavascript本を読んでUnityに適用することはやるべきでない。ただ混乱するだけだ。 ###10. waitやyield後のcoroutineの終了条件を勘違いしないこと ※gamesonytabletブログでは未投稿 ```C#:誤ったコルーチンの記述 void Update() { if(health < 0) { StartCoroutine(Die()); Destroy(gameObject); //あるいはenabled = false; } } IEnumerator Die() { animation.Play("wobble"); yield return new WaitForSeconds(3); //次の行はコールされることがない animation.Play("die"); } ``` ###11. C#スクリプトからJavascriptの相互参照を軽く考えすぎないこと >プロジェクトを記述する言語を一つ選ぶこと。もしサードパーティのコンポーネントの場合に完全に独立したものを使用する場合、一番最初にコンパイルされる特別なフォルダにこれらのスクリプトを置く、すなわちStandard Assetsフォルダ、Pro Standard Assetsフォルダ、 またはPluginsフォルダに置いて、かつ参照する側のスクリプトをこれらのフォルダには置かないようにすれば、どの言語で記述されていようとも問題ない。 ##上記で特に勉強になったところ ###学習ステップ ####1.「別スクリプトの変数やメソッドへの参照」 ####2.「OnTriggerやOnCollisionの判定条件」 ####4.「未来事象の正しい文法」 プログラミングではデザインパターンを学ぶことが学習の早道とされることが多いが、#1は経験からしか学ぶことが難しい変数やメソッドの使用について整理がなされている。 一方、#2、#4は「こういう風なやり方があることを知らなかった」という観点で推奨したい。 あらためてUnityマニュアルを読み返したが、確かにTriggerやCollisionの条件について詳細に書いてあるのだが、最初から全部を読むことはないだろうからハマるまで気付きにくいポイントだ。 「何秒後にこういうことをしたい」といったように、1フレームを超えた処理でコルーチンを使用しないといけないところで自分の理解が止まっていて、パターンによって簡単な記述方法があることを改めて認識できた。 さて、人によって気になる箇所が違うのではないかとおもう。他の人もどういうところが勉強になったかTwitterなどで共有してお互いの参考にしようぜ!! |
|
| 343位 |
|
|||
|
10:55:12 |
(フリーランス 所属) |
|
## ローカルブランチ ### ブランチの一覧を見る - ローカルブランチの一覧を表示する ```bash % git branch master foo bar ``` ### 削除 - HEAD にマージしたブランチを削除する ```bash % git branch --delete foo ``` - マージしたかどうかを問わずに削除する ```bash % git branch -D foo ``` ## リモートブランチ ### ブランチの一覧を見る - リモートブランチの一覧を表示する ```bash % git branch --remote origin/HEAD -> origin/master origin/foo origin/bar ``` - ローカルブランチとリモートブランチの一覧を表示する ```bash % git branch --all master foo bar origin/HEAD -> origin/master origin/foo origin/bar ``` ### リモートブランチを削除する - リモートブランチ foo を削除する ```bash % git push --delete origin foo ``` または ```bash % git push origin :foo ``` ## 参照 [3.5 Git のブランチ機能 - リモートブランチ](http://git-scm.com/book/ja/ch3-5.html) [Gitの内側-参照仕様(Refspec)](http://git-scm.com/book/ja/Git%E3%81%AE%E5%86%85%E5%81%B4-%E5%8F%82%E7%85%A7%E4%BB%95%E6%A7%98%EF%BC%88Refspec%EF%BC%89) |
|
| 344位 |
|
|||
|
18:48:31 |
|
|
心構え
・HTMLは最小限にする ・JSも最小限。不要なら ・デザインとか全部決まってから手を付ける。 開発環境 ・リアルタイム入力補完ができるエディタ (プログラマならEcripseとかEmeditorとか、MacならCodaか) ・動作確認の InternetExplorer >Macで動いてWindowsでレイアウト崩れた!Windowsが悪い! ←なに言ってんだ・・・?? ・おまけでDreamwaver。 下準備 ・予め画像を切り出しておく。 HTMLコーディング開始 ・画像切り出しは全部後回しにして、 ZencodingでHTMLを「一気に」組む。 HTML”だけ”組む。 IDやclass名もここでつけちゃう。 ブラウザで開くとめちゃくちゃだけど気にしない。 文字も入力しない。 画像やCSSパスを設定する。 ・作ったHTMLをDreamwaverにコピペして、 Dreamwaver上から「画像パスと画像サイズ」を突っ込んでいく。 Dreamwaverはここだけ。 CSSセレクタを自動生成する。 ・OneClickCSS( http://css.miugle.info/ )を利用して、CSSセレクタを自動生成する。 CSSを直接打たず、LESSやSASSを使う。 ・CSSを直接打たないで、LessやSassを使ってCSSコーディングを行う。 http://lesscss.org/ http://sass-lang.com/ 開発環境が準備できない人はLessの方が便利 ・Class、ID名ベースのCSSは使わない。 子孫セレクタ、子セレクタベースのコーディングを心がける。 LessやSassは子孫セレクタベース。 文字入力、ALT、metaタグを入れていく。 ・レイアウトが7割りできた段階で、文字やALTを入れていく。 いいねボタンやJSを入れて、最後の調整をする。 ・JSはcoffee scriptを利用する。 賛否両論あるけど早い 完成! ○組む ○レイアウトする ○入れる の3アクションで行けるからめちゃくちゃ早い。 HTMLコーディングくらい超速で終わらせよう。 |
|
| 345位 |
|
|||
|
13:56:09 |
(Toreta 所属) |
|
「えっ、こんなのあったの?」的なマイナー機能の紹介ではなく、あくまでプログラミング中によく出くわす場面で使いたい基本的な機能のまとめです。主にUtils系メソッドの話。 ※ここのサンプルは以下のライブラリ/バージョンを使ってます。 - commons-io 2.4 - commons-lang 2.6 - commons-httpclient 3.1 - commons-collections 3.2.1 #文字列の処理 nullを考慮しながらStringが空文字列かどうかチェックする。 ```java:StringUtils if (StringUtils.isEmpty(str)) { System.out.println("strは空文字列かnull."); } ``` これを使わずにstr==null || str.length()==0 みたいな条件式書くと見づらいし、nullチェック忘れてlength()を呼んで不具合が出ることも。 同様にListのサイズチェクでは**CollectionUtils.isEmpty**が使えて、これもよく活用する。 ## 日付のパース 文字列の日付をDate型に変換する。また書式だけでなく日付自体が異常じゃないか(4/32みたいに)もチェックしてくれる。 ```java:DateUtils String[] acceptFormats = { "yyyy-MM-dd" }; //書式は複数指定できる try { Date yesterday = DateUtils.parseDateStrictly("2013-04-06", acceptFormats); Date invalid = DateUtils.parseDateStrictly("2013-04-32", acceptFormats);// Exception } catch (ParseException e) { // エラー処理... } ``` 参考:[日付が正しいかどうかをチェックする #Java - Qiita](http://qiita.com/items/832723eba10d2b52d9bf) ## ファイル名取得 ファイルのフルパスから、拡張子を除いたファイル名のみを取得したいようなケース。 ```java:FilenameUtils String filePath = "/path/to/sample.txt"; FilenameUtils.getBaseName(filePath); // "sample" ``` ファイルの拡張子が欲しければ、FilenameUtils.getExtension(filePath)が使える。 ## 渡された文字列が数字かどうかを判定する Validationとか使わず手っ取り早くチェックしたいときに。 ```java:NumberUtils NumberUtils.isDigits("12345"); // true NumberUtils.isDigits("12ab45"); // false ``` #ファイル関連の処理 ## テキストファイルの中身をStringで取得する 地道にやるとFileからBufferedReaderを生成して、一行づつreadLine…みたいにするところも、FileUtilsを使えばカンタンに。 ```java:FileUtils String contents = FileUtils.readFileToString(new File("src.txt")); ``` 逆に、Stringの文字列をファイルに書き出したいときはこんな感じ。 ```java:FileUtils FileUtils.writeStringToFile(new File("out.txt"), "書き込む内容"); ``` ##ファイルのコピー ファイルからファイルへのコピーもFileUtilsで。 ```FileUtils File src = new File("src.txt"); FileUtils.copyFile(src, new File("dest.txt")); ``` #HTTP通信 HttpClientのHandlerを使うと、IOException等が発生すると成功するまでリクエストを再送してくれる。 ```java:HttpClient DefaultHttpMethodRetryHandler handler = new DefaultHttpMethodRetryHandler(10, true) { @Override public boolean retryMethod(HttpMethod method, IOException exception, int executionCount) { boolean tryAgain = super.retryMethod(method, exception, executionCount); if (tryAgain) { try { Thread.sleep(1000); } catch (InterruptedException e) { } } return tryAgain; } }; HttpClient httpClient = new HttpClient(); GetMethod method = new GetMethod("http://localhost:8080"); method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, handler); httpClient.executeMethod(method); ``` この例だとリクエストが成功するまで最大10回まで再試行する。HandlerにThread.sleepを入れておけば、次の試行までの間隔も調整できる。 # ストリーム処理 ##InputStreamからOutputStreamへの変換 IOUtilsを使うと、InputStreamからreadしてOutputStreamにwriteして・・・といった面倒な処理は必要ナシ。 ```java:IOUtils in = new FileInputStream("src.txt"); out = new FileOutputStream("dest.txt"); IOUtils.copy(in, out); ``` ##Streamのクローズ finallyブロックでStreamをクローズしたい場合、IOExceptionをハンドリングするためにブロックが深くなったりする。 ```java:見づらいStream処理 InputStream in = null; try { in = new FileInputStream("input.txt"); // 何かの処理… } catch (FileNotFoundException e) { e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); System.err.println("close失敗"); } } ``` これもIOUtilsを使うと簡潔に書ける。 ```java:IOUtils try { in = new FileInputStream("input.txt"); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { IOUtils.closeQuietly(in); } ``` #その他 ##乱数の取得 一度Randomインスタンスを生成して、rand.nextInt・・・みたいな処理も1行で。 ```java:RandomUtils for (int i = 0; i < 10; i++) { System.out.println(RandomUtils.nextInt(10)); } ``` ##クラス間でフィールドをコピーする 異なるクラスのインスタンス間で、同じ名前・同じ型のフィールドをコピーしたいとき。 ```java:地道にフィールドをコピーする Item item = getItem(); //Itemインスタンス取得 DetailItem detail = new DetailItem(); //Itemに加えて幾つかフィールドが追加されたクラス detail.setTitle(item.getTitle()); detail.setPrice(item.getPrice()); //Itemのフィールド数だけ繰り返す… ``` BeanUtilsなら一行にまとめる事ができる。 ```java:BeanUtils BeanUtils.copyProperties(detail, item); ``` ## 見やすいtoStringを生成する ToStringBuilderでフィールド一覧を人間の見やすいフォーマットで返せる。 ```java:ToStringBuilder @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE).toString(); } ``` title・priceフィールドがあるItemクラスだったら、こんな感じにprintできるよう整形してくれる。 ```:インスタンスをprintした結果 com.example.Item@1aa8c488[title=sample,price=100] ``` 指定するToStringStyleを変えること、フィールド単位で見やすく改行を入れてくれたりもする。 [ToStringBuilerを使って簡単にBeanの中身を表示 - Yamkazu's Blog](http://yamkazu.hatenablog.com/entry/20090823/1251016360) #まとめ commonsには他にもloggingとかCLIとか便利なものが沢山あるけど、ひとまず今回はプログラミングで遭遇する頻度が高そうな処理で、commonsを使えば超簡単に解決できるものだけをまとめてみた。他にもオススメの機能があれば教えてください。 ##参考リンク - [Javaメモ目次(Hishidama's Java Memo)](http://www.ne.jp/asahi/hishidama/home/tech/java/) - [[commons] - 都元ダイスケ IT-PRESS](http://d.hatena.ne.jp/daisuke-m/searchdiary?word=%2A%5Bcommons%5D) - [HttpClient でウェイト付きリトライを行う。 - 考え得る最高を常に行う](http://d.hatena.ne.jp/Climber/20060117/1137487461) - [簡単にtoString()を実装する #Java - Qiita](http://qiita.com/items/0a9fea8a226095b64729) |
|
| 346位 |
|
|||
|
00:24:06 |
(Freelance 所属) |
|
Rails Advent Calendar 6日目。
Railsで普段使うようなGemも、Rails2系から3系への移行で大分デファクトのGemなども入れ替わったり、新しいGemが台頭して来たり、RailsやRubyを使う上では切っても切れないGem達。 なので、現在、主に自分が使用しているGemの一覧を紹介します。 普段Railsを使っている人にはあまり目新しいものは無いと思いますが、これからRailsを始める人がとりあえず知っておくと良いかも、みたいな観点でピックアップしてみました。 ※2012/09/06時点 Rails3.2.8対応 ### DBアダプタ * [sqlite3](https://github.com/luislavena/sqlite3-ruby) * [pg](https://bitbucket.org/ged/ruby-pg/wiki/Home) * [mysql2](https://github.com/brianmario/mysql2) おなじみ。各種DB用アダプタ。Railsデフォルトはsqlite3。 ### ログイン認証 * [omniauth](https://github.com/intridea/omniauth) * [omniauth-twitter](https://github.com/arunagw/omniauth-twitter) * [omniauth-facebook](https://github.com/mkdynamic/omniauth-facebook) * [omniauth-github](https://github.com/trevorturk/omniauth-github) * [omniauth-identity](https://github.com/intridea/omniauth-identity) OmniAuth認証によるログイン機能実装でよく使う面々。 ### 定数/設定値管理 * [rails_config](https://github.com/railsjedi/rails_config) 指定の設定用ymlファイルに値を記述しておくと、下記のようにSettings.〜として呼び出す事が出来るようになります。 development/test/productionなどの各環境ごとに設定を分ける事も出来るので、煩雑になりがちな定数や設定値の管理のお供に。 ローカル環境だけこの値を使いたいなどの設定も可能です。 ```yaml default_limit: 100 ``` ```ruby Settings.default_limit # => 100 ``` ### ページネーター * [kaminari](https://github.com/amatsuda/kaminari) rails2系ではwill\_paginateがデファクトだったかと思いますが、Rails3系に移行するぐらいの時期から、ほぼ完全にkaminariに乗り換えてる人がほとんどかと思います。 Kaminari.paginate\_arrayでArray objectにも対応。 ### ユーティリティ * [action_args](https://github.com/asakusarb/action_args) params[:id]などのパラメータをdef show(id)のようにアクションの引数として受け取る事が出来るようになります。 paramsを書くのが面倒、使用しているparamsを列挙したいなどの場合に使うとコードがスッキリすると思います。 * [active_decorator](https://github.com/amatsuda/active_decorator) DBにはfirst\_name、last\_nameで別々に入っていて、表示する時はフルネームとして```"#{user.last_name} #{user.first_name}"```のように出したい時など、Decoratorに```def full_name```を定義して、```user.full_name```のように呼べるようになります。 モデルに定義しても同じように使う事は出来ますが、データやビジネスロジックを扱わない表示用のメソッドなどをDecoratorに分離できます。 ### デバッグ * [tapp](https://github.com/esminc/tapp) ハッシュなどを人が見やすい形で表示してくれます。 また、メソッドチェインの間に挟む事も出来るので、中間の状態を見たい時などにも使えます。 ★2012/10/10 追記 * [awesome_print](https://github.com/michaeldv/awesome_print) ### アプリケーションサーバ * [thin](https://github.com/macournoyer/thin) 主にHeroku用。最近ではthinをインストールしておくとrails sでローカルサーバを起動した時にも自動的にthinを使用してくれるので、ローカルの開発環境でもthinをインストールしています。 * [unicorn](https://github.com/defunkt/unicorn) プロダクション環境用はこちら。 アプリケーションサーバとしてはそろそろデファクトになりつつあるような気がします。 自分はやった事ないですが、Herokuでも使えるらしいです。 ### Markdown * [redcarpet](https://github.com/vmg/redcarpet) * [pygments.rb](https://github.com/tmm1/pygments.rb) * [rubypython](https://github.com/halostatue/rubypython) Markdownやシンタックスハイライトを実装したい時に使っています。 下記のようにヘルパーメソッドを呼ぶだけで簡単にMarkdown記法/シンタックスハイライトを表示したい場合は[markdown-scaffold](https://github.com/shu0115/markdown-scaffold)(※拙作)というGemを入れて指定のGenerateコマンドを叩くと変換用のメソッドをインストール出来ます。 ``` <%= raw show_markdown( "markdown_text" ) %> ``` ### 画像管理 * [paperclip](https://github.com/thoughtbot/paperclip) * [carrierwave](https://github.com/jnicklas/carrierwave) 少し前まではpaperclipがデファクトだったのかな、という感じがしますが、最近はcarrierwaveの名前もよく聞きます。 両方共、S3へのアップロードなども簡単に実装できると思います。 ★2012/10/10 追記 * [mini_magick](https://github.com/probablycorey/mini_magick) ### 論理削除 ★2012/10/10 追記 * [rails3_acts_as_paranoid](https://github.com/goncalossilva/rails3_acts_as_paranoid) ### フォロー機能 ★2012/10/10 追記 * [party_boy](https://github.com/mnelson/party_boy) ### アクティビティ機能 ★2012/10/10 追記 * [timeline_fu](https://github.com/jamesgolick/timeline_fu) ### ジョブキュー処理 ★2012/10/10 追記 * [resque](https://github.com/defunkt/resque) * [delayed_job](https://github.com/collectiveidea/delayed_job) 参考: [ジョブキュー処理のResqueとDelayed Jobの使い分けの方針などはありますか? - QA@IT](http://qa.atmarkit.co.jp/q/2406) ### Twitter/Facebook API操作 ★2012/10/10 追記 * [twitter](https://github.com/sferik/twitter) * [fb_graph](https://github.com/nov/fb_graph) ### リアルタイム処理 ★2012/10/10 追記 * [private_pub](https://github.com/ryanb/private_pub) ### タグ機能 ★2012/10/10 追記 * [acts-as-taggable-on](https://github.com/mbleigh/acts-as-taggable-on) ### ルーティング ★2012/10/12 追記 * [schneems/sextant](https://github.com/schneems/sextant) * [Rails 4.0 は rake routes いらず - willnet.in](http://willnet.in/30) ### エラーハンドリング ★2012/11/10 追記 * [smartinez87/exception_notification](https://github.com/smartinez87/exception_notification) * [Rails で捕捉されない例外が発生したらメールを送る #Ruby #Rails #rack #AdventCalendar - Qiita](http://qiita.com/items/adb89ede814b86e9c04a) 以上です。 この他にも、たくさん便利なGemがあると思うので、何か良いGemがあったら是非、教えてください。 ---- ★2012/09/12 追記 某界隈からの、このGemを紹介しないなんて有り得ないという異議申立てがあったので追記します。 * [soplana/to_gunma](https://github.com/soplana/to_gunma) 全てのオブジェクトをto_gunmaとするだけで群馬県に出来るという、とても便利なGemですw |
|
| 347位 |
|
|||
|
01:12:27 |
|
|
Command Lineで使用しているツールを簡単にではありますが、まとめてみようと思います。私が知らない便利ツールはまだまだたくさんありますので、ここに掲載されているもの以外で便利なものがあれば、ぜひ教えてください。
この記事に貼り付けてあるリンクは、キーワードに関連する適切な場所へと誘導してくれます。非常に有効なので、ぜひ参照してみてください。 また、私は基本的にパソコンのことは何もわかっていないので、記事内には誤りを含む可能性があります。よって、できる限り自分の環境を把握した上で、適時読み換えてもらえればと思います。特に、設定ファイルの場所については気をつけてください。また、`Git`や`Homebrew`、`MacPort`の導入説明などは省略していますので、注意が必要です。 #zsh Shellである`zsh`を拡張する設定の数々を紹介していきたいと思います。簡易にではありますが、もっと良い設定があれば、コメントにて補足していただければありがたいと思います。 ```sh brew install zsh chsh -s /usr/local/bin/zsh ``` ##autojump `autojump`は、移動を簡単にしてくれるツールです。`autojump`を導入し、補完関数のパッケージを[$fpath](http://blog.glidenote.com/blog/2012/02/29/autojump-zsh/)以下においてやると、効果を発揮します。また、[補完関数を自作](http://hakobe932.hatenablog.com/entry/2012/02/13/214934)することもできます。 ```sh brew install autojump ``` ##z `autojump`よりも[z](http://project-p.jp/halt/?p=1724)のほうがいいという人もいます。私もそう思います。 ```sh git clone https://github.com/rupa/z.git または brew install z ``` そして、設定ファイルに以下を付け足します。これで、`j`を押すと、履歴JUMPが使えます。もし使用できない場合は、[こちら](https://github.com/sjl/z-zsh)を使用してみてください。 ```sh:~/.zshrc _Z_CMD=j source ~/z/z.sh precmd() { _z --add "$(pwd -P)" } ``` ##auto-fu.zsh `auto-fu.zsh`は、自動補完を簡単にしてくれるツールです。[更新](http://d.hatena.ne.jp/hchbaw/20110830/1314717481)されてからさらに使いやすくなりました。 ```sh git clone https://github.com/hchbaw/auto-fu.zsh.git echo "source ~/auto-fu.zsh/auto-fu.zsh" >> ~/.zshrc ``` ##canything `zsh`で`anything.el`ライクな環境を整えるには、[canything](http://filmlang.org/soft/canything)がお勧めです。まあ、`auto-fu.zsh`と同一作者の[zaw.zsh](http://d.hatena.ne.jp/hchbaw/20120401/1333208529)でもいいのですが。 ```sh sudo aptitude install libncursesw5-dev git clone https://github.com/keiji0/canything cd canything && make mkdir -p ~/bin && mv canything ~/bin ``` [おすすめの設定](http://arataka.github.com/2011/04/14/zaw-zsh-or-canything-with-mendeley-gisty-and-j-z.html)は`gisty`で管理しているリポジトリのワーキングディレクトリに一覧から選んで移動する設定です。 ```sh # -*- sh -*- zmodload zsh/parameter function zaw-src-gisty() { candidates=("${(ps:\n:)$(gisty list)}") actions=("zaw-callback-gisty-append-to-buffer") act_descriptions=("append to edit buffer") } zaw-register-src -n gisty zaw-src-gisty function zaw-callback-gisty-append-to-buffer() { local gitdir=`echo "${(j:; :)@}" | cut -d":" -f1` local destpath=$GISTY_DIR/$gitdir if [ -n "$gitdir" -a -d "$destpath" ] then LBUFFER="${BUFFER}${destpath}" fi } ``` ##コマンドラインスタック `zsh`では、`コマンドラインスタック`というものを使うと利便性が向上します。これは、簡単には、入力を一時避難しておくようなものです。`コマンドラインスタック`の使い方は、`Esc-q`や`Esc-h`です。また、[避難したものを表示](http://qiita.com/items/1f2c7793944b1f6cc346)しておくようにすると、更に便利です。 ```sh:~/.zshrc show_buffer_stack() { POSTDISPLAY=" stack: $LBUFFER" zle push-line-or-edit } zle -N show_buffer_stack ``` ##oh-my-zsh `oh-my-zsh`は、`zsh`のカスタマイズを簡単にするようなものです。設定ファイルの集まりといえば分かりやすいでしょうか。有志たちが公開してくれている設定を簡単に取り込めます。特に、[Prompt](http://d.hatena.ne.jp/mollifier/20101009/p1)の設定においては、大いに参考になると思います。`zsh`のPromptの設定を書くのはとても面倒くさいので、[oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh)を参考に構築するのもありかなとは思っています。 ```sh git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh cp -i ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc ``` ##zsh-git-prompt zsh の Prompt に Git のRepository 情報を表示するには、[zsh-git-prompt](https://github.com/olivierverdier/zsh-git-prompt)がお手軽です。ただ、私の場合は、[ククログのzshrc](http://www.clear-code.com/blog/2011/9/5.html)を参考にして書きました。 ```sh git clone https://github.com/olivierverdier/zsh-git-prompt.git cd zsh-git-prompt mkdir -p ~/.zsh/git-prompt cp gitstatus.py ~/.zsh/git-prompt echo "source ~/zsh-git-prompt/zshrc.sh" >> ~/.zshrc echo "PROMPT='%B%m%~%b$(git_super_status) %#" >> ~/.zshrc ```  #tmux `tmux`とは、Virtual環境を構築するツールです。`screen`とともに有名ですが、私は、`tmux`を使っています。ここでは、`tmux`を便利にする設定の数々を紹介していきたいと思います。 ```sh brew install tmux ``` ##screen風のキーバインドを使用する ```sh mkdir -p ~/.tmux.d sudo cp /opt/local/share/doc/tmux/screen-keys.conf ~/.tmux.d echo "source ~/.tmux.d/screen-keys.conf" > ~/.tmux.conf ``` ##Setting File Sample 設定ファイルのサンプルは、[GitHub](https://github.com/masudaK/dotfiles/blob/master/.tmux.conf)に挙げられているものが参考になります。設定ファイルの書き方は[こちら](https://bytebucket.org/ns9tks/tmux-ja/wiki/tmux-ja.html)です。 ```:~/.tmux.conf #prefixキーの変更 set-option -g prefix ^_ #viのキーバイドを使用する set-window-option -g mode-keys vi #C-h,lでウィンドウの移動 unbind-key C-h unbind-key C-l bind -n C-h previous-window bind -n C-l next-window ``` ##MacのtmuxでClipboardを利用する ```sh mkdir ~/bin && echo "PATH=$HOME/bin:$PATH" >> ~/.zshrc git clone git://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard.git cd ~/tmux-MacOSX-pasteboard make reattach-to-user-namespace cp reattach-to-user-namespace ~/bin vim ~/.tmux.conf ``` 以下のように書きます。すると、Macの`tmux`で普通にClipboardを利用することができます。 ```~/.tmux.conf #クリップボードの共有 set-option -g default-command "~/bin/reattach-to-user-namespace -l $SHELL" #C-cでコピー bind-key C-c run "tmux save-buffer - | ~/bin/reattach-to-user-namespace pbcopy" #C-vで貼り付け bind-key C-v run "~/bin/reattach-to-user-namespace pbpaste | tmux load-buffer - && tmux paste-buffer" ``` ##tmuxのPromptに様々な情報を表示する tmuxのPromptに様々な情報を表示するには、情報を取得する[シェルスクリプト](http://d.hatena.ne.jp/yonchu/20120414/1334422075)が必要になります。それらを作成した後、設定ファイルに以下のように書きます。長くなりそうなので、詳しくは、上の記事を見てください。 ```~/.tmux.conf # ステータスライン - Right set -g status-right '< #[fg=black,bold]#(echo $LANG)#[default] < #[fg=blue]up #(pc-running-time)#[default] < #[fg=magenta](#(loadaverage)) Mem #(used-mem)%%#[default] < #[fg=red,bold]%y/%m/%d(%a)%H:%M:%S#[default] ' # Rightの表示領域最大長 set -g status-right-length 90 ```  #weechat `weechat`とは、IRC Clientのことです。IRC Clientの中では、使いやすいので、お勧めです。[Growl](http://blog.tnmt.info/2012/02/08/weechat-growl-for-windows/)との連携で更に便利になります。 ```sh brew install weechat ``` **追記** [120reset](http://qiita.com/users/120reset)さんからコメントをいただきました。pluginのことも考えると、以下のようするほうがいいようです。 ```sh brew install weechat --ruby --perl --python ``` #mutt+Gmail mutt+GmailでCUIメール環境を構築する方法です。これについても、簡易手順なので、それぞれにカスタマイズしていただければと思います。 ```sh brew install mutt brew install msmtp mkdir -p ~/.mutt/cache/headers && mkdir ~/.mutt/cache/bodies && touch ~/.mutt/certificates curl http://cache.gawker.com/assets/images/lifehacker/2010/06/muttrc-gmail.txt > ~/.muttrc vim ~/.muttrc ``` 以下の項目を自分のアカウントのものに書き換えます。  #iTerm2 [iTerm2](http://www.iterm2.com/)は、MacのTerminalアプリです。私は、Macではこれを使用しています。 ##フォントをRictyに変更する フォントを[Ricty](http://save.sys.t.u-tokyo.ac.jp/~yusa/fonts/ricty.html)に変更すると、アルファベットや数字の判別が分かりやすくなると思います。  ```sh brew install fontforge --use-gcc Inconsolata のインストール Migu 1M のインストール git clone https://github.com/yascentur/Ricty.git cd Ricty && sh ricty_generator.sh auto cp -f Ricty*.ttf ~/Library/fonts ``` あとは、`iTerm2`の設定を開き、フォントを`Ricty`に変更します。 ##ディレクトリを維持する `iTerm2`の設定にて、Profiles>General>Working_DirectoryをReuse…にしておくと、ディレクトリを維持することができます。  #fu `fu`とは、ワンライナーを簡単に投稿、共有できる[Command-Line-Fu](http://www.commandlinefu.com/commands/browse)のCLI Clientです。[cmdline-fu](http://d.hatena.ne.jp/rx7/20110117/p1)のほうがお勧めですが、私は、`fu`を使っています。 ```sh git clone git://github.com/samirahmed/fu.git cd fu/ sudo make install ``` ##fuの使い方 ```sh #検索して結果をすべてを表示 fu -a キーワード #2番目をコピー fu -c 2 ``` #peep `peep`とは、Command LineでGoogleReaderを確認するツールです。使い方は簡単で`-n`で未読数を表示します。キーは、ヘルプは`?`で終了は`q`です。記事を読むには`O`です。 ```sh git clone https://github.com/ryuji/peep echo "alias peep='~/peep/peep'" >> ~/.zshrc ``` ##zshで使用するデフォルトのブラウザを変更する これをやっておくと、記事を`w3m`で開くことができます。 ```sh echo "export BROWSER=w3m" >> ~/.zshrc ``` #Sheet `Sheet`とは、Command Lineで動作するチートシート補助ツールのようなものです。依存関係の解消には、[bundler](http://pspfour.blog106.fc2.com/blog-entry-1165.html)を使います。`rvm install`を使用するには、[gcc](https://github.com/kennethreitz/osx-gcc-installer)を使います。Xcode4.3以降は、`gcc`がインストールされないので、インストールしておきましょう。 ```sh brew install ruby gem install bundler git clone --depth 1 git://github.com/wayneeseguin/rvm.git cd rvm ./install rvm install 1.9.2 rvm use 1.9.2 cd ~/ git clone https://github.com/oscardelben/sheet.git cd sheet bundle install gem install sheet ``` 使い方は、簡単で、`sheet new test`などで作成したシートを`sheet list`などで開きます。 #Sublime Text 2 Command Lineで動作するというわけではありませんが、コマンドラインに替わるものとして、[Sublime Text 2](http://www.sublimetext.com/2)がお勧めです。`Coda2`人気の中、私は、こちらのほうがいいと思っています。 ##パッケージ管理 コンソールを`Ctrl+Shift+@`を押して表示し、以下のコマンドを実行します。 ``` import urllib2,os; pf='Package Control.sublime-package'; ipp=sublime.installed_packages_path(); os.makedirs(ipp) if not os.path.exists(ipp) else None; urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler())); open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read()); print 'Please restart Sublime Text to finish installation' ``` ##Themeの設定 パッケージコントロールを`Commnad+Shift+p`を押して表示し、`install`にて、以下のパッケージを追加していきます。 ``` Theme - Soda ``` Sublime Text 2 -> Preferences -> Settings - User を選択し、設定ファイルに以下を追記します。 ```json { "theme": "Soda Dark.sublime-theme" } ```  ##パッケージの追加 **1. URLを用いたパッケージの追加** `Commnad+Shift+p`を押し、`add repository`を選択します。そして、URLを入力し`Enter`を押します。 **2. 検索を用いたパッケージの追加** `Commnad+Shift+p`を押し、`install`を選択します。そして、キーワードで検索し、`Enter`を押します。 おすすめパッケージは、[こちら](http://brettterpstra.com/some-sublime-text-2-packages-of-note/)が参考になります。もうそろそろ表示領域の上限がきそうなので、記述は、相当省略していきます。 #TweetVim [TweetVim](https://github.com/basyura/TweetVim)は、Twitter Clientです。導入は少しばかり面倒ですが、`Vim`上で動作するためかなり便利だと思います。また、Command Lineで動作するTwitter Clientとしては、[t](http://sferik.github.com/t/)などもあります。 ##Vim Pluginの管理 ```sh git clone https://github.com/gmarik/vundle.git ~/.vim/bundle/vundle echo "source ~/.vim/bundle/vundle/test/minirc.vim" >> ~/.vimrc vim ~/.vim/bundle/vundle/test/minirc.vim ``` ##TweetVimの導入 ```vim:~/.vim/bundle/vundle/test/minirc.vim set nocompatible syntax on filetype off set rtp+=~/.vim/bundle/vundle/ call vundle#rc() Bundle 'gmarik/vundle' "TweetVimとその依存関連 Bundle 'basyura/TweetVim' Bundle 'mattn/webapi-vim' Bundle 'basyura/twibill.vim' Bundle 'tyru/open-browser.vim' Bundle 'h1mesuke/unite-outline' Bundle 'basyura/bitly.vim' filetype plugin indent on ``` ファイルを保存した後は、`:BundleInstall`とすると、プラグインがインストールされます。 #blogger.vim [blogger.vim](http://pspfour.blog106.fc2.com/blog-entry-1171.html)とは、Vim上からブログへポストするためのプラグインです。ブログを書くことはアウトプットにいいということで導入してみました。  ##プラグインのインストール ```vim:~/.vim/bundle/vundle/test/minirc.vim "gitリポジトリの内容を参照する Bundle 'kana/vim-metarw' "bloggerに投稿する Bundle 'ujihisa/blogger.vim' "プラグインの読み込みパスを変更 "Bundle 'tpope/vim-pathogen' ``` `:BundleInstall`を実行すると、プラグインのインストールが完了します。 ##必要なものをインストール ```sh sudo port install pandoc gem install nokogiri gem install net-https-wrapper ``` ##必要な設定 `vim-pathogen`を使用しない場合は、以下の設定を行います。 ``` git clone https://github.com/ujihisa/blogger.vim.git cd blogger.vim mkdir -p ~/.vim/autoload/metarw/ cp autoload/metarw/blogger.vim ~/.vim/autoload/metarw/ && cp autoload/metarw/blogger.rb ~/.vim/autoload/metarw/ ``` 後は、設定ファイルに、アカウント情報を記入していきます。`your_blogid_here`などの箇所を自分のアカウント情報と置き換えます。 ```vim:~/.vimrc "Bloggerアカウント情報 let g:blogger_blogid = 'your_blogid_here' let g:blogger_email = 'your_email_here' let g:blogger_pass = 'your_blogger_password_here' ``` `Vim`から`Blogger`にポストする方法は簡単です。また、[Markdown](http://blog.2310.net/archives/6)形式にも対応しているので、[zencoding-vim](https://github.com/mattn/zencoding-vim/)を使わなくてもある程度書けると思います。 ``` #投稿したものを編集する :e blogger:list #新しく投稿する :w blogger:create ``` #gist-vim `Vim`からCodeをGistに投稿してみましょう。そして、それをブログに貼り付けてみます。ただし、公式アプリもそれなりに便利なので、公式アプリの導入も検討してみてはどうでしょう。 ##公式アプリの導入とgist-vimの動作に必要なアプリの導入 ```sh brew install gist brew install curl ``` ##プラグインのインストール ```vim:~/.vim/bundle/vundle/test/minirc.vim Gistにポストする Bundle 'mattn/gist-vim' ``` あとは`:BundleInstall`というVimコマンドを実行してプラグインをインストールします。そして、設定ファイルにアカウント情報を書き込んでいきます。 ##アカウント情報などの設定(Macの場合) ```vim:~/.vimrc let g:gist_clip_command = 'pbcopy' let g:gist_detect_filetype = 1 let g:github_user = 'usrename' ``` ##必要な設定 必要なファイルを必要な場所に置きます。 ```sh mkdir ~/.vim/cookies && touch ~/.vim/cookies/github ``` ##Gistにポストしたコードをブログに埋め込むには?  #VimFiler `VimFiler`とは、`Vim`で動くファイルマネージャーのことです。`brew install mc`で[mc](http://ja.wikipedia.org/wiki/Midnight_Commander)を入れてもいいのですが、Vimの操作に慣れている方は、[VimFiler](http://www.karakaram.com/vim/vimfiler/)がお勧めです。 ```vim:~/.vimrc.bundle "VimFilerと依存関連 Bundle 'Shougo/vimfiler' Bundle 'Shougo/unite.vim' Bundle 'Shougo/vimproc' ``` `:BundleInstall`を実行します。 ```vim:~/.vimrc "VimFilerエクスプローラーの設定 let g:vimfiler_as_default_explorer=1 "VimFilerセーフモードの設定 let g:vimfiler_safe_mode_by_default=0 ``` #VimShell `VimShell`とは、`Vim`で動くShellエミュレータです。導入しておくと、開発環境が便利になります。 ```~/.vimrc.bundle Bundle 'Shougo/vimshell' Bundle 'Shougo/vimproc' ``` ##コンパイルと必要なファイルの移動 ```sh cd ~/.vim/bundle/vimproc/ make -f make_mac.mak cp vimproc_mac.so ~/.vim/bundle/vimproc/autoload ``` ##デバッガなどのインストール 実行可能バイナリを検査するためのツールなどを入れていきます。 ```sh brew install binutils brew install cgdb ``` ##VimShellの使用例 `Vim`を起動し、`:VimShell`を実行します。 ```sh echo "int main(void) {return 0;}" > ~/hello.c gcc hello.c gobjdump a.out cgdb ./a.out ``` #vim-quickrun [vim-quickrun](http://iphone-dev.g.hatena.ne.jp/tokorom/20120520/1337542304)は、`Vim`で編集中のプログラムの実行結果を確認できるVim Pluginです。 ```~/.vimrc.bundle Bundle 'thinca/vim-quickrun' ``` デフォルトでは、`<Leader>r`を押すと、実行結果を確認できます。 #pkill プロセスの終了には、通常、PID(プロセスIDのことだと思う)が必要になります。しかし、`top`コマンドなどを使って、PIDを調べるのも面倒ですので、私は、プロセス名からPIDを検索し、それを`kill`する方法をよく利用します。`pkill`を使えば、それが簡単にできます。 ``` brew install pkill ``` #rtorrent よく容量が大きい`iso`ファイルの配布などには、`torrent`ファイルが使われることがあります。これは、あるファイルをダウンロードしたいという場合に、ユーザー間でファイルの不足分を補う形でダウンロードとアップロードをすることで、人気のあるものほど高速でダウンロードできるというものです。また、ファイルの配布者にとっても負担軽減になるため、広く使われている技術でもあります。 よって、個人的には、人気のある Linux ディストリビューションは、Torrent Clientでダウンロードすることがよくあります。ここでは、そのTorrent Clientの一つである`rtorrent`の使い方を紹介します。 ただし、`torrent`ファイルを使用する場合、アップロードも同時に行なってしまうので、ダウンロードするファイルには、細心の注意をはらいましょう。また、アップロードには特定のPortを使用するため、ファイアウォールを一部解除しなければならないことがあります。よって、セキュリティ上も注意が必要です。 ```sh brew install rtorrent ``` 使い方は簡単で、`^Q`で終了です。`^S`でスタートとストップです。`Enter`でModeを切り替えてファイルを選択できます。  なお、ファイルの総容量が大きすぎるため、ファイルが分割されていることがあります。そういったときは、ファイルを結合しましょう。例えば、`CentOS-5.8-i386-bin-1of7.iso`-`7of7.iso`があったとして、その場合は以下のようにします。なお、進行度を表示したい場合はオプションを付けてください。 ```sh cat CentOS-5.8-i386-bin-* > CentOS-5.8-i386-bin.iso ``` #VirtualBox 私は、[VirtualBox](https://www.virtualbox.org/wiki/Downloads)を使って仮想環境を構築し、複数のデスクトップやサーバーを起動することがよくあります。そんな時、仮想環境を直接操作することが通常ですが、たまにリモート操作したほうが便利なことがあります。そんな時は、リモート操作するために、`VirtualBox`のネットワーク設定と`SSH`の設定が必要になります。ここでは、その設定方法を書いていきたいと思います。 ##VirtualBoxの初期設定 非常に申し訳ないのですが、[VirtualBoxでOSをインストール手順](http://calves.jugem.jp/?eid=997)については省略させて頂きます。ここでは、仮想環境に[Ubuntu12.04](http://ftp.jaist.ac.jp/pub/Linux/ubuntu-jp-cdimage/releases/12.04/ubuntu-ja-12.04-desktop-i386.iso.torrent)をインストールすることで、話を進めていきたいと思います。 ###VirtualBox上でUbuntuを起動する VirtualBox上でUbuntuを起動させます。[VBoxManage](http://www.sssg.org/blogs/hiro345/archives/7199.html)などのコマンドを使用するといいと思います。 ``` #OSリストの確認 VBoxManage list vms #VRDP接続を有効にする VBoxManage modifyvm ubuntu12.04 --vrde on #ゲストOSの起動 VBoxManage startvm ubuntu12.04 ``` ##VirtualBoxのネットワーク設定 `VirtualBox`ホストをリモート操作する場合は、[NAT + ポートフォーワーディング](http://vboxmania.net/node/351)または、[ホストオンリーアダプタ](http://blog.youria.jp/kyo/mac/ssh_ubuntu_on_virtualbox/)の方法が一般的です。ここでは、後者の方法を採用し、設定していきたいと思います。 **VirtualBox > 環境設定 > ネットワーク**  **VirtualBox > Ubuntu > 設定 > ネットワーク**  ##SSHの設定 以下、サーバーが`Ubuntu`であり、クライアントが`Mac`となります。これを前提に読み進めていただけるとありがたいです。 ###SSHの接続設定 **サーバー側の操作** ```sh ifconfig sudo vi /etc/network/interfaces ``` ```:/etc/network/interfaces auto eth1 iface eth1 inet static address 192.168.56.2 netmask 255.255.255.0 network 192.168.56.0 broadcast 192.168.56.255 gateway 192.168.56.1 up route del default gateway 192.168.56.1 ``` ```sh sudo /etc/init.d/networking restart または sudo shutdown -r now sudo apt-get update && sudo apt-get install openssh-server sudo ufw allow ssh which ssh-copy-id ``` **クライアント側の操作** ``` sftp 192.168.56.2 >get /usr/bin/ssh-copy-id >exit mv ssh-copy-id /usr/local/bin source .zshrc ssh-keygen [保存先を聞かれたら'Enter'を押す。鍵のパスワード設定を聞かれるので入力する。決定したパスワードを再入力する。実行後、秘密鍵と公開鍵が'~/.ssh'以下に保存される] cd ~/.ssh && mv id_rsa.pub identity.pub ssh-copy-id 192.168.56.2 ssh 192.168.56.2 ```  ###SSHの認証設定 ```sh sudo vi /etc/ssh/sshd_config ``` ```:/etc/ssh/sshd_config #パスワード認証を無効化 PasswordAuthentication no ``` #ドットインストール [ドットインストール](http://dotinstall.com/)とは、無料でプログラミングを学べるサイトのことです。このサイトを利用すると、プログラミングの学習が捗ります。ただし、ここでは、いちいちサイトにアクセスするのも面倒なので、当該サイトに貼り付けてある`YouTube`動画をダウンロードして視聴してみたいと思います。 ##動画のダウンロード ```sh brew install youtube-dl brew install wget wget -r http://dotinstall.com/ find ~/dotinstall.com/lessons -name "[0-9]*" | xargs -I{} mv {} ~/dotinstall.com/lessons && cd ~/dotinstall.com/lessons && cat [0-9]* [0-9]* > filename.txt && grep videoId filename.txt > videoid.txt && sed "s|videoId: '|http://jp.youtube.com/watch?v=|" videoid.txt > url.txt && sed "s/',//g" url.txt > url2.txt && youtube-dl -t -a url2.txt mkdir -p ~/Movie/dt/ && mv *mp4 ~/Movie/dt ``` ##動画の視聴 ```sh brew install mplayer cd ~/Movie/dt #フォルダ内にあるファイルのパスをmylistに抽出する(ただし、隠しファイルは含まない) echo alias mylist='find `pwd` -maxdepth 1 -mindepth 1 | grep -v "\/\." > mylist' >> ~/.zshrc source ~/.zshrc mplayer -playlist mylist ```  ##Mplayerの操作 私は、動画を再生するときは基本的に倍速再生します。なので、`]`を押して、倍速再生してみます。 ``` Speace //スタートまたは、ストップ Enter //次へまたは、終了 < //前へ > //次へ [ //遅い ] //速い q //終了 ``` しかし、どうやら音声が高くなりすぎてしまって、他のプレイヤーと比べると、とても聞こえにくいです。 **追記** 投稿の上限が来たので、これ以降のツールの紹介は、次回になります。よろしくお願いします。 |
|
| 348位 |
|
|||
|
21:55:58 |
|
|
AngularJS で、よくあるログインのようなフローを作ってみました。
- 未ログインならログインページを表示する - 他の任意のURLはログインページへリダイレクトする - ログイン済みでログインページを表示しようとしたときはトップページにリダイレクトする - ナビゲーションの表示をログイン状態に応じて変化させる ## index.html 最低限の見た目のために Bootstrap を使っていますが、Bootstrap の CSS で `!important` な `display:block` が指定されているところがあり、ngCloak や ngShow が効かなくなることがあったのでそれらを上書きするための CSS を html 文中に書いています。 ```html:index.html <!doctype html> <html ng-app="App"> <head> <meta charset="utf-8"> <title>AngularJS Example</title> <link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.css"/> <style> #body [ng-cloak], #body .ng-hide { display: none !important; } </style> </head> <body id="body"> <nav class="navbar navbar-default" ng-controller="NavCtrl"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="#/">AngularJS Example</a> </div> <div class="collapse navbar-collapse" ng-cloak ng-show="user"> <ul class="nav navbar-nav"> <li><a href="#/">Home</a></li> <li><a href="#/page1">Page1</a></li> <li><a href="#/page2">Page2</a></li> </ul> <form class="navbar-form navbar-right"> <button type="button" class="btn btn-default" ng-click="logout()">Logout</button> </form> <div class="navbar-text navbar-right"> {{user.username}} </div> </div> </div> </nav> <div class="container-fluid"> <div ng-view></div> </div> <script src="lib/jquery/dist/jquery.js"></script> <script src="lib/angular/angular.js"></script> <script src="lib/angular-route/angular-route.js"></script> <script src="js/app.js"></script> </body> </html> ``` body にはまず最初にナビゲーションがあります。 ナビゲーションはログインの前後で表示する内容が異なるため NavCtrl コントローラーで制御します。 ログイン後であれば、各ページヘのリンク、ユーザー名、ログアウトボタン、などを表示します。 未ログインであればそれらが表示されないように `ng-show` を利用しています。 ナビゲーションの次に ngView があります。 ここには未ログインであればログインページを表示し、ログイン済みならなにかしらのコンテンツを表示します。 ## login.html ログインページのテンプレートです。 ```html:login.html <form class="form-inline" ng-submit="login()"> <input type="text" class="form-control" placeholder="username" ng-model="username"> <input type="password" class="form-control" placeholder="password" ng-model="password"> <button type="submit" class="btn btn-default" ng-disabled="disabled">Login</button> <span class="text-danger" ng-show="alert">{{alert.msg}}</span> </form> ``` ## ルーティング js でルーティングを定義します。otherwise を除いて全部で4つのルートを定義しています。 /login がログインページのルート、その他の3つはログイン後のページです。 ログインページ以外のルートはコントローラーやテンプレートを別に用意するのが面倒なので手抜きです。 ```js:app.js var app = angular.module('App', ['ngRoute']); app.config(function($routeProvider){ $routeProvider.when('/', {template: '<h1>home</h1>', controller: function(){}}); $routeProvider.when('/page1', {template: '<h1>page1</h1>', controller: function(){}}); $routeProvider.when('/page2', {template: '<h1>page2</h1>', controller: function(){}}); $routeProvider.when('/login', {templateUrl: 'view/login.html', controller: 'LoginCtrl'}); $routeProvider.otherwise({redirectTo: '/'}); }); ``` ## 認証 認証のためのサービスを定義します。 認証はユーザー名とパスワードに同じ値を入れれば通ります。というか、なにも入力しなくても通ります。 本当に実装するときは非同期の問合せになるので、500 ミリ秒遅延する Promise を返しています。 ```js:app.js app.factory('AuthService', function($q, $timeout){ var _user = null; return { isLogged: function(){ return !!_user; }, getUser: function(){ return _user; }, login: function(username, password){ var deferred = $q.defer(); $timeout(function(){ if (username == password) { _user = {username: username}; deferred.resolve(); } else { deferred.reject(); } }, 500); return deferred.promise; }, logout: function(){ _user = null; return $q.all(); } }; }); ``` ## \$routeChangeStart イベント 次にモジュールの run で `$rootScope` に `$routeChangeStart` イベントをバインドします。 このイベントは ngRoute のルーティングの開始時に呼ばれます。 これから表示しようとしているルートのコントローラーが LoginCtrl であるかどうか、ログイン済みであるかどうか、を元に必要に応じて適切なルートにリダイレクトします。 ```js:app.js app.run(function($rootScope, $location, $route, AuthService){ $rootScope.$on('$routeChangeStart', function(ev, next, current){ if (next.controller == 'LoginCtrl') { if (AuthService.isLogged()) { $location.path('/'); $route.reload(); } } else { if (AuthService.isLogged() == false) { $location.path('/login'); $route.reload(); } } }); }); ``` ## ログインコントローラー ログインコントローラーを作ります。 AuthService.login は Promise を返すので、成功時はトップにリダイレクト、失敗時はメッセージを表示します。 さらに失敗/成功に関わらずフォームをクリアしていますが、よく考えると成功時はこの処理必要ありませんでした。 ```js:app.js app.controller('LoginCtrl', function($scope, $location, AuthService){ $scope.login = function(){ $scope.disabled = true; AuthService.login($scope.username, $scope.password) .then(function(){ $location.path('/'); }) .catch(function(){ $scope.alert = {msg: "Login failed"}; }) .finally(function(){ $scope.username = ""; $scope.password = ""; $scope.disabled = false; }) ; }; }); ``` ## ナビコントローラー 最後にナビコントローラーを作ります。 ログイン状態は AuthService サービスが持っているので `$watch` で監視してナビコントローラーのスコープに設定します。 また、ログアウトでログインページにリダイレクトします。 ```js:app.js app.controller('NavCtrl', function($scope, $location, AuthService) { $scope.$watch( function(){ return AuthService.getUser() }, function(newVal, oldVal){ $scope.user = newVal } ); $scope.logout = function () { AuthService.logout().finally(function(){ $location.path('/login') }); }; }); ``` ## 問題点? "/" のルートに home.html などのテンプレートを使うと判るのですが、一番最初にサイトに訪れてログインページが表示されるとき、home.html がダウンロードされています。 `$routeChangeStart` イベントが呼ばれる時点ではまだテンプレートのダウンロードは開始されていないのですが、そのタイミングで `$location.path()` で遷移させてもテンプレートのダウンロードは止められないようです。 特に何が困るわけでもありませんが、なんとなく気持ち悪いです。 ## \$routeProvider の resolve での実装 \$routeChangeStart イベントは使わずに(app.run をごっそり削除)して、ルーティングを次のようにする方法も考えられます。 ```js app.config(function($routeProvider){ var requireAuth = { login: function($q, $location, AuthService){ if (AuthService.isLogged() == false) { $location.path('/login'); return $q.reject(); } } }; var skipLogin = { login: function($q, $location, AuthService){ if (AuthService.isLogged()) { $location.path('/'); return $q.reject(); } } }; $routeProvider.when('/', { template: '<h1>home</h1>', controller: function(){}, resolve: requireAuth }); $routeProvider.when('/page1', { template: '<h1>page1</h1>', controller: function(){}, resolve: requireAuth }); $routeProvider.when('/page2', { template: '<h1>page2</h1>', controller: function(){}, resolve: requireAuth }); $routeProvider.when('/login', { templateUrl: 'view/login.html', controller: 'LoginCtrl', resolve: skipLogin }); $routeProvider.otherwise({ redirectTo: '/' }); }); ``` ## ログイン時にルーティングを再設定する実装 あるいは、ログアウトは実際にブラウザをリロードするという前提で、ログイン後にルーティングを再設定する方法も考えられます。 ```js app.config(function($routeProvider){ // 最初は otherwise な LoginCtrl のルートしかない $routeProvider.otherwise({ templateUrl: 'view/login.html', controller: 'LoginCtrl', resolve: { login: function($route, AuthService){ if (AuthService.isLogged()) { // ログインに成功したらルートを追加&上書きする $routeProvider.when('/', { template: '<h1>home</h1>', controller: function(){} }); $routeProvider.when('/page1', { template: '<h1>page1</h1>', controller: function(){} }); $routeProvider.when('/page2', { template: '<h1>page2</h1>', controller: function(){} }); $routeProvider.otherwise({ redirectTo: '/' }); $route.reload(); } } } }); }); ``` ```js // 不要 // app.run(); ``` ```js app.controller('LoginCtrl', function($scope, $route, AuthService){ $scope.login = function(){ $scope.disabled = true; AuthService.login($scope.username, $scope.password) .then(function(){ // ログインに成功したらルーティングを呼ぶ $route.reload(); }) .catch(function(){ $scope.alert = {msg: "Login failed"}; }) .finally(function(){ $scope.username = ""; $scope.password = ""; $scope.disabled = false; }) ; }; }); ``` ```js app.controller('NavCtrl', function($scope, $window, AuthService) { $scope.$watch( function(){ return AuthService.getUser() }, function(newVal, oldVal){ $scope.user = newVal } ); $scope.logout = function () { AuthService.logout().finally(function(){ // ログアウトはブラウザのリロード // ($routeProvider に追加したルートをクリアする他の方法がわからない) $window.location.reload(); }); }; }); ``` この方法なら `/page1` とかをダイレクトに開いた場合でも、ログインページを挟んだあとに `/page1` を表示することが出来ます。 - デモ - [http://jsbin.com/dojoq/4/](http://jsbin.com/dojoq/4/) |
|
| 349位 |
|
|||
|
18:19:37 |
(FaithCreates Inc. 所属) |
|
よーしお前ら、今日はシェルスクリプトを勉強するぞ。シェルの種類はbashだ。ただ、他のシェルでも基本は同じなので安心してくれ。
シェルスクリプトと言っても、findの使い方とかglob展開とかそういう細かいTIPSはやらん。そういうのは他の記事でも読んでくれ。そうじゃなくて、基本的な処理の流れ、考え方の勉強だ。 だいたいシェルスクリプトなんて普段大してやってなくて、いざ必要になったときに「まあPerlやRubyみたいなもんだろ」とか軽く考えてたらしょーもないことでハマって時間を無駄にしたりするもんだ。 なんでそんなことが起きるのか。 それはシェルスクリプトの基本的な発想が分かってないからだ。PerlやRubyのような普通に使われてるプログラム言語とシェルスクリプトの書き方はけっこう違うところがあって、それを理解せずに書いてしまってるからだ。 そもそもシェルとは何かもう一度思い出してみろ。 今普通にコンピュータを使っていて、テキストファイルを1つ作りたいとしよう。「XXXというディレクトリの下に」「YYYという名前で」「ファイルを作る」という命令を送らなければならない。これを誰に送るか。最終的な送り先はOSだ。もっと正確に言うとKernelだ。 もちろんKernelはこういう命令を受け付ける。しかし、Kernelは機械だ。マシンだ。全然人にやさしくない。その「命令」の書き方がまったくユーザーフレンドリーじゃない。 ファイルを作るという簡単なことだけでCの`fopen`に相当することをしなくてはならない。引数もなんだかよくわからない。その引数はKernelにとっては必要なのだろうが、人間にはややこしすぎる。厳しい。めんどくさい。 というわけで、直接Kernelに命令を送る作戦はやってやれないことはないが、そんなことやってたらたぶん死ぬ。 そこでシェルの登場だ。シェルは人にやさしい。ユーザーフレンドリーだ。人間からの命令を受け付けることを想定している。そしてそれをKernelに分かる形に変換して渡す。いいやつだ。みんな大好きだろ。俺も大好きだ。 大事なので繰り返す。シェルは人間からの命令を受け付けるために存在する。命令と言っても大したことはない。みんなもいつもやってるだろ。 ``` % touch ~/work/memo.txt ``` とかいうやつだ。「~/work/memo.txtにファイルを作ってくれ」という意味だ。いや、実を言うとこれは「touchという実行可能なファイルがあるので、~/work/memo.txtを引数としてそいつを呼び出してくれ」だ。そのへんのことはまた後で説明する。 とにかく、みんな日常的にシェルに命令を送ってる。その命令というのは、文字列だ。普通はキーボードから手で入力する。でも、文字列だったらテキストファイルに書いておいてそいつを読ませてもいいと思うだろ。それがシェルスクリプトだ。 シェルは起動したら ``` % ``` とかプロンプトを表示して、入力を待ってる。そして改行までを1行の命令として解釈する。 さっきの例で言うと、`touch ~/work/memo.txt<CR>`とキーボードを打つだろ。そうしたら1行分の命令が入力されたということで、そいつを実行するわけだ。ちなみに`<CR>`ってのはReturnキー(Enterキー)を押したという意味だ。この記事ではこう書くことにする。 1行解釈して実行したら、また次の命令を待つ。そこでまた適当に命令を入れて`<CR>`とすると、そいつをまた実行してくれる。 じゃあ、この命令をあらかじめファイルに書いておいたとしよう。 ``` touch ~/work/memo.txt ``` こんな感じのテキストファイルを作った。そのまま書いただけだ。ファイル名は何でもいいんだが、memo.shとしたとしよう。最後に改行コードも入れておいたので、正確に書くとこうだ。 ``` touch ~/work/memo.txt<CR> ``` こいつをシェル(bash)に読ませるにはこうすればいい。 ``` % bash memo.sh ``` bashはこのファイルの中身を読みこむ。`touch ~/work/memo.txt<CR>`と書いてあるので、`touch ~/work/memo.txt`までを1行の命令と解釈して実行する。 これがシェルスクリプトだ。これは手でキーボードから入力したのと全く同じ動作だ。 - キーボードで命令を入れて`<CR>`(Returnキーを押す) - ファイルに命令を書いて末尾に`<CR>`(改行) 同じだ。シェルは手で入力した時もファイルに書いてある時も`<CR>`までを読み取ってそいつを実行する。 もっと言うと、キーボードからの入力を読み取るというのはUNIX的に言うと「標準入力」という「ファイル」を読み取ることだ。テキストファイルを読み取るのと同じだ。細かいマニアックなところで違いはあるかもしれないが、それは全然本質的ではない。 というわけで、シェルスクリプトというのは、仕組みとしては普段のコマンドラインの操作と同じということをまず理解しろ。 もちろん、シェルスクリプトとして書くときは複雑な命令を書くことが多い。普通は変数も使うし、`if`とか条件分岐を使うことも多い。一方、手入力の場合はそんな難しいことはせずに、目で確認しながら入力するだろう。 ただ、それはそういう使い方をしているというだけの話だ。手入力のような簡素なシェルスクリプトを書いてもいいし、逆に手入力の時に`if`で条件分岐したっていい。 もう一度繰り返すぞ。コマンドラインの入力とファイルに書いてあるシェルスクリプトは同じだ。 そして、さっきシェルはユーザーフレンドリーだと言ったのを覚えているか。そう、シェルは人間からの手入力を前提としてる。設計思想としてそうだ。なので手で打った時に楽になるような文法になってる。 例えば`ls`でルートディレクトリの中を見たかったとしよう。ついでに`-a`オプションもつけてみよう。 ``` % ls -a / ``` こうだ。当たり前の書き方だが、よく考えてみると普通のプログラム言語とはだいぶ違う。 例えば、CやJavaのように書くとしたらこうなるだろう。 ``` # こんな書き方はできない % ls("-a", "/") ``` めんどくさい。毎日こんなのをやってたら死ぬ。もちろん実際にはこんなふうにはなってなくて、`ls -a /`のように書ける。 ここで大事なのは以下だ。 - 文字列は`"`で囲まなくても良い。文字をずらずら並べたらそれが文字列だ。 - `,`も必要ない。単語はスペースで区切る - 引数のカッコもいらない。1つ目に書いたものがコマンドで、それ以降が全部引数だ つまり基本的な書き方は以下のようになる。 ``` コマンド 引数1 引数2 ... ``` いつも普通に書いてると思うが、この形が重要だ。覚えておけよ(また後で出てくる) ちょっとここで例を出そう。変数の代入だ。変数の代入はシェルスクリプトの中でも間違えやすいものだ。例えば、「fooという変数にbarという文字列を入れる」というときの正しい書き方はこうだ。 ``` foo=bar ``` よくある間違いとしてこういうのがある。 ``` # 間違い foo = bar ``` `=`の周りにスペースを入れているパターンだ。普通のプログラム言語をやってたらこう書きたくなるのも分かる。 でもさっきの「コマンド 引数1 引数2 ...」という形に当てはめて考えてみると、これは「`foo`というコマンドを、`=`と`bar`を引数として実行する」という意味になる。 だいたい、本当にfooというコマンドが存在して、それに=とbarを引数に渡したかったらどう書く? `foo = bar` と書くだろ。こう書いて変数の代入になったら困るだろ。そういうことだ。 つまり、`=`を引数にしたいことは普通にありえるので、代入はそれとは違う形でなくてはならない(`=`は予約語でも何でもないことに注意)。「コマンド 引数1 引数2 ...」という形が大前提としてあって、代入はそれよりは頻度が少ないのでそれより特殊な書き方をルールとしているということだ。 もう1つ、ifの例にいくぞ。「fooという変数の値がbarという文字列に等しかったら処理1を行う」というときの正しい書き方はこうだ。 ``` if [ bar = $foo ] then # 処理1 fi ``` 普通は`if [ $foo = bar ]`と書くと思うが、説明の都合で順番を逆にした。どっちでも正しい書き方だ。 よくある間違いとして、こんなのがある。 ``` # 間違い if[bar = $foo] then # 処理1 fi ``` 適当にスペースを詰めている書き方だ。 でもさっきと同じだ。「コマンド 引数1 引数2 ...」という形に当てはめて考えてみると、「`if[bar`というコマンドを `=`と`$foo]`を引数として実行する」という意味になる。そして`if[bar`なんてコマンドは(普通は)ないのでエラーなるわけだ。逆に本当にあったらそれが実行される。というわけで、これも全く違う意味になっていることが分かるだろう。 ここで大事なのは、「スペースのありなしで意味が変わる」ということだ。「引数はスペースで区切る」というルールなので、スペースがあるかないかは普通のプログラム言語で言うと`,`があるかないかと同じぐらい違う意味になる。これでさっきの例でスペースを無くしてエラーになったことも納得できるだろう。 だらだら話をしてきたが、強引にまとめに入るぞ。 - 手入力のコマンドラインとファイルに書いてあるシェルスクリプトは本質的に同じだ - 普通の言語で言うところの `func("param1", "param2")` がシェルスクリプトの `func param1 param2` にあたる - スペースの有無は重要だ ここまででシェルスクリプトの基本的な構造が分かっただろう。 なぜシェルスクリプトが書きにくいのか、間違えやすいのか。それはシェルの文法が手入力を前提としているからだ。対話的にキーボードから入力して使いやすいようになっている。その範囲内で`if`やらなんやらのプログラム言語っぽい文法を兼ね備えている。なので、はっきり言って本格的なスクリプトは書きにくい。もしシェルの力では難しいようなことになったら、そのときは他の言語で実装することを考えたほうがいいだろう。 他にも色々と言いたいことがあるが、キリがないので今日はこのくらいにしておこう。 |
|
| 350位 |
|
|||
|
08:49:16 |
|
|
サンプルアプリのソースコードをGitHubで公開しました。 https://github.com/gabu/android-twitter-sample
## はじめに [Android再入門 - 目次](http://qiita.com/items/a8f59b31f7e6408ac049) に引き続き、 [第7回名古屋Android勉強会 Java+Eclipse再入門 on Zusaar](http://www.zusaar.com/event/512004 "第7回名古屋Android勉強会 Java+Eclipse再入門 on Zusaar") [第7回名古屋Android勉強会 Java+Eclipse再入門 学生枠 on Zusaar](http://www.zusaar.com/event/512003 "第7回名古屋Android勉強会 Java+Eclipse再入門 学生枠 on Zusaar") のために書きました。 イベントの流れの都合上 **再入門** になっていますが、全然 **再** な内容を含んでいないので **入門** でいいんじゃないかと思ってます。 あと、Java + Eclipseというよりは初心者の方でも **Androidアプリを作る体験ができること** と **イヤにならないこと** を考えた結果、逆に、Java + Eclipseにあまり触れられませんでした。Java + Eclipseについては今後の宿題ということで考えていきたいと思います。 ## 開発環境について 環境構築については書いても書いてもバージョンが上がるので割愛しています。 以下の開発環境を構築した上でお読み頂ければ幸いです。 * Eclipse Helios (Version 3.6.2) 以降 * スクリーンショットは Eclipse Juno 4.2.1 で撮ってますが、Heliosでも開発には問題ないはずです。 * ADT (Android SDK Tools) r21 * http://developer.android.com/tools/sdk/eclipse-adt.html * Android 4.2 (API 17) SDK Platform ## 目次 1. [Twitter APIを使うアプリを登録](http://qiita.com/items/4a63e6b56dd50da1f528) 1. [Twitter4Jのダウンロードと準備](http://qiita.com/items/f6f39900fd5e449045f9) 1. [OAuth認証](http://qiita.com/items/673288c3a5b39f89aa92) 1. [タイムラインを表示](http://qiita.com/items/53857fcfa871b921af45) 1. [つぶやく](http://qiita.com/items/ac505aac0ccf2b3d0b42) 想定読者は、Androidアプリ開発の未経験者の方です。どんな些細な事でもフィードバックを頂けると嬉しいです。 [Eclipseショートカット(最低限のやつだけ)](https://docs.google.com/spreadsheet/ccc?key=0Anky18ZgFygwdHVjb1NVMVVOWlhrTGprVkVPSFFaWFE&usp=sharing#gid=0) を作ったのでこれを見ながらショートカットを使って覚えましょう。 ## Twitter APIについて 今回、Twitterクライアントをサンプルアプリの題材として選んだのですが、本気でTwitterクライアントを作ることを考えている人は必ず以下を確認してください。非常に重要な変更がありました。 [Twitter APIと開発者規約変更のインパクトまとめ:結局、Twitter API 1.1で何が変わる? 5つのポイント - @IT](http://www.atmarkit.co.jp/ait/articles/1209/26/news120.html "Twitter APIと開発者規約変更のインパクトまとめ:結局、Twitter API 1.1で何が変わる? 5つのポイント - @IT") ## おすすめ書籍 良かったら僕の本買ってください。 **初心者向け** [最小限のJavaも一緒に解説するAndroid入門本を書きました。 - gabuchanの日記](http://d.hatena.ne.jp/gabuchan/20120905/1346837197 "最小限のJavaも一緒に解説するAndroid入門本を書きました。 - gabuchanの日記") **中級者向け** [「Android SDK開発のレシピ」が第2版になりました。 - gabuchanの日記](http://d.hatena.ne.jp/gabuchan/20121002/1349179288 "「Android SDK開発のレシピ」が第2版になりました。 - gabuchanの日記") |
|
| 351位 |
|
|||
|
20:26:10 |
|
|
定期的なメンテナンスをRakeタスクとして登録して呼び出したいと考えました。 Rakeタスクの作り方を調べたのでメモ。 シナリオは、メール認証が一週間以上されないユーザーのレコードを削除するです。 ``` $ rails g task inactive_user #=> inactive_userが名前 #=> rake inactive_user:xxx のように使うので名前に気をつけて ``` ```ruby:lib/tasks/destroy_inactive_user.rake # encoding: utf-8 namespace :inactive_user do desc "Users中confirm_atから1週間が経過していれば削除" #=> 説明 # $ rake inactive_user:destroy_unconfirmed のように使う # :environmentは超大事。ないとモデルにアクセスできない task :destroy_unconfirmed => :environment do User.all.each do |user| user.destroy if (Time.now > user.confirm_at + 1.weeks.ago) end end end ``` これでOK タスクが登録されているか確認してみる ``` $ rake -vT # 略 rake inactive_user:destroy_suspended # Users中suspended_atから3ヶ月経過していていれば削除 rake inactive_user:destroy_unconfirmed # Users中confirm_atから1週間が経過していれば削除 rake inactive_user:list_suspended # Users中suspended_atから3ヶ月経過していていれば表示 rake inactive_user:list_unconfirmed # Users中confirm_atから1週間が経過していれば表示 ``` ちゃんと入ってますね。 実際に動かしてみる、、、前にいきなり削除すると怖いので、 ```ruby:lib/tasks/destroy_inactive_user.rake # encoding: utf-8 namespace :inactive_user do desc "Users中confirm_atから1週間が経過していれば削除" task :destroy_unconfirmed => :environment do User.all.each do |user| puts user.to_yaml if (Time.now > user.confirm_at + 1.weeks.ago) end end end ``` こう変えましょう。 先にデータベースの下ごしらえをします。 モデルは何となく想像してください。 ``` $ rails c >> User.create({:name => "new", :email =>"a@example.com", :confirmed => false, :confirm_at => Time.now}) #=> 成功すること >> User.create({:name => "old", :email =>"b@example.com", :confirmed => false, :confirm_at => 1.weeks.ago}) #=> 成功すること ``` 実行します。 ``` $ rake inactive_user:destroy_unconfirmed --- !ruby/object:User attributes: id: 3 name: old email: b@example.com created_at: 2012-09-18 10:42:32.000000000 Z updated_at: 2012-09-18 10:42:32.000000000 Z confirmed: false confirm_at: 2012-09-11 10:42:32.000000000 Z ``` ちゃんと捕捉できました。 では削除用スクリプトに書き直して、、、ログも残しておきたいですよね? ```ruby:lib/tasks/destroy_inactive_user.rake # encoding: utf-8 namespace :inactive_user do desc "Users中confirm_atから1週間が経過していれば削除" task :destroy_unconfirmed => :environment do User.all.each do |user| logger = Logger.new('log/inactive_user.log') logger.info "#{Time.now} -- destroy_unconfirmed_inactive_user -- #{user.to_yaml}" user.destroy if (Time.now > user.confirm_at + 1.weeks.ago) end end end ``` こうしました。 実行します。 ``` $ rake inactive_user:destroy_unconfirmed Process finished with exit code 0 ``` ```ruby:log/inactive_user.log # Logfile created on 2012-09-18 19:58:37 +0900 by logger.rb/31641 2012-09-18 19:58:37 +0900 -- destroy_unconfirmed_inactive_user -- --- !ruby/object:User attributes: id: 3 name: old email: b@example.com created_at: 2012-09-18 10:42:32.000000000 Z updated_at: 2012-09-18 10:42:32.000000000 Z confirmed: false confirm_at: 2012-09-11 10:42:32.000000000 Z ``` ログもとれました。 以上で機能を達成できましたが、もっと良い方法だったり問題点があれば教えてください。 具体的なシナリオを言及した投稿がもっとたくさんあるといいですね。 |
|
| 352位 |
|
|||
|
11:19:20 |
(co-meeting, Inc. 所属) |
|
# WebRTCとは
WebRTCはWebブラウザ間でP2P通信をするための仕様です。プラグインなしでビデオチャットが可能になることが一番注目されているところです。 詳しくは今年4月のイベントで吉川さんが発表した資料がすごくわかりやすいので、それを見てください。 - スライド: [WebRTCで変わる未来(PDF)](http://www.qcontokyo.com/data_2013/ToruYoshikawa_QConTokyo2013.pdf) - その記事: [HTML5で情報最適化/視覚化&WebRTCで変わる未来~QCon Tokyo 2013レポート (2/3) - @IT](http://www.atmarkit.co.jp/ait/articles/1305/30/news024_2.html) NAT周りの話がよくわからなかったら、ちょっと古いですが、この記事「[こてさきAjax:WebRTC事始め - livedoor Blog(ブログ)](http://blog.livedoor.jp/kotesaki/archives/1794148.html)」を読むとよいと思います。 なお、上記資料ではData ChannelはまだStableでは使えないと書かれていますが、現在は既にChrome、FirefoxのStableバージョンで実装されています。オプションも変更せずに利用できます。 # オープンソースのWebRTCライブラリがなんで必要か ## 1. ネイティブAPIは実装が面倒 WebRTCの[ネイティブAPI](http://www.webrtc.org/reference/native-apis)を使うと[apprtc.appspot.com](https://apprtc.appspot.com/)のような簡単なビデオチャットを実装するだけでも、[main.js](https://code.google.com/p/webrtc/source/browse/trunk/samples/js/apprtc/js/main.js)、[adapter.js](https://code.google.com/p/webrtc/source/browse/trunk/samples/js/base/adapter.js)のような長ったらしいコードになります。 ライブラリを使うとこんな簡単で済む。PeerJSの例 [peerjs/examples/videochat/index.html at master · peers/peerjs](https://github.com/peers/peerjs/blob/master/examples/videochat/index.html) ## 2. サーバーを用意しないといけない 上の資料を読むとわかりますが、P2Pなんだけど、以下の2種類のサーバーが必要になります。 - Signalingサーバー(ブローカーサーバーと言われたりもする) - ICEサーバー(STUN+TRUNサーバー) ICEサーバーがNAT越えのための情報を登録するサーバー、Signalingサーバーはその情報をクライアントPC同士で交換するためのサーバー(WebSocketで実装されることが多い)と理解しておけばだいたい合ってると思います。 多くのオープンソースWebRTCライブラリはSignalingサーバーも提供しています。 # WebRTCのOSS一覧 | 名前 | 最終コミット | ドキュメント | Signalingサーバー | ライセンス | 備考 | |-----------|------------|------------|------------|------------|------------| | [PeerJS](http://peerjs.com/) | 3日前 | 多い | 有 | MIT | SignalingサーバーをSaaSでも提供 | | [EasyRTC](http://easyrtc.com/) | 4日前 | 多い | 有 | BSD2 | masterではなく[alpha](https://github.com/priologic/easyrtc/tree/alpha)と[beta](https://github.com/priologic/easyrtc/tree/beta)ブランチで開発されているので注意。 | | [XSockets](http://xsockets.net/) | 19日前 | 多い | 有 | MIT | .NETだったのであまり見てない | | [simpleWebRTC](http://simplewebrtc.com/) | 1ヶ月前 | 少ない | 別の[OSS](https://github.com/andyet/signalmaster)を使えと書かれている | MIT | [デモ](https://talky.io/)はよくできているが、ソースは公開されていない。 | | [Holla](http://wearefractal.com/holla/) | 4ヶ月前 | 多い | 有 | MIT | | | [webRTC.io](https://github.com/webRTC/webRTC.io) | 7ヶ月前 | 少ない | 有 | MIT | クライアントサイドはライセンスの記載なし | WebRTCは変化が速いのできちんとキャッチアップしているPeerJSとEasyRTCがよさそうです。 ただし、EasyRTCはなぜかサンプルのビデオチャットが真っ黒になってうまく動きませんでした。原因は調べていません。 # ICEサーバー 上記のOSSにはICEサーバーは含まれておらず、だいたい`stun.l.google.com:19302`というGoogleのSTUNサーバーをデフォルトで指定しています。 本番で動かすにはICEサーバーも自前で立てるのが望ましいです。 ICEサーバーは元々いろいろなOSSがあります。 表にまとめるのは力尽きたので、リストで。一番上のがいいんじゃないかなってなんとなく思っています。 * [rfc5766-turn-server - High-performance free open source TURN and STUN Server implementation.](https://code.google.com/p/rfc5766-turn-server/) * [STUN Client and Server | Free software downloads at SourceForge.net](http://sourceforge.net/projects/stun/) * [Stuntman - open source STUN server](http://www.stunprotocol.org/) * [restund - Open Source STUN/TURN Server](http://www.creytiv.com/restund.html) * [TurnServer - open-source TURN server implementation | Main / HomePage](http://turnserver.sourceforge.net/) * [ReTurn Overview - reSIProcate](http://www.resiprocate.org/ReTurn_Overview) # WebRTCのクラウドサービス ここでは簡単に紹介するだけにしますが、WebRTCを簡単にアプリに組み込めるようにするクラウドサービスもたくさん出てきています。 代表的なのだけでも、以下のようなサービスがあります。 - [vLine](https://vline.com/) - [OpenTok](http://tokbox.com/) - [AddLive](http://www.addlive.com/) - [TenHands](https://tenhands.net/developer/index.htm) - [Phono - WebRTC](http://phono.com/webrtc) - [Weemo](http://www.weemo.com/) 国内でもこれらを自社サービスに組み込んで提供しているところも出てきています。 [ChatWork](http://www.chatwork.com/ja/)はAddLiveを使っているし、オンライン音楽レッスンサービスの[muty](http://muty.jp/)はvLineを使っています。 これらのクラウドサービスの利点は大きく以下の2つかなと思います。 - iOS, Android向けなどSDKが豊富。 - 多人数同時ビデオチャットが可能。 実のところ2番目の問題がOSSでは解決するのが難しい壁です。 OSSで多人数同時を試すともう3人くらいでCPUが張り付いてファンがブオンブオン言います。 これは以下の問題があるからです。 > マルチ参加者コール は、実装することがより困難である。1つのオプションは、各参加者が他のすべての参加者に接続するフルメッシュネットワークであるが、高CPUと高帯域幅の使うために、このアプローチは5-6人の参加者を超える拡張ができない。代替案は、すべてのストリームを集約し、それらを他の全ての参加者に、別々にあるいは単一のストリームにマージして、ブロードキャストする中間サーバーを使うことだ。 > 出典:[議論多いWebRTC:リアルタイム通信の課題と機会](http://www.infoq.com/jp/news/2013/06/wrangling-webrtc) そして上記のクラウドサービスたちはこの問題を解決して、多人数同時ビデオチャットを可能にしています。OpenTokは[Mantis](http://www.tokbox.com/blog/mantis-next-generation-cloud-technology-for-webrtc/)という独自のソフトウェアで解決し、vLineはMCUというハードでこれを解決しているらしいです。他は知りません。 Mantisと同等のことができるOSSで、[Licode](http://lynckia.com/licode/)というのがあるのですが、かなりのヘビーっぷりで運用するのは骨が折れそうです。 この辺りの説明はこの記事「[Beyond one-to-one: multi-party WebRTC - HTML5 Rocks](http://www.html5rocks.com/en/tutorials/webrtc/infrastructure/#beyond-one-to-one-multi-party-webrtc)」にわかりやすく書かれています。 # おわりに OSSには上記のような課題もありますし、コスト的にクラウドサービスで問題ないなら使ってしまうのがいいと思います。 ビデオチャットが注目されていますが、WebRTCのDataChannelはテキストでもファイルでも何でも送れるのでいろいろと活用できると思います。WebRTCを活用したおもしろサービスが出てくるといいですね。 と長々とビデオチャット絡みのことを書いてきましたが、私は[co-meeting](https://www.co-meeting.com/ja/)という **テキストだけでミーティングができちゃうほどリアルタイムなチームコミュニケーションツール** を作っていますので、仕様・設計・実装の相談、トラブル時のやり取りなど開発チーム間のコミュニケーションによかったら試してみてください。 [co-meeting](https://www.co-meeting.com/users/invitation/new) |
|
| 353位 |
|
|||
|
13:53:44 |
|
|
Web ブラウザーは通常 HTTP 要求の <a href=https://wiki.suikawiki.org/n/Referer%3A><code>Referer:</code></a> ヘッダーに参照元ページの URL を入れますが (あるいは <a href=https://wiki.suikawiki.org/n/document.referrer><code>document.referrer</code></a> で参照元ページの URL を取得できますが)、 Web サイト側でこれを制御したいことがあります。
例えば、次のような場面が想定されます。 - URL にユーザー名や秘密の ID などを含めざるを得ない時は、プライバシーやセキュリティーの観点から、この URL を外部に漏らしたくありません。 - 社内システムに URL を貼りたいことがありますが、社内システムの URL を外部に漏らしたくありません。 - Web アプリケーションの開発用サーバーは、その所在を外部に漏らしたくありません。 - 投稿者と友達のみに公開される SNS の投稿にリンクが含まれる時、その個別 URL を漏らしたくありません。 (SNS 全体の URL が漏れることは問題ありません。場合によっては、流入元を示すため、敢えて残したいです。) - 提携 Web サイト間での利用者の動向を分析するに当たり、 `https:` から `http:` へのリンクであっても `Referer:` が送信されてほしい場合もあります。 - 外部リンクで `Referer:` は送出したくないとしても、同じサイトの `POST` では CSRF 検査のため `Referer:` や `Origin:` を送ってほしいことがあります。 このような場合に、 Web サイト側で出来る対策を、古典的な方法から順に紹介します。 この記事は2013年に執筆したものですが、2016年に最新の状況に合わせて改訂しています。 # 中間ページを挟む ***この方法は、おすすめできません。*** 本来のリンク先の URL に直接リンクするのではなく、その URL へのリンクを含むページへリンクします。例えば、 <code>http://www.example.com/</code> にリンクしたいなら、 <code>http://redirector.example.com/<mark>http://www.example.com/</mark></code> にリンクして、そちらでは <code>http://www.example.com/</code> へのリンクが表示されるようにします。 <code>www.example.com</code> に送られる <code>Referer:</code> は <code>redirector.example.com</code> の URL になりますから、元のページの URL は漏れません。 これだけでは閲覧者が余分にリンクをたどる必要がありますから、 <code>redirector.example.com</code> にはリンクを自動的にたどる機能があると便利です。 <code><meta http-equiv=Refresh></code> や <code>location.replace</code> を使ったり、リンクの <code>a</code> 要素の <code>click</code> メソッドを呼び出したりする方法があります。これらの場合には仕様上は <code>redirector.example.com</code> の <code>Referer:</code> が送られるはずですが、<a href="http://www.facebook.com/notes/facebook-engineering/protecting-privacy-with-referrers/392382738919">ブラウザーによっては送られないことがある</a>ようです。ただ単に特定のページの <code>Referer:</code> を送らないようにしたいのではなく、その値を制御したいのであれば、どの方法を採るか検討する必要がありそうです。なお、 <code>location.href</code> への代入だと履歴に余分なページが残ってしまい、「戻る」が正常に動かなくなってしまいます。また、 HTTP リダイレクトは元の <code>www.example.com</code> の URL がリダイレクト先の <code>Referer:</code> にも使われてしまうので、この目的では使えません。 この方法は <code>img</code> 要素などには使えません。 <code>data:</code> URL を使えば中間ページを別途用意する必要はありません。ただし対応していない古い Web ブラウザーではリンクをたどれなくなってしまいます。 ```html <a href="data:text/html,<a href='http://www.example.com/'>進む</a>">リンク</a> ``` どの方法もリンクが汚くなってしまうのが難点です。 CSS の <code>:visited</code> や Web ブラウザーの「リンクの URL をコピー」のような機能が思った通りに動かなくなりますし、制作者側もリンク時に一手間必要になります。 10年以上前から 2ch で使われている <a href=https://wiki.suikawiki.org/n/ime.nu><code>ime.nu</code></a> が有名です。かつてはこれがほとんど唯一の方法で、よく使われていました。 # HTTPS (HTTP/TLS or HTTP/SSL) を使う ***この方法は、今となっては不適切です。*** <a href=https://wiki.suikawiki.org/n/Referer%3A#section-%E8%A6%81%E6%B1%82%E3%81%AE%E5%8F%82%E7%85%A7%E5%85%83%E3%81%AE%E6%B1%BA%E5%AE%9A data-old-href="https://tools.ietf.org/html/rfc2616#section-15.1.3"><code>https:</code> から <code>http:</code> へのリンクでは、 <code>Referer:</code> は送らない</a>ことになっています。これを利用して、漏らしたくない URL を含むホスト全体を <code>https</code> で提供すれば、 <code>http:</code> URL への参照があっても <code>Referer:</code> の送信を抑制できます。 TLS (SSL) 証明書を用意しなければならないなど多少の手間はありますが、対象となるページやリンク自体に手を入れなくてもいいという意味では、とても手軽な方法です。 ただし、 <code>https:</code> のページへの <code>Referer:</code> は送信されてしまいます。 <code>https:</code> 化することで <code>Referer:</code> が送信されなくなったと安心してしまいがちですが、実際には対策として不完全で、**危険**です。最近は <code>https:</code> を使うのが当然という流れになってきていますから、 `Referer:` が送信されてしまうケースはどんどん増えていきます。 # <code>rel=noreferrer</code> <code>a</code> 要素や <code>area</code> 要素に <a href="https://www.whatwg.org/specs/web-apps/current-work/#link-type-noreferrer"><code>rel=noreferrer</code></a> を指定すると、そのリンクは <code>Referer:</code> を送信しなくなります。 ```html <a href="http://www.example.com/" rel=noreferrer>リンク</a> ``` リンクそれぞれで <code>Referer:</code> を送信するか制御できるので便利ですが、先述のような用途だと指定漏れのおそれがあり、あまり安心できないかもしれません。 CMS などでサーバーやページ全体を変更できない場合には、自分で編集できるリンクに属性を指定するだけで良くて便利かもしれません。 Chrome、Safari など <a href="http://www.webkit.org/blog/907/webkit-nightlies-support-html5-noreferrer-link-relation/">WebKit/Blink 系のブラウザーは早くから実装しています</a>。 Firefox でも<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=530396">実装されました</a>。以前は未対応の Web ブラウザーがまだ多いのが難点でしたが、現在となってはかなり実用的な方法です。 この方法は <code>img</code> 要素などには使えません。 この機能は <a href=https://html.spec.whatwg.org/#link-type-noreferrer>HTML Standard</a> で規定されています。 # <code><meta name=referrer></code> <a href="https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-delivery-meta"><code><meta name=referrer></code></a> は、ページ内のリンク等で referrer をどう処理するべきかを指定するものです。 <code>Referer:</code> の送信の有無に加え、送る場合に <a href=https://wiki.suikawiki.org/n/%E8%B5%B7%E6%BA%90>origin</a> (URL scheme とホストの部分のみで、 path や query を除いたもの。) だけを送るオプションも選ぶことができます。 URL の path や query の部分に漏らしたくない文字列が含まれていても、どの Web サイトからの流入なのかリンク先に伝えたいなら、 origin のみ送信すれば良いというわけです。 - 実際に Google 検索は origin のみを送るように指定されており、 query に含まれる検索キーワードは <code>Referer:</code> に含まれませんが、 Google のリンクをたどったアクセスであることは伝わるようになっています。 - ユーザーIDがURLに含まれるソーシャルブックマークサービスや友達のみに公開される SNS の記事などで、プライバシー保護のため origin のみ送るように指定したいかもしれません。 ```html <!-- Referer: を送らない --> <meta name="referrer" content="no-referrer"> <!-- content="never" は旧仕様 --> ``` ```html <!-- Referer: に origin のみ含める --> <meta name="referrer" content="origin"> ``` この指定はすべてのリンクに適用されますし、 <code>img</code> や <code>iframe</code> などにも有効です。また、通常 <code>Referer:</code> を送信しない <code>https:</code> のページから <code>http:</code> の URL への参照でも <code>Referer:</code> を送信するように指定できます。 (リンクごとに個別に指定することはできませんが、スクリプトで動的に書き換えて、同じドメインのサイトには URL 全体を、違うドメインのサイトには origin だけを送るように無理矢理しているサイトも<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=704320#c58">あるようですw</a> さすがにどうかと思いますが...) `unsafe-url` という値を使うと、通常は referrer が送られないことになっている、 `https:` のページから `http:` URL へのリンクでも、 referrer を送信することができます。近年 HTTPS への移行が進んでいるとはいえ、まだ `http:` しかない Web サイトも多いですが、流入元が自サイトであることを知らせたいとき、便利です。 (もちろん、自ページの URL が流出するべきでないものであるとき、この値を使ってはいけません。値に「unsafe」とある通り、 `https:` の URL が平文で送信されることになります。) 利用できる値と動作をまとめると、次の表のようになります。 | 値 | 同じ origin | 同じ scheme、`http:` → `https:` | `https:` → `http:` | 旧仕様の値 | |-------------|------------|-------------|----------------|-------| |`no-referrer`| 送信しない | 送信しない | 送信しない | `never` | |`origin` | origin のみ | origin のみ | origin のみ | | |`no-referrer-when-downgrade` | 送信する | 送信する | 送信しない | `default` | |`origin-when-cross-origin` | 送信する | origin のみ | origin のみ | | |`unsafe-url` | 送信する | 送信する | 送信する | `always` | 本記事の最初の執筆時点では残念ながらまだ Chrome しか実装していませんでしたが、現在は Firefox でも <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=704320">実装されています</a>。 ※ Safari と Edge は「旧仕様の値」にしか対応していません。「旧仕様の値」は新たに使うべきではないのですが、当面はやむなくそちらを使う方が良い場面もありそうです。「旧仕様の値」は Chrome や Firefox のような新しい Web ブラウザーでも正常に認識されます。 この他に、新たに追加された `same-origin`, `strict-origin`, `strict-origin-when-cross-origin` という値があります。まだ実装されていないかもしれませんが、 ```html <meta name="referrer" content="origin"> <meta name="referrer" content="strict-origin"> ``` ... のように複数の値を指定すると、ブラウザーが対応しているもので最後に指定された値が適用されます。この例では、 古めのブラウザーなら `origin` が、新しいブラウザーなら `strict-origin` が適用されます。 この機能は <a href=https://html.spec.whatwg.org/#meta-referrer>HTML Standard</a> で規定されています。 (<a href="https://wiki.whatwg.org/wiki/New_Features_Awaiting_Implementation_Interest">当初 WHATWG Wiki で提案され</a>、いずれ HTML Standard にも追加されるだろうと思われていましたが、<a href="https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-delivery-meta">Referrer Policy</a> という仕様書で標準化されることになりました。この時、指定できる値が一部変更されました (旧仕様の値も認識はされますが、新しい値を使うべきだとされています)。その後改めて HTML Standard と統合されました。) # CSP ***この方法は、廃止されました。*** CSP (HTTP <code>Content-Security-Policy:</code> ヘッダー) で <code><meta name=referrer></code> 相当の指定をできるようにすることが<a href="https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#referrer">検討されていました</a>。 ```http Content-Security-Policy: referrer no-referrer; ``` 個別の Web ページを書き換えずに Web サイト全体に適用したいときに、サーバーの設定を書き換えるだけで済むのは便利かもしれません。 <a href="https://w3c.github.io/webappsec-referrer-policy/#directive-referrer">Referrer Policy</a> 仕様書で規定されていました。 本記事の最初の執筆時点ではどの Web ブラウザーも実装していませんでしたが、その後 Chrome と Firefox が実装しました。 しかし、 <a href=https://lists.w3.org/Archives/Public/public-webappsec/2015Oct/0043.html>CSP よりも独立した HTTP ヘッダーとするのが適切</a>と判断され、この方法は削除されました。 # <code>referrerpolicy=""</code> 新たに <code>a</code> 要素、 <code>img</code> 要素などに <code>referrerpolicy</code> 属性が追加され、 <code><meta name=referrer></code> と同様の指定がリンクごとに行えるようになります。 この機能は <a href=https://html.spec.whatwg.org/#attr-hyperlink-referrerpolicy>HTML Standard</a> で規定されています。 (当初は <a href="https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-delivery-referrer-attribute">Referrer Policy</a> 仕様書で規定されていました。) # <code>request.referrerPolicy</code> 新しい <code>fetch</code> API で使う <code>Request</code> オブジェクトには <code>referrerPolicy</code> 属性があり、 <code><meta name=referrer></code> 同様の値を指定できます。 <a href="https://fetch.spec.whatwg.org/#dom-request-referrerpolicy">Fetch Standard</a> で規定されています。 # <code>Referrer-Policy:</code> ヘッダー CSP の方法にかわって、新設の独立した HTTP ヘッダー `Referrer-Policy` で <code><meta name=referrer></code> 同様の値を指定できるようになります。 ```http Referrer-Policy: origin ``` 昔からある `Referer` ヘッダーとは違って、綴りが `Referrer` となっていることに注意が必要です。 <a href=https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-header>Referrer Policy</a> 仕様書 (と <a href=https://html.spec.whatwg.org/#initialise-the-document-object>HTML Standard</a>) で規定されています。 # <code>history.replaceState</code> ここまでの方法は <code>Referer:</code> の送信自体を制御していましたが、 <code>Referer:</code> で送信される URL の方を変更する手もあります。 <a href="https://www.whatwg.org/specs/web-apps/current-work/#dom-history-replacestate"><code>history.replaceState</code></a> を使うと現在のページの表示はそのままで、 URL を書き換えることができます (いわゆる <a href=https://wiki.suikawiki.org/n/Pjax>Pjax</a> です)。そうすると <code>Referer:</code> で送られる URL も変更されます。ただし新しい URL は元の URL と同じホスト (正確には同じ <a href=https://wiki.suikawiki.org/n/%E8%B5%B7%E6%BA%90>origin</a>) でなければなりません。サーバーの存在自体を漏らしたくないときには使えませんが、 path や query に漏らしたくない文字列が含まれているときには有用です。なお <code>history.pushState</code> では Web ブラウザーの履歴に余分な項目が追加されてしまいますから不適当です。 注意したいのは、書き換えた新しい URL も同じ内容のページを表すものでないといけないことです。そうしないと「戻る」で移動してきた時に意図しないページに戻ってしまいます。 <code>replaceState</code> の第1引数には任意のデータを保持しておけますから、書き換える前の path や query に含まれていた情報をそこに入れておいて、書き換え先の URL はそれを使って (<code>history.state</code> でアクセスできます。) 元のページを復元すると良いでしょう。 ```html <script> var m = location.href.match(/\/([^\/]+)\/$/); if (m) { history.replaceState ({user_name: m[1]}, document.title, location.href.replace(/\/[^\/]+\/$/, '/my/')); } </script> ``` この方法は主要 Web ブラウザーすべてで使えます。実装の始まった頃には <code>Referer:</code> の URL が変更されないブラウザーもありましたが、現在では完全に無視して問題ありません。 サーバーの設定を変更したり個別のリンクに手を入れたりせずに、すべての Web ブラウザーで <code>Referer:</code> を制御できるのはこの方法だけです。 # まとめ Web ブラウザーの <code>Referer:</code> の送出を Web サイト側で制御する方法を紹介しました。本記事の最初の執筆 (2013年) 時点では、どのブラウザーでも実装されている `replaceState` の方法が (手間がかかるものの) 最善かもしれないと述べていました。現在では <code><meta name=referrer></code> が最も使い勝手が良さそうですが、ブラウザーによっては新しい値に対応していなかったりするので、注意が必要です。 ところで <code>Referer:</code> HTTP ヘッダーの名前以外はすべて「r」が2つの referrer ですので注意しましょう。 - Referrer (r が2つの正しい綴り) - <code>document.referrer</code> (DOM) - <code>request.referrerPolicy</code> (DOM) - <code>rel=noreferrer</code> (HTML) - <code>referrerpolicy=""</code> (HTML) - <code><meta name=referrer></code> (HTML) - <code>Referrer-Policy:</code> (HTTP) - <code>referrer no-referrer</code> (CSP) - Referer (r が1つ) - <code>Referer:</code> (HTTP) |
|
| 354位 |
|
|||
|
13:20:04 |
(SonicGarden 所属) |
|
以下のスライドを意訳したものです。Compress周りについては触れていません。「いやいや、最新の書き方だともっと良い書き方があるんだよ!」という方のコメントをお待ちしております!
http://www.slideshare.net/paul.irish/perfcompression ## クエリをキャッシュする ```javascript: // 悪い例 var id = $("#content").data("id"); var itemId = $("#content").data("item-id"); // 良い例 var content = $("#content") var id = content.data("id"); var itemId = content.data("item-id"); ``` ## HTMLを生成するときにjQueryオブジェクトにアクセスし続けない ```javascript: // 悪い例 $.each(reallyLongArray, function(count, item) { var newLi = '<li>' + item + '</li>'; $('#ballers').append(newLi); }); // 良い例 : DocumentFragmentを使用 var frag = document.createDocumentFragment(); $.each(reallyLongArray, function(count, item) { var newLi = '<li>' + item + '</li>'; frag.appendChild(newLi[0]); }); $('#ballers')[0].appendChild(frag); // 良い例 : Stringを使用 var myhtml = ""; $.each(reallyLongArray, function(count, item) { myhtml += '<li>' + item + '</li>'; }); $('#ballers').html(myhtml); ``` ## Keep things DRY ```javascript: // 悪い例 if ($ventfade.data('currently') != 'showing') { $ventfade.stop(); } if ($venthover.data('currently') != 'showing') { $venthover.stop(); } if ($spans.data('currently') != 'showing') { $spans.stop(); } // 良い例 var elems = [$ventfade, $venthover, $spans]; $.each(elems, function(k, v){ if (v.data('currently') != 'showing') { v.stop(); } }); ``` ## アンチパターン:オブジェクトリテラルを使わない宣言 無名関数で定義されているせいで、どこで何が起こっているのか判断がつかなくなってしまうケース。 ```javascript: $(document).ready(function() { ... $('#magic').click(function(e) { $('#yayeffects').slideUp(function() { ... }); }); $('#happiness').load(url + ' #unicorns', function() { ... }); }); ``` オブジェクトリテラルを使用して記述するようにする。 ```javascript: var PI = { onReady: function() { ... $('#magic').click(PI.candyMtn); $('#happiness').load(url + ' #unicorns', PI.unicornCb); }, candyMtn: function(e) { $('#yayeffects').slideUp(PI.slideCb); }, slideCb: function() { ... }, unicornCb: function() { ... } }; ``` オブジェクトリテラルを使用することで、下記の利点を得られる。 * コードの見通しがよくなる。 * プロファイラで関数名を参照できる。 * 各関数をコンソールで実行してみることができる。 * 無名関数のときと比べて、テストが書きやすくなる。 ## アンチパターン:再クエリ ```javascript: // create and append your element $(document.body).append('<div class="baaron" />'); // requery to bind stuff $("div.baaron").click(function(){ … }); // better // swap to appendTo to hold your elem $('<div class="baaron" />') .appendTo(document.body) .click(function(){ … }); ``` ## $('\#whats .the', context) ```javascript: // パフォーマンスはこっちよりも var arms = $('div.robotarm', '#container'); // こっちの方がいい var arms = $('#container').find('div.robotarm'); // 読みやすさ的にはcontext?好みの問題かも。。 ``` ## セレクタのパフォーマンス最適化 一番パフォーマンスが良いのは\#idから下降する書き方。 ```javascript: // 普通の書き方 var arms = $('#container div.robotarm'); // もっと良い書き方 var arms = $('#container').find('div.robotarm'); ``` 不必要なセレクタを挟まないほうが、より高速にヒットする。 ```javascript: // 余計なものが混じっている .data table.attendees td.gonzalez // 中間をはぶこう .data td.gonzalez ``` ユニバーサルセレクタはパフォーマンスを劣化させる。明示的に書いていなくても、暗黙のユニバーサルセレクタが使われている場合があるので、注意すること。 ```javascript: $('.buttons > *') // 超コストがかかる $('.buttons').children() // 良い例 $('.gender :radio') // 暗黙のユニバーサルセレクタが含まれている $('.gender *:radio') // 上記と同じ意味になる $('.gender input:radio') // 良い例 ``` [効率的なCSSの書き方(https://developer.mozilla.org/ja/Writing_Efficient_CSS)](https://developer.mozilla.org/ja/Writing_Efficient_CSS) ## イベントのデリゲート live()を使うより、delegate()を使うことでより高速化できる。 ```javascript: // 汚い書き方。。 $('a.trigger', $('#container')[0]).live('click', handerFn); // 良い書き方 $('#container').delegate('click', 'a.trigger', handlerFn); ``` ## DOMの操作は総じて重い タグのスタイル属性を直接操作するのはもってのほか。スタイルをいじりたい場合はクラスを変えよう。(DOMの操作は最小限におさえるのが原則) ## ブラックボックスとしてjQueryを扱わない 単にjQueryを隠蔽する書き方は、単に可読性を下げるだけだ。 ```javascript: // これでは単にjQueryを隠蔽しているだけで、無駄にメソッドを増やしていだけに過ぎない。。 getScript: function( url, callback ) { return jQuery.get( url, null, callback, "script" ); }, getJSON: function( url, data, callback ) { return jQuery.get( url, data, callback, "json"); }, ``` よく使うメソッドは覚えておこう。 map(), slice(), stop(), (de)queue(), prevAll(), pushStack(), inArray(), etc… ```javascript: // index() in jQuery <= 1.3.2 $('#rdworth').parent().children().index( $('#rdworth')[0] ) // prevAll()を使うと10%速くなる $('#rdworth').prevAll().length // jQuery1.4からはこう書ける $('#rdworth').index() ``` ## 存在しない要素に対するアクションを書かないこと jQueryは凄く空気を読んでくれるので、そんなコードを書いたとしてもエラーは出さない・・・けど、ものすごいオーバーヘッドが裏で生じることになる。 ```javascript: $('#doesntexist').slideUp() // genFx(), speed(), animate()といったメソッドが実行されるが、要素は存在しないため画面上では何の動きもあらわれない ``` ## 便利なメソッド ### eq(), first(), last() ```javascript: var lastelem = $elems.eq(-1); // get()と同じ $('#nav li:first') === $('#nav li').first(); $('#nav li:last') === $('#nav li').last(); ``` ### data() ```javascript: // 普通の書き方 $(elem).data(key, value); // 10倍速い! $.data(elem, key, value); ``` |
|
| 355位 |
|
|||
|
09:29:09 |
(コロプラ 所属) |
|
Appleの[iOS View Controllerプログラミングガイド](https://developer.apple.com/jp/devcenter/ios/library/documentation/ViewControllerPGforiOS.pdf)は色々と参考になるものが多かったです。 まだ読んでない人はぜひ一度読んでみてください。設計する上でもとても有用な情報が載っています。 ##Container View Controller(コンテナ) Container View Controllerは、コンテナとしての役割を担うViewControllerです。 すでにあるものとしては`NavigationViewController`や`TabViewController`がそれに当たります。 つまり、子となるViewControllerを内包するちょっと特殊なViewControllerです。 ###コンテナViewControllerの役割 コンテナというくらいなので、なにかしらのViewController郡をまとめる役割を持ちます。 `NavigationViewController`であればスタックとしてViewControllerを管理し、`TabViewController`であれば並列関係のViewContrllerを管理します。 そして必要であれば、コンテナViewController独自のビュー領域(ナビゲーションバーやタブバーみたいなもの)を用意して、それを利用して内包しているViewControllerを表示したり、ということもできます。 ただ、実装を見ると分かりますが、基本的には`UIViewController`のサブクラスでしかないので、コンテナの中にコンテナが、という入れ子構造ももちろん実現することができます。 ###コンテナViewControllerの作成手順 コンテナ用のViewControllerを作る際は、通常のViewControllerとは少し考慮することが変わります。 手順としては、 1. `addChildViewController:`メソッドでViewControllerの親子関係を作る 2. ViewControllerの`view`を自身の`view`のsubviewに追加する 3. ViewControllerの`didMoveToParentViewController:`メソッドを、自身を引数にして呼ぶ という流れです。 (2)のview追加に関しては、通常のUIViewなどを管理するのと同じです。 (1)と(3)が特殊な部分ですが、よく考えればむずかしくありません。 ViewControllerを内包するということは親子関係を作るということ。 そのためのメソッドが`addChildViewController:`メソッドです。 (逆に削除するには`removeFromParentViewController`メソッドを子ViewController側で呼びます) このあたりは`view`に対する`addSubview`(`removeFromSuperview`)などと同じ理屈ですね。 ちなみに`addChildViewController:`を呼び出すと、自動的に、引数に渡されたViewControllerの`willMoveToParentViewController:`メソッドが呼び出され、これからコンテナViewControllerの子要素になることが通知されるようになっています。 逆に、`removeFromParentViewController`を呼ぶと、`didMoveToParentViewController:`が呼ばれます。 ####ライフサイクルはどうなる? 上記のメソッドを適切に呼び出すことで、普段使っているライフサイクル、`viewDidLoad`や`viewWillAppear`などのメソッドが順次呼び出されていきます。 ###サンプルコード Appleのプログラミングガイドのサンプルコードをコメントをつけつつ掲載します。 (サンプルはすべて、コンテナViewControllerのメソッドです) ####子のViewControllerを追加する ```objc - (void)displayContentController:(UIViewController *)content { // 自身のビューコントローラ階層に追加 // 自動的に子ViewControllerの`willMoveToParentViewController:`メソッドが呼ばれる [self addChildViewController:content]; // 子ViewControllerの`view`を自身の`view`階層に追加 [self.view addSubview:content.view]; // 子ViewControllerに追加が終わったことを通知する [content didMoveToParentViewController:self]; } ``` ####子のViewControllerを削除する ```objc - (void)hideContentController:(UIViewController *)content { // これから取り除かれようとしていることを通知する(引数が`nil`なことに注意) [content willMoveToParentViewController:nil]; // 子ViewControllerの`view`を取り除く [content.view removeFromSuperview]; // 子ViewControllerを取り除く // 自動的に`didMoveToParentViewController:`が呼ばれる [content removeFromParentViewController]; } ``` ####ViewControllerの入れ替わりをアニメーションさせる `NavigationViewController`などのように、ViewControllerの入れ替え時にアニメーションさせることもできます。 ```objc - (void)cycleFromViewController:(UIViewController *)oldC toViewController:(UIViewController *)newC { // 古いViewControllerに取り除かれようとしていることを通知する [oldC willMoveToParentViewController:nil]; CGFloat width = self.view.bounds.size.width; CGFloat height = self.view.bounds.size.height; // アニメーションスタート位置を画面下部にする CGRect startFrame = CGRectMake(0, height, width, height); [self addChildViewController:newC]; newC.view.frame = startFrame; CGRect endFrame = CGRectMake(0, 100, width, height); [self transitionFromViewController:oldC toViewController:newC duration:0.25 options:0 animations:^{ newC.view.frame = oldC.view.frame; oldC.view.frame = endFrame; } completion:^(BOOL finished) { [oldC removeFromParentViewController]; [newC didMoveToParentViewController:self]; }]; } ``` `transitionFromViewController:toViewController:duration:options:animations:completion:`メソッドを使うことによって入れ替わりのアニメーションを実装することができます。 `animations:`にblocksを渡すことで、どういうアニメーションをさせるか、ということができます。 これを使えば、ちょっと変わったアニメーションをもったコンテナViewControllerが作れそうですね。 [今回のサンプルはgithub](https://github.com/edom18/CustomContainerViewControllerSample/tree/master/CustomViewController)にあげてみました。プログラミングガイドをほぼそのまま使ったものですが。 |
|
| 356位 |
|
|||
|
14:44:28 |
|
|
Linux ディストリビューションなどで標準搭載されている bash ですが、基本的にデフォルトで使えるデータ構造は配列くらいです。そこで、その扱いについてまとめました。
## ■ 配列を生成する たとえば、空の配列を生成するには以下のようにします。 array=() また、ビルトインコマンドの `declare`(関数スコープでの定義は `local`)で生成することも可能です。 declare -a array=() # 宣言 declare -a array=("a" "b" "c") # 初期化 some_func() { local local_array=() # 関数内スコープは local で定義できる ... } ## ■ 配列の要素数 echo ${#array[@]} # 3 echo ${#array[*]} # 3 配列の要素数をチェックする場合は、`[@]` でも `[*]` でも差異はないようです。 ## ■ 配列のデータを操作する ### ● データを追加する # 先頭に追加 array=(3 "${array[@]}") # array は (3 “a” "b" "c") # 末尾に追加 array=("${array[@]}" 4) # array は (3 "a" "b" "c" 4) array+=( 5 ) # array は (3 "a" "b" "c" 4 5) 末尾に追加する書き方は二通りですが、どちらでも同じようです。しかし、前者のほうが「先頭に追加」の表記方法と整合性が取れるので、可読性を高めそうです。 ### ● 配列からデータを取り出す # データの先頭要素を取り出す(破壊的操作) array=("${array[@]:1}") # array は ("a" "b" "c" 4 5) # データの末尾要素取り出す(破壊的操作) declare -i num=${#array[@]}-1 array=("${array[@]:0:$num}") # array は ("a" "b" "c" 4) # 一行でも書けます array=(${array[@]:0:((${#array[@]}-1))}) # array は ("a" "b" "c") ### ● 配列のデータを参照する i=0 for e in ${array[@]}; do echo "array[$i] = ${e}" let i++ done array[0] = a array[1] = b array[2] = c # C言語風に記述する for ((i = 0; i < ${#array[@]}; i++)) { echo "array[$i] = ${array[i]}" } array[0] = a array[1] = b array[2] = c # Bash 独自の記述 1 echo "${array[@]}" a b c # Bash 独自の記述 2 IFS=$'\n' echo "${array[*]}" a b c ### ● 配列のデータから任意の要素を削除する # ある要素を削除 unset array[1] echo "${array[@]}" # arrayは("a" "c") echo ${array[0]} # array[0]は"a" echo ${array[1]} # array[1]は ""(空) echo ${array[2]} # array[2]は"c" 配列の中身のデータは削除できても、配列自体は削除できません。 添字を詰めたければ、 array=("${array[@]}" 代入しなおす必要があります。 unset array[@] # 全削除する echo "${array[@]}" # 配列 array は ""(空) echo "${#array[@]}" # 要素数は0 ### ● 配列にデータを格納する array[0]=1 array[1]="a" 以上です。 |
|
| 357位 |
|
|||
|
23:31:15 |
|
|
こちらも blog からの転載。
シェルスクリプトを作成する際にシェル変数に値を代入したり参照したりする事は頻繁に発生するが、 シェル変数の展開にも便利な使い方がある。 *basename* (1) や *dirname* (1) と同様な動作がシェルの組込みとして利用できるので資源の節約にもつながり、 上手に利用すると可読性の高いスクリプトが作成できる。 ## 機能一覧 |記述 |機能 | |:--------------------|-------------------- |${parameter:-word} |デフォルト値への置換 |${parameter:=word} | デフォルト値の代入 |${parameter:?[word]} | 値の検査とエラー |${parameter:+word} | 代替値の使用 |${#parameter} | 文字列長の取得 |${parameter%word} | 最短後置パターンの削除 |${parameter%%word} | 最長後置パターンの削除 |${parameter#word} | 最短前置パターンの削除 |${parameter##word} | 最長前置パターンの削除 ## 実例 ### デフォルト値への置換 **${parameter:-word}** ${parameter} が NULL の場合 word に置換される。 ```sh:sample $ echo ${foo} $ echo ${foo:-FOO} FOO $ echo ${foo} $ foo=BAR $ echo ${foo:-FOO} BAR ``` ### デフォルト値の代入 **${parameter:=word}** ${parameter} が NULL の場合 word に置換され、 かつ parameter に代入される。 ```sh:sample $ echo ${foo} $ echo ${foo:=FOO} FOO $ echo ${foo} FOO $ echo ${foo:=BAR} FOO ``` ### 値の検査とエラー **${parameter:?[word]}** ${parameter} が NULL の場合 word が指定されていればその値を、 指定されていない場合はデフォルトの値を表示し、 非対話実行されているシェルをエラー終了させる。 ```sh:sample $ echo ${foo} $ echo ${foo:?value not set} value not set ``` ### 代替値の使用 **${parameter:+word}** ${parameter} が NULL 以外の場合 word に置換される ```sh:sample $ echo ${foo:+FOO} $ echo ${foo} $ foo=BAR $ echo ${foo} BAR $ echo ${foo:+FOO} FOO ``` ### 文字列長の取得 **${#parameter}** ${parameter} の文字列としての長さに置換される ```sh:sample $ echo ${foo} $ echo ${#foo} 0 $ foo=FOO $ echo ${foo} FOO $ echo ${#foo} 3 ``` ### 最短後置パターンの削除 **${parameter%word}** ${parameter} の右から word で示されるパターンの最短部分を削除する ```sh:sample $ foo=/foo/bar/baz $ echo ${foo%/*} /foo/bar $ foo=foo.c $ echo ${foo%.*} foo $ foo=foo $ echo ${foo%.*} foo ``` ### 最長後置パターンの削除 **${parameter%%word}** ${parameter} の右から word で示されるパターンの最長部分を削除する ```sh:sample $ foo=foo.example.com $ echo ${foo%%.*} foo $ foo=http://www.example.com:8888/ $ echo ${foo%%:*} http $ foo=foo $ echo ${foo%%.*} foo ``` ### 最短前置パターンの削除 **${parameter#word}** ${parameter} の左から word で示されるパターンの最短部分を削除する ```sh:sample $ foo=foo.c $ echo ${foo#*.} c $ foo=foo.example.com $ echo ${foo#*.} example.com $ foo=foo $ echo ${foo#*.} foo ``` ### 最長前置パターンの削除 **${parameter##word}** ${parameter} の左から word で示されるパターンの最長部分を削除する ```sh:sample $ foo=foo.example.com $ echo ${foo##*.} com $ foo=/foo/bar/baz $ echo ${foo##*/} baz $ foo=foo $ echo ${foo##*.} foo ``` |
|
| 358位 |
|
|||
|
14:52:47 |
|
|
> **追記(2.x):** Git for Windows は2015年8月にリリースされた 2.5.0 以降日本語まわりの問題もいくつか改善されているので追記しました。
2.x 系では開発ベースも変わり、もはや msysgit ではなくなったので、本稿のタイトルも変更させていただきます。 "Git for Windows"(いわゆる Git Bash)は Windows 上で git 利用を可能とする bash シェル環境だ。 Gitが使えるだけでなく、オールインワンで基本的なコマンドを備え、 ssh, perl, curl などのツールも入っていて、cygwin なんかに比べお手軽かつお得な環境だ。 筆者の周りでは "Git Bash" で通用している。 かつては日本語を扱うのが面倒であったが、1.8.3 から Bash 上で日本語入力できるようになるなど、最近の状況はだいぶよくなっている。 ```bash $ git commit -m '日本語OK' $ git log -1 Author: kumazo <kumazo@localhost.local> Date: Mon Jan 13 00:00:00 2014 +0900 日本語OK ``` それでもまだいくつか設定変更は必要だ。 すでに日本語周りのノウハウは多くの方が取り上げられているが、新旧の情報が散在し錯綜しているので、現状をまとめる意味でもここにメモしておく。 ここで取り上げるのは以下の設定項目。 * トウフ化 * ls で日本語ファイル名 * vim で日本語コミットメッセージ * git 出力の日本語ファイル名 * Git GUI の diff の文字化け * 外部エディタの設定 # トウフ化 (**2.x** 解消済み) もし、日本語文字がすべて四角い□(トウフ)になってしまったらそれはコンソールのフォント設定の問題だ。 タイトルバーを右クリックー→プロパティ→フォントタブ→フォントで適切な日本語対応フォントになっているか確認する。 ここで、日本語フォントになっていなければ日本語フォントに変更すればよい。 しかし、日本語フォントになっていたとしても、あえて別の日本語フォントに変更する。 変更後は正常に日本語が表示されるようになる。 再度フォント設定を開き、フォントを元に戻してみる。 なぜか元のフォント設定でも正常に日本語が表示される。 そもそも「MS ゴシック」と「ラスターフォント」しか選択肢がなく、両方日本語対応しているはず。 いったんフォント設定を変更することで、それ以降のトウフ化が解消されるようだ。 なぜこうなるのか理由はわからない。 ちなみにフォントサイズを変更しただけでも同様の効果がある。 タイトルバーからの設定は一時的なものだ。 これを保存するには、Git Bash のデスクトップアイコンを右クリックし、同様の手順をふんで設定をしておく。 # ls で日本語ファイル名(**2.x** 設定済み) ls で UTF-8 ファイル名が ???? になるのは ls 固有の問題らしい。 以下のオプションをつければ日本語ファイル名が表示される。 ```bash: $ ls --show-control-chars ``` いまいち納得がいかないが .bash_profile あたりにエイリアスを定義して、以後気にしないことにする。 ```bash: alias ls='ls --show-control-chars' ``` # vim で日本語コミットメッセージ git commit 時にはデフォルトで同梱の vim エディタが起動する。 日本語でコミットメッセージを残すには、環境によって vim の設定が必要になる。 **2.x以降** 特に設定変更しなくても、そのままで日本語(UTF-8)が使える。 日本語入力に不具合のあった Windows 8 以降の環境でも問題なく使えるようになった。 デフォルトの vim 設定は以下の通り。 ``` encoding=utf-8 termencoding= fileencoding= fileencodings=ucs-bom,utf-8,default,latin1 ``` `git commit` 時には以下のようになる。 ``` encoding=utf-8 termencoding= fileencoding=utf-8 fileencodings=utf-8 ``` termencodingは空のままでないとかえって入力が壊れるようなので、1.9.x系でtermencodingを設定していたらこれを外しておく。 デフォルトでシフトJIS等のレガシーな日本文字コードは対応していない。 必要なら fileencodings に cp932 や iso-2022-jp、euc-jp を追記する。 ```bash:~/.vimrc set fileencodings=ucs-bom,utf-8,cp932,default,latin1 ``` **1.9.x**(もやは不要ですが参考までに残しておきます) git 同梱のvimの文字コード関連の初期設定は以下のようになっている。 ``` encoding=cp932 termencoding= fileencoding= filesencoding= ``` この状態で日本語を入力してみて、表示上文字化けしなければ OK。 文字化けするようなら、以下の vim 設定を試す。 ```vim :set termencoding=cp932 ``` これで入力時の文字化けはしなくなるはず。 それでも文字化けするのが Windows 8/8.1 。 どうも、Windows 8 環境では 入力したマルチバイト文字の最初の 1バイトしか拾えていないようだ。 これは vim に限らず、Windows 8 以降の CUI アプリ全般でみられる問題らしい。(下リンク参照) vim 側で対応されなければどうにもならないようなので、Windows 8 環境では当面これまで通り core.editor に外部エディタを設定するしかない。 GUI 版の gvim(KaoriYa版)なら Windows 8/8.1 環境でも問題なく日本語入力できるのでこれをエディタ設定するものありだろう。 外部エディタの設定例は後にまとめたので参考にしてほしい。 さて、メッセージを保存しコミットを完了した後、ログを確認すると正しく日本語が表示されているはずだ。 もし、コミットログでメッセージが文字化けしていたら、保存時の文字コードが UTF-8 になっていなかった可能性が高い。 コミット時の vim 上で以下 fileencoding の設定を確認してほしい。(fenc は fileencoding の短縮形) ```vim :set fenc fileencoding=utf-8 ``` Gi Bash 環境では上記のように utf-8 が設定されているはず。 vim 単独ではもともと空だが、コミットメッセージのテンプレートファイルを開いたら UTF-8 に変更されるようになっている(/share/vim/vimrc)。 もし何らかの問題でこれが空だったり別の文字コードになっていたら、保存前に `:set fenc=utf-8` すれは取りあえずOKなはず。 常に UTF-8 を強制するには .gitconfig の設定で上書きして置けばよい。 ```bash $ git config --global core.editor "vim -c 'set fileencoding=utf-8' " ``` とはいえコンフリクト時など vim でソース修正をすることもあるだろうから、ファイル用の文字コードも合わせて.vimrcの方で設定しておいたほうがいいだろう。 新規ファイルの場合は fileencoding の文字コードが使われる。 ```vim :set fileencoding=utf-8 ``` 既存ファイルの場合は fileencodings で自動判別する文字コードとその優先順を指定する。 コミットメッセージのテンプレートのようにASCIIしかなければ自動判別は役に立たない。 utf-8 を先頭にしておけば、日本語がなくても UTF-8 とみなされる。 ```vim :set fileencodings=utf-8,cp932 ``` これらをまとめて $HOME/_vimrc に保存しておく。 ```vim:_vimrc set encoding=cp932 set termencoding=cp932 set fileencoding=utf-8 set fileencodings=utf-8,cp932 ``` Windows では .vimrc ではなく _vimrc が正しい。 _vimrc にしておけばトラブルがなく確実なようだ。 # git 出力の日本語ファイル名 `git status` や `git log`、`git diff` などの出力で表示される日本語ファイル名は "\nnn" にエスケープされてしまって読めない。 以下の設定を追加することで日本語ファイル名が表示されるようになる。 ```bash $ git config --global core.quotepath false ``` シェルスクリプトなどで git の設定に影響を受けたくなければ、`printf` か `/bin/echo -e` を通すのが簡便な方法だ。 ```bash $ printf "$(git log --name-status)" ``` * [エスケープされた日本語文字列を読みたい](http://qiita.com/kumazo@github/items/39500f259500a424800b) - Qiita # Git GUI の diff の文字化け git-gui や gitk で diff 表示が文字化けしたら、ソースファイルの文字コードに合わせて設定変更する。 デフォルトはcp932(SJIS)。 ```bash $ git config --global gui.encoding utf-8 ``` # 外部エディタの設定 Windows用の外部エディタをコミットログに使用するには、エディタの起動オプションを調べておく必要がある。 保存時の文字コードを UTF-8 に指定するほか、git がエディタプロセスの終了を待てるように、タブウィンドウや子プロセスを使わないようにするためだ。 日本でメジャーなエディタの`git config`をコピペ用にまとめた。 | Editor | git config --global core.editor | | | :-------- | :------------- | :--- | | Sublime Text 3 | "'C:/Program Files/Sublime Text 3/subl.exe' --new-window --wait " | > Build 3065 | | Sublime Text 2 | "'C:/Program Files/Sublime Text 2/sublime_text.exe' --wait " | | | GVim (KaoriYa版) | "'C:/vim74-kaoriya-win64/gvim.exe' --nofork -c 'set fenc=utf-8' +1 " | ZIP | | EmEditor ver.14 | "'C:/Program Files/EmEditor/EmEditor.exe' //sp //cp 65001 " | 64 ビット版 | | 秀丸エディタ ver.8 | "'C:/Program Files (x86)/Hidemaru/Hidemaru.exe' //fu8 " | | | サクラエディタ ver.2 | "'C:/Program Files (x86)/sakura/sakura.exe' -CODE=4 " | | | TeraPad | "'C:/Program Files (x86)/TeraPad/TeraPad.exe' //cu8 " | | | WZ EDITOR 8 | "'C:/Program Files (x86)/WZ EDITOR 8/wzeditor.exe' //sdi //new " | | 基本的にエディタをフルパス名で指定する必要がある。 パスの途中にスペースが入るため、エディタのフルパス名をシングルクオートで囲っておく必要があるので注意。 パスの区切り文字は`/`でよいが、オプションの `/` は2重`//`にしてエスケープする。 ドライブ文字の `C:/` が気持ち悪ければ、 `/c/` としてもよい。 ただし、`git confg`コマンドから設定する場合、`/c/` を使うとなぜかオプションが削られるので、ハマりたくなければ`C:/`にするか、`.gitconfig` を直接編集するようにする。 ところで、コミットメッセージだけでなくプロジェクトのソースファイルもいつものエディタで編集したいだろうか。 ソースファイルの拡張子がそれ用のエディタに関連付けられていることがわかっているなら、PATH が通ってなくても `start` 一発で起動できる。 `/bin/start` は DOS の `start` コマンドのラッパースクリプトだ。 ```bash $ start hello.c ``` ただし、うかつに実行すると思わぬ動作をする可能性もある。 たとえば `.c`拡張子で VisualStudio のような重厚 IDE を召喚してしまうなんてのはマシなほうで、`.js`拡張子の JavaScript ファイルを開くつもりが WSH で実行されてしまうこともあり得る。 ```bash $ echo 'WScript.Echo("Hello")' > hello.js $ start hello.js # WSH で実行 ? ``` ちなみに `start` にディレクトリを指定するとエクスプローラが開く。 素早くカレントディレクトリを開きたければ `start . ` と打てばよい。 ```bash $ start . # カレントディレクトリをExplorerで開く $ start ~ # ホームディレクトリ(C:\Users\Taro)を開く ``` ついでに言えば、URL を与えて Web ブラウザを立ち上げることもできる。 **追記** メモ帳(notebad.exe)のラッパー(/usr/bin/notepad)が用意されていてメモ帳を起動できる。 ```bash $ notepad test.txt ``` これはメモ帳の終了を待てるのでコミット用エディタに指定することができる。 ```bash $ git config --global core.editor notepad ``` ただし、残念ながら日本語を使うとエラーになる。 # Git for Windows 2.x について Git のバージョンアップは速いのだが、msysgit は Git-1.9.5-preview20150319.exe で長らく停滞していた。実はその裏で別のプロジェクトに移行して開発が進められていて、2.5.0 からようやく正式版が公開された。 * [Releases · msysgit/msysgit · GitHub](https://github.com/msysgit/msysgit/releases) * [Releases · git-for-windows/git · GitHub](https://github.com/git-for-windows/git/releases) もともと、mysygit は MSys をカスタマイズして実現していたが MinGW/MSys ベースではいろいろ限界があったようで、2.x では Mingw-w64/MSys2 ベースに乗り換え、ターミナルもMinTTY が選択できるようになった。 といかにも知った風だがその辺の事情に疎いので、何がどうなったのか実はさっぱりわからない。個人的に確認できた改善点として * Windows 8 以降の環境でも vim の日本語入力ができるようになった。 ただそれは、vim 開発陣のおかげ。vim ではだいぶ前から対応済みだったが、Git Bash がようやく取り込んだ。 * Windows 7 64bit 環境上で操作がなぜか異常に重くなる(ls だけで10秒かかる)というぐぬぬな経験をしてたのだが、同じ環境上に 2.x をインストールしたところ解消していることを確認できた。 MSys2 なら tmux や tig が使えてかなりうれしいはずだが、 Git for Windows には入っていない。 * [MSYS2でtmuxを使う](http://qiita.com/magichan/items/b9374e7219cbeef6e457) - Qiita Git for Windows には SDK もあって、そちらの方をインストールすれば、MSys2 のパッケージマネージャが使えるので試せるはず。 * https://github.com/git-for-windows/build-extra しかし、なぜか自分の環境ではインストールエラーとなり、調べると次のリリース(Git for Windows SDK 1.0.2)を待てとの由。 # 参考 * [[傾向と対策] なぜ、Windows 8 から多くのコンソールアプリケーションで突如日本語が入力できなくなったのか](http://nyaos.org/d/index.cgi?p=(2013.03.25)+1823) MHI 5.0 * [viで日本語の文字コードを自動判別](http://d.hatena.ne.jp/over80/20080907/1220794834) 玉虫色に染まれ! * [git/gitで日本語を扱う方法](http://tobysoft.net/wiki/index.php?git%2Fgit%A4%C7%C6%FC%CB%DC%B8%EC%A4%F2%B0%B7%A4%A6%CA%FD%CB%A1) TOBY SOFT wiki * [gitconfigに設定するWindowsのいろいろなエディタのオプション](http://orangeclover.hatenablog.com/entry/20121004/1349353957) みちしるべ * [msysgit でどうしても日本語文字化けが治らなかった際の対処](http://qiita.com/sisomoti/items/f66e87fc5a859c30cb24) Qiita |
|
| 359位 |
|
|||
|
01:29:16 |
(ALBERT inc. 所属) |
|
Python for Data Analysisの日本語版発売記念に よく使いそうなものとか詰まりそうなところとか めちゃくちゃ長くなってしまったので目次 * [複数のSeriesを結合してDataFrameに](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-1) * [DataFrameのインデックス参照](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-2) * [columnの参照](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#3-1) * [rowの参照](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#3-2) * [行と列を同時に範囲指定して参照 (ixによる参照)](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#3-3) * [Seriesのインデックス参照](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-3) * [bool値によるマスキング](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-4) * [Series同士, DataFrame同士の演算](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-5) * [階層的インデックス](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-6) * [stackメソッドとunstackメソッド](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-7) * [名前付け](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-8) * [行または列の名前付け](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#3-4) * [インデックスラベル自体の名前付け](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#3-5) * [ビンニング](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-9) * [列とインデックスの変換](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-10) * [ランダムサンプリング](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-11) * [カテゴリカルデータをダミー変数化](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-12) * [グルーピング](http://qiita.com/airtoxin/items/d66a22c5c7074e23be17#2-13) ##複数のSeriesを結合してDataFrameに concatを使ってでaxis=1にすれば良い ```py s1 = pd.Series([1,2,3,4,5], index=[0,1,2,3,4]) s2 = pd.Series([10,20,30,40,50], index=[10,11,12,13,14]) s3 = pd.Series([100,200,300,400,500], index=[0,2,4,6,8]) print pd.concat([s1, s2, s3], axis=1) """ 0 1 2 0 1 NaN 100 1 2 NaN NaN 2 3 NaN 200 3 4 NaN NaN 4 5 NaN 300 6 NaN NaN 400 8 NaN NaN 500 10 NaN 10 NaN 11 NaN 20 NaN 12 NaN 30 NaN 13 NaN 40 NaN 14 NaN 50 NaN """ ``` ##DataFrameのインデックス参照 色々用意されていて混乱する めんどくさいけどixによる参照が一番わかり易いと思う ```py # DataFrameを用意 df = DataFrame(arange(30).reshape((6, 5)), index=list("ASDFGH"), columns=list("abcde")) print df """ a b c d e A 0 1 2 3 4 S 5 6 7 8 9 D 10 11 12 13 14 F 15 16 17 18 19 G 20 21 22 23 24 H 25 26 27 28 29 """ ``` ###columnの参照 カラムの参照はカラム名を指定する 複数指定する場合はリスト形式にして指定 指定するカラムが1つの場合はインスタンス変数がカラム名で用意されているのでそれを指定しても良い ```py # カラム名を指定 print df["a"] """ A 0 S 5 D 10 F 15 G 20 H 25 Name: a, dtype: int64 """ # 複数のカラム名を指定 print df[["a", "c", "e"]] """ a c e A 0 2 4 S 5 7 9 D 10 12 14 F 15 17 19 G 20 22 24 H 25 27 29 """ # カラム名のインスタンス変数を指定 print df.a """ A 0 S 5 D 10 F 15 G 20 H 25 """ ``` ###rowの参照 スライシングなどによって指定 行に付いている名前 (インデックスラベル) による指定は後述 ```py print df[0:3] """ a b c d e A 0 1 2 3 4 S 5 6 7 8 9 D 10 11 12 13 14 """ # 負のインデックス指定 print df[-2:] """ a b c d e G 20 21 22 23 24 H 25 26 27 28 29 """ ``` ###行と列を同時に範囲指定して参照 (ixによる参照) ixアトリビュートをスライシングする事で参照が可能 実際これだけ覚えておけばどんな時にも同じ形式で使えるし便利 指定はNumPyの2次元配列のスライシングのように[行, 列]で行う インデックスラベルによる指定も可能 ただしインデックスラベルによるスライシングの場合、後ろのインデックスラベルは範囲に含まれる事に注意 ```py # インデックスによる指定 print df.ix[0, 3] """ 3 """ # インデックスによるスライシング print df.ix[0:3, 0:2] """ a b A 0 1 S 5 6 D 10 11 """ # 離れた行または列を複数インデックスで指定 print df.ix[[0, 2, 4], [0, 2, 4]] """ a c e A 0 2 4 D 10 12 14 G 20 22 24 """ # インデックスラベルによる指定 print df.ix["D", "a"] """ 10 """ # インデックスラベルによる範囲指定 print df.ix["A":"D", "a":"b"] """ a b A 0 1 S 5 6 D 10 11 """ # 離れた行または列を複数インデックスラベルで指定 print df.ix[["A", "H", "S"], ["c", "b", "a"]] """ c b a A 2 1 0 H 27 26 25 S 7 6 5 """ ``` ##Seriesのインデックス参照 DataFrameをやったのでついでにSeriesも DataFrameと違って最初からインデックスによる指定も、 インデックスラベルによる指定も可能なので特につまる所は無いと思う スライシングも可能 ```py # Seriesを用意 s1 = pd.Series(range(6), index=list("abcdef")) print s1 """ a 0 b 1 c 2 d 3 e 4 f 5 dtype: int64 """ # インデックスによる指定 print s1[0:3] """ a 0 b 1 c 2 dtype: int64 """ # インデックスラベルによる指定 print ["b":"e"] """ b 1 c 2 d 3 e 4 dtype: int64 """ ``` ##bool値によるマスキング NumPyのndarrayのようにSeriesまたはDataFrameに対して評価を行うと、それぞれの要素に対してその評価が行われたboolのSeriesまたはDataFrameがかえってくる ```py print df > 5 """ a b c d e A False False False False False S False True True True True D True True True True True F True True True True True G True True True True True H True True True True True """ print s1 > 3 """ a False b False c False d False e True f True dtype: bool """ ``` 返ってきたもので元のSeriesまたはDataFrameをマスクすることができる ```py # DataFrameのマスク print df[df > 5] """ a b c d e A NaN NaN NaN NaN NaN S NaN 6 7 8 9 D 10 11 12 13 14 F 15 16 17 18 19 G 20 21 22 23 24 H 25 26 27 28 29 """ # Seriesのマスク print s1[s1 > 3] """ e 4 f 5 dtype: int64 """ ``` ##Series同士, DataFrame同士の演算 同じインデックスラベル同士で演算が行われる 同じインデックスラベルがないものはNaNになる NaNにしたくない場合はfill_valueでNaNに0を充ててから演算すればよい ```py # Seriesを用意 s1 = pd.Series(range(6), index=list("abcdef")) s2 = pd.Series([10,20,30,40,50], index=list("asdfg")) print s1 """ a 0 b 1 c 2 d 3 e 4 f 5 dtype: int64 """ print s2 """ a 10 s 20 d 30 f 40 g 50 dtype: int64 """ # s1 + s2でもよい print s1.add(s2) """ a 10 b NaN c NaN d 33 e NaN f 45 g NaN s NaN dtype: float64 """ # NaNを0に置換してから演算 print s1.add(s2, fill_value=0) """ a 10 b 1 c 2 d 33 e 4 f 45 g 50 s 20 dtype: float64 """ # DataFrameを準備 df1 = DataFrame(arange(30).reshape((6,5)), index=list("ASDFGH"), columns=list("abcde")) df2 = DataFrame(arange(42).reshape((7,6)), index=list("ASDFGHJ"), columns=list("abcdef")) print df1 """ a b c d e A 0 1 2 3 4 S 5 6 7 8 9 D 10 11 12 13 14 F 15 16 17 18 19 G 20 21 22 23 24 H 25 26 27 28 29 """ print df2 """ a b c d e f A 0 1 2 3 4 5 S 6 7 8 9 10 11 D 12 13 14 15 16 17 F 18 19 20 21 22 23 G 24 25 26 27 28 29 H 30 31 32 33 34 35 J 36 37 38 39 40 41 """ # df1 + df2でもよい print df1.add(df2) """ a b c d e f A 0 2 4 6 8 NaN D 22 24 26 28 30 NaN F 33 35 37 39 41 NaN G 44 46 48 50 52 NaN H 55 57 59 61 63 NaN J NaN NaN NaN NaN NaN NaN S 11 13 15 17 19 NaN """ # NaNを0に置換してから演算 print df1.add(df2, fill_value=0) """ a b c d e f A 0 2 4 6 8 5 D 22 24 26 28 30 17 F 33 35 37 39 41 23 G 44 46 48 50 52 29 H 55 57 59 61 63 35 J 36 37 38 39 40 41 S 11 13 15 17 19 11 """ ``` また、演算するデータの片方のインデックスラベルがもう片方のデータのインデックスラベルに完全に含まれる場合はreindexでfill_valueしてから演算をしても良い ```py # 上のdf1とdf2を例に # df1のインデックスラベルはdf2に完全に含まれている print df1.index.isin(df2.index).all(), df1.columns.isin(df2.columns).all() """ True True """ print df1.reindex(index=df2.index, columns=df2.columns, fill_value=0) + df2 """ a b c d e f A 0 2 4 6 8 5 S 11 13 15 17 19 11 D 22 24 26 28 30 17 F 33 35 37 39 41 23 G 44 46 48 50 52 29 H 55 57 59 61 63 35 J 36 37 38 39 40 41 """ ``` ##階層的インデックス 一つの行または列に対して複数のインデックスラベルをつけることができる 階層的インデックスのラベルでソートする場合はsortlevelメソッドを使う ```py # Series s = Series(range(10), index=[list("aaabbbccdd"),[1,2,3,2,1,3,1,2,1,2]]) print s """ a 1 0 2 1 3 2 b 2 3 1 4 3 5 c 1 6 2 7 d 1 8 2 9 dtype: int64 """ # 外側のインデックスラベルを参照 print s["a"] """ 1 0 2 1 3 2 dtype: int64 """ # 内側のインデックスラベルを参照 print s[:, 1] """ a 0 b 4 c 6 d 8 dtype: int64 """ # インデックスラベルの階層を変更 # 0階層目のインデックスラベルと1階層目のインデックスラベルを入れ替え # s.swaplevel(1, 0)でもよい print s.swaplevel(0, 1) """ 1 a 0 2 a 1 3 a 2 2 b 3 1 b 4 3 b 5 1 c 6 2 c 7 1 d 8 2 d 9 dtype: int64 """ # 外側のインデックスラベルでソート print s.swaplevel(0,1).sortlevel(0) """ 1 a 0 b 4 c 6 d 8 2 a 1 b 3 c 7 d 9 3 a 2 b 5 dtype: int64 """ ``` ##stackメソッドとunstackメソッド stackは列から行へのピボット変換 unstackは行から列へのピボット変換 行と列の変換なのでDataFrameか階層的インデックスラベルを持つSeriesでないと使えない ```py # 階層的インデックスラベルをもつSeriesオブジェクト print s """ a 1 0 2 1 3 2 b 2 3 1 4 3 5 c 1 6 2 7 d 1 8 2 9 dtype: int64 """ # 行を列へ変換 print s.unstack() """ 1 2 3 a 0 1 2 b 4 3 5 c 6 7 NaN d 8 9 NaN """ print df """ a b c d e A 0 1 2 3 4 S 5 6 7 8 9 D 10 11 12 13 14 F 15 16 17 18 19 G 20 21 22 23 24 H 25 26 27 28 29 """ # 列を行へ print df.stack() """ A a 0 b 1 c 2 d 3 e 4 S a 5 b 6 c 7 d 8 e 9 D a 10 b 11 c 12 d 13 e 14 F a 15 b 16 c 17 d 18 e 19 G a 20 b 21 c 22 d 23 e 24 H a 25 b 26 c 27 d 28 e 29 dtype: int64 """ # 行を列へ print df.unstack() """ a A 0 S 5 D 10 F 15 G 20 H 25 b A 1 S 6 D 11 F 16 G 21 H 26 c A 2 S 7 D 12 F 17 G 22 H 27 d A 3 S 8 D 13 F 18 G 23 H 28 e A 4 S 9 D 14 F 19 G 24 H 29 dtype: int64 """ ``` ##名前付け ###行または列の名前付け 行の場合はindex、列の場合はcolumns属性を上書きすると名前を変えることができる もしくはrenameメソッドを使って変更前の名前と変更後の名前を辞書形式で渡す renameメソッドを使うとデータのコピーが返され、元のデータは変更されない ```py print df """ a b c d e A 0 1 2 3 4 S 5 6 7 8 9 D 10 11 12 13 14 F 15 16 17 18 19 G 20 21 22 23 24 H 25 26 27 28 29 """ # 行名を変更 df.index = ["AAA", "SSS", "DDD", "FFF", "GGG", "HHH"] # 列名を変更 df.columns=["one", "two", "three", "four", "five"] print df """ one two three four five AAA 0 1 2 3 4 SSS 5 6 7 8 9 DDD 10 11 12 13 14 FFF 15 16 17 18 19 GGG 20 21 22 23 24 HHH 25 26 27 28 29 """ # renameを使う print df.rename(index={"AAA":"a", "SSS":"s", "DDD":"d", "FFF":"f", "GGG":"g", "HHH":"h"}, columns={"one":1, "two":2, "three":3, "four":4, "five":5}) """ 1 2 3 4 5 a 0 1 2 3 4 s 5 6 7 8 9 d 10 11 12 13 14 f 15 16 17 18 19 g 20 21 22 23 24 h 25 26 27 28 29 """ ``` ###インデックスラベル自体の名前付け インデックスラベル自体に名前を付ける事がができる 付けた名前はswaplevelやunstackなどの軸の変換をするときに使うことができる ```py # インデックスの名前を変更 df.index.names = ["UPPER"] # カラムの名前を変更 df.columns.names = ["lower"] print df """ lower one two three four five UPPER AAA 0 1 2 3 4 SSS 5 6 7 8 9 DDD 10 11 12 13 14 FFF 15 16 17 18 19 GGG 20 21 22 23 24 HHH 25 26 27 28 29 """ ``` ##ビンニング (ビンとはヒストグラムの柱のこと) pd.cutメソッドを使うとデータの値をビンに分割することができる ```py # 年齢と性別のデータ df = DataFrame([[20,"F"],[22,"M"],[25,"M"],[27,"M"],[21,"F"],[23,"M"],[37,"F"],[31,"M"],[61,"F"],[45,"M"],[41,"F"],[32,"M"]], columns=["age", "sex"]) print df """ age sex 0 20 F 1 22 M 2 25 M 3 27 M 4 21 F 5 23 M 6 37 F 7 31 M 8 61 F 9 45 M 10 41 F 11 32 M """ # ビンに分割するときの値 bins = [18, 25, 35, 60, 100] # ビンの名前 group_names = ["Youth", "YoungAdult", "MiddleAged", "Senior"] # ビン化 print pd.cut(df.age, bins, labels=group_names) """ Categorical: [Youth, Youth, Youth, YoungAdult, Youth, Youth, nan, YoungAdult, nan, nan, nan, YoungAdult] Levels (4): Index(['Youth', 'YoungAdult', 'MiddleAged', 'Senior'], dtype=object) """ # dfにビンの列を追加 df["bin"] = pd.cut(df.age, bins, labels=group_names) print df """ age sex bin 0 20 F Youth 1 22 M Youth 2 25 M Youth 3 27 M YoungAdult 4 21 F Youth 5 23 M Youth 6 37 F MiddleAged 7 31 M YoungAdult 8 61 F Senior 9 45 M MiddleAged 10 41 F MiddleAged 11 32 M YoungAdult """ ``` ##列とインデックスの変換 特定の列のみをインデックスに変換したい場合はDataFrameのset_indexメソッドを使う ```py print df """ age sex bin 0 20 F Youth 1 22 M Youth 2 25 M Youth 3 27 M YoungAdult 4 21 F Youth 5 23 M Youth 6 37 F MiddleAged 7 31 M YoungAdult 8 61 F Senior 9 45 M MiddleAged 10 41 F MiddleAged 11 32 M YoungAdult """ # bin列をインデックスに変換 print df.set_index("bin") """ age sex bin Youth 20 F Youth 22 M Youth 25 M YoungAdult 27 M Youth 21 F Youth 23 M MiddleAged 37 F YoungAdult 31 M Senior 61 F MiddleAged 45 M MiddleAged 41 F YoungAdult 32 M """ # 複数の列を階層的インデックスに変換するにはリストを引数に与える print df.set_index(["bin", "sex"]) """ age bin sex Youth F 20 M 22 M 25 YoungAdult M 27 Youth F 21 M 23 MiddleAged F 37 YoungAdult M 31 Senior F 61 MiddleAged M 45 F 41 YoungAdult M 32 """ # ソート print df.set_index(["bin", "sex"]).sortlevel("bin") """ age bin sex MiddleAged F 37 F 41 M 45 Senior F 61 YoungAdult M 27 M 31 M 32 Youth F 20 F 21 M 22 M 25 M 23 """ ``` ##ランダムサンプリング DataFrameのtakeを使うと必要な行だけ取り出すことができる これを利用してランダムに列を取り出せばランダムサンプリングすることができる ```py # サンプリングする行を指定 print df.take([1, 3, 5]) """ age sex bin 1 22 M Youth 3 27 M YoungAdult 5 23 M Youth """ # ランダムに5つサンプリング sampler = np.random.permutation(len(df)) print df.take(sampler[:5]) """ age sex bin 8 61 F Senior 2 25 M Youth 7 31 M YoungAdult 1 22 M Youth 0 20 F Youth """ ``` ##カテゴリカルデータをダミー変数化 pandasのget_dummiesメソッドを使うとカテゴリカルデータ (質的データ) をダミー変数に変換することができる 数量化I類とかするときに使うアレ ```py # 性別columnをダミー変数化 dum = pd.get_dummies(df["sex"]) print dum """ F M 0 1 0 1 0 1 2 0 1 3 0 1 4 1 0 5 0 1 6 1 0 7 0 1 8 1 0 9 0 1 10 1 0 11 0 1 """ # dfに追加 df = pd.concat((df, dum), axis=1) df = df.drop("sex", axis=1) print df """ age bin F M 0 20 Youth 1 0 1 22 Youth 0 1 2 25 Youth 0 1 3 27 YoungAdult 0 1 4 21 Youth 1 0 5 23 Youth 0 1 6 37 MiddleAged 1 0 7 31 YoungAdult 0 1 8 61 Senior 1 0 9 45 MiddleAged 0 1 10 41 MiddleAged 1 0 11 32 YoungAdult 0 1 """ ``` ## グルーピング groupbyを使うことで値によるグルーピングが行える それぞれのグループに同じ処理を適用するにはgroupbyオブジェクトのapplyメソッドを使う ```py print df.groupby("bin") """ <pandas.core.groupby.DataFrameGroupBy object at 0x106af9a90> """ # グループごとにmeanを計算 f = lambda x: x.mean() print df.groupby("bin").apply(f) """ age F M bin MiddleAged 41.0 0.666667 0.333333 Senior 61.0 1.000000 0.000000 YoungAdult 30.0 0.000000 1.000000 Youth 22.2 0.400000 0.600000 """ ``` 例: ビン分析 ビンの値グルーピングし、それぞれのビンごとに統計量を出す ```py frame = DataFrame({"data1": np.random.randn(1000), "data2":np.random.randn(1000)}) factor = pd.cut(frame.data1, 4) # data1の値の範囲を均等に4つのビンに分割 # applyするための関数を定義 def get_stats(group): return {"min": group.min(), "max": group.max(), "count": group.count(), "mean": group.mean()} # data2の値の統計量をdata1のビンごとに出す grouped = frame.data2.groupby(factor) print grouped.apply(get_stats).unstack() """ count max mean min data1 (-3.205, -1.673] 44 2.544910 -0.108558 -1.984124 (-1.673, -0.147] 414 3.420818 0.003078 -3.168528 (-0.147, 1.379] 447 2.563359 -0.018062 -3.175148 (1.379, 2.905] 95 2.498611 0.140166 -2.439985 """ ``` aggメソッドを使うとGroupByオブジェクトのカラムごとに同じ関数を適用できる ```py df["random"] = [random.choice((0,1)) for i in range(len(df))] print df """ age sex random 0 20 F 1 1 22 M 1 2 25 M 1 3 27 M 0 4 21 F 1 5 23 M 1 6 37 F 1 7 31 M 0 8 61 F 1 9 45 M 1 10 41 F 0 11 32 M 0 """ print df.groupby("sex").agg([np.max, np.min, np.mean]) """ age random amax amin mean amax amin mean sex F 61 20 36.000000 1 0 0.800000 M 45 22 29.285714 1 0 0.571429 """ ``` |
|
| 360位 |
|
|||
|
14:13:08 |
(Cookpad 所属) |
|
やっちまった〜 commit してたら最強の味方 reflog 使えるけど add しかしてないしなぁ…… でも、git は objectdb だし、add している時点で 何処かに保存されているはず!! と思って調べたら `git fsck` を発見!! どこにも属してないものが見れる!! やった!! これで `hash` とれるので `git unpack-file` で内容だけは復元できる!! つまり ```sh $ git fsck | grep blob | cut -d ' ' -f3 | xargs git unpack-file ``` ってやるとどこにも管理されてないファイルがとりあえず復元します。 ``` .merge_file_KhsjP3 ``` みたいな名前になっているので、ファイル開いて中身はなんとか救出できた!! もっと良い方法あったら教えて下さい>< って社内ブログに書いたら > 上のワンライナーと同じことが実現できる `git fsck --lost-found` というのがあります。 参考: http://git-scm.com/docs/git-fsck って教えてもらった!! 超便利〜 git 奥が深い…… |
|
| 361位 |
|
|||
|
10:05:52 |
(garbs 所属) |
|
[前回](http://qiita.com/tbaba/items/68463451a4e060097dbb)とは記事を分けることにした。長くなるから。
今回は、だれでもやったことがあるであろう、「え、ちょ、3つ前のコミットにtypo見つけちゃったよ!!」に対応する。 やりかたはいくつかある。例えば、最初に思いつきそうなことが、 - そのコミットまで`git reset`を繰り返して、編集して、もう一回`git commit`していく である。だけど、前の編集内容を覚えてなきゃいけないし、「Gitを使ってるくせに」的なアナログ感を感じざるをえない……ので、もっと上手い方法は無いのか。 ある。 ## git rebase -i `i`は`interactive`のことだ。多分。`man git-rebase`をちゃんと読めば書いてあるのかもしれない。英語だけど気になる人は読んでみて欲しい。 で、このコマンド、何ができるのか。試しにやってみよう。 `git log --oneline`したら、こんな感じのログがあった。 ``` ee88db4 Add picture of '平塚の海岸線' f182a59 Write the continuance of the diary 80dfd45 Add nikki ``` しかし、`git log -p`して詳細を眺めていくと、`80dfd45`の中に、重大なミスを発見してしまった。 **俺10時って書いてるけど、10時半だよこれ!!** ```diff commit 80dfd45d305cb7b9807419e31115929781b1f94b Author: Tatsuro Baba <harakirisoul@gmail.com> Date: Tue Oct 8 09:33:33 2013 +0900 Add nikki diff --git a/nikki b/nikki new file mode 100644 index 0000000..0551e98 --- /dev/null +++ b/nikki @@ -0,0 +1,7 @@ +今、富士の某ホテルでこの日記を書いていている。 + +朝8:40、自宅を出る。すでに出発予定時刻を40分過ぎている。普段の生活態度がうかがい知れる。 +ひたすら南に下って行く。途中自動車専用道があったので、回避したら、300m程度のとんでもない激坂があって、萎えかけた。 +そのあとはひたすら平坦な道を平塚に向かって進むだけだったので、楽しかった。時速で言うと30km/hくらいか。 + +10時ごろに平塚に着いて、海に出てみる。 ``` しかし、このコミット、もう3つも前のやつである。`git show HEAD~2`でようやく出てくるやつである。 ここまで根深いところにあると、`git commit --amend`では対処できない。ふつうに修正して`Typo`とか`fix forgotten time`とかでコミットするのも手だが、まだリモートにpushしていないところなので、(ダサいし)パパっと直してしまいたい。 というわけで前置きが長くなったが、`git rebase -i`の出番である。 おもむろにターミナルで、 ```sh $ git rebase -i HEAD~3 ``` こうすることで、過去3つのコミットをrebaseの対象にできる。 前回の記事と同じく、エディタが立ち上がって、以下のように表示されるはずだ。 ```vi 1 pick 80dfd45 Add nikki 2 pick f182a59 Write the continuance of the diary 3 pick ee88db4 Add picture of '平塚の海岸線' 4 5 # Rebase fa33ce4..ee88db4 onto fa33ce4 6 # 7 # Commands: 8 # p, pick = use commit 9 # r, reword = use commit, but edit the commit message 10 # e, edit = use commit, but stop for amending 11 # s, squash = use commit, but meld into previous commit 12 # f, fixup = like "squash", but discard this commit's log message 13 # x, exec = run command (the rest of the line) using shell 14 # 15 # These lines can be re-ordered; they are executed from top to bottom. 16 # 17 # If you remove a line here THAT COMMIT WILL BE LOST. 18 # 19 # However, if you remove everything, the rebase will be aborted. 20 # 21 # Note that empty commits are commented out ``` これの1行目、`pick`と書いてあるところを、おもむろに`edit`と書き換えよう。すると、 ```sh Stopped at 80dfd45... Add nikki You can amend the commit now, with git commit --amend Once you are satisfied with your changes, run git rebase --continue ``` と言われるはずだ。この時点でどうなっているかというと、 **次にある2つのコミットは一旦よそに置いといて、`80dfd45`だけがコミットされた状態**まで巻き戻っている、みたいな、だいたいそんな感じだ。現状はそんな程度の認識でも大丈夫。 ここまでくればあとは楽勝である。おもむろに修正したコミットを作り、 ```diff diff --git a/nikki b/nikki index 6a66ce0..0551e98 100644 --- a/nikki +++ b/nikki @@ -4,4 +4,4 @@ ひたすら南に下って行く。途中自動車専用道があったので、回避したら、300m程度のとんでもない激坂があって、萎えかけた。 そのあとはひたすら平坦な道を平塚に向かって進むだけだったので、楽しかった。時速で言うと30km/hくらいか。 -10時ごろに平塚に着いて、海に出てみる。 +10時半ごろに平塚に着いて、海に出てみる。 ``` さくっとamendしてやる。 ```sh $ g ad . $ d --cached diff --git a/nikki b/nikki index 6a66ce0..0551e98 100644 --- a/nikki +++ b/nikki @@ -4,4 +4,4 @@ ひたすら南に下って行く。途中自動車専用道があったので、回避したら、300m程度のとんでもない激坂があって、萎えかけた。 そのあとはひたすら平坦な道を平塚に向かって進むだけだったので、楽しかった。時速で言うと30km/hくらいか。 -10時ごろに平塚に着いて、海に出てみる。 +10時半ごろに平塚に着いて、海に出てみる。 $ git commit --amend ``` 直った!! この後、まだやることはある。現状、2つくらいコミットがどこか俺の知らない所に旅行してしまっていて、行き先を知っているのはGitのみである。ので、Gitにコミットたちを呼び戻してもらう必要がある。 つまり、rebase中で止まっている状態なので、rebaseを継続するコマンドを打ってやればよい。 ```sh $ g rebase --continue Successfully rebased and updated refs/heads/git-rebase. ``` これで全工程は完了。ちゃんとログも、 ``` f6dfa25 Add picture of '平塚の海岸線' f1b9f54 Write the continuance of the diary 00ea658 Add nikki ``` と、今までどおりだ。 `git-rebase`というコマンド、他にも色々機能があって奥深い。黒魔術というほどでもないが、使えるようになって損はないと思う。 ちなみにこの業を何も知らないGit初心者の前でスラスラとやったりすると、目の前で何が起こっているのか分からずに戸惑うので、ちょっと楽しい。俺は楽しかった(でもちゃんと説明してあげよう)。 あ、あと最後に。 これ、すでにリモートにpushしちゃった後にやったりすると、チームメンバーにものすごい迷惑をかける可能性が高いので、気をつけよう。 つまり、リモートにあるやつと、あなたが今rebaseしたやつには、大きな乖離が生じていて、リモートを強制的に変更することも出来るけど、それをやってしまうと、すでにリモートをpullしていたチームメンバーからしてみたら、「俺のリポジトリとリモートが違う…」ということになってしまうからだ。 このへんのリスクとかは、別の記事にしようと思う。 |
|
| 362位 |
|
|||
|
00:15:23 |
|
|
重複を削除する方法はいくつか方法があるみたいですが、 filterを使用する事で非常に楽に実現出来ます ```js:duplicate_check.js var a = [1,2,3,3,2,2,5]; // 重複を削除したリスト var b = a.filter(function (x, i, self) { return self.indexOf(x) === i; }); // 重複のみをリスト var c = a.filter(function (x, i, self) { return self.indexOf(x) !== self.lastIndexOf(x); }); // 重複を検出したものを重複しないでリスト var d = a.filter(function (x, i, self) { return self.indexOf(x) === i && i !== self.lastIndexOf(x); }); console.log(a); // [ 1, 2, 3, 3, 2, 2, 5 ] console.log(b); // [ 1, 2, 3, 5 ] console.log(c); // [ 2, 3, 3, 2, 2 ] console.log(d); // [ 2, 3 ] ``` ie8とかで動かないなら、PolyfillでArrayのプロトタイプを拡張してください。 * [filterのPolyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Polyfill) * [indexOfのPolyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill) * [lastIndexOfのPolyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf#Polyfill) ## (追記)ES2015対応 この記事も2年半前(2016/2時点)に書かれたもので、古い情報になったきたので、アップデート。 ES2015でも便利な機能も増えてきたので、それを使ってみましょう。 実戦投入は、環境によるので慎重に。 今回は、使用頻度が高い **重複を削除したリスト**のみを対象に記事を追記してみます 紹介するのは3つ。 * アロー関数式を使った場合(基本的に処理方法は前のものと同じ) * filterの代わりにreduceを使った場合 * ES2015で追加されたArray.fromとSetを使った場合 ```js:duplicate_check2.js var a = [1,2,3,3,2,2,5]; // アロー関数式 var b1 = a.filter((x, i, self) => self.indexOf(x) === i); // reduceを使用した場合 var b2 = Object.keys(a.reduce((r, x)=>(r[x]=1, r), {})).map(x=>+x); // Array.from & Setを使用した場合 var b3 = Array.from(new Set(a)); console.log(b1); // [ 1, 2, 3, 5 ] console.log(b2); // [ 1, 2, 3, 5 ] console.log(b3); // [ 1, 2, 3, 5 ] ``` |
|
| 363位 |
|
|||
|
19:22:48 |
(Drivemode, inc. / DroidKaigi 所属) |
|
## イベントバス
Android では、非同期処理の返り値はコールバックインタフェースを介してやり取りされる。 これ以外にも、Observer パターンに基いて設計されているクラス(`SharedPreferences`など)や、Activity と Fragment とのやりとりなどでも、コールバックインタフェースを定義して、その実装とライフサイクル管理をする。 一方で、機能が増えるとその分コールバックインタフェースの定義も増え、Activity が幾つものインタフェースを実装することがある。コールバックインタフェースの定義が増えてくると、その分だけ依存関係が複雑になりやすくなったり、コールバックを受けて更に非同期処理を呼び出して…としていくと、どんどんネストが深くなったりしていく(コールバック地獄)。 そこで、コールバックメソッドを呼ぶタイミングでイベントを発火し、コールバックインタフェースの実装ではなく、イベントを常時監視するメソッドを用意しておいて、発火したタイミングで呼び出されるようにする仕組みとして、EventBus を使うことで、インタフェースによる依存関係を整理したり、コールバック地獄を解消したりすることができる。 ## square/otto https://github.com/square/otto square 社が提供する EventBus の仕組み。 使い方は以下のようにする。 ```java public final class EventBusHolder { // EventBus のインスタンス自体はどの Activity からでも同じものを使うようにする // DI フレームワークで Producer を使うと便利 public static final Bus EVENT_BUS = new Bus(); } // =========== public class SampleActivity extends Activity { // ... @Override protected void onResume() { super.onResume(); // EventBus の登録 EventBusHolder.EVENT_BUS.register(this); } @Override protected void onPause() { // 登録の解除 EventBusHolder.EVENT_BUS.unregister(this); super.onPause(); } // イベントハンドラの宣言 @Subscribe public void onClickButton(ButtonClickEvent event) { Toast.makeText(getApplicationContext(), "Button clicked " + event.getClickCount() + " times.", Toast.LENGTH_SHORT).show(); } } // =========== public class SampleFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>{ private int mClickCount; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_sample, null, false); Button button = (Button) view.findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // イベントの発火 EventBusHolder.EVENT_BUS.post(new ButtonClickEvent(mClickCount++)); } }); return view; } @Override public Loader<Cursor> onCreateLoader(int id, Bundle bundle) { new CursorLoader(MediaStore.Images.Media.EXTERNAL_CONTENT_URI); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor result) { // 以下は上手くイベントがハンドラにわたらない(FragmentTransaction をこのコールバックで取り扱えないのと同じ理由) EventBusHolder.EVENT_BUS.post(new CursorLoadedEvent(result)); } @Override public void onLoaderReset(Loader<Cursor> loader) {} } // =========== // イベントオブジェクト public class ButtonClickEvent { private final int mClickCount; public ButtonClickEvent(final int clickCount) { mClickCount = clickCount; } public int getClickCount() { return mClickCount; } } // =========== // イベントオブジェクト public class CursorLoadedEvent { private final Cursor mCursor; public CursorLoadedEvent(final Cursor cursor) { mCursor = cursor; } public Cursor getCursor() { return mCursor; } } ``` Google Guava をベースとしているが、いくつかの点で Guava とは異なる実装をしている。また、Android 向けに最適化されていたり、挙動に制限を加えていたりする。 1. クラスの継承階層を辿らず、otto に登録したオブジェクトの型で宣言されている`@Subscribe`なメソッドのみをイベントハンドラとして登録する。 2. `@Producer`なメソッドを宣言することで、予め定義したデータを、非同期処理後のイベントを待つこと無く配信できる。 3. 基本的には、メインスレッド上でイベントを発火し、メインスレッド上でイベントを受信する設計になっている。設定次第で、メインスレッド以外からもイベントを発火可能だが、イベントを受信するハンドラ側は常にメインスレッドでなければならない。 ## greenrobot/EventBus https://github.com/greenrobot/EventBus Android に最適化された EventBus の仕組み。 考え方や使い方は otto と同じで、使用するメソッドもほぼ同じものが提供されている。 一方で、イベントハンドラの宣言やスレッドに関するところで otto と異なる点がある。 1. クラスの継承階層を辿り、親クラスのイベントハンドラ宣言にもイベントが通知される。 2. アノテーションではなく、メソッドの命名によってイベントハンドラの宣言をする。 3. 異なるスレッド間どうしでイベントを通知しあうことが出来る。ただし、イベントの通知を受ける側は、その処理を実行するスレッドをブロックしないように設計する必要がある。 4. `@Producer`によるイベントキャッシュの仕組みを自分で実装する必要はなく、StickyEvent として EventBus が管理してくれる。 ```java // =========== public class SampleActivity extends Activity { // ... @Override protected void onResume() { super.onResume(); // EventBus の登録 EventBus.getDefault().register(this); } @Override protected void onPause() { // 登録の解除 EventBus.getDefault().unregister(this); super.onPause(); } // イベントハンドラの宣言 public void onEvent(ButtonClickEvent event) { Toast.makeText(getApplicationContext(), "Button clicked " + event.getClickCount() + " times.", Toast.LENGTH_SHORT).show(); } // イベントハンドラの宣言。メインスレッド上で実行することを強制する場合 public void onEventMainThread(CursorLoadedEvent event) { } } // =========== public class SampleFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>{ private int mClickCount; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_sample, null, false); Button button = (Button) view.findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // イベントの発火 EventBus.getDefault().post(new ButtonClickEvent(mClickCount++)); } }); return view; } @Override public Loader<Cursor> onCreateLoader(int id, Bundle bundle) { new CursorLoader(MediaStore.Images.Media.EXTERNAL_CONTENT_URI); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor result) { // イベントの通知 EventBus.getDefault().post(new CursorLoadedEvent(result)); } @Override public void onLoaderReset(Loader<Cursor> loader) {} } // =========== // イベントオブジェクト public class ButtonClickEvent { private final int mClickCount; public ButtonClickEvent(final int clickCount) { mClickCount = clickCount; } public int getClickCount() { return mClickCount; } } // =========== // イベントオブジェクト public class CursorLoadedEvent { private final Cursor mCursor; public CursorLoadedEvent(final Cursor cursor) { mCursor = cursor; } public Cursor getCursor() { return mCursor; } } ``` ## BroadcastReceiver との違い otto, EventBus ともに、単純な Java オブジェクトを用いてイベントを通知できる。 BroadcastReceiver の場合、Intent の仕組みに準じる実装をしなければならないため、イベントオブジェクトは Parcelable である必要があったり、Extra として Intent にオブジェクトを詰め込んだり、イベントを受け取る側も、IntentFilter を宣言したり、受け取った Intent から Extra を取り出したりと、実装コストが高くなりがちだが、otto や EventBus であれば、イベントオブジェクトを単純なクラスとして宣言すれば、あとはメソッド宣言を規約に沿った形で実装するだけで済む。 また、BroadcastReceiver を使う場合、セキュリティに気を使わないと、インストールされているアプリすべてで Intent が受け取れてしまう問題がある。LocalBroadcastReceiver やパーミッションを使えば解決できるが、イベントの通知の実装に掛かるコストは同じか、パーミッションの分だけ増えることになるので、特に理由がなければ otto や EventBus を使うのが良さそう。 |
|
| 364位 |
|
|||
|
01:14:55 |
(Bitjourney, inc 所属) |
|
##### ※ Route::controller のルーティングはLaravel 5.2 で非推奨、Laravel5.3 で削除されました
--- これは[Laravel Advent Calendar](http://qiita.com/advent-calendar/2013/laravel) 17日目の記事です。 昨日は[HiroKwsさんの最強のデバッグツールlaravel-debugger](http://kore1server.com/229)についてでした。 今回はLaravelのルーティングの書き方をまとめてみようと思います。 まず基本ですがLaravelではルーティングの設定はapp/routes.phpにまとめて記載するようになっていますので今回ご紹介するルーティングのコードは全てapp/routes.phpに記載するものと思ってください。 Laravelのルーティングは大きく分けて3つの書き方がありますので順番に見ていきましょう。 ## 1, ルーティングと同時に表示内容もクロージャで記載してしまう方法 これは一番シンプルなルーティングの書き方かもしれません。 コードと一緒に見ていきます。 ```php Route::get('hello/', function() { return 'Hello World'; }); ``` ↑ http://example.com/hello にアクセスされたら「Hello World」と表示させるというルーティングです。 ```php Route::get('hello/{message}', function($message) { return 'Hello World' . $message; }); ``` ↑ helloの次に指定されたパスをパラメータとして受け取り、受け取ったパラメータをクロージャに引数として渡し表示内容を変化させるといったルーティングです。 ```php Route::get('hello/{message}', function($message) { return 'Hello World' . $message; }) ->where('message', '[A-Za-z]+'); ``` ↑ 受け取るパラメータの値をwhereメソッドで指定し、指定した値のものであった場合に適用するといったルーティングです。 whereでの指定の仕方は1番目の引数にパラメータ名を指定し、2番目にパラメータ内容を指定します。 パラメータ内容指定には正規表現が使用可能です。 getの部分はpostやput等のhttpメソッドに対応していますので適宜応用が可能です。 ## 2, コントローラーとメソッドを指定しルーティングさせる方法 フレームワークでおなじみのルーティングです。 なんの変哲もないと思います。 ```php Route::get('hello/', 'App\Controllers\helloController@goodmorning'); ``` ↑ hello/にアクセスされたらhelloControllerコントローラーのgoodmorningメソッドを呼び出せというルーティングです。 ```php Route::get('hello/{message}', 'App\Controllers\helloController@goodmorning'); ``` ↑ helloの次に指定されたパスをパラメータとして受け取り、受け取ったパラメータをメソッドの引数として渡すというルーティングです。 コントローラーは下記のように書けばいいと思います。 ```php namespace App\Controllers; class helloContorller extends BaseController { public function goodmorning($message) { 〜 } } ``` おなじみですね。 ## 3, RESTfulにルーティングさせる方法 このルーティングはすてきです。 僕が最初にLaravelでおおっ!と思ったところはこのルーティングでした。 まずはルーティングのコード。 ```php Route::controller('hello/', 'App\Controllers\helloController'); ``` ↑ このワンライナーのコードを書くだけで下記のような多数のルーティングに対応することができます。 ```php namespace App\Controllers; class helloController extends BaseController { // getでhello/にアクセスされた場合 public function getIndex() { 〜 } // getでhello/goodmorningにアクセスされた場合 public function getGoodmorning() { 〜 } // postでhello/goodmorningにアクセスされた場合 public function postGoodmorning() { 〜 } // getでhello/goodmorning/messageでアクセスされた場合 public function getGoodmorning($message) { 〜 } } ``` 説明不要だと思いますがアクセスされたパスと同じ名前を持つアクションメソッドが実行されています。 各アクションメソッド名の前についているgetやpostはアクセス時のhttpメソッドです。 とても分かりやすくてすてきですね。 ## 4, Resourcefulにルーティングさせる方法 これもすてきです。 でも今回調べるまで知りませんでした。orz てかResourcefulとか言われてもピンとこないと思うのでまずはルーティングコードから見ていきましょう。 ```php Route::resource('hello', 'App\Controllers\helloController'); ``` これも3のRestfulルーティングと同様ワンライナーで多数のルーティングに対応させることができます。 ```php namespace App\Controllers; class helloController extends BaseController { // getでhello/にアクセスされた場合 public function index() { 〜 } // getでhello/createにアクセスされた場合 public function create() { 〜 } // postでhello/にアクセスされた場合 public function store() { 〜 } // getでhello/messageにアクセスされた場合 public function show($message) { 〜 } // getでhello/message/editにアクセスされた場合 public function edit($message) { 〜 } // putまたはpatchでhello/messageにアクセスされた場合 public function update($message) { 〜 } // deleteでhello/messageにアクセスされた場合 public function destroy($message) { 〜 } } ``` ちょっとわかりにくいかと思いますので、Laravelの公式ドキュメントに図が掲載されていたのでこちらとあわせてご覧ください。  図をみるとわかりますが指定したresource(今回の例ではhello)に対してhttpメソッドやパスで直感的にアクセスできるようルーティングしてくれています。 ## 最後に LaravelのルーティングはRestfulルーティングやResourcefulルーティングのおかげで非常にわかりやすくそして綺麗に書けます。 ルーティングの書きやすさは間違いなくLaravelの目玉の一つだと言えると思います。 今回は基本的なルーティングについて説明させて頂きましたがLaravelは他にもルーティングをする前に認証をかけられたり、ルーティングに名前をつけられたりとまだまだ機能は豊富です。 Laravelが気になった方はそちらも確認してみてください。 さて明日の[Laravel Advent Calendar](http://qiita.com/advent-calendar/2013/laravel) はkam01さんで「laravelでmodule」についてです。 おたのしみに! |
|
| 365位 |
|
|||
|
12:42:14 |
(スタジオピグボ 所属) |
|
はじめてQiitaで記事を書いています。先日「Node.js + Express.js + Backbone.js」でサーバとクライアントの両方のシステムを構築する"Rendr"というウェブフレームワークを試しました。Rendrは、アメリカで人気のサービス"AirBnb"のチームが開発し、オープンソースとして公開したものです。クライアント側での採用事例は増えているBackbone.jsですが、サーバでの利用は珍しいのでは無いでしょうか。Rendr自体もまだ日本語での紹介例が無い事もあり、システムを紹介しつつ、使用感やメリット、そしてデメリットをメモしておこうと思います。
※2回目を書きました: [JavaScript - Rendr入門(2): リクエスト処理の流れ(Router, Controller、View、Template)](http://qiita.com/mshk/items/b9fa3a1886241e579497) #Rendrとは? はじめにも書いたとおり、Rendrはairbnbのチームが開発したウェブフレームワーク(彼ら自身はフレームワークという言葉を避けて"ライブラリ"と呼んでいます)です。Node.js + Express.jsの上にミドルウェアとして構築されており、「Backbone.jsをサーバ側で(も)動かす」事を主眼としています。 * airbnb/rendr (Rendrのgithubリポジトリ) https://github.com/airbnb/rendr Rendrを開発した経緯は、airbnbの技術ブログで紹介されています。 * We’ve open sourced Rendr: Run your Backbone.js apps in the browser and Node.js - Airbnb Engineering http://nerds.airbnb.com/weve-open-sourced-rendr-run-your-backbonejs-a/ 元々airbnbのモバイル版を開発する際に作られたもので、 * 環境に依存しないアプリケーションロジックを書く。 * `if (server) {・・・} else {・・・}` 形式のコードを最小化する。 * RESTful API * フレームワークではなく、ライブラリ。 * ライブラリの複雑さを隠蔽する。 * サーバサイドのDOMを使わない。 * シンプルなExpressのミドルウェア を目標にしています。 airbnbチームも元々はRails + Backbone.jsでウェブアプリを開発していたようですが、 * アプリケーションコードがサーバとクライアントで分割される。あるいは、同じコードが重複してしまう。 * サーバ側でのレンダリングをERBなどで行うと、テンプレートが重複する。 などの問題意識をもったようです。 では全てをクライアント側でレンダリングしてしまえば良いかというとそういうわけでも無く、後で紹介するプレゼンテーションの動画では「レスポンスをよくするために、サーバ側でもレンダリングして最初のHTMlを早く表示したい。しかし、インタラクティブであるためにクライアント側でもレンダリングをしたい」という背景が説明されています。 #サーバ・コード Rendrを使ったアプリケーションの構造を知るには、下記のURLで公開されているスケルトンコードを読むのが良いと思います。 * airbnb/rendr-app-template (Rendrベースのスケルトンアプリ) https://github.com/airbnb/rendr-app-template 「app」ディレクトリの下は下記の様な構成になっています。 * collections ・・・Backbone.Collection * models ・・・ Backbone.Model * views ・・・ Backbone.View * templates ・・・ Handlebars(mustacheの機能強化版)ベースのテンプレート * controllers ・・・Collection/Modelの生成とViewの呼び出しを行う * app.js * router.js * routes.js controllersはBackboneには無かった概念です。元々はRouterで行っていたアプリケーション・ロジックを、独自の拡張で吸収しています。Routerのコードが"fat"になりがちだったので分割したとのこと。 Controller、View、Templateはid('StatementListView')をベースに連携していて、ファイル名を同一にしておけば自動的に関連したものが呼び出される仕組みになっています。 例: statements_controller.js → views/statements/index.js → templates/statements/index.hbs #クライアント・コード コントローラでmodel/collectionを生成してテンプレートで描画を行うとHTMLが生成される訳ですが、Rendrが面白いのはこの部分からで、生成したHTMLからクライアント側でもBackbone.jsのコードがキックされ、Viewの生成やイベントバインディングが行われるようになっています。HTML中のビュー生成部分にはサーバ側で自動的にベースになったViewとデータIDが割り振られ、HTMLとViewのバインディングについてアプリコードで特に意識をする必要はありません。 サーバ側で全てのページのレンダリングを行いつつ、まるでクライアント側でBackbone.Viewを生成してレンダリングしたように以降の処理をクライアント側で引き継ぐことができるのです。繰り返しになりますが、コントローラ、ビュー、テンプレートシステムは、同一のコードがサーバとクライアント両方で動きます。 この連携を実現するために、RendrではBackbone.Viewのコードをオーバーライドしていて、Backbone.View#render()とBackbone.View#initialize()は予約されて使用されなくなっています。代わりにBackbone.View#postInitialize()とBackbone.View#postRender()にアプリコードを書きます。この点、従来のBackboneのコードがそのまま動くわけでは無いので注意が必要です。 Viewのメソッドは下記の様にサーバとクライアント両方で動くものと、クライアントのみで動くものに分けられています。 * `view.postInitialize()` ・・・サーバ/クライアント両方で動く * `view.preRender()` ・・・サーバ/クライアント両方で動く * `view.render()` ・・・クライアントのみで動く * `view.postRender()` ・・・クライアントのみで動く * `view.getTemplateData()` ・・・サーバ/クライアント両方で動く データの取得(サーバクライアントで共通のfetchメソッドを使う)とテンプレートへの流し込みをサーバ側で、イベントのバインディングとそこから呼び出されるハンドラ内のDOM操作をクライアント側でという切り分けになるかと思います。最初はどちらでどちらが動くか確認しつつ書く感じになりますが、慣れると特に意識をせずに「レンダリング前と、後」という振り分けができるようになります。この振り分けが、同じBackbone.Viewの中で行われます。 # データのやりとり サーバ側でレンダリングを行う際にコントローラで生成されたmodel/collectionは、HTMLにJSONとして埋め込まれてクライアント側の「初期値」として使われます。その後、例えば記事の一覧をアップデートする場合は、Backbone.Collectionに設定された「url」にアクセスが行われます。何も考えなければ、レンダリングを行ったサーバの「/api/-/コレクション・モデルの名前」にアクセスが行われます。 データの扱いは、`server/lib/data_layer`で抽象化が行われ、サーバ側でのデータ取得とクライアントからのHTTP経由でのAPIコールが同一のコードで処理されます。data_layerは自前で実装する必要がありますが、サーバ内でのデータ処理とクライアントからのHTTPリクエストを透過的に記述できます。 サーバ側でCollectionを生成してHTMLをレンダリング、クライアントで「もっと見る」がクリックされたらCollection.fetchでさらにデータを取り込むというようなパターンが適用できます。 # Stitchを使ったアセットパイプラインと、CommonJS形式の「require()」 サーバ側と共通のモジュール管理を実現するために、Rendrは'Stitch'というライブラリを使用しています。 * sstephenson/stitch https://github.com/sstephenson/stitch サーバ側で指定されたJavaScriptを連結してクライアントに配信することで、クライアント側JavaScriptでも `var hoge = require (モジュール)`の形式でライブラリをロードできます。これにより、Node.js向けに書かれたサーバ側コードをクライアント側でも動かすことができます。 #使用感(メリット・デメリット ) ウェブサービスがリアルタイム化、インタラクティブ化し「ウェブアプリ化」が進む中、これまでのサーバを中心としたHTMLレンダリングシステムから、クライアント側JavaScriptを含めた、あるいは多くのコードがクライアント側に移行したシステムへの移行が進んでいます。こうした中で「どうせならサーバもJavaScriptで」と考えるケースも増えているのでは無いでしょうか? 筆者の場合も"Railsを中心に、クライアント側JavaScirptで一部書き換えを行う"システムから"RailsがほぼAPIサーバ化し、クライアント側JavaScriptに比重が移った”システムへと移り変わりを経験しました。その際に感じたのは、「クライアント側JavaScriptのコードを書きつつサーバ側も修正しようとすると言語のスイッチによるコストが高い」という事。APIコードの確認をしつつクライアントでの動作を調整する時などに違和感を感じていました。 Rendrは、サーバ/クライアントの言語の違いによる開発コストの増大を抑え、またサーバロジックとクライアントロジックを部分的に共通化することによる全体のコード量の減少にも貢献してくれます。 フレームワークとしてもBackbone.jsに全てが統一されることによる知識の集約化、学習コストの低減の効果があり、非常にコンパクトな開発ができたと思います。 デメリットをあげるとすると、まだ非常に若いプロジェクトで、もともとフルスタックを志向していない事もあり、足りない機能が多いことがあるでしょう。サンプルのプロジェクトは、データ取得を外部のHTTP APIに依存していてDBへの接続コードすらありません。コードのアップデートも頻繁で、あらゆるパートでドキュメントが足りていない状況です(開発者は非常にレスポンスが良く、github issuesに質問を立てると驚くようなスピードで返事が来ますが)。 たとえば今回自前で実装を行ったのは、下記の部分です。 * UserAgentでPC/モバイルの環境の違いをチェックして、レイアウトやテンプレートを切り替える。 * データを取得するためのサーバ側実装(server/lib/data_layer)を書き換えて、MySQLに接続してデータを渡すためのレイヤーを追加。 全体的な印象としては、開発チームのプレゼン資料やgithubのissuesなどに全て目を通して、それでようやく動きを理解できたという感じでした。 また、個人的にはこれが初のNode.jsベースの開発だったので、サーバの運用やデータベースへの接続コードの実装などありとあらゆる部分で試行錯誤が必要だったことも、デメリットと言えばデメリットでした。Rails + Backbone.js的な環境からの移行を考える方には、これもまた考慮が必要なポイントでしょう。 #まとめ すでに実戦投入しておいてあれですが、まだ試行錯誤を続けている状況です。ベースの大部分がExpress.jsであることから、システムとしては安定していて問題の押さえ込みも比較的容易である印象です。Backbone.jsでの開発経験があり、テンプレートシステムとしてMustacheを使ってきていたので、その部分の新規学習コストが低かったこともありますが、スムーズに開発を軌道に乗せることもできました。 今回のプロジェクトでは、コードの一部をインターンの学生に手伝ってもらい、クライアント側のUIのパーツの開発を協力会社に依頼していましたが、Backbone.jsという共通言語を介して(自分が把握している限りは)非常にスムーズなやりとりができました。開発したプロダクトは、モバイル版の後にPC版を構築したのですが、デザイン段階からパーツの流用を意識していたことも有り、ほぼレイアウトとテンプレートの差し替えのみの作業で数日で完了することもできました。 なにより「コードをサーバクライアントで共通化する」という点がスマートに実現されていて、開発そのものが楽しくなった事を大きく評価しています。 安易に「おすすめ」をするものではありませんが、「ぜひ興味をもってもらいたい」プロダクトです。 (メモを残す意味で一気にこの文章を書きました。不足があればコメントしてください。ひょっとしたら続編でもう少し詳しく開発の実際について説明できるかもしれません。) #ポインタ その他Rendrに関する記事や、スタッフによるプレゼンがいくつか公開されています。 * Introduction to Airbnb's Rendr - YouTube http://www.youtube.com/watch?v=JmS0HrH15ZI * Airbnb Open Sources Rendr, A Library For Running Backbone.js Apps On Both Client And Server | TechCrunch http://techcrunch.com/2013/04/19/airbnb-open-sources-rendr-librar/ * Our First Node.js App: Backbone on the Client and Server | Hacker News https://news.ycombinator.com/item?id=5137859 |
|
| 366位 |
|
|||
|
21:04:02 |
(Recruit Lifestyle Co. Ltd. 所属) |
|
さあiBeaconのアプリを作ってみよう!
となった時に、一番困るのがiBeaconフォーマットのBluetoothを発信してくれるようなデバイスがパッとないことです。 自分で実装?めんどいな…っていうかiPhone2台も持ってないしなぁ…… aplixやestimoteを買う?金かかるのもなぁ…… というわけで、Macを使った最も簡単にiBeaconの電波を発信する(Advertiseする)方法をまとめたいと思います。 最も簡単に「受信」をしたい場合はこちら! [たった4行!最も簡単にiBeaconの電波を「受信」する方法](http://qiita.com/Morikuma_Works/items/c2899e548da1c5e2c28e) #環境 * Bluetooth Low Energy(BLE, Bluetooth4.0)が搭載されたMac製品 * Macbook Pro, Macbook Pro Retina, Macbook Airなどなど * 最近の製品なら大抵OK * node.js v0.10.24 #準備 Macでnode.jsとnpmを使えるようにする必要があります。 http://nodejs.org/ から"INSTALL"をクリックしpkgファイルをダウンロードし、インストールします。 その後、適当にディレクトリを作成しましょう。 ここでは`~/ibeacon`を作成します。 ```Bash mkdir ~/ibeacon ``` これで準備は完了です! #iBeaconを発信してみる 今回はbleaconを使います。 まず、bleaconをnpm installしましょう。 ```Bash cd ~/ibeacon npm install bleacon ``` その後、適当にjsを書きます。下記をコピペでも構いません。 ```Javascript:beacon.js Bleacon = require('bleacon'); var uuid = 'E86367B8D8A84BE082DE4653A7333113'; var major = 0; var minor = 0; var measuredPower = -59; Bleacon.startAdvertising(uuid, major, minor, measuredPower); ``` これで準備は完了です。 実際に実行してみましょう。 ```Bash node ~/ibeacon/beacon.js ``` これで特にエラーがでなければ飛んでいるはずです。 とても簡単! ####追記 blenoを使用していましたが、bleaconに変更しました。 blenoをbleaconに書き換えるだけで動くはずです。 また、メソッド名も変更されてました。 startAdvertisingIbeacon -> startAdvertising zonoさんありがとうございます! |
|
| 367位 |
|
|||
|
23:09:29 |
|
|
##Vagrantとは
Vagrantの使い方を忘れないようにメモしておこうと思います。 Vagrantとは、VirtualBoxやVMFusionなどの仮想化マシンの自動作成・管理ツールになります。 最近、バージョン1.1が公開されました。 ##どんな事ができるのか boxと呼ばれるひな形を使って、簡単に仮想マシンを構築する事が出来ます。 本来 VirtualBoxなどを使う際は、イメージファイルをダウンロードして設定そしてインストールなどの手間が生じます。 テストするために環境を作りすぐ破棄する場合には、とても面倒な作業になることでしょう。 そのようなテスト環境構築のためなどにVagrantを使用します。 <br> #インストール方法 ##vagrantインストール `gem`を使ってインストールします。 <<追記>> 最近公開された1.1.x系統ではMac,Windowns,redhat系でもパッケージが存在します。 パッケージからインストールする事も出来ます。 しかし * net-scpあたりの依存関係が未対応なところが多いこと * veeweeなどを使う場合、vagrantの古いバージョンに依存していること などが挙げられるので、今回もgemからインストールすることをお勧めします。 ```ruby:vagrant-install gem install vagrant --no-ri --no-rdoc ``` ちなみに1.1.x系統は以下のページにございます。 * [Vagrant 1.1](http://downloads.vagrantup.com/tags/v1.1.0) <font color="red">重要!!</font> 以前gemを使ってvagrantをインストールされた方で、vagrant1.1X系統にアップグレードされる方は一回アンインストールしなければなりません。 詳しくは[ドキュメント](http://docs.vagrantup.com/v2/installation/upgrading-from-1-0.html)参考をお願いします。 ```ruby:vagrant-uninstall gem uninstall vagrant ``` バージョンによっては、対応されてないプラグインなども存在します。 どのバージョンをインストールするのかお気をつけ下さい。 <br> #Vagrantの使い方 ##Vagrantコマンド | vagrantコマンド | 説明 | |:---------------:|:---| |vagrant init | vagrant 初期化(Vagrantfileの作成) |vagrant up | vagrant 起動| |vagrant ssh | vagrant ログイン| |vagrant halt | vagrant 終了| |vagrant reload | vagrant リロード(Vagrantfile編集時にいちいちhalt,upしなくて良い| ほかにもpackageなどのオプションが存在します。 ##セットアップ 流れとしては以下のようになります。 1.boxファイルのインストール 2.Vagrantの初期化 3.vagrantの実行 4.vagrantの終了 1.boxファイルのインストール あらかじめテンプレートとしてboxが用意されています。 * [Vagrant box](http://www.vagrantbox.es/) `vagrant`コマンドに`box add`オプションを使用すると、boxファイルのインストールができます。 今回はCentOS-6.3-x86_64-minimal.boxを使います。 ```bash:vagrant-setup vagrant box add vagrant-test https://dl.dropbox.com/u/7225008/Vagrant/CentOS-6.3-x86_64-minimal.box ``` これで、`vagrant-test`というboxが作られました。 `vagrant box list`コマンドで確認してみます。 ```bash:vagrantbox-check vagrant box list vagrant-test ``` <br> 2.Vagrantの初期化 `vagrant init`コマンドを最初に打たないといけません。 コマンドを打つと、Vagrantfileという設定ファイルが作られます。 設定ファイル+chefなどと組み合わせることで自分独自の環境が構築できます。 ```bash:Vagrantfile vagrant init vagrant-test #vagrant-testの部分はboxの名前になります ``` <br> 3.vagrantの実行 vagrantの実行=仮想マシンの実行になります。 `vagrant up`コマンドでvagrantを実行します。 ```bash:vagrantup vagrant up ``` `vagrant ssh`コマンドでログインできます。 ```bash:vagrant-ssh vagrant ssh ``` <br> 4.vagrant終了 `vagrant halt`コマンドで終了できます。 ```bash:vagrant-shutdown vagrant halt ``` この仮想マシンもう必要ないなという時は以下のコマンドで、破棄できます。 ```bash:vagrant-destroy vagrant destroy ``` **ベースなっているbox自体を破棄する訳ではありません。** boxが保存される場所は、私の環境ですと以下の場所になります。 ```zsh:vagrant-box /Users/username/.vagrant.d/boxes ``` `vagrant destroy`コマンドを入力してもbox自体は破棄されておりません。 仮想マシンが保存されている場所が変更されています。 ```bash:vagrant-destroy /Users/username/VirtualBox VMs ``` `vagrant destroy`コマンドを入力するまえと入力したあとで、見比べて下さい。 仮想マシンが減っている事にお気づきになると思います。 よって、`vagrant up`コマンドを打つ事でまたテスト環境を作る事が可能です。 以上です。 |
|
| 368位 |
|
|||
|
19:10:20 |
(スタートアップ 所属) |
|
**追加がありましたら「編集リクエスト」をお願いします**
###ユーザーにレビューをうながす機能 ##Appirater (0.0.1, 0.0.2) A utility that reminds your iPhone app's users to review the app. - Homepage: http://arashpayan.com/blog/2009/09/07/presenting-appirater/ - Source: https://github.com/mk/appirater.git ###テーブルをページのようにめくれる ##ARTableViewPager (1.0.0) The ARTableViewPager is an iOS component for horizontal table view scrolling/paging. - Homepage: https://github.com/arconsis/ARTableViewPager - Source: https://github.com/arconsis/ARTableViewPager.git ###レーティング ##ASStarRatingView (0.1.0) ASStarRatingView is a control to rating with stars, it is simple to use and configurable. - Homepage: https://github.com/yggg/ASStarRatingView - Source: https://github.com/yggg/ASStarRatingView.git ###ポップアイテムいろいろ ###TechMovie ##ATMHud (0.0.1) Library for the creation of HUDs in iPhone applications. - Homepage: https://github.com/atomton/ATMHud - Source: https://github.com/atomton/ATMHud.git ###自動的にNSCodingしてくれる ##AutoCoding (1.2) AutoCoding is a category on NSObject that provides automatic support for NSCoding to any object. - Homepage: https://github.com/nicklockwood/AutoCoding - Source: https://github.com/nicklockwood/AutoCoding.git ###綺麗な写真のグリッドビュー ##BDDynamicGridViewController (0.0.2) Data-aware view-controller that displays a UIView list in an automatically laid out grid. - Homepage: https://github.com/norsez/BDDynamicGridViewController - Source: https://github.com/norsez/BDDynamicGridViewController.git ###複数並行ダウンロード ##BDMultiDownloader (0.0.1) Simple block-based concurrent multiple-URL data downloader based only on NSURLConnection. - Homepage: https://github.com/norsez/BDMultiDownloader - Source: https://github.com/norsez/BDMultiDownloader.git ###ポップアップメッセージ ##BDToastAlert (0.0.1, 0.1.0, 1.0.0) Easy way to display non-obstructive messages to user without having to worry about its behavior and conflicts with other views. - Homepage: https://github.com/norsez/BDToastAlert - Source: https://github.com/norsez/BDToastAlert.git ###ミュージックそっくりな音楽プレイヤー ##BeamMusicPlayerViewController (0.1.0) An iPhone view controller to visualize and control music playback. - Homepage: https://github.com/BeamApp/MusicPlayerViewController - Source: https://github.com/BeamApp/MusicPlayerViewController.git - Sub specs: - BeamMusicPlayerViewController/MediaPlayer (0.1.0) ###いろいろボタンを置けるアクションシート ##BlockAlertsAnd-ActionSheets (1.0.0) Beautifully done UI and UIActionSheet replacements inspired by TweetBot. - Homepage: https://github.com/gpambrozio/BlockAlertsAnd-ActionSheets - Source: https://github.com/gpambrozio/BlockAlertsAnd-ActionSheets.git ###キーボードに「次へ」などを表示 ##BSKeyboardControls (0.0.1, 1.1) Put controls above the keyboard on your iPhone or iPad app. - Homepage: https://github.com/SimonBS/BSKeyboardControls - Source: https://github.com/SimonBS/BSKeyboardControls.git ###フルスクリーンのテキストビュー ##BWLongTextViewController (1.0) Simple IOS controller that show a full screen textView with keyboard. - Homepage: https://github.com/brunow/BWLongTextViewController - Source: https://github.com/brunow/BWLongTextViewController.git ###モバイルSafariのコピー ###AutoEver ##CIALBrowser (1.0) A MobileSafari like open source implementation. - Homepage: https://github.com/sylverb/CIALBrowser - Source: https://github.com/sylverb/CIALBrowser.git ###カレンダーを小さく表示して選ばせる ##CKCalendar (0.0.1) A sleek, easily customized calendar control for iOS. - Homepage: www.cozykozy.com - Source: https://github.com/jaykz52/CKCalendar.git ###HTMLの書けるビュー ##CMHTMLView (0.0.1) CMHTMLView is UIWebView wrapper to provide easy access to show rich text content (HTML) with native look and feel. - Homepage: https://github.com/mureev/CMHTMLView - Source: https://github.com/mureev/CMHTMLView.git ###ボタンなどに説明の吹き出しを付ける ###TechMovie ##CMPopTipView (0.0.1) Custom UIView for iOS that pops up an animated "bubble" pointing at a button or other view. Useful for popup tips. - Homepage: https://github.com/chrismiles/CMPopTipView - Source: https://github.com/chrismiles/CMPopTipView.git ###HTTPサーバ ##CocoaHTTPServer (2.2.1) A small, lightweight, embeddable HTTP server for Mac OS X or iOS applications. - Homepage: https://github.com/robbiehanson/CocoaHTTPServer - Source: https://github.com/robbiehanson/CocoaHTTPServer.git ###カラーピッカー ##Color-Picker-for-iOS (0.0.1) ColorPicker for iPhone and iPod touch. - Homepage: https://github.com/hayashi311 - Source: https://github.com/hayashi311/Color-Picker-for-iOS.git ###折れ線グラフ <img src="http://core-plot.googlecode.com/files/Promo%20slide%20for%20core%20plot.001.jpg" width="600"> ##CorePlot (1.0) Cocoa plotting framework for Mac OS X and iOS. - Homepage: http://code.google.com/p/core-plot/ - Source: https://code.google.com/p/core-plot ###時刻を場所に合わせてくれる ###TechMovie-International ##CupertinoYankee (0.1.0) An NSDate Category With Locale-Aware Calculations for Beginning & End of Day, Week, Month, and Year. - Homepage: https://github.com/mattt/CupertinoYankee - Source: https://github.com/mattt/CupertinoYankee.git ###いろんなものにバッヂを付けられる ##CustomBadge (2.0) Draws a typical iOS badge indicator with a custom text on any view. - Homepage: http://www.spaulus.com/2011/04/custombadge-2-0-retina-ready-scalable-light-reflex/?lang=en - Source: https://github.com/ckteebe/CustomBadge.git ###円形のプログレスビュー ##DACircularProgress (2.0.0) DACircularProgress is a UIView subclass with circular UIProgressView properties. - Homepage: https://github.com/danielamitay/DACircularProgress - Source: https://github.com/danielamitay/DACircularProgress.git ###テキストフィールドの入力の不正をチェックする ###AutoEver ##DCTTextFieldValidator (1.0) Validates an array of UITextFields before allowing an action to take place. - Homepage: https://github.com/danielctull/DCTTextFieldValidator - Source: https://github.com/danielctull/DCTTextFieldValidator.git ###横に並んだページを横スワイプで行き来できる ###TechMovie ##DDPageControl (0.1.0) An easily customizable alternative to UIKit's UIPageControl. - Homepage: https://github.com/ddeville/DDPageControl - Source: https://github.com/ddeville/DDPageControl.git ###クールなプログレスバー ##DDProgressView (0.1) A custom UIProgressView à la Twitter for iPhone. - Homepage: https://github.com/ddeville/DDProgressView - Source: https://github.com/ddeville/DDProgressView.git ###いろんなアクティビティ・インジケーター ##DejalActivityView (1.0) DejalActivityView conveniently displays a horizontal, bezel-style, or keyboard-covering view with a spinning activity indicator and adjustable text. - Homepage: http://www.dejal.com/developer/#dejalactivityview - Source: https://github.com/Dejal/DejalActivityView.git ###写真を選択する一覧画面 ### MomoJourney ##DIYAssetPicker (0.1.0, 0.1.1, 0.2.0) Drop-in UIImagePickerController replacement w/ landscape support. - Homepage: https://github.com/dongle/AssetPicker - Source: https://github.com/dongle/AssetPicker.git ###フィルタの付けられるカメラ ##DLCImagePickerController (0.0.1) ImagePickerController with live filters, radial blur and more. Brought to you by the fine ladies and gents at Backspaces. - Homepage: www.backspac.es - Source: https://github.com/gobackspaces/DLCImagePickerController.git ###レーティング ##DLStarRating (1.1) iOS star rating component. - Homepage: https://github.com/dlinsin/DLStarRating - Source: https://github.com/dlinsin/DLStarRating.git ###便利なロケーションマネージャ ##DMLocationManager (1.0.0) Convenient CLLocationManager wrapper. Control location caching, cache age and accuracy. - Homepage: https://github.com/martinstolz/DMLocationManager - Source: https://github.com/martinstolz/DMLocationManager.git ###ドロップボックス ##Dropbox-iOS-SDK (1.2.3) The Dropbox SDK for iOS. - Homepage: https://www.dropbox.com/developers/reference/sdk - Source: https://www.dropbox.com/static/developers/dropbox-ios-sdk-1.2.3.zip ###角丸や反射など、画像を加工する ##DSGraphicsKit (1.0) Utilties for common and advanced graphics operations. - Homepage: https://github.com/Discontinuity-srl/DSGraphicsKit - Source: https://github.com/Discontinuity-srl/DSGraphicsKit.git ###小さな円形のプログレスビュー ### TechMovie ##DZProgressController (1.0.0) A dead simple, drop-in HUD view for iOS. - Homepage: https://github.com/zwaldowski/DZProgressController - Source: https://github.com/zwaldowski/DZProgressController.git ###Facebookのような2枚のビューを重ねてずらすインターフェース ##ECSlidingViewController (0.9.0) ECSlidingViewController is a view controller container that presents its child view controllers in two layers. It provides functionality for sliding the top view to reveal the views underneath it. This functionality is inspired by the Path 2.0 and Facebook iPhone apps. - Homepage: https://github.com/edgecase/ecslidingviewcontroller - Source: https://github.com/edgecase/ECSlidingViewController.git ###テーブルを引っ張ると更新する ### TechMovie ##EGOTableViewPullRefresh (0.1.0) A similar control to the pull down to refresh control created by atebits in Tweetie 2. - Homepage: https://github.com/enormego/EGOTableViewPullRefresh - Source: https://github.com/enormego/EGOTableViewPullRefresh.git ###絵文字に変換する ##Emoticonizer (0.0.1, 1.0.0) Converts a given string with common chat smileys like :) to emoji symbols. - Homepage: https://github.com/larsschwegmann/Emoticonizer - Source: https://github.com/larsschwegmann/Emoticonizer.git ###Evernote ##Evernote-SDK-iOS (0.1.0, 0.1.5) Evernote SDK for iOS. - Homepage: https://github.com/evernote/evernote-sdk-ios - Source: https://github.com/evernote/evernote-sdk-ios.git ##Facebook-iOS-SDK (0.0.1, 1.2, 3.0.5.b, 3.0.6.b, 3.0.7, 3.0.8) The iOS SDK provides Facebook Platform support for iOS apps. - Homepage: http://developers.facebook.com/docs/reference/iossdk - Source: https://github.com/facebook/facebook-ios-sdk.git ###自前のフォントを使うことができる ##FontLabel (0.0.1) A project that includes a module for drawing arbitrary .ttf fonts for iPhone. - Homepage: http://zynga.com/ - Source: https://github.com/zynga/FontLabel.git ###配列や場所、時間などを地域に合わせてフォーマットする ##FormatterKit (0.6.0, 0.7.0, 1.0.0) `stringWithFormat:` for the sophisticated hacker set. - Homepage: https://github.com/mattt/FormatterKit - Source: https://github.com/mattt/FormatterKit.git - Sub specs: - FormatterKit/ArrayFormatter (1.0.0) - FormatterKit/LocationFormatter (1.0.0) - FormatterKit/OrdinalNumberFormatter (1.0.0) - FormatterKit/TimeIntervalFormatter (1.0.0) - FormatterKit/UnitOfInformationFormatter (1.0.0) - FormatterKit/URLRequestFormatter (1.0.0) 3Dグラフ ##FRD3DBarChart (0.0.1, 1.0.0) Interactive iOS 3D bar chart control made with GLKit. - Homepage: https://github.com/sebastienwindal/FRD3DBarChart - Source: https://github.com/sebastienwindal/FRD3DBarChart.git ###左側に階層を積み重ねていくナビゲーション(iPadのみ) ##FRLayeredNavigationController (0.3.0) Layered navigation controller for hierarchical iPad apps. - Homepage: https://github.com/weissi/FRLayeredNavigationController - Source: https://github.com/weissi/FRLayeredNavigationController.git ###Core Animationを書きやすくしてくれる ### MomoJourney ##FTUtils (1.1.1) Phone utilities mostly for Core Animation. - Homepage: http://ftutils.com/ - Source: https://github.com/neror/ftutils.git ###文字ラベルに様々なエフェクトをかける ##FXLabel (1.3.1) FXLabel improves upon the standard UILabel by providing a subclass that supports soft shadows, inner shadow and gradient fill, and which can easily be used in place of any standard UILabel. - Homepage: http://charcoaldesign.co.uk/source/cocoa#fxlabel - Source: https://github.com/nicklockwood/FXLabel.git ###WebView, JavaScript ### TechMovie ##GAJavaScript (1.0b1) Library to simplify working with JavaScript and UIWebView. - Homepage: https://github.com/newyankeecodeshop/GAJavaScript - Source: https://github.com/newyankeecodeshop/GAJavaScript.git ###画面の上からちろっと通知する ##GCDiscreetNotificationView (0.0.1) A discreet, non-modal, notification view for iOS. - Homepage: https://github.com/gcamp/GCDiscreetNotificationView - Source: https://github.com/gcamp/GCDiscreetNotificationView.git ###テキストビューにプレースホルダーを設定できる ##GCPlaceholderTextView (0.0.1) A subclass of UITextView that allow a placeholder. - Homepage: https://github.com/gcamp/GCPlaceholderTextView - Source: https://github.com/gcamp/GCPlaceholderTextView.git ##GData (1.9.1) The Google data APIs provide a simple protocol for reading and writing data on the web. Many Google services provide a Google data API. - Homepage: https://code.google.com/p/gdata-objectivec-client - Source: http://gdata-objectivec-client.googlecode.com/svn - Sub specs: - GData/YouTube (1.9.1) ##GDataXML-HTML (0.0.1, 1.0.0) An XML/HTML Parser for iOS and OSX, based on Google's GDataXML. - Homepage: https://github.com/graetzer/GDataXML-HTML - Source: https://github.com/graetzer/GDataXML-HTML.git ###Google Analytics ##GoogleAnalytics-iOS-SDK (1.4) GoogleAnalytics for iOS SDK. - Homepage: https://developers.google.com/analytics/devguides/collection/ios/ - Source: http://dl.google.com/gaformobileapps/GoogleAnalyticsiOS_1.4.tar.gz ###リッチテキスト ##GrannySmith (0.0.1, 1.0.1) GrannySmith is Hulu's Objective C open source project. The first tool provided in GrannySmith is GSFancyText. It allows you to format rich text in iOS in HTML/CSS fashion. - Homepage: https://github.com/hulu/GrannySmith - Source: https://github.com/hulu/GrannySmith.git - Sub specs: - GrannySmith/GSFancyText (1.0.1) ###クラッシュレポートをロギング ##Hoptoad-iOS (3.1.0) A Hoptoad Notifier for iOS. - Homepage: http://airbrake.io/pages/ios-notifier - Source: https://github.com/guicocoa/hoptoad-ios.git ### Selector ###メッセージのような伸縮するテキストフィールド  ##HPGrowingTextView (0.0.1) Multi-line/Autoresizing UITextView similar to SMS-app. - Homepage: https://github.com/HansPinckaers/GrowingTextView - Source: https://github.com/HansPinckaers/GrowingTextView.git ###XML/HTMLパーサ。XPathが使える。 ##hpple (0.0.1, 0.1.0) An XML/HTML parser for Objective-C, inspired by Hpricot. - Homepage: https://github.com/topfunky/hpple - Source: https://github.com/topfunky/hpple.git ###JSONなどの読むことのできるオブジェクトにアーカイブ ##HRCoder (1.0) HRCoder is a replacement for the NSKeyedArchiver and NSKeyedUnarchiver classes that uses a human-readable/editable format that can easily be stored in a regular Plist or JSON file. - Homepage: https://github.com/nicklockwood/HRCoder - Source: https://github.com/nicklockwood/HRCoder.git ###RESTクライアント ##HTTPRiot (0.6.1) Simple HTTP Rest Library for iPhone and Cocoa projects. - Homepage: https://github.com/ognen/httpriot - Source: https://github.com/ognen/httpriot.git ###シンプルないろいろなフォーム    ##IBAForms (1.0.0) A simple iPhone forms library. - Homepage: https://github.com/ittybittydude/IBAForms - Source: https://github.com/ittybittydude/IBAForms.git ### TechMovie ###カバーフロー ##iCarousel (1.6.2, 1.6.3, 1.7) A simple, highly customisable, data-driven 3D carousel for iOS and Mac OS. - Homepage: http://www.charcoaldesign.co.uk/source/cocoa#icarousel - Source: https://github.com/nicklockwood/iCarousel.git ###画像を拡大してフルスクリーン表示 ##iFuga (1.1) iOS component for displaying a fullscreen image gallery Supports orientation changes. - Homepage: http://github.com/gavrix/ifuga - Source: https://github.com/gavrix/ifuga.git ###非同期IMAPクライアント ##IMAPClient (0.0.2) An asynchrounous IMAP client for iOS. - Homepage: https://github.com/bcoe/IMAPClient - Source: https://github.com/bcoe/IMAPClient.git ###「設定」画面を作る  ##InAppSettingsKit (0.0.1, 1.0) This iPhone framework allows settings to be in-app in addition to being in the Settings app. - Homepage: https://github.com/futuretap/InAppSettingsKit - Source: https://github.com/futuretap/ InAppSettingsKit.git ###HSBカラーピッカー  ##InfColorPicker (0.0.1) iOS color picker view controller which presents a hue bar and a saturation/brightness box to allow selection of any RGB color. - Homepage: https://github.com/InfinitApps/InfColorPicker - Source: https://github.com/InfinitApps/InfColorPicker.git ###シンプルなTDDフレームワーク ##Injective (0.2.0, 0.2.1, 0.2.2) Cocoa / Cocoa Touch Dependency Injection framework with features for simpler TDD. - Homepage: http://farcaller.github.com/Injective/ - Source: https://github.com/farcaller/Injective.git ###開発をラクにするフレームワーク ##InnerBand (0.0.1) A set of classes, macros, components and constants that speed up development. - Homepage: https://github.com/ZaBlanc/InnerBand - Source: https://github.com/ZaBlanc/InnerBand.git ###アプリ内の通知 ##iNotify (1.5.2) Library for displaying remotely administered notifications within a Mac or iPhone app. Similar to Apple's push notifications, but more lightweight and only displayed at app launch time. Perfect for cross-promoting apps or pointing out non-obvious features. - Homepage: http://www.charcoaldesign.co.uk/source/cocoa#inotify - Source: https://github.com/nicklockwood/iNotify.git ### Selector ###メッセージのようなテキストフィールド   ##InputToolbar (0.0.1) Messages style input toolbar for iOS. - Homepage: https://github.com/brandonhamilton/inputtoolbar - Source: https://github.com/brandonhamilton/inputtoolbar.git ###使えなさそうな画像エフェクト  ##iOS-Flip-Transform (0.0.1) Core Animation framework for navigating data by flipping. - Homepage: https://github.com/Dillion/iOS-Flip-Transform - Source: https://github.com/Dillion/iOS-Flip-Transform.git ###画面構成を目視でチェックできる  ##iOS-Hierarchy-Viewer (1.3) iOS Hierarchy Viewer allows developers to debug their user interfaces. If there are problems with layout calculations, it will catch them by giving a real time preview of the UIViews hierarchy. - Homepage: https://github.com/glock45/iOS-Hierarchy-Viewer - Source: https://github.com/glock45/iOS-Hierarchy-Viewer.git ###バッジやテーブルのパーツ ##iPhoneMK (0.0.1) iPhoneMK is a loose collection of Objective-C classes for the iPhone SDK. - Homepage: https://github.com/michaelkamprath/iPhoneMK - Source: https://github.com/michaelkamprath/iPhoneMK.git ###しばらくたってから評価をうながす。Appiraterより簡単。 ##iRate (1.3.2, 1.4.7) A handy class that prompts users of your iPhone or Mac App Store app to rate your application after using it for a while. Similar to Appirater, but with a simpler, cleaner interface and automatic support for iOS fast application switching. - Homepage: http://charcoaldesign.co.uk/source/cocoa#irate - Source: https://github.com/nicklockwood/iRate.git ###メールで「友だちに教える」   ##iTellAFriend (1.0.0) iTellAFriend is an iOS toolkit for displaying a preconfigued mail composerwith a "Tell a Friend" template in ios apps. - Homepage: https://github.com/aporat/iTellAFriend - Source: https://github.com/aporat/iTellAFriend.git ###ファイルダウンロードのクラス  ##ITWLoadingPanel (1.0.0, 1.0.1) Drop in classes for adding a download info panel. - Homepage: https://github.com/brunow/ITWLoadingPanel - Source: https://github.com/brunow/ITWLoadingPanel.git ###更新後に更新内容を表示する ##iVersion (1.9) Library for dynamically checking for updates to Mac/iPhone App Store apps from within the application and notifying users about the new release. Can also notify users about new features in the app the first time they launch after an upgrade. - Homepage: http://www.charcoaldesign.co.uk/source/cocoa#iversion - Source: https://github.com/nicklockwood/iVersion.git ###JSONやプロパティリストにシリアライズ ##JAGPropertyConverter (0.2.0) JAGPropertyConverter allows easy serialization/deserialization of Model objects to/from NSDictionaries in JSON or PropertyList formats. - Homepage: http://github.com/jagill/JAGPropertyConverter - Source: https://github.com/jagill/JAGPropertyConverter.git ###Facebookのスライドビュー   ##JASidePanels (0.0.1) JASidePanels is a UIViewController container designed for presenting a center panel with revealable side panels - one to the left and one to the right. - Homepage: https://github.com/gotosleep/JASidePanels - Source: https://github.com/gotosleep/JASidePanels.git ###iPhone所有者の名前やメアドを取得 ##JBDeviceOwner (0.0.3) Cocoa Touch library for finding information on an iOS device's owner. Inspired by Square's iOS app. - Homepage: https://github.com/jakeboxer/JBDeviceOwner - Source: https://github.com/jakeboxer/ JBDeviceOwner.git ###Ken & Burnsエフェクトで写真が切り替わる ##JBKenBurnsView (0.1) UIView that can generate a Ken Burns transition when given an array of UIImage objects. - Homepage: https://github.com/jberlana/iOSKenBurns - Source: https://github.com/jberlana/iOSKenBurns.git ###たくさん作ることができるタブ  ##JBTabBarController (0.3, 0.4, 0.4.1) JBTabBarController aims to be a drop-in replacement for UITabBarController with the intention of letting developers easily customise its appearance. - Homepage: http://jinthagerman.github.com/JBTabBarController - Source: https://github.com/jinthagerman/JBTabBarController.git ###ビューをドラッグ&ドロップ  ##JDDroppableView (1.0.0, 1.0.1) A base class for any draggable views. (Drag & Drop, even out of scrollviews) - Homepage: https://github.com/jaydee3/JDDroppableView - Source: https://github.com/jaydee3/JDDroppableView.git ###数字のフリップボード    ##JDFlipNumberView (1.0.0, 1.0.1) A simple, yet powerful, customizable FlipNumberView based on CoreAnimation. Contains also a Date Countdown Flip View. - Homepage: https://github.com/jaydee3/JDFlipNumberView - Source: https://github.com/jaydee3/JDFlipNumberView.git ###リモートの画像をキャッシュ ##JMImageCache (0.2.1) NSCache based remote-image caching and downloading mechanism for iOS. - Homepage: https://github.com/jakemarsh/JMImageCache - Source: https://github.com/jakemarsh/JMImageCache.git ###引っ張り更新+下方オートロードなテーブル   ##JMStatefulTableViewController (0.1.0) A subclassable table view controller with empty, loading and error states, also supports infinte-scrolling and pull to refresh. - Homepage: https://github.com/jakemarsh/JMStatefulTableViewController - Source: https://github.com/jakemarsh/JMStatefulTableViewController.git ###シンプルなブロック文法のスタティックテーブル      ##JMStaticContentTableViewController (0.1.0, 0.1.1) Cleanly implement a table view controller much like those in Settings.app, using a simple, convienent block-based syntax. - Homepage: https://github.com/jakemarsh/JMStaticContentTableViewController - Source: https://github.com/jakemarsh/JMStaticContentTableViewController.git ###独特なタブ   ##JMTabView (0.0.1) Stylish and animated tab view for iOS rendered entirely using core graphics. - Homepage: https://github.com/jasonmorrissey/JMTabView - Source: https://github.com/jasonmorrissey/JMTabView.git ###Facebook風のサイドバー       ##JTRevealSidebarDemo (2.2) A carefully implemented iOS objective-c library to mimic the sidebar layout of the new Facebook app and Path 2.0 app. - Homepage: https://github.com/mystcolor/JTRevealSidebarDemo - Source: https://github.com/mystcolor/JTRevealSidebarDemo.git ###iOSのフォルダのようなアニメーション  ##JWFolders (0.0.1, 0.5.0, 0.9.0) JWFolders is a class that attempts to mimic the folder animation present on the iOS SpringBoard. - Homepage: https://github.com/jwilling/JWFolders - Source: https://github.com/jwilling/JWFolders.git ###カレンダーそっくりさん  ##Kal (1.0rc1) A calendar component for the iPhone (the UI is designed to match MobileCal). - Homepage: http://www.thepolypeptides.com - Source: https://github.com/klazuka/Kal.git ###BDD開発 ##Kiwi (1.0.0, 1.1.0) A Behavior Driven Development library for iPhone and iPad development. - Homepage: http://kiwi-lib.info - Source: https://github.com/allending/Kiwi.git ###複数選択可能なリスト <a href='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ss2.png' target='_blank' title='click to zoom'><img src='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ss2.png' height='360px' /></a>. <a href='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ss3.png' target='_blank' title='click to zoom'><img src='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ss3.png' height='360px' /></a>. <a href='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ss4.png' target='_blank' title='click to zoom'><img src='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ss4.png' height='360px' /></a>. <a href='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ipad_ss1.png' target='_blank' title='click to zoom'><img src='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ipad_ss1.png' height='240px' /></a>. <a href='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ipad_ss2.png' target='_blank' title='click to zoom'><img src='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ipad_ss2.png' height='240px' /></a>. <a href='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ipad_ss3.png' target='_blank' title='click to zoom'><img src='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ipad_ss3.png' height='240px' /></a>. <a href='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ipad_ss4.png' target='_blank' title='click to zoom'><img src='https://github.com/kentnguyen/KNMultiItemSelector/raw/master/Docs/screenshots/ipad_ss4.png' height='240px' /></a> ##KNMultiItemSelector (1.0) KNMultiItemSelector is a versatile drop in multiple items selector for iOS projects. - Homepage: https://github.com/kentnguyen/KNMultiItemSelector - Source: https://github.com/kentnguyen/KNMultiItemSelector.git [32m 画面を暗くして下からせり出るモーダルビュー <img src="https://github.com/kentnguyen/KNSemiModalViewController/blob/master/Docs/original.png?raw=true" /> . <img src="https://github.com/kentnguyen/KNSemiModalViewController/blob/master/Docs/original2.png?raw=true" /> <img src="https://github.com/kentnguyen/KNSemiModalViewController/blob/master/Docs/ss1.png?raw=true" /> . <img src="https://github.com/kentnguyen/KNSemiModalViewController/blob/master/Docs/ss2.png?raw=true" /> ##KNSemiModalViewController (0.1) UIViewController+KNSemiModal is an effort to make a replica of semi-modal view with pushed-back stacked animation found in the beautiful Park Guides by National Geographic app. - Homepage: https://github.com/kentnguyen/KNSemiModalViewController - Source: https://github.com/kentnguyen/KNSemiModalViewController.git ###iADとGoogle Adsを同じコンテナで ##LARSAdController (2.1.1) Singleton Objective-C class to not only easily integrate iAds into your app, but have AdMob ads running as a backup. - Homepage: https://github.com/larsacus/LARSAdController - Source: https://github.com/larsacus/LARSAdController.git ###アニメーションGIFからアニメーションを自動生成 ##LBGIFImage (0.0.1) This is a small category that creates an animated UIImage out of a gif image. - Homepage: https://github.com/larcus94/LBGIFImage - Source: https://github.com/larcus94/LBGIFImage.git ###ロガー ##LibComponentLogging-Core (1.1.4, 1.1.5, 1.1.6, 1.2.1, 1.2.2) Core files of LibComponentLogging, a small logging library for Objective-C on Mac OS X and iOS. - Homepage: http://0xc0.de/LibComponentLogging - Source: https://github.com/aharren/LibComponentLogging-Core.git ###プッシュ通知のサービス ##libPusher (1.1, 1.2, 1.3) An Objective-C client for the Pusher.com service. - Homepage: https://github.com/lukeredpath/libPusher - Source: https://github.com/lukeredpath/libPusher.git ###いろんなバッジ ##LKbadgeView (0.0.1, 1.0.0)  Custom badge view. - Homepage: https://github.com/hayashi311 - Source: https://github.com/lakesoft/LKbadgeView.git ###丸いスイッチ  ##LLRoundSwitch (1.0.0, 1.0.1) Customizable replacement for UISwitch with ARC support. - Homepage: http://github.com/LordLobo/LLRoundSwitch - Source: https://github.com/LordLobo/LLRoundSwitch.git ###テスト用のモック ##LRMocky (0.9.0, 0.9.1) A mock object library for Objective C, inspired by JMock 2.0. - Homepage: http://github.com/lukeredpath/LRMocky - Source: https://github.com/lukeredpath/LRMocky.git ###Restクライアント ##LRResty (0.11.0) Resty is a simple to use HTTP library for Cocoa and iOS apps, aimed at consuming RESTful web services and APIs. - Homepage: http://projects.lukeredpath.co.uk/resty/ - Source: https://github.com/lukeredpath/LRResty.git ###カレンダー  ##MACalendarUI (1.0.0) MACalendarUI is a project which offers calendar user interface for iPhone applications. - Homepage: https://github.com/muhku/calendar-ui - Source: https://github.com/muhku/calendar-ui.git ###App Storeの購入ボタン  ##MAConfirmButton (0.0.1) MAConfirmButton is an animated subclass of UIButton that replicates and improves upon the behavior of the AppStore “Buy Now” buttons. Built and animated with Core Animation layers, it is completely image free. - Homepage: https://github.com/mikeahmarani/MAConfirmButton - Source: https://github.com/mikeahmarani/MAConfirmButton.git ##ActiveRecordもどき(Core Data) ##MagicalRecord (1.7.1, 1.8.3, 2.0, 2.0.3) Super Awesome Easy Fetching for Core Data 1!!!11!!!!1!. - Homepage: http://github.com/magicalpanda/MagicalRecord - Source: https://github.com/magicalpanda/MagicalRecord.git ##改良KVO ###MAKVONotificationCenter (0.0.1) Key-Value Observing Done Right. - Homepage: http://www.mikeash.com/pyblog/key-value-observing-done-right.html - Source: https://github.com/mikeash/MAKVONotificationCenter.git ##NSObjectにクラス一覧やメソッド一覧などを出力するメソッドを追加する ##MAObjCRuntime (0.0.1) ObjC wrapper for ObjC runtime API. - Homepage: https://github.com/mikeash/MAObjCRuntime - Source: https://github.com/mikeash/MAObjCRuntime.git ##よりカスタマイズ可能なMapKit ###MapBox (0.4.0) Open source alternative to MapKit. - Homepage: http://mapbox.com/mobile - Source: https://github.com/mapbox/mapbox-ios-sdk.git ##Localization支援? ###MBCommon (1.0, 1.0.1) MBCommon is a lightweight, generic Cocoa library for iOS and OS X. - Homepage: https://github.com/mobiata/MBCommon - Source: https://github.com/mobiata/MBCommon.git ##プログレスHUD [](http://dl.dropbox.com/u/378729/MBProgressHUD/1.png) [](http://dl.dropbox.com/u/378729/MBProgressHUD/2.png) [](http://dl.dropbox.com/u/378729/MBProgressHUD/3.png) [](http://dl.dropbox.com/u/378729/MBProgressHUD/4.png) [](http://dl.dropbox.com/u/378729/MBProgressHUD/5.png) [](http://dl.dropbox.com/u/378729/MBProgressHUD/6.png) [](http://dl.dropbox.com/u/378729/MBProgressHUD/7.png) ###MBProgressHUD (0.5) An iOS activity indicator view. - Homepage: https://github.com/matej/MBProgressHUD - Source: https://github.com/jdg/MBProgressHUD.git ##キーチェーンのラッパー ###MCSMKeychainItem (1.0) A Keychain Wrapper for iOS and OS X. - Homepage: https://github.com/ObjColumnist/MCSMKeychainItem - Source: https://github.com/ObjColumnist/MCSMKeychainItem.git ##Aboutページ作成支援  ##MDAboutController (0.7) Automatically populated about view controller for iOS apps!. - Homepage: http://mochidev.com/ - Source: https://github.com/mochidev/MDAboutController.git ##Pathのような時間スクロールバー  <iframe width="640" height="480" src="http://www.youtube.com/embed/EIQfIgpLA98" frameborder="0"></iframe> ###MDCScrollBarLabel (0.1.0, 0.1.1) Like Path's timestamp scrollbar label. - Homepage: https://github.com/modocache/MDCScrollBarLabel - Source: https://github.com/modocache/MDCScrollBarLabel.git <<<<<<< HEAD ## Facebookのようなサイドメニュー ### MFSideMenu (0.0.1, 0.2) ======= ##Facebookのようなサイドビュー ###MFSideMenu (0.0.1) >>>>>>> 編集用 Facebook-like side menu for iOS. - Homepage: https://github.com/mikefrederick/MFSideMenu - Source: https://github.com/mikefrederick/MFSideMenu.git <<<<<<< HEAD ## ちょっと毛色の違うテーブル    ### MGBox (0.0.1) ======= ##雰囲気のあるTableView    ###MGBox (0.0.1) >>>>>>> 編集用 A UITableView replacement with simplified API. - Homepage: https://github.com/sobri909/MGBox - Source: https://github.com/sobri909/MGBox.git <<<<<<< HEAD ## UIImageを切り抜いたりハイライトしたりするカテゴリ <img src="http://mattgemmell.com/images/faves_buttons.jpg" width="300" height="339" alt="Screenshot of contact photos in Favorites"> <img src="http://mattgemmell.com/images/faves_badges.jpg" width="400" height="240" alt="Screenshot of badge choosing screen in Favorites"> <img src="http://mattgemmell.com/files/source/mgimageutilities.jpg" width="400" height="575" alt="Screenshot of MGImageUtilities demo app"> ## MGImageUtilities (0.0.1) Useful UIImage categories for iPhone/iPad developers. - Homepage: http://mattgemmell.com/2010/07/05/mgimageutilities/ - Source: https://github.com/mattgemmell/MGImageUtilities.git ## ウィンドウの中に小さくポップアップするビュー <img src="https://raw.github.com/martinjuhasz/MJPopupViewController/master/assets/demo1.png" width="320" height="480"/> <img src="https://raw.github.com/martinjuhasz/MJPopupViewController/master/assets/demo2.png" width="330" height="480"/> ### MJPopupViewController (0.3) ======= ##UIImageに様々なカテゴリを追加する ###MGImageUtilities (0.0.1) Useful UIImage categories for iPhone/iPad developers. - Homepage: http://mattgemmell.com/ - Source: https://github.com/mattgemmell/MGImageUtilities.git ##ビューをポップアップさせる <img src="https://raw.github.com/martinjuhasz/MJPopupViewController/master/assets/demo1.png" width="320" height="480"/> <img src="https://raw.github.com/martinjuhasz/MJPopupViewController/master/assets/demo2.png" width="330" height="480"/> ###MJPopupViewController (0.3) >>>>>>> 編集用 A UIViewController Category to display a ViewController as a popup with different transition effects. - Homepage: https://github.com/martinjuhasz/MJPopupViewController - Source: https://github.com/martinjuhasz/MJPopupViewController.git <<<<<<< HEAD ## 水平なメニューバー。雰囲気がマット仕上げっぽい。 http://www.youtube.com/watch?v=HbQ71Ny5i1s ### MKHorizMenu (0.0.1) ======= ##水平なメニューを表示する ###MKHorizMenu (0.0.1) >>>>>>> 編集用 Demo of MKHorizMenu a simple, drop-in replacement for the Three20 fame horizontal menus used in news apps. - Homepage: http://mugunthkumar.com - Source: https://github.com/MugunthKumar/MKHorizMenuDemo.git <<<<<<< HEAD ## NSUserDefaultsとiCloudを自動で同期する ### MKiCloudSync (0.0.1) ======= ###NSUserDefaultsを自動的にiCloudに保存する ##MKiCloudSync (0.0.1) >>>>>>> 編集用 Sync your NSUserDefaults to iCloud automatically. - Homepage: https://github.com/MugunthKumar/MKiCloudSync - Source: https://github.com/MugunthKumar/MKiCloudSync.git <<<<<<< HEAD ## メッセージが上からニョキッと出てきて元に戻る http://www.youtube.com/watch?v=rszbxAUCEDs ### MKInfoPanel (0.0.1) Non-modal, non-intrusive Info Panel implementation as seen on some "popular" apps. - Homepage: https://github.com/MugunthKumar/MKInfoPanelDemo - Source: https://github.com/MugunthKumar/MKInfoPanelDemo.git ======= ##アプリ内課金のためのライブラリ ###MKStoreKit (4.3) In-App Purchases StoreKit for iOS devices. - Homepage: https://github.com/MugunthKumar/MKStoreKit - Source: https://github.com/MugunthKumar/MKStoreKit.git ##アプリ内画像をまとめる方法  ###MOOMaskedIconView (0.1.0) UIView subclass that uses black-and-white masks to draw icons. - Homepage: https://github.com/peyton/MOOMaskedIconView - Source: https://github.com/peyton/MOOMaskedIconView.git ##フリップするビューを提供  ###MPFlipViewController (0.0.1) A custom container view controller following the iOS 5 containment API that navigates between child view controllers via touch gestures and page-flip animations. - Homepage: http://markpospesel.com/2012/07/28/mpflipviewcontroller/ - Source: https://github.com/mpospese/MPFlipViewController.git ##アコーディオンのようなビュー  ##MPFoldTransition (0.0.1) Easily add custom folding and page-flipping transitions to UIViews and UIViewControllers. - Homepage: http://markpospesel.com/2012/05/07/mpfoldtransition/ - Source: https://github.com/mpospese/MPFoldTransition.git ##UILabelを拡張する ###MTLabel (0.0.1) A lightweight replacement for Apple's UILabel. - Homepage: http://tuszy.github.com - Source: https://github.com/Tuszy/MTLabel.git ##Status Barに文字列を表示する  ###MTStatusBarOverlay (0.9) A custom iOS status bar overlay seen in Apps like Reeder, Evernote and Google Mobile App. - Homepage: https://github.com/myell0w/MTStatusBarOverlay - Source: https://github.com/myell0w/MTStatusBarOverlay.git ##画面を一部から全画面に広げられる  ###MTZoomWindow (0.5) A UIWindow that can be used to zoom in a specific UIView and displays it fullscreen. - Homepage: https://github.com/myell0w/MTZoomWindow - Source: https://github.com/myell0w/MTZoomWindow.git ##フィードのパーサ ###MWFeedParser (0.0.1) An Objective-C RSS / Atom Feed Parser for iOS. - Homepage: https://github.com/mwaterfall/MWFeedParser - Source: https://github.com/mwaterfall/MWFeedParser.git ##上下左右にスライドするUIView ###MWFSlideNavigationViewController (0.0.1, 0.0.2) MWFSlideNavigationViewController is a container view controller that manages primary and secondary view controller. - Homepage: http://meiwinfu.com - Source: https://github.com/meiwin/MWFSlideNavigationViewController.git ##下方延長ローディングできるテーブル ###MwfTableViewController (0.0.2, 0.0.3, 0.0.4, 0.0.5) Extension to UITableViewController in attempt to provide additional features that are reusable in most scenarios. - Homepage: http://github.com/meiwin/MwfTableViewController - Source: https://github.com/meiwin/MwfTableViewController.git ##写真アプリのようなもの [![Alt][screenshot1_thumb]][screenshot1] [![Alt][screenshot2_thumb]][screenshot2] [![Alt][screenshot3_thumb]][screenshot3] [![Alt][screenshot4_thumb]][screenshot4] [screenshot1_thumb]: http://dl.dropbox.com/u/2111839/Permanent/MWPhotoBrowser/mwphotobrowser1_thumb.png [screenshot1]: http://dl.dropbox.com/u/2111839/Permanent/MWPhotoBrowser/mwphotobrowser1.png [screenshot2_thumb]: http://dl.dropbox.com/u/2111839/Permanent/MWPhotoBrowser/mwphotobrowser2_thumb.png [screenshot2]: http://dl.dropbox.com/u/2111839/Permanent/MWPhotoBrowser/mwphotobrowser2.png [screenshot3_thumb]: http://dl.dropbox.com/u/2111839/Permanent/MWPhotoBrowser/mwphotobrowser3_thumb.png [screenshot3]: http://dl.dropbox.com/u/2111839/Permanent/MWPhotoBrowser/mwphotobrowser3.png [screenshot4_thumb]: http://dl.dropbox.com/u/2111839/Permanent/MWPhotoBrowser/mwphotobrowser4_thumb.png [screenshot4]: http://dl.dropbox.com/u/2111839/Permanent/MWPhotoBrowser/mwphotobrowser4.png ### MWPhotoBrowser (0.0.1) A simple iOS photo browser. - Homepage: https://github.com/mwaterfall/MWPhotoBrowser - Source: https://github.com/mwaterfall/MWPhotoBrowser.git ##キー値コーディングを簡単にできる? ###NanoStore (2.0.1, 2.1.0, 2.1.1, 2.1.2, 2.1.3, 2.1.4) NanoStore is an open source, lightweight schema-less local key-value document store written in Objective-C for Mac OS X and iOS. - Homepage: https://github.com/tciuro/NanoStore - Source: https://github.com/tciuro/NanoStore.git ##three20よりシンプルな総合フレームワーク ###Nimbus (0.9.0, 0.9.1, 0.9.2, 0.9.3) An iOS framework whose growth is bounded by O(documentation). - Homepage: http://docs.nimbuskit.info/index.html - Source: https://github.com/jverkoey/nimbus.git ## CoreDataのコードを読みやすくする ###NLCoreData (0.1.0, 0.1.1, 0.2.0) Library that wraps Core Data for iOS for easier and more readable operations. - Homepage: https://www.github.com/jksk/NLCoreData - Source: https://github.com/jksk/NLCoreData.git ##モックのHTTPサーバ ###NLTHTTPStubServer (0.1.1, 0.1.2) NLTHTTPStubServer is mocking server. launch simple HTTPServer on Testcodes. - Homepage: http://github.com/yaakaito/NLTHTTPStubServer - Source: https://github.com/yaakaito/NLTHTTPStubServer.git ## Tweetbotのような通知ビュー  ###NoticeView (1.0.0, 2.1) A TweetBot-like notice component for iOS. - Homepage: https://github.com/tciuro/NoticeView/ - Source: https://github.com/tciuro/NoticeView.git ## オーディオを扱いやすくする ###Novocaine (0.0.1) Painless high-performance audio on iOS and Mac OS X. - Homepage: http://alexbw.github.com/novocaine/ - Source: https://github.com/auser/novocaine.git ## NSArrayやNSDictionaryをそのまま印刷する ###NSContainers-PrettyPrint (0.0.1, 0.1.0, 0.2.0) Print your own object descriptions just like Apple's own NSArray and NSDictionary. - Homepage: https://github.com/NSError/NSContainers-PrettyPrint - Source: https://github.com/NSError/NSContainers-PrettyPrint.git ## Base64で暗号化。いくつかのライブラリで使われている。 ###NSData+Base64 (1.0.0) Base64 for NSData. - Homepage: - Source: https://github.com/l4u/NSData-Base64.git ## MD5で暗号化。 ###NSData+MD5Digest (1.0.0) Add MD5 digest to NSData. - Homepage: https://github.com/siuying/NSData-MD5 - Source: https://github.com/siuying/NSData-MD5.git ## NSDateを扱いやすくする ##NSDate+Helper (0.0.1) A category to extend Cocoa's NSDate class with some convenience functions. - Homepage: https://github.com/billymeltdown/nsdate-helper - Source: https://github.com/billymeltdown/nsdate-helper.git ## OAuthクライアント ###NXOAuth2Client (1.0.1, 1.1.0) Client library for OAuth2 (currently built against draft 10 of the OAuth2 spec). - Homepage: https://github.com/nxtbgthng/OAuth2Client - Source: https://github.com/nxtbgthng/OAuth2Client.git ## UIImageのカテゴリ。トリミングやリサイズなど。 ###NYXImagesKit (2.1.1, 2.1.2) A set of efficient categories for UIImage class. It allows filtering, resizing, masking, rotating, enhancing... and more. - Homepage: https://github.com/Nyx0uf/NYXImagesKit - Source: https://github.com/Nyx0uf/NYXImagesKit.git ### オーディオの取り扱いを簡単にする ##ObjectAL-for-iPhone (2.1) iOS Audio development, minus the headache. ObjectAL is the easy Objective-C interface to OpenAL, AVAudioPlayer, and audio session management. - Homepage: http://kstenerud.github.com/ObjectAL-for-iPhone/ - Source: https://github.com/kstenerud/ObjectAL-for-iPhone.git ## HTMLパーサ ###Objective-C-HMTL-Parser (0.0.1) An objective c wrapper around libxml for parsing HTMLr. - Homepage: http://www.benreeves.co.uk - Source: https://github.com/zootreeves/Objective-C-HMTL-Parser.git ## Flickr APIフレームワーク ###objectiveflickr (0.0.1) ObjectiveFlickr, a Flickr API framework for Objective-C. - Homepage: http://lukhnos.org - Source: https://github.com/lukhnos/objectiveflickr.git ## RubyのMixInのようなことができるようになる ##ObjectiveMixin (0.0.1, 1.0.0) Ruby-like mixin functionality for Objective-C programs. - Homepage: https://github.com/vl4dimir/ObjectiveMixin - Source: https://github.com/vl4dimir/ObjectiveMixin.git ## Tumblr API ###ObjectiveTumblr (0.1.1, 0.1.2, 0.1.3) Tumblr API Client for Objective-C with minimal features, based on Tumblr API v2 (OAuth). - Homepage: https://github.com/IgnitionSoft/ObjectiveTumblr - Source: https://github.com/IgnitionSoft/ObjectiveTumblr.git ## QRエンコーダ ###ObjQREncoder (0.0.1) Objective C QR Encoder. - Homepage: https://github.com/jverkoey/ObjQREncoder - Source: https://github.com/jverkoey/ObjQREncoder.git ## 長方形でないボタンを作ることができる ### OBShapedButton (1.0.0) A UIButton subclass that works with non-rectangular button shapes. - Homepage: https://github.com/ole/OBShapedButton - Source: https://github.com/ole/OBShapedButton.git ## 間欠的なスライダー ### OBSlider (1.0.0, 1.1.0) A UISlider subclass that adds variable scrubbing speeds. - Homepage: https://github.com/ole/OBSlider - Source: https://github.com/ole/OBSlider.git ## iPhoneでも使えるポップオーバーするシンプルなカレンダー ### OCCalendar (0.0.1, 0.0.3) OCCalendar is a very simple component for iPhone/iPad that provides a 'Popover' date picker controller. - Homepage: https://github.com/ocrickard/OCCalendar - Source: https://github.com/ocrickard/OCCalendar.git ## テストのMatcherのライブラリ(GHUnitやCedarと連携可能) ###OCHamcrest (1.6, 1.7, 1.8) Unit test assertions on steroids: Hamcrest matchers for Objective-C. - Homepage: https://github.com/hamcrest/OCHamcrest - Source: https://github.com/hamcrest/OCHamcrest.git ## ログ・フレームワーク ### OCLogTemplate (1.0) A logging framework for Objective-C projects. - Homepage: https://github.com/jasperblues/OCLogTemplate - Source: https://github.com/jasperblues/OCLogTemplate.git ## テスト用のモック ###OCMock (1.77.1, 2.0.1) OCMock is an Objective-C implementation of mock objects. - Homepage: http://ocmock.org - Source: https://github.com/erikdoe/ocmock.git ## iOS6メールアプリのようなプルダウン・リフレッシュ  ### ODRefreshControl (0.0.1, 0.0.2, 1.0.0) A pull down to refresh control like the one in Apple's iOS6 Mail App. - Homepage: https://github.com/Sephiroth87/ODRefreshControl - Source: https://github.com/Sephiroth87/ODRefreshControl.git ## 書式色付ラベル ###OHAttributedLabel (0.0.1.pre.1, 0.0.1.pre.2, 1.2.0) UILabel that supports NSAttributedString. - Homepage: https://github.com/AliSoftware/OHAttributedLabel - Source: https://github.com/AliSoftware/OHAttributedLabel.git ## UDIDを取得? ### OpenUDID (1.0.0) Open source initiative for a universal and persistent UDID solution for iOS.. - Homepage: http://OpenUDID.org - Source: https://github.com/ylechelle/OpenUDID.git ## iPhone App Storeのスクリーンショットのように左右に動かせるビュー ### PagedFlowView (0.0.1) A Paging Enabled Flow View, like screenshots view in iPhone App Store. - Homepage: https://github.com/fictorial/PagedFlowView - Source: https://github.com/fictorial/PagedFlowView.git ## BaaS ###Parse (1.0.36, 1.0.39) The mobile app platform for developers. - Homepage: http://parse.com/ - Source: https://github.com/fictorial/Parse.git ## 文字列をトークン化したりパースしたり ## ParseKit (0.0.1, 0.0.2) Objective-C/Cocoa String Tokenizer and Parser toolkit. Supports Grammars. - Homepage: http://parsekit.com/ - Source: http://parsekit.googlecode.com/svn/trunk ## プル・リフレッシュ ###PHFRefreshControl (1.0.0) YAPTR(TM): Yet another pull-to-refresh. - Homepage: https://github.com/fphilipe/PHFRefreshControl - Source: https://github.com/fphilipe/PHFRefreshControl.git ## Pivotalのテスト用ライブラリ ### PivotalCoreKit (0.0.1) Shared library and test code for iOS projects. - Homepage: https://github.com/pivotal/PivotalCoreKit - Source: https://github.com/pivotal/PivotalCoreKit.git - Sub specs: - PivotalCoreKit/CoreLib (0.0.1) - PivotalCoreKit/UICoreLib (0.0.1) - PivotalCoreKit/SpecHelperLib (0.0.1) ## Facebookのようなサイドビュー ###PPRevealSideViewController (0.1.0) A new container controller to easily push views on side like Path or Facebook. - Homepage: www.ipup.pro - Source: https://github.com/ipup/PPRevealSideViewController.git ## テーブルやナビゲーションバーなどをカスタマイズ ###PrettyKit (0.1.0, 0.2.0) PrettyKit for iOS is a small set of new widgets and UIKit subclasses that gives you a deeper UIKit customization. - Homepage: http://github.com/vicpenap/PrettyKit - Source: https://github.com/vicpenap/PrettyKit.git ## 画像補完を行う ###PRTween (0.0.1) PRTween is a lightweight tweening library built for iOS. - Homepage: https://github.com/dominikhofmann/PRTween - Source: https://github.com/dominikhofmann/PRTween.git ## 画像を指でズーム、回転 ###PSPushPopPressView (0.0.1) Zoom, Rotate, Drag ? everything at the same time. A view-container for direct manipulation, inspired by Our Choice from Push Pop Press. - Homepage: http://petersteinberger.com - Source: https://github.com/steipete/PSPushPopPressView.git ## iPadのTwitterのようなビュー   ### PSStackedView (0.1) Open source implementation of Twitter/iPad stacked ui - done right. - Homepage: https://github.com/steipete/PSStackedView - Source: https://github.com/steipete/PSStackedView.git ## iPhone App Storeのようなボタン ### PSStoreButton (0.0.1) UIButton that is styled like iPhone's AppStore-Button. - Homepage: https://github.com/steipete/PSStoreButton - Source: https://github.com/steipete/PSStoreButton.git ## プル・リフレッシュ ###PullToRefresh (0.0.1) A simple iPhone TableViewController for adding pull-to-refresh functionality. - Homepage: https://github.com/leah/PullToRefresh - Source: https://github.com/leah/PullToRefresh.git ## プル・リフレッシュ ###PullToRefreshView (0.0.1) A UIView for easily implementing pull-to-refresh functionality in a UIScrollView that doesn't suck. - Homepage: https://github.com/btaylor/PullToRefreshView - Source: https://github.com/btaylor/PullToRefreshView.git ## 小さな矢印をタップすると水平にアイコンがいくつも表示される  ### QBKOverlayMenuView (0.0.1) UIView object that tries to mimic the behavior of the floating control used by Sparrow. - Homepage: https://github.com/sendoa/QBKOverlayMenuView - Source: https://github.com/sendoa/QBKOverlayMenuView.git ## 雰囲気の良いログインや設定のダイアログ <img class="alignleft" title="QuickDialog1" src="https://github.com/escoz/QuickDialog/raw/master/other/quickdialog2.png" alt="" width="297" height="558"> <img class="alignleft" title="QuickDialog 2" src="https://github.com/escoz/QuickDialog/raw/master/other/quickdialog3.png" alt="" width="297" height="558"> ### QuickDialog (0.1, 0.2, 0.3, 0.4, 0.5) Quick and easy dialog screens for iOS. - Homepage: http://escoz.com/quickdialog-released/ - Source: https://github.com/escoz/QuickDialog.git ## レンジを表すスライダ <img src="http://cdn.buildmobile.com/files/2011/08/Image-of-parts.png" alt="The Parts of the Slider" width="584" height="222" class="size-full wp-image-1780"> ### RangeSlider (0.0.1) Source code for the iOS Range Slider series by Mal Curtis at BuildMobile. - Homepage: http://buildmobile.com/wicked-ios-range-slider-part-one/ - Source: https://github.com/buildmobile/iosrangeslider.git ## 簡単にカスタマイズできるアクションシート http://f.cl.ly/items/2Z2I431z1F40293M2i1f/Screeny%20Video%2022%20Mar%202012%2017.00.51.mov ### RDActionSheet (0.0.1) Class to make a easily customisable Action Sheet for iOS. - Homepage: http://riothq.com - Source: https://github.com/reddavis/RDActionSheet.git ## 圏外かどうかを判定する ###Reachability (2.0.4, 2.0.5, 3.0.0) ARC and GCD Compatible Reachability Class for iOS and OS X. Drop in replacement for Apple Reachability. - Homepage: https://github.com/tonymillion/Reachability - Source: https://github.com/tonymillion/Reachability.git ## 最も使われているネットワークライブラリ ###RestKit (0.9.3, 0.10.0, 0.10.1, 0.10.2) RestKit is a framework for consuming and modeling RESTful web resources on iOS and OS X. - Homepage: http://www.restkit.org - Source: https://github.com/RestKit/RestKit.git - Sub specs: - RestKit/JSON (0.10.2) - RestKit/XML (0.10.2) - RestKit/Network (0.10.2) - RestKit/UI (0.10.2) - RestKit/ObjectMapping (0.10.2) - RestKit/ObjectMapping/JSON (0.10.2) - RestKit/ObjectMapping/XML (0.10.2) - RestKit/ObjectMapping/CoreData (0.10.2) - RestKit/Testing (0.10.2) ## Rump(位置情報を使ったミーティングサービス)クライアント ### rump-ios (1.0.0) Rump-iOS is Rump client for iOS devices.. - Homepage: https://github.com/tpuronen/rump-ios - Source: https://github.com/tpuronen/rump-ios.git ## タイルビュー ### SBTickerView (0.0.1) An easy to use ticker view with inspiration from Flipboard. - Homepage: https://github.com/blommegard/SBTickerView - Source: https://github.com/blommegard/SBTickerView.git ## ドラッグアンドドロップできるビュー ### SEDraggable (1.0.0) Drag and drop UIView subclasses for iOS. - Homepage: http://github.com/brynbellomy/iOS-DragAndDrop - Source: https://github.com/brynbellomy/iOS-DragAndDrop.git ## 時間換算 ### SEHumanizedTimeDiff (0.0.1) An NSDate category to have humanly meaningful time intervals since todays date. - Homepage: https://github.com/fictorial/SEHumanizedTimeDiff - Source: https://github.com/fictorial/SEHumanizedTimeDiff.git ## グラフAPIやFQLも使うことができるFacebook SDK ### SFSocialFacebook (1.0.0, 1.1.0, 1.2.0, 1.2.1, 1.2.2) Façade implementation for Facebook-iOS-SDKIt enables you to access the Facebook Platform APIs including the Graph API, FQL, and Dialogs. - Homepage: http://developers.facebook.com/docs/reference/iossdk - Source: https://github.com/indigotech/facebook-ios-sdk.git ## 「共有」ボタン ### ShareKit (2.0) Drop in sharing features for all iPhone and iPad apps. - Homepage: http://getsharekit.com/ - Source: https://github.com/ShareKit/ShareKit.git - Sub specs: - ShareKit/Evernote (2.0) - ShareKit/Facebook (2.0) - ShareKit/Flickr (2.0) - ShareKit/Foursquare (2.0) - ShareKit/GoogleReader (2.0) - ShareKit/Instapaper (2.0) - ShareKit/LinkedIn (2.0) - ShareKit/Pinboard (2.0) - ShareKit/ReadItLater (2.0) - ShareKit/Tumblr (2.0) - ShareKit/Twitter (2.0) - ShareKit/Vkontakte (2.0) ## Web SocketライブラリのiOS版クライアント ### SignalR-ObjC (0.5.0, 0.5.2, 0.5.3) Objective-C Client for the SignalR Project works with iOS and Mac. - Homepage: https://github.com/DyKnow/SignalR-ObjC - Source: https://github.com/DyKnow/SignalR-ObjC.git ## 画面下にチロッと表示されるシンプルでスマートな通知バー      ## SJNotificationViewController (1.0.1) Lightweight notifications in iOS apps. - Homepage: http://github.com/ekdevdes/SJNotificationViewController - Source: https://github.com/ekdevdes/SJNotificationViewController.git MKMapView以外でも同じようなポップアップを表示する  ## SMCalloutView (1.0.0) A lightweight callout view class for iOS mimicking UICalloutView. - Homepage: https://github.com/nfarina/calloutview - Source: https://github.com/nfarina/calloutview.git ## HUDというよりはシックなポップアップメッセージのようなもの  ## SNRHUDKit (0.0.1) Code drawn AppKit HUD interface elements. - Homepage: https://github.com/indragiek/SNRHUDKit - Source: https://github.com/indragiek/SNRHUDKit.git ## WebSocketクライアントライブラリ ### SocketRocket (0.1, 0.2.0) A conforming WebSocket (RFC 6455) client library. - Homepage: https://github.com/square/SocketRocket - Source: https://github.com/square/SocketRocket.git ## 月だけを選ぶピッカー ### SRMonthPicker (0.1.1) Like UIDatePicker, but without the days. - Homepage: https://www.github.com/simonrice/SRMonthPicker - Source: https://github.com/simonrice/SRMonthPicker.git ## いろんな形のチェックボックス <img class="size-medium wp-image-266" title="SSCheckBoxView Demo" src="http://farm8.staticflickr.com/7012/6473293001_ab905bc6cc.jpg" alt="SSCheckBoxView Demo" width="333" height="500"> ### SSCheckBoxView (0.0.1) SSCheckBoxView is a check box UI control for iOS apps. - Homepage: http://www.ardalahmet.com/2011/12/07/sscheckboxview-a-check-box-ui-control-for-ios-apps/ - Source: https://github.com/ardalahmet/SSCheckBoxView.git ## コーディングをラクにするための様々なツール <img src="http://sstoolk.it/images/catalog.png" alt="SSCatalog"> ### SSToolkit (0.1.1, 0.1.2, 1.0.0, 1.0.1, 1.0.2) A collection of well-documented iOS classes for making life easier. - Homepage: http://sstoolk.it - Source: https://github.com/fictorial/sstoolkit.git ## zip圧縮と解凍 ### SSZipArchive (0.1.0, 0.1.1, 0.1.2, 0.2.0, 0.2.1, 0.2.2) Utility class for zipping and unzipping files on iOS and Mac. - Homepage: https://github.com/samsoffes/ssziparchive - Source: https://github.com/samsoffes/ssziparchive.git ## StackMobというBaaSのSDK ### StackMob (0.4.11, 0.4.12, 0.5.0, 0.5.1, 0.5.2, 0.5.3, 1.0.0beta.1, 1.0.0beta.2, 1.0.0beta.3) StackMob's SDK for accessing the StackMob Services on iOS. - Homepage: http://stackmob.com - Source: https://github.com/stackmob/stackmob-ios-sdk.git ## StackMobを使ったプッシュ通知 ### StackMobPush (1.0.0) StackMob's Push SDK for sending push notifications via the StackMob Services on iOS. - Homepage: http://stackmob.com - Source: https://github.com/stackmob/stackmob-ios-push-sdk.git ## パスを自然なも |
|
| 369位 |
|
|||
|
17:01:04 |
|
|
`.gitignore`書き忘れて `add` & `commit` しちゃったとかよくありますよね。そんな人いないっすよね:-) そんな時は `git rm`。 ## ファイルも一緒に削除したいとき〜 ```bash $ git rm [削除したいファイル] ``` ## ディレクトリごと逝きたいとき〜 ```bash $ git rm -r [削除したいディレクトリ] ``` ## ファイルを残したいとき〜 `-cached`オプションを付けることにより、ファイルを残したまま管理対象から外すことができます。 ```bash $ git rm —-cached [削除したいファイル] ``` ファイルを残した場合は必ず`.gitignore`に追記するように。 ## ちなみに~ `.gitignore`作るのは”[gibo](https://github.com/simonwhitaker/gibo)”を使うと便利です。 [.gitignoreを作ってくれるgiboが便利すぎる - hnwの日記](http://d.hatena.ne.jp/hnw/20121221) |
|
| 370位 |
|
|||
|
00:27:57 |
(dely株式会社 所属) |
|
URLから画像を生成するときは、普通に実装すると以下のようになります。 ```ViewController.m NSURL *url = [NSURL URLWithString:@"画像のURL"]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; ``` しかし、この実装だとシングルスレッドで読み込むので、その間他の処理が止まり固まったように見えてしまいます。 なので、画像は非同期に読み込むことが必要になります。今回は非同期での画像読み込みの実装を3種類してみました。 3つの実装をまとめたソースコードは、[こちらからダウンロード](https://github.com/EntreGulss/AsyncLoading)してください。 1. **一度読み込んだ画像をキャッシュしない。(UITableViewCellの画像)** 2. **一度読み込んだ画像をキャッシュして、再び読み込まない。(UITableViewCellの画像)** 3. **UIImageViewのインスタンスに読み込み処理を任せて、それぞれで読み込む。(UIImageView)** ## 1. 一度読み込んだ画像をキャッシュしない。 参考URL: [ネット上の画像を表示させた UITableView をぬるぬる動作させる方法](http://rakuishi.com/iossdk/3881/) ```UncacheAsyncTableController.m - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } // この部分が重要 dispatch_queue_t q_global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_queue_t q_main = dispatch_get_main_queue(); cell.imageView.image = nil; dispatch_async(q_global, ^{ NSString *imageURL = @"http://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Apple_Computer_Logo_rainbow.svg/150px-Apple_Computer_Logo_rainbow.svg.png"; UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString: imageURL]]]; dispatch_async(q_main, ^{ cell.imageView.image = image; [cell layoutSubviews]; }); }); return cell; } ``` ## 2. 一度読み込んだ画像をキャッシュして、再び読み込まない。 参考URL: [UITableViewの要素を非同期に設定する](http://kkinukawa.hatenablog.com/entry/20110327/1301219131) 以下のクラスを組み込んで下さい。 * Downloader.h (非ARC) * Downloader.m (非ARC) ```CacheAsyncTableController.h #import "Downloader.h" @interface CacheAsyncTableController : UITableViewController @property (nonatomic, strong) NSMutableDictionary *imageCache; @property (nonatomic, strong) NSMutableDictionary *downloaderManager; @end ``` ```CacheAsyncTableController.m - (void)viewDidLoad { [super viewDidLoad]; // 非同期表示・キャッシュ用の配列の初期化 self.imageCache = [NSMutableDictionary dictionary]; self.downloaderManager = [NSMutableDictionary dictionary]; } ***(略)*** - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } if ([_imageCache objectForKey:indexPath]) { // すでにキャッシュしてある場合 cell.imageView.image = [_imageCache objectForKey:indexPath]; } else { if (self.tableView.dragging == NO && self.tableView.decelerating == NO) { // キャッシュがない場合、読み込む [self startIconDownload:indexPath]; } } return cell; } ***(略)*** // 以下のメソッドを実装する(ソースコード参照) - (void)startIconDownload:(NSIndexPath *)indexPath; - (void)loadImagesForOnscreenRows; - (void)downloader:(NSURLConnection *)conn didLoad:(NSMutableData *)data identifier:(id)identifier; - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate; - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; ``` ## 3. UIImageViewのインスタンスに読み込み処理を任せて、それぞれで読み込む。 参考URL: [非同期通信で画像をロードする方法について](http://d.hatena.ne.jp/ntaku/20091031/1256977032) 以下のクラスを組み込んで下さい。 * AsyncImageView.h * AsyncImageView.m UIImageViewを生成するところで、以下のように書きます。 ```AsyncImageViewController.m AsyncImageView *imageView = [[AsyncImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)]; NSString *imageURL = @"http://upload.wikimedia.org/wikipedia/commons/a/a5/Apple_gray_logo.png"; [imageView loadImage:imageURL]; // 画像を非同期で読み込む ``` |
|
| 371位 |
|
|||
|
22:29:59 |
|
|
.gitignore ファイルを手動で書くのは面倒だし、漏れもありそうです。 GitHub の人気プロジェクトの1つである [github/gitignore](http://github.com/github/gitignore) にはさまざまなプロジェクト・環境に合わせた.gitignore ファイルのテンプレートが置いてあり、ここを参考にファイルを作る人も多いでしょう。
[gitignore.io](http://www.gitignore.io) はこのプロジェクトのテンプレートを Web から見やすくした感じのサービスです。開発環境に使うものを指定すると自動で .gitignore ファイルのテンプレートを生成してくれます。 これをブラウザから使うのもいいのですが、 API が用意されているのでそこから使うこともできます。つまりターミナルから以下のようにコマンドを叩くと OSX と Linux で開発する Ruby のプロジェクトにあわせた .gitignore テンプレートを生成してくれます。 ``` $ curl http://www.gitignore.io/api/osx,linux,ruby # Generated by http://www.gitignore.io ### OSX ### .DS_Store .AppleDouble .LSOverride Icon # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ### Linux ### .* !.gitignore *~ ### Ruby ### *.gem *.rbc .bundle .config coverage InstalledFiles lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp # YARD artifacts .yardoc _yardoc doc/ ``` さらに環境指定を省略して API を叩くと ``` $ curl http://www.gitignore.io/api/ gitignore.io help: list - lists the operating systems, programming languages and IDE input types :types: - generates .gitignore files for types of operating systems, programming languages or IDEs ``` このように簡単なヘルプまで表示してくれます。となればこの API を叩くコマンドを定義して CUI から使おうという発想に至りますね。 http://www.gitignore.io/cli に例があるのですが、こういうコマンドはたまにしか使わないので忘れやすい。そこでいっそのこと git のサブコマンドとして使えるようにするのが良いと思います。 ~/.gitconfig ファイルの`[alias]`セクションに次のように書いておきます。 ``` [alias] ignore = !"f() { local s=$1; shift; \ while [ $# -gt 0 ]; do s=\"$s,$1\"; shift; done;\ curl \"https://www.gitignore.io/api/$s\"; }; f" ``` すると、次のようにして 簡単に .gitignore ファイルを生成できるようになりました。 ``` $ git ignore osx linux ruby > .gitignore $ cat .gitignore # Generated by http://www.gitignore.io ### OSX ### .DS_Store .AppleDouble .LSOverride Icon # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ### Linux ### .* !.gitignore *~ ### Ruby ### *.gem *.rbc .bundle .config coverage InstalledFiles lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp # YARD artifacts .yardoc _yardocp doc/ ``` その他、`git ignore`や`git ignore help`でヘルプを表示、`git ignore list`で指定できる環境のリストを表示できます。 ``` $ git ignore gitignore.io help: list - lists the operating systems, programming languages and IDE input types :types: - generates .gitignore files for types of operating systems, programming languages or IDEs $ git ignore list finale,opencart,sdcc,delphi,coq,magento,lemonstand,nanoc,rhodesrhomobile,opa, ocaml,turbogears2,r,drupal,compass,concrete5,ruby,lilypond,wordpress,c++, actionscript,joomla,appceleratortitanium,sugarcrm,visualstudio,tasm,android, python,cakephp,yii,forcedotcom,kohana,objective-c,unity,waf,bancha,seamgen,qt, cmake,leiningen,sketchup,latex,rails,typo3,maven,clojure,haskell,c,jboss, codeigniter,erlang,autotools,go,expressionengine,textpattern,java,zendframework, scala,perl,qooxdoo,playframework,plone,gwt,cfwheels,jekyll,symfony,grails, symfony2,dart,node,symphonycms,lithium,eagle,django,jython,target3001,fuelphp, gcov,oracleforms,freepascal,monotouch,laravel,codekit,dotsettings, elasticbeanstalk,joe,openfoam,cloud9,meteor,prestashop,vvvv,justcode,komodoedit, linux,intellij,sublimetext,osx,xilinxise,windows,rubymine,modelsim,tags,sbt, flexbuilder,pycharm,eclipse,textmate,phpstorm,quartus2,netbeans,espresso, archives,redcar,sass,cvs,vim,monodevelop,matlab,virtualenv,svn,emacs,mercurial ``` |
|
| 372位 |
|
|||
|
21:43:38 |
|
|
JSDoc で API ドキュメントを生成する方法と、一部のドキュメンテーションタグの使い方についてメモする。
#環境 ##OS Windows7 64bit ##Java 1.7.0_51 ##JSDoc 3.2.2 #インストール ##Java Mozilla Rhino を使った方法でドキュメントを生成するので、 Java が必要。 インストール方法は割愛。 ##JSDoc 3 [GitHub](https://github.com/jsdoc3/jsdoc) からダウンロードできる。 tags から v3.2.2 を選択して、 zip でダウンロードする。 zip を解凍したら、解凍後のフォルダにパスを通し、コマンドラインからヘルプを表示できることを確認する。 ```bat: >jsdoc --help OPTIONS: -t, --template <value> The path to the template to use. Default: path/to/jsdoc/templates/default -c, --configure <value> The path to the configuration file. Default: path/to/jsdoc/conf.json -e, --encoding <value> Assume this encoding when reading all (以下略) ``` #ドキュメントを生成する ```text:フォルダ構成 src └─main └─java └─webapp └─js test.js ``` ```js:test.js /** * @classdesc This is MyClass. * @constructor */ function MyClass() { /** * method * @param hoge {Object} - hoge Object */ this.method = function() { }; } ``` コマンドプロンプトでトップのフォルダに移動し、以下のコマンドを実行する。 ```bat: >jsdoc -r src ``` すると、 `out` フォルダの下に API ドキュメントが HTML 形式で出力される。 ```bat: >cd out >tree /f │ global.html │ index.html │ MyClass.html │ test.js.html │ ├─scripts │ │ linenumber.js │ │ │ └─prettify │ Apache-License-2.0.txt │ lang-css.js │ prettify.js │ └─styles jsdoc-default.css prettify-jsdoc.css prettify-tomorrow.css ``` ブラウザで開くと↓のような感じ。  #ドキュメンテーションタグの書き方 ##namepath について `@link` タグなどでクラスのインスタンスメンバーやインナーメンバーを参照するときは、 `namepath` という記法を使う。 ```js: /** * <ul> * <li>{@link Hoge#instanceMethod} * <li>{@link Hoge~innerFunction} * <li>{@link Hoge.staticMethod} * </ul> * @constructor */ function Hoge() { /** * インスタンスメソッド */ this.instanceMethod = function() {}; /** * インナー関数 */ function innerFunction() {} } /** * スタティックメソッド */ Hoge.staticMethod = function() {}; ```  |記法|指すモノ| |:--|:--| |ClassName#memberName|インスタンスメンバー| |ClassName~memberName|インナーメンバー| |ClassName.memberName|スタティックメンバー| ##@param : 関数の引数を宣言する ###基本形 ```js: /** * 関数 * @param {string} str - 文字列の引数 * @param {number} num - 数値の引数 * @param {Object} obj - オブジェクトの引数 * @param {MyClass} myClass - MyClass インスタンスの引数 */ function func() {} /** * @constructor */ function MyClass() {} ```  ###引数が任意であることを宣言する ```js: /** * 関数 * @param {string} [str] - 文字列の引数 * @param {number} [num=300] - 数値の引数 */ function func() {} ```  省略したときのデフォルト値も宣言できる。 ##@return : 戻り値を説明する ```js: /** * 関数 * @return {boolean} 真偽値を返す */ function func() {} ```  ##@throws : 関数がスローする例外について説明する ```js: /** * 関数 * @throws {string} エラーメッセージをスローする */ function func() {} ```  ##@constructor : 関数をコンストラクタ関数として宣言する ```js: /** * ここの説明はコンストラクタの説明になる。 * @constructor */ function MyClass() {} ```  ##@classdesc : クラスの説明を記述する ```js: /** * コンストラクタの説明 * @constructor * @classdesc クラスの説明 * @param {string} param - パラメータ */ function MyClass(param) { } ```  ##@extends : 親クラスを宣言する ```js: /** * 親クラス * @constructor */ function ParentClass() { } /** * 子クラス * @constructor * @extends ParentClass */ function ChildClass() { } ChildClass.prototype = new ParentClass(); ```  `@augments` タグでも同じように親クラスを明示できる。 ##@link : リンクを貼る ```js: /** * <ul> * <li>{@link http://www.google.co.jp Google} * <li>{@link MyClass#hoge} * </ul> * @constructor */ function MyClass() { /** * hoge 関数 * {@link MyClass} */ this.hoge = function() {}; } ```  ##@example : 実装例を提示する ```js: /** * 関数 * @example * var result = plus(1, 3); * // result = 4 */ function plus(a, b) { return a + b; } ```  ##@deprecated : 非推奨を宣言する ```js: /** * 関数 * @deprecated 使ってはいけない */ function func() {} ```  ##@type : 変数の型を明示的に宣言する ```js: /** * これはオブジェクトです。 * @type {object} */ var hoge = {}; /** * これは数値です。 * @type {number} */ var fuga = 10; /** * これは {@link MyClass} のインスタンスです。 * @type {MyClass} */ var piyo = new MyClass(); /** * @constructor */ function MyClass() {} ```  ##@typedef : 独自の型を宣言する ```js: /** * Hoge 型です。 * @typedef {object} Hoge */ ```  ##@prop : プロパティを宣言する ```js: /** * Hoge 型です。 * @typedef {object} Hoge * @prop {string} str - 文字列のプロパティ * @prop {number} num - 数値のプロパティ */ ```  ##@file : ファイルの説明を記述する ```js:hoge.js /** * @file ほげファイル */ ``` ```js:fuga.js /** * @file ふがファイル */ ```  ##@callback : コールバック関数の定義を宣言する ```js: /** * @constructor */ function MyClass() { /** * メソッド * @param {MyClass~MyCallback} callback - コールバック関数 */ this.execute = function(callback) { }; } /** * コールバック関数の説明 * @callback MyClass~MyCallback * @param {number} hoge - 引数1 * @param {string} fuga - 引数2 */ ```  ドキュメントコメントだけ書いて、 `@callback` タグをつけると、コールバック関数単独で型を定義できる。 上記例ではタグのパラメータに `MyClass~MyCallback` と指定しているので、 `MyClass` の内部メンバーとして型が定義されている。 他のクラスなどでも使用するようなコールバック関数の場合は以下のようにしてグローバルに宣言することもできる。 ```js: /** * @constructor */ function MyClass() { /** * メソッド * @param {MyCallback} callback - コールバック関数 */ this.execute = function(callback) { }; } /** * コールバック関数の説明 * @callback MyCallback * @param {number} hoge - 引数1 * @param {string} fuga - 引数2 */ ```  ##@event : イベントオブジェクトを宣言する ```js: /**@constructor*/ function MyClass() { /** * イベントをキックする関数 * @fires MyClass~MyEvent */ this.kickEvent = function() {}; /** * イベント実行キック時に渡されるオブジェクト * @event MyClass~MyEvent * @type {object} * @property {string} name - イベント名 */ } ```  `@fire` タグで、実行するイベントを宣言できる。 ##@namespace : 名前空間を宣言する ###基本 ```js: /** * @namespace */ var aaa = { /** * @constructor */ Hoge: function() {}, /** * @namespace */ bbb: { /** * @constructor */ Fuga: function() {} } }; /** * @namespace */ aaa.bbb.ccc = { /** * @constructor */ Piyo: function() {} }; ```    ###名前空間のオブジェクトを一旦別の変数に代入している場合 ```js: /** * @namespace */ var aaa = {}; var alias = aaa; /** * @namespace */ alias.bbb = {}; ```   そのままだと `bbb` は単独の名前空間として認識されてしまう。 `aaa.bbb` というふうにしたい場合は以下のようにする。 ```js: /** * @namespace */ var aaa = {}; var alias = aaa; /** * @namespace bbb * @memberof aaa */ alias.bbb = {}; ```   `@namespace` で名前を明示して、 `@memberof` で所属している名前空間を明示する (2階層以上ある場合は `@memberof aaa.bbb` のように `.` 区切りで所属する名前空間の完全名を指定する)。 #参考 - [jsdoc3/jsdoc · GitHub](https://github.com/jsdoc3/jsdoc) - [JsDoc Toolkitを使う!](http://www12.atwiki.jp/aias-jsdoctoolkit/) - [JSDoc Toolkit→JSDoc 3移行ガイド - Technology of DeNA](http://engineer.dena.jp/2013/05/migration-from-jsdoc2-to-jsdoc3.html) |
|
| 373位 |
|
|||
|
16:12:19 |
|
|
JMockit の使い方メモ。
まとめといて何だけど、 JMockit が提供している強力な機能はなるべく使わないで済むように設計(実装)することが、理想的なんだろうなと思うわけです。 (たぶん、 JMockit の機能を下手に使いすぎると、可読性が落ちて、テストコードのメンテナンスも大変になりそうな気がする) でも、いざテスト困難な実装とぶち当たったときに備えて、ひと通り機能は知っておいた方がいいとも思うわけです。 <font color="red">※このメモは ver1.6 の頃のもので 2016年 12 月現在の最新(ver 1.29)とは異なる点が多数あると思われます。JMockit は API の非推奨化→削除もかなり頻繁に行われているようなので、中には最新版では使えなくなっているものもあるのでご注意ください。</span> #環境 ##Java 1.7.0_40 ##JMockit 1.6 ※一部 1.21 で確認 #インストール ##jar のダウンロード 直接 jar をダウンロードする場合は、 [ここ](https://github.com/jmockit/artifacts1x) からダウンロードする。 maven などを使っている場合は、セントラルリポジトリから落とす。 ```groovy:1.8より前 testCompile 'com.googlecode.jmockit:jmockit:1.6' ``` ※ver 1.8 からグループID が `com.googlecode.jmockit` から `org.jmockit` に変わってます。 ```groovy:1.8以降 testCompile 'org.jmockit:jmockit:1.21' ``` ##クラスパスに追加する ###JDK1.6 以上 かつ JUnit 4.5 以上の場合 JMockit の jar が JUnit の jar より先に読み込まれるようにクラスパスを設定する。 ###それ以外の場合 JDK が 1.5 とかいう残念な環境だったりする場合は、 JVM の引数に以下を追加する。 `-javaagent:<jmockit.jar へのパス>` # モック ## モックの特徴 ### モック化されたオブジェクトのメソッドが返す値 ```java:Hoge.java package sample.jmockit; public class Hoge { public Fuga getFuga() { return null; } public String getString() { return "HOGE"; } public Object getObject() { return new Object(); } public Collection<?> getCollection() { return null; } public int getInt() { return Integer.MAX_VALUE; } } ``` ```java:Fuga.java package sample.jmockit; public class Fuga { @Override public String toString() { return "FUGA"; } } ``` ```java:JMockitTest.java package sample.jmockit; import org.junit.Test; import mockit.Mocked; public class JMockitTest { @Mocked private Hoge hoge; @Test public void test() throws Exception { System.out.println(this.hoge.getFuga()); System.out.println(this.hoge.getString()); System.out.println(this.hoge.getObject()); System.out.println(this.hoge.getCollection()); System.out.println(this.hoge.getInt()); } } ``` ```text:実行結果 sample.jmockit.Fuga@490d6c15 null null [] 0 ``` - モック化されたオブジェクトのメソッドがなんらかのオブジェクトを返す場合、そのオブジェクトも自動的にモック化されたものになる。 - `getFuga()` で取得した `Fuga` オブジェクトがモック化されている。 - `Fuga#toString()` の動作が本来の実装と違っているので、モック化されていることが分かる。 - これをカスケーディング(`Cascading`)と呼ぶ。 - 以前は `@Cascading` というアノテーションで明示的に指定しなければならなかったが、 ver 1.14 からデフォルトでカスケーディングが有効になった。 - ただし、 `String` と `Object` を返すメソッドの場合は `null` を返す。 - また、コレクションを返すメソッドの場合はモック化されていない空のコレクションを返す。 - それ以外はデフォルト値(`int` や `long` なら `0`, `boolean` なら `false` など)を返す。 - 本来の実装は一切実行されなくなる。 ### モックが返す値を `null` にしたい場合 ```java:JMockitTest.java package sample.jmockit; import org.junit.Test; import mockit.Mocked; import mockit.NonStrictExpectations; public class JMockitTest { @Mocked private Hoge hoge; @Test public void test() throws Exception { new NonStrictExpectations() {{ hoge.getFuga(); result = null; }}; System.out.println(this.hoge.getFuga()); } } ``` ```text:実行結果 null ``` - カスケーディングで自動的にモックが返るのを止めたい場合は、 `Expectations` を使って明示的に戻り値を `null` にする。 - `result` の動作も以前調べたときとは変わっているので [こちら](#multi_result) を参照。 ###final 修飾されたメソッドもモック化される ```java:Parent.java package sample.jmockit; public class Parent { public static String PARENT_CLASS_FIELD = "parent class field"; public String parentInstanceField = "parent instance field"; public static String parentClassMethod() { return "Parent#parentClassMethod()"; } public String parentInstanceMethod() { return "Parent#parentInstanceMethod()"; } } ``` ```java:Child.java package sample.jmockit; public class Child extends Parent { public static int CLASS_FIELD = -1; public int instanceField = -1; public static int classMethod() { return -1; } public int instanceMethod() { return -1; } public final int finalInstanceMethod() { return -1; } } ``` ```java: package sample.jmockit; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Child child; @Test public void test() { System.out.println("finalInstanceMethod() = " + child.finalInstanceMethod()); } } ``` ```text:実行結果 finalInstanceMethod() = 0 ``` - final 修飾されたメソッドもモック化される。 ###インスタンスフィールドもデフォルト値になる ```java: package sample.jmockit; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Child child; @Test public void test() { System.out.println("instanceField = " + child.instanceField); System.out.println("parentInstanceField = " + child.parentInstanceField); System.out.println("CLASS_FIELD = " + Child.CLASS_FIELD); System.out.println("PARENT_CLASS_FIELD = " + Child.PARENT_CLASS_FIELD); } } ``` ```text:実行結果 instanceField = 0 parentInstanceField = null CLASS_FIELD = -1 PARENT_CLASS_FIELD = parent class field ``` - モック化されると、インスタンスフィールドもデフォルト値が設定されるようになる。 - でも、クラスフィールドは変化しない。 ###コンストラクタはモックオブジェクトを返すようになる ```java: package sample.jmockit; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Child mock; @Test public void test() { Child child = new Child(); System.out.println("child.instanceMethod() = " + child.instanceMethod()); Parent parent = new Parent(); System.out.println("parent.parentInstanceMethod() = " + parent.parentInstanceMethod()); System.out.println("(this.mock == child) = " + (this.mock == child)); } } ``` ```text:実行結果 child.instanceMethod() = 0 parent.parentInstanceMethod() = Parent#parentInstanceMethod() (this.mock == child) = false ``` - モック化されたクラスのコンストラクタは、 __新しいモックオブジェクト__ を返すようになる。 - 親クラスのコンストラクタはそのまま。 ##モックを定義する方法 ###テストクラスのインスタンスフィールドで `@Mocked` アノテーションを使う ```java:JmockitTest.java package sample.jmockit; import mockit.Mocked; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class JmockitTest { public static class TestClass1 { @Mocked private Child child; @Test public void test1() { Child child = new Child(); System.out.println("TestClass1#test1() : " + child.instanceMethod()); } @Test public void test2() { Child child = new Child(); System.out.println("TestClass1#test2() : " + child.instanceMethod()); } } public static class TestClass2 { @Test public void test1() { Child child = new Child(); System.out.println("TestClass2#test1() : " + child.instanceMethod()); } } } ``` ```text:実行結果 TestClass1#test1() : 0 TestClass1#test2() : 0 TestClass2#test1() : -1 ``` - テストクラスのインスタンスフィールドに `@Mocked` アノテーションをつけると、そのクラスはモック化される。 - `@Mocked` アノテーションをつけたフィールドには、モックオブジェクトが自動でセットされる。 - モックオブジェクトは、そのテストクラス内のテストメソッドでのみ有効。 ###テストメソッドの引数で定義する ```java:JmockitTest.java package sample.jmockit; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Test public void test1(@Mocked Child child) { Child mock = new Child(); System.out.println("test1() : " + mock.instanceMethod()); } @Test public void test2() { Child child = new Child(); System.out.println("test2() : " + child.instanceMethod()); } } ``` ```text:実行結果 test1() : 0 test2() : -1 ``` - テストメソッドに引数を定義すると、その引数のクラスがモック化される。 - `@Mocked` アノテーションは省略可能(でも警告がでる)。 - モックは、そのメソッド内のみ有効。 ### `@Injectable` で特定のインスタンスだけモック化する **ver 1.21 で動作確認** ```java:Hoge.java package sample.jmockit; public class Hoge { public String getString() { return "HOGE"; } } ``` ```java:JMockitTest.java package sample.jmockit; import org.junit.Test; import mockit.Injectable; public class JMockitTest { @Injectable private Hoge hoge; @Test public void test() throws Exception { System.out.println(this.hoge.getString()); System.out.println(new Hoge().getString()); } } ``` ```text:実行結果 null HOGE ``` - `@Injectable` でアノテートしたフィールドだけがモック化される。 - それ以外の場所で別途 `new` したインスタンスはモックかされない。 # `@Tested` でテスト対象のインスタンスを生成する **ver 1.21 で動作確認** ## 基本 ```java:Hoge.java package sample.jmockit; public class Hoge { private Hoge() { System.out.println("Hoge Constructor"); } @Override public String toString() { return "HOGE"; } } ``` ```java:JMockitTest.java package sample.jmockit; import org.junit.Test; import mockit.Tested; public class JMockitTest { @Tested private Hoge hoge; @Test public void test() throws Exception { System.out.println(this.hoge); } } ``` ```text:実行結果 Hoge Constructor HOGE ``` - `@Tested` でフィールドをアノテートすると、そのインスタンスが自動的に生成されてインジェクションされる。 - テスト対象クラスのインスタンスを生成するのに使用する。 - このクラスはモック化されていない、普通のインスタンスになる。 - インスタンスの生成はデフォルトコンストラクタを使用する。 - `private` で問題ない。 - デフォルトコンストラクタが存在しないとエラーになる。 - ただし、 `@Injectable` で宣言されたクラスが存在する場合は少し条件が変わる([詳細後述](#コンストラクタの条件))。 ## テスト対象クラスがフィールドを持つ場合 ```java:Hoge.java package sample.jmockit; public class Hoge { private Fuga fuga; private Piyo piyo; private String string; public void print() { System.out.println("fuga=" + this.fuga); System.out.println("piyo=" + this.piyo); System.out.println("string=" + this.string); } } ``` ```java:Fuga.java package sample.jmockit; public class Fuga { @Override public String toString() { return "FUGA"; } } ``` ```java:Piyo.java package sample.jmockit; public class Piyo { } ``` ```java:JMockitTest.java package sample.jmockit; import org.junit.Test; import mockit.Injectable; import mockit.Mocked; import mockit.NonStrictExpectations; import mockit.Tested; public class JMockitTest { @Tested private Hoge hoge; @Injectable private Fuga fuga; @Mocked private Piyo piyo; @Test public void test() throws Exception { new NonStrictExpectations() {{ fuga.toString(); result = "Mocked Fuga"; }}; this.hoge.print(); } } ``` ```text:実行結果 fuga=Mocked Fuga piyo=null string=null ``` - `@Tested` でアノテートされたテスト対象クラスが、 `@Injectable` でアノテートされたものと同じ型のフィールドを持つ場合、自動的にモックがインジェクションされる。 ## コンストラクタの条件 - デフォルトコンストラクタが - 存在する:OK - 存在しない - `@Injetable` 対象のクラスを引数に受け取るコンストラクタが - 存在する:OK - 存在しない:NG #`Expectations` ```java:Hoge.java package sample.jmockit; public class Hoge { public String method() { return "hoge"; } public String method(String parameter) { return parameter; } } ``` ```java: package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void 記録した通りにメソッドが実行されるので_テストは成功する() { new Expectations() {{ hoge.method("hoge"); }}; hoge.method("hoge"); } @Test public void 記録した通りの引数でメソッドが実行されないので_テストは失敗する() { new Expectations() {{ hoge.method("hoge"); }}; hoge.method("fuga"); } @Test public void 記録した通りの順番でメソッドが実行されないので_テストは失敗する() { new Expectations() {{ hoge.method(); hoge.method("hoge"); }}; hoge.method("hoge"); hoge.method(); } @Test public void 記録で使用したインスタンスでメソッドが実行されないので_テストは失敗する() { new Expectations() {{ hoge.method(); }}; Hoge other = new Hoge(); other.method(); } @Test public void 記録した回数と実行した回数が異なるので_テストは失敗する() { new Expectations() {{ hoge.method(); }}; hoge.method(); hoge.method(); } } ``` - `Expectations` を使うと、期待するメソッドの実行を記録することができる。 - メソッドを呼んだインスタンス、引数の値、呼び出し回数、呼び出し順序などが __厳密に記録した通りに__ 実行されないと、テストは失敗する。 #`NonStrictExpectations` ```java: package sample.jmockit; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void 記録したメソッドが実行されなくても_テストは成功する() { new NonStrictExpectations() {{ hoge.method(); }}; } @Test public void 指定した通りの引数で実行しなくても_テストは成功する() { new NonStrictExpectations() {{ hoge.method("hoge"); }}; hoge.method("fuga"); } @Test public void 記録で使用したインスタンスで実行されなくても_テストは成功する() { new NonStrictExpectations() {{ hoge.method(); }}; Hoge other = new Hoge(); other.method(); } @Test public void 記録した回数と実行した回数が異なっていても_テストは成功する() { new NonStrictExpectations() {{ hoge.method(); }}; hoge.method(); hoge.method(); } } ``` - 基本的には `Expectations` と同じ要領で使える。 - `Expectations` とは異なり、指定した通りにメソッドが実行されなくても、テストは成功する。 - 主に、スタブを作成したいときなどに利用する。 ※`NonStrictExpectations` は ver1.23 で非推奨となり、 ver1.25 で削除されました。`Expectations` の期待操作実行回数を `minTimes = 0` とすることで `NonStrictExpectations` を代替できます(@7tsuno さんの編集リクエストをベースに追記しました。ありがとうございます!)。 #期待する動作を記録する ```java: package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() {{ hoge.method(); }}; } } ``` ```text:実行結果 mockit.internal.MissingInvocation: Missing invocation of: sample.jmockit.Hoge#method() on mock instance: sample.jmockit.Hoge@32e701fb (以下略) ``` - `Expectations` または `NonStrictExpectations` の匿名サブクラスを定義し、そのオブジェクト初期化ブロック内でモックオブジェクトのメソッドを実行する。 - 上記例の場合、記録したメソッドがテストメソッド内で実行されていないので、テストが失敗している。 # 期待される動作の実行回数を指定する ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(); times = 3; } }; hoge.method(); hoge.method(); hoge.method(); } } ``` - メソッドを記録した直後に、 `times` フィールドに数値を代入すると、記録したメソッドが実行されなければならない回数を指定できる。 - `Expectations` で `times` を指定していない場合は、 `times = 1` を指定したことと同じになる。 - __1回も実行されない__ ことを期待したい場合は `times = 0` と指定する。 - `times` に負数を指定すると、回数のチェックは行われなくなる。 ## 最小回数を指定する ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(); minTimes = 2; } }; hoge.method(); hoge.method(); hoge.method(); } } ``` - `minTimes` に値を代入すると、「最低でも実行されなければならない回数」を指定できる。 ## 最大回数を指定する ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(); maxTimes = 2; } }; hoge.method(); hoge.method(); } } ``` - `maxTimes` に値を代入すると、「実行されて良い最大の回数」を指定できる。 - `maxTimes` を指定して1回もメソッドを実行しない場合は、テストが失敗する。 ## `minTimes` と `maxTimes` を組み合わせて使用する ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(); minTimes = 1; maxTimes = 3; } }; hoge.method(); hoge.method(); } } ``` - `minTimes` と `maxTimes` は組み合わせて使用できる。 - 上記例の場合、 1 ~ 3 回は実行されることが期待されるようになる。 # メソッドの動作をダミーに差し替える ## メソッドの戻り値を指定する ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(); result = "mock"; } }; System.out.println("hoge.method() = " + hoge.method()); } } ``` ```text:実行結果 hoge.method() = mock ``` - メソッドを記録した直後に、 `result` フィールドに値を代入すると、記録したメソッドの戻り値を指定できる。 ## コンストラクタの戻り値は指定できない ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { new Hoge(); result = hoge; } }; System.out.println("(this.hoge == new Hoge()) = " + (this.hoge == new Hoge())); } } ``` ```text:実行結果 (this.hoge == new Hoge()) = false ``` ## メソッドを実行するたびに異なる値を返すようにする ### 【この方法は最新版では使えません】<del id="multi_result">`result` に連続して値を代入する</del> 追記:この方法は最新版(ver 1.21 で確認)では使えなくなっています。代わりに [returns() メソッド](#returns_method) を使用してください。 ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new NonStrictExpectations() { { hoge.method(); result = "1"; result = "2"; result = "3"; } }; System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method() = " + hoge.method()); } } ``` ```text:実行結果(1.21での動作) hoge.method() = 3 hoge.method() = 3 hoge.method() = 3 hoge.method() = 3 ``` - 最後に記録した値が常に返される。 ```text:実行結果(1.6の頃の動作) hoge.method() = 1 hoge.method() = 2 hoge.method() = 3 hoge.method() = 3 ``` - <del>`result` に続けて値を代入すると、メソッドの実行回数に合わせて戻り値を切り替えることができる。</del> - <del>`result` に代入した回数以上にメソッドを実行すると、最後に `result` に代入した値が return され続ける。</del> ### <span id="returns_method">`returns()` メソッドを使う</span> ```java package sample.jmockit; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new NonStrictExpectations() { { hoge.method(); returns("a", "b", "c"); } }; System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method() = " + hoge.method()); } } ``` ```text:実行結果 hoge.method() = a hoge.method() = b hoge.method() = c hoge.method() = c ``` - `returns()` メソッドを使えば、引数に渡した順序で戻り値が返されるようになる。 ### `result` に配列(リスト)を代入する ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new NonStrictExpectations() { { hoge.method(); result = new String[] { "A", "B", "C" }; } }; System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method() = " + hoge.method()); } } ``` ```text:実行結果 hoge.method() = A hoge.method() = B hoge.method() = C hoge.method() = C ``` - `result` に配列またはリストを代入すると、 `result` に複数回代入するのと同じことができる - ただし、記録したメソッドが配列やリストを返す場合は使えない。 ## メソッドを実行したら例外をスローするように指定する ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(); result = new NullPointerException("sample"); } }; hoge.method(); } } ``` ```text:実行結果 java.lang.NullPointerException: sample at sample.jmockit.Hoge.method(Hoge.java) (以下略) ``` - `result` に例外オブジェクトを代入すると、直前に記録したメソッドを実行したときに代入した例外がスローされる。 ### `returns()` で例外をスローするように指定することはできない ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(); returns("hoge", new NullPointerException("sample")); } }; System.out.println(hoge.method()); System.out.println(hoge.method()); } } ``` ```text:実行結果 java.lang.ClassCastException: java.lang.NullPointerException cannot be cast to java.lang.String at sample.jmockit.Hoge.method(Hoge.java) (以下略) ``` - `NullPointerException` を `Hoge#method()` の戻り値型である `String` にキャストしようとして `ClassCastException` が発生してしまう。 # Delegate を使ってモック化したメソッドの動作を指定する ```java package sample.jmockit; import mockit.Delegate; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new NonStrictExpectations() { { hoge.method("hoge"); result = new Delegate<String>() { public String delegate(String parameter) { System.out.println("[Delegate] parameter = " + parameter); return "delegate mock"; } }; } }; System.out.println("hoge.method(\"hoge\") = " + hoge.method("hoge")); } } ``` ```text:実行結果 [Delegate] parameter = hoge hoge.method("hoge") = delegate mock ``` - result に Delegate クラスのオブジェクトを渡すと、直前に記録したメソッドが呼ばれた際に Delegate オブジェクトで定義されたメソッド(仮に __Delegate メソッド__ と呼ぶ)が実行される。 - Delegate メソッドの可視性は非 private である必要がある。 - 非 private なメソッドが複数存在すると、実行時にエラーになる。 - Delegate メソッドの名前は何でも良い。 - Delegate メソッドの引数は、使用しないなら宣言しなくても良い。 - Delegate メソッドの戻り値の型は void でも良い(その場合、モックメソッドは null を返す)。 - Delegate メソッドの戻り値の型と、記録したメソッドの戻り値の型が一致しない場合、実行時に ClassCastException が発生する。 # 引数のマッチング ## 基本 ```java package sample.jmockit; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new NonStrictExpectations() { { hoge.method("hoge"); result = "mock"; } }; System.out.println("hoge.method(\"hoge\") = " + hoge.method("hoge")); System.out.println("hoge.method(\"fuga\") = " + hoge.method("fuga")); } } ``` ```text:実行結果 hoge.method("hoge") = mock hoge.method("fuga") = null ``` - 記録時に使用したメソッドの引数も、検証のときのマッチングで対象となる。 - `result` で指定した値は、引数も含め記録したとおりにメソッドが実行された場合に限り適用される。 ## `any` オブジェクトを使って任意の引数にマッチさせる ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(anyString); result = "1"; result = "2"; } }; System.out.println("hoge.method(\"hoge\") = " + hoge.method("hoge")); System.out.println("hoge.method(\"fuga\") = " + hoge.method("fuga")); } } ``` ```text:実行結果 hoge.method("hoge") = 1 hoge.method("fuga") = 2 ``` - `Expectations` および `NonStrictExpectations` には `any~` というインスタンスフィールドが定義されており、それを記録時の引数に使用すると、任意の値にマッチさせることができる。 - `any~` は次の3種類が定義されている。 - プリミティブ型(`anyInt` 、 `anyBoolean` など) - String 型(`anyString`) - Object 型(`any`) - 「プリミティブ型」・「String 型」以外のクラスのオブジェクトにマッチさせたい場合は `(Hoge)any` というふうに、 Object 型の any をキャストして使用する。 ## `with~()` メソッドを使ってマッチングする ### `with(Object)` ```java package sample.jmockit; import static org.hamcrest.Matchers.*; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method((String) with(not("hoge"))); result = "mock"; } }; System.out.println("hoge.method(\"fuga\") = " + hoge.method("fuga")); } } ``` ```text:実行結果 hoge.method("fuga") = mock ``` - Hamcrest の Matcher を使って引数をマッチングさせることができる。 ### `withAny(T)` ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(withAny("any string")); result = "mock"; } }; System.out.println("hoge.method(\"test\") = " + hoge.method("test")); } } ``` ```text:実行結果 hoge.method("test") = mock ``` - `withAny()` の引数に渡したインスタンスと同じクラス、またはそのサブクラスのインスタンスを渡した場合にマッチする。 ### `withEqual(double, double)` ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Fuga fuga; @Test public void test() { new Expectations() { { fuga.method(withEqual(1.0, 0.5)); result = "mock"; } }; System.out.println("fuga.method(1.5) = " + fuga.method(1.5)); } public static class Fuga { public String method(double d) { return "fuga"; } } } ``` ```text:実行結果 fuga.method(1.5) = mock ``` - 第一引数で基準となる値を指定し、第二引数でモック化の対象となる前後の範囲を指定する。 - 上記例の場合 `0.5 ~ 1.5` がマッチする。 - float 版もある。 ### `withInstanceOf(Class)` ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Fuga fuga; @Test public void test() { new Expectations() { { fuga.method(withInstanceOf(Parent.class)); result = "mock"; } }; System.out.println("fuga.method(new Child()) = " + fuga.method(new Child())); } public static class Fuga { public String method(Parent parent) { return "fuga"; } } public static class Parent { } public static class Child extends Parent { } } ``` ```text:実行結果 fuga.method(new Child()) = mock ``` - 指定したクラス、またはそのサブクラスのインスタンスを渡した場合にマッチする。 ### `withNotEqual(T)` ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(withNotEqual("hoge")); result = "mock"; } }; System.out.println("hoge.method(\"fuga\") = " + hoge.method("fuga")); } } ``` ```text:実行結果 hoge.method("fuga") = mock ``` - 指定した値 __ではない__ 値が渡された場合にマッチする。 ### `withNotNull()` ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method((String) withNotNull()); result = "mock"; } }; System.out.println("hoge.method(\"some string\") = " + hoge.method("some string")); } } ``` ```text:実行結果 hoge.method("some string") = mock ``` - null でない値が渡された場合にマッチする。 ### `<T extends CharSequence> withMatch(T)` ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(withMatch("[0-9]+")); result = "mock"; } }; System.out.println("hoge.method(\"321\") = " + hoge.method("321")); } } ``` ```text:実行結果 hoge.method("321") = mock ``` - 正規表現でマッチするパターンを指定する。 ### `<T extends CharSequence> withPrefix(T)` ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(withPrefix("hoge")); result = "mock"; } }; System.out.println("hoge.method(\"hogeString\") = " + hoge.method("hogeString")); } } ``` ```text:実行結果 hoge.method("hogeString") = mock ``` - 指定した文字で始まる文字列を渡した場合にマッチする。 ### `<T extends CharSequence> withSuffix(T)` ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(withSuffix("hoge")); result = "mock"; } }; System.out.println("hoge.method(\"stringhoge\") = " + hoge.method("stringhoge")); } } ``` ```text:実行結果 hoge.method("stringhoge") = mock ``` - 指定した文字で終わる文字列を渡した場合にマッチする。 ### `<T extends CharSequence> withSubstring(T)` ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new Expectations() { { hoge.method(withSubstring("hoge")); result = "mock"; } }; System.out.println("hoge.method(\"Abc hoge xyZ\") = " + hoge.method("Abc hoge xyZ")); } } ``` ```text:実行結果 hoge.method("Abc hoge xyZ") = mock ``` - 指定した文字を間に含む文字列を渡した場合にマッチする。 ### `withSameInstance(T)` ```java package sample.jmockit; import mockit.Expectations; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { final Fuga fuga = new Fuga(); new Expectations() { { hoge.method(withSameInstance(fuga)); result = "mock"; } }; System.out.println("hoge.method(fuga) = " + hoge.method(fuga)); } public static class Hoge { public String method(Fuga fuga) { return "hoge"; } } public static class Fuga { } } ``` ```text:実行結果 hoge.method(fuga) = mock ``` - 指定したオブジェクトと全く同じインスタンス(`==` で比較)が渡された場合にマッチする。 - 普通のマッチングは `equals(Object)` メソッドを使用した比較が行われている。 # 一部のメソッドだけをモック化する(部分モック化) ## 静的な部分モック化 ```java package sample.jmockit; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked("method()") private Fuga fuga; @Test public void test() { new NonStrictExpectations() { { fuga.method(); result = "mock"; } }; System.out.println("fuga.method() = " + fuga.method()); System.out.println("fuga.method2() = " + fuga.method2()); } public static class Fuga { public String method() { return "fuga"; } public String method2() { return "FUGA"; } } } ``` ```text:実行結果 fuga.method() = mock fuga.method2() = FUGA ``` - `@Mock` アノテーションの value 属性に部分モック化させたいメソッドのシグネチャを表す文字列を渡すと、そのメソッドだけがモック化される。 - value 属性は String の配列なので、複数指定することも可能。 ### 引数が存在するメソッドを部分モック化する ```java package sample.jmockit; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked("method(String)") private Fuga fuga; @Test public void test() { new NonStrictExpectations() { { fuga.method("fuga"); result = "mock"; } }; System.out.println("fuga.method() = " + fuga.method()); System.out.println("fuga.method(\"fuga\") = " + fuga.method("fuga")); } public static class Fuga { public String method() { return "fuga"; } public String method(String value) { return value; } } } ``` ```text:実行結果 fuga.method() = fuga fuga.method("fuga") = mock ``` ### 正規表現を使って部分モック化するメソッドを指定する ```java package sample.jmockit; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked("mock.*") private Fuga fuga; @Test public void test() { new NonStrictExpectations() { { fuga.mockTargetAlpha(); result = "mock"; fuga.mockTargetBeta(); result = "mock"; } }; System.out.println("fuga.mockTargetAlpha() = " + fuga.mockTargetAlpha()); System.out.println("fuga.mockTargetBeta() = " + fuga.mockTargetBeta()); System.out.println("fuga.notMockTarget() = " + fuga.notMockTarget()); } public static class Fuga { public String mockTargetAlpha() { return "fuga"; } public String mockTargetBeta() { return "FUGA"; } public String notMockTarget() { return "Fuga"; } } } ``` ```text:実行結果 fuga.mockTargetAlpha() = mock fuga.mockTargetBeta() = mock fuga.notMockTarget() = Fuga ``` ### コンストラクタを部分モック化する ```java package sample.jmockit; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked("(String)") private Fuga fuga; @Test public void test() { Fuga fuga = new Fuga(); System.out.println("fuga.getName() = " + fuga.getName()); Fuga mock = new Fuga("FUGA"); System.out.println("mock.getName() = " + mock.getName()); } public static class Fuga { private String name = "fuga"; public Fuga() { } public Fuga(String name) { this.name = name; } public String getName() { return this.name; } } } ``` ```text: fuga.getName() = fuga mock.getName() = null ``` - コンストラクタを部分モック化する場合は、メソッド名を空にする。 ### 静的な部分モック化はおすすめできない 静的な部分モック化は、メソッド名を文字列で指定するため、リファクタリングに対して貧弱になるという欠点があるので、あまりおすすめできない。 みたいなことがチュートリアルに書いてある。 > Static partial mocking has the inconvenience that we need to explicitly specify the methods/constructors to be mocked, and do so inside strings instead of in Java code. In short, it entails extra work and is not "refactoring friendly". [Partial mocking - Dynamic partial mocking | The JMockit Tutorial - Behavior-based testing](http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#dynamicPartial) なので、動的な部分モック化を使ったほうが良い。 ## 動的な部分モック化 ### `Expectations` ブロックのコンストラクタに部分モック化したいクラスの Class オブジェクトを渡す ```java package sample.jmockit; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Test public void test() { new NonStrictExpectations(Hoge.class) { { new Hoge().method(); result = "mock"; } }; Hoge hoge = new Hoge(); System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method(\"hoge\") = " + hoge.method("hoge")); Hoge other = new Hoge(); System.out.println("other.method() = " + other.method()); System.out.println("other.method(\"other\") = " + other.method("other")); } } ``` ```text:実行結果 hoge.method() = mock hoge.method("hoge") = hoge other.method() = mock other.method("other") = other ``` - `Expectations` または `NonStrictExpectations` ブロックのコンストラクタに、部分モック化したいクラスの Class オブジェクトを渡し、ブロックの中でモック化したいメソッドを記録すると、記録したメソッドだけがモック化される。 - new したオブジェクトは、全て部分モック化されている。 ### 部分モック化したいオブジェクトを、 `Expectations` のコンストラクタに渡す ```java package sample.jmockit; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Test public void test() { final Hoge hoge = new Hoge(); new NonStrictExpectations(hoge) { { hoge.method("hoge"); result = "mock"; } }; System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method(\"hoge\") = " + hoge.method("hoge")); Hoge other = new Hoge(); System.out.println("other.method() = " + other.method()); System.out.println("other.method(\"other\") = " + other.method("other")); } } ``` ```text:実行結果 hoge.method() = hoge hoge.method("hoge") = mock other.method() = hoge other.method("other") = other ``` - `Expectations` または `NonStrictExpectations` ブロックのコンストラクタに、部分モック化したいオブジェクトを渡し、ブロックの中でモック化したいメソッドを記録すると、記録したメソッドだけがモック化される。 - 記録で使用したオブジェクトだけが部分モック化され、それ以外のオブジェクトは変化しない。 # モックオブジェクトから取得したオブジェクトも自動でモック化させる `@Cascading` は ver 1.14 で削除され、この動きがデフォルトになった。 詳しくは [モック化されたオブジェクトのメソッドが返す値](#モック化されたオブジェクトのメソッドが返す値) を参照。 # 匿名クラスをモック化する ```java package sample.jmockit; import mockit.Capturing; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Capturing private MyInterface mock; @Test public void test() { new NonStrictExpectations() { { mock.method(); result = "mock"; } }; MyInterface anonymous = new MyInterface() { @Override public String method() { return "anonymous"; } }; System.out.println("anonymous.method() = " + anonymous.method()); } public static interface MyInterface { String method(); } } ``` ```text: anonymous.method() = mock ``` - `@Capturing` を使うと匿名クラスをモック化できる。 - `@Mocked` だと、匿名クラスはモック化できない。 ```java:@Mockedを使用した場合 package sample.jmockit; import mockit.Capturing; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked private MyInterface mock; @Test public void test() { new NonStrictExpectations() { { mock.method(); result = "mock"; } }; MyInterface anonymous = new MyInterface() { @Override public String method() { return "anonymous"; } }; System.out.println("anonymous.method() = " + anonymous.method()); } public static interface MyInterface { String method(); } } ``` ```text:実行結果 anonymous.method() = anonymous ``` ## `maxInstances` 属性 ```java package sample.jmockit; import mockit.Capturing; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Capturing(maxInstances = 2) private Hoge hoge; @Test public void test() { new NonStrictExpectations() { { hoge.method(); result = "mock"; } }; Hoge a = new Hoge(); System.out.println("a.method() = " + a.method()); Hoge b = new Hoge(); System.out.println("b.method() = " + b.method()); Hoge c = new Hoge(); System.out.println("c.method() = " + c.method()); } } ``` ```text:実行結果 a.method() = mock b.method() = mock c.method() = null ``` - `@Capturing` の `maxInstances` を指定すると、記録した動作を適用するインスタンスの数を制限できる。 - 今のところ、使いドコロはよくわからない。 # テスト対象クラスのオブジェクトに自動で依存するモックオブジェクトをインジェクションする ```java package sample.jmockit; import mockit.Injectable; import mockit.NonStrictExpectations; import mockit.Tested; import org.junit.Test; public class JmockitTest { @Tested private Hoge hoge; @Injectable private Fuga fuga; @Injectable private String name = "hogeee"; @Injectable private int i = 55; @Injectable private Piyo piyo; @Test public void test() { new NonStrictExpectations() { { fuga.method(); result = "mock fuga"; } }; System.out.println("hoge.fuga.method() = " + hoge.fuga.method()); System.out.println("hoge.name = " + hoge.name); System.out.println("hoge.i = " + hoge.i); System.out.println("hoge.fuga.piyo = " + hoge.fuga.piyo); } public static class Hoge { private Fuga fuga; private String name; private int i; } public static class Fuga { private Piyo piyo; public String method() { return "fuga"; } } public static class Piyo { public String method() { return "piyo"; } } } ``` ```text:実行結果 hoge.fuga.method() = mock fuga hoge.name = hogeee hoge.i = 55 hoge.fuga.piyo = null ``` - インジェクション先のオブジェクトを `@Tested` で定義し、インジェクションする値を `@Injectable` で定義すると、 `@Injectable` を指定した値が `@Tested` で指定したオブジェクトのフィールドで自動でインジェクションされる。 - インジェクションの連鎖は対応していない(Piyo クラス)。 # 特定のオブジェクトでメソッドを実行した場合にのみ、指定した result を返すように指定する ```java package sample.jmockit; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new NonStrictExpectations() {{ hoge.method(); result = "mock"; }}; Hoge other = new Hoge(); System.out.println(other.method()); } } ``` ```text:実行結果 mock ``` `NonStrictExpectations` を使った場合、記録で使用したオブジェクトとは異なるオブジェクトでメソッドを実行しても、戻り値は result で指定した値が返される。 これを、「このオブジェクトでメソッドを実行したときだけ、指定した result が返されるようにしたい」場合は以下のようにする。 ```java package sample.jmockit; import mockit.Mocked; import mockit.NonStrictExpectations; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { new NonStrictExpectations() {{ onInstance(hoge).method(); result = "mock"; }}; System.out.println("hoge.method() = " + hoge.method()); Hoge other = new Hoge(); System.out.println("other.method() = " + other.method()); } } ``` ```text:実行結果 hoge.method() = mock other.method() = null ``` - `onInstance(T)` メソッドを使うと、特定のオブジェクトでメソッドを実行した場合にのみ、指定した result を返すように指定できる。 # `Verifications` ```java package sample.jmockit; import mockit.Mocked; import mockit.Verifications; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void 指定した通りにメソッドが実行されているので_テストは成功する() { hoge.method("hoge"); new Verifications() {{ hoge.method("hoge"); }}; } @Test public void 指定したオブジェクト以外でメソッドが実行されている場合でも_テストは成功する() { Hoge other = new Hoge(); other.method("hoge"); new Verifications() {{ hoge.method("hoge"); }}; } @Test public void 指定したメソッドが最低でも1回実行されていれば_同じメソッドを何回実行していても_テストは成功する() { hoge.method("hoge"); hoge.method("hoge"); new Verifications() {{ hoge.method("hoge"); }}; } @Test public void 指定したメソッドが最低でも1回実行されていれば_関係ないメソッドを実行していても_テストは成功する() { hoge.method("hoge"); hoge.method(); new Verifications() {{ hoge.method("hoge"); }}; } @Test public void 指定した順番でメソッドが実行されていなくても_テストは成功する() { hoge.method("hoge"); hoge.method(); new Verifications() {{ hoge.method(); hoge.method("hoge"); }}; } @Test public void 指定した通りにメソッドが実行されていないので_テストは失敗する() { hoge.method("fuga"); new Verifications() {{ hoge.method("hoge"); }}; } } ``` - `Verifications` を使うと、メソッドが意図した通りに実行されたかどうかを検証できる。 - ただし、デフォルトの検証はわりと緩い。 ## `Expectations` を使うか、 `NonStrictExpectations` + `Verifications` を使うか `NonStrictExpectations` でダミーの動作を定義して `Verifications` で検証をする、というのがナウなやり方みたいなことがチュートリアルに書いにあった。 > So, how do we choose between strict and non-strict expectations for a given test? There is really no general-purpose answer to this question. It will depend on the particulars of the unit under test, and on personal preferences. The most common preference in recent years has been for non-strict expectations, combined with explicit verification after the code under test is exercised. [Strict and non-strict expectations | The JMockit Tutorial - Behavior-based testing](http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#strictness) 個人的には、 `NonStrictExpectations` と `Verifications` の合わせ技の方が、 setup, exercise, verify の3フェーズが明確になってコードが読みやすくなる気はする。 (`Expectations` 単独だと、 setup と verify のフェーズが一緒になってしまい、コードが読みにくい気が若干している) ## メソッドの実行回数をチェックする ```java package sample.jmockit; import mockit.Mocked; import mockit.Verifications; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { hoge.method("hoge"); hoge.method(); hoge.method(); new Verifications() {{ hoge.method("hoge"); times = 1; hoge.method(); maxTimes = 2; }}; } } ``` - `Expectations` のときと同じように、 `times`、 `minTimes`、 `maxTimes` が指定できる。 ## メソッドを実行するオブジェクトを厳密にチェックする ```java package sample.jmockit; import mockit.Mocked; import mockit.Verifications; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { hoge.method(); new Verifications() {{ onInstance(hoge).method(); }}; } } ``` - `onInstance(T)` メソッドを使えば、メソッドを実行するオブジェクトを厳密に検査できる。 ## 引数のマッチング ```java package sample.jmockit; import mockit.Mocked; import mockit.Verifications; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { hoge.method("Hoge"); new Verifications() {{ hoge.method(withPrefix("H")); }}; } } ``` - `Expectations` で使用した `with~` や `any~` は、 `Verifications` でも使用できる。 ## メソッドの実行順序を厳密にチェックする場合は `VerificationsInOrder` を使う ```java package sample.jmockit; import mockit.Mocked; import mockit.VerificationsInOrder; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void 指定した順序でメソッドが実行されているので_テストは成功する() { hoge.method("hoge"); hoge.method(); new VerificationsInOrder() {{ hoge.method("hoge"); hoge.method(); }}; } @Test public void 指定した順序でメソッドが実行されていないので_テストは失敗する() { hoge.method(); hoge.method("hoge"); new VerificationsInOrder() {{ hoge.method("hoge"); hoge.method(); }}; } } ``` - `VerificationsInOrder` を使えば、メソッドの実行順序も厳密にチェックできる。 ## 余計なメソッドが実行されていないことをチェックしたい場合は `FullVerifications` を使う ```java package sample.jmockit; import mockit.FullVerifications; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void 指定したメソッドだけが実行されているので_テストは成功する() { hoge.method("hoge"); new FullVerifications() {{ hoge.method("hoge"); }}; } @Test public void 指定したメソッド以外のメソッドが実行されているので_テストは失敗する() { hoge.method("hoge"); hoge.method("fuga"); new FullVerifications() {{ hoge.method("hoge"); }}; } } ``` - `FullVerifications` を使うと、指定したメソッド以外のメソッドが実行されているとテストが失敗する ## 余分なメソッドが実行されていないことと、実行順序の両方をチェックしたい場合は `FullVerificationsInOrder` を使う ```java package sample.jmockit; import mockit.FullVerificationsInOrder; import mockit.Mocked; import mockit.VerificationsInOrder; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void 指定した順序で_指定したメソッドだけが実行されているので_テストは成功する() { hoge.method(); hoge.method("hoge"); new FullVerificationsInOrder() {{ hoge.method(); hoge.method("hoge"); }}; } @Test public void 指定した順序でメソッドが実行されていないので_テストは失敗する() { hoge.method("hoge"); hoge.method(); new FullVerificationsInOrder() {{ hoge.method(); hoge.method("hoge"); }}; } @Test public void 指定したメソッド以外のメソッドが実行されているので_テストは失敗する() { hoge.method(); hoge.method("hoge"); hoge.method("fuga"); new FullVerificationsInOrder() {{ hoge.method(); hoge.method("hoge"); }}; } } ``` ## `Expectations` と `FullVerificationsInOrder` の違い ```java package sample.jmockit; import mockit.Expectations; import mockit.FullVerificationsInOrder; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Mocked private Fuga fuga; @Test public void Expectationsは_別のモックオブジェクトのメソッド実行まではチェックしないので_テストは成功する() { new Expectations() {{ hoge.method("hoge"); hoge.method("fuga"); }}; hoge.method("hoge"); hoge.method("fuga"); fuga.method(); } @Test public void FullVerificationsInOrderは_指定したメソッド以外は全てのモックオブジェクトのメソッドについて実行されていないことをチェックするので_テストは失敗する() { hoge.method("hoge"); hoge.method("fuga"); fuga.method(); new FullVerificationsInOrder() {{ hoge.method("hoge"); hoge.method("fuga"); }}; } public static class Hoge { public String method(String value) { return value; } } public static class Fuga { public String method() { return "fuga"; } } } ``` - `Expectations` は、記録に使用したモッククラス以外についてはチェックしない。 - `FullVerifications(InOrder)` は、使用したモッククラス以外についても、実行されていないことを厳密にチェックする。 ## `FullVerifications(InOrder)` で、特定のモッククラスだけ検証する ```java package sample.jmockit; import mockit.FullVerifications; import mockit.Mocked; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Mocked private Fuga fuga; @Test public void テストは成功する() { hoge.method("hoge"); hoge.method("fuga"); fuga.method(); new FullVerifications(hoge) {{ hoge.method("hoge"); hoge.method("fuga"); }}; } public static class Hoge { public String method(String value) { return value; } } public static class Fuga { public String method() { return "fuga"; } } } ``` - `FullVerifications(InOrder)` のコンストラクタにモックオブジェクト、または Class オブジェクトを渡すと、検証対象をそのモッククラスだけに絞ることができる。 ## 実際にテスト実行時に使用されたメソッド引数をキャプチャーする ```java package sample.jmockit; import mockit.Mocked; import mockit.Verifications; import org.junit.Test; public class JmockitTest { @Mocked private Hoge hoge; @Test public void test() { hoge.method("HOGE"); new Verifications() {{ String parameter; hoge.method(parameter = withCapture()); System.out.println("parameter = " + parameter); }}; } } ``` ```text:実行結果 parameter = HOGE ``` - `withCapture()` メソッドを使うと、テストが実行されたときに実際に渡された値を取得できる。 - 使いドコロは分からない。 # `MockUp<T>` でモックを定義する ```java package sample.jmockit; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { new MockUp<Hoge>() { @Mock public void $init() { System.out.println("[MockUp] called constructor"); } @Mock public String method(String parameter) { System.out.println("[MockUp] called method(String). parameter = " + parameter); return "mock"; } }; Hoge hoge = new Hoge(); System.out.println("hoge.method() = " + hoge.method()); System.out.println("hoge.method(\"hoge\") = " + hoge.method("hoge")); } } ``` ```text:実行結果 [MockUp] called constructor hoge.method() = hoge [MockUp] called method(String). parameter = hoge hoge.method("hoge") = mock ``` - `MockUp<T>` クラスを継承したクラスを作成すると、 `<T>` で指定したクラスをモック化できる。 ******************************************************************************** - 主に、テスト対象クラスが、依存しているクラスと正しくコラボレーションできているか(渡しているパラメータが意図したとおりかどうか)をチェックするのに利用する。 ******************************************************************************** - モック化したいメソッドと全く同じシグネチャを持つメソッドを定義し、 `@Mock` アノテーションを付与すると、そのメソッドをモック化できる。 ******************************************************************************** - コンストラクタをモック化する場合は、 `$init` という特別な名前のメソッドを定義する。 ******************************************************************************** - モック化されなかったメソッドは本来の実装が実行される。 ******************************************************************************** - `@Mock` アノテーションを付与したメソッドと同じシグネチャのメソッドが `<T>` で指定したクラスに存在しない場合は `IllegalArgumentException` がスローされる。 ******************************************************************************** - つまり、テストを実行した時に `IllegalArgumentException` がスローされた場合は、 `@Mock` アノテーションを付与したメソッドが実際のメソッドのシグネチャと異なっている可能性があるので、 `IllegalArgumentException` の原因は注意してチェックしないといけない。 ```text:実際にIllegalArgumentExceptionがスローされたときのスタックトレース java.lang.IllegalArgumentException: Matching real methods not found for the following mocks: sample.jmockit.JmockitTest$1#notExistsMethod() (以下略) ``` ## クラスメソッドをモック化する ```java package sample.jmockit; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { new MockUp<Fuga>() { @Mock public void classMethod() { System.out.println("mock"); } }; Fuga.classMethod(); } public static class Fuga { public static void classMethod() {} } } ``` ```text:実行結果 mock ``` - `@Mock` アノテーションを付けるメソッドに static 修飾子を付けなくてもいい。 ## private メソッドをモック化する ```java package sample.jmockit; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { new MockUp<Hoge>() { @Mock private void privateMethod() { System.out.println("mock"); } }; Hoge hoge = new Hoge(); hoge.method(); } public static class Hoge { public void method() { this.privateMethod(); } private void privateMethod() { System.out.println("privateMethod() is called."); } } } ``` ```text:実行結果 mock ``` - シグネチャさえ合っていれば、 private メソッドもモック化できる。 ## String クラスもモック化できちゃう ```java package sample.jmockit; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { new MockUp<String>() { @Mock public String toString() { return "mock"; } }; System.out.println("\"hoge\".toString() = " + "hoge".toString()); } } ``` ```text:実行結果 "hoge".toString() = mock ``` - なにこれこわい。 ## インターフェースのモック化を宣言して、そのモックオブジェクトをその場で取得する ```java package sample.jmockit; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { MyInterface mock = new MockUp<MyInterface>() { @Mock public String method() { return "mock"; } }.getMockInstance(); System.out.println("mock.method() = " + mock.method()); } public static interface MyInterface { String method(); } } ``` ```text:実行結果 mock.method() = mock ``` - `getMockInstance()` メソッドを実行すると、モックオブジェクトをその場で取得できる。 - 普通のクラスをモック化している場合は不要だけど、具体的な実装を持たないインターフェースをモック化したい場合に有用。 ## モックメソッドの呼び出し回数を制限する ```java package sample.jmockit; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { new MockUp<Fuga>() { @Mock(invocations = 1) public void method1() {} @Mock(minInvocations = 2) public void method2() {} @Mock(maxInvocations = 3) public void method3() {} }; Fuga fuga = new Fuga(); fuga.method1(); fuga.method2(); fuga.method2(); fuga.method3(); fuga.method3(); } public static class Fuga { public void method1() {} public void method2() {} public void method3() {} } } ``` - `@Mock` アノテーションの属性で、そのメソッドが呼び出される回数を制限できる。 - `invocations` を指定すると、指定した回数だけ呼び出されないとテストが失敗する。 - `minInvocations` を指定すると、「最低限実行されないと行けない回数」を定義できる。 - `maxInvocations` を指定すると、「実行されていい最大の回数」を定義できる。 ## static 初期化ブロックをモック化する ```java package sample.jmockit; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { new MockUp<Fuga>() { @Mock public void $clinit() { System.out.println("[mocked static initialization]"); } }; System.out.println("Fuga.FINAL_STATIC_FIELD = " + Fuga.FINAL_STATIC_FIELD); System.out.println("Fuga.NOT_FINAL_STATIC_FIELD = " + Fuga.NOT_FINAL_STATIC_FIELD); } public static class Fuga { static final String FINAL_STATIC_FIELD = "final static field"; static String NOT_FINAL_STATIC_FIELD = "not final static field"; static { System.out.println("[original static initialization]"); } } } ``` ```text:実行結果 Fuga.FINAL_STATIC_FIELD = final static field [mocked static initialization] Fuga.NOT_FINAL_STATIC_FIELD = null ``` - `$clinit()` メソッドを定義すると、 static 初期化ブロックをモック化できる。 - final で無いクラスフィールドは、デフォルト値で初期化されるようになるので注意。 ### モック化する場合は、 JVM が初期化する前に `MockUp` を宣言する ```java package sample.jmockit; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { System.out.println("【JVM による初期化】"); System.out.println("Fuga.NOT_FINAL_STATIC_FIELD = " + Fuga.NOT_FINAL_STATIC_FIELD); new MockUp<Fuga>() { @Mock public void $clinit() { System.out.println("[mocked static initialization]"); } }; System.out.println("【MockUp による宣言後】"); System.out.println("Fuga.NOT_FINAL_STATIC_FIELD = " + Fuga.NOT_FINAL_STATIC_FIELD); } public static class Fuga { static final String FINAL_STATIC_FIELD = "final static field"; static String NOT_FINAL_STATIC_FIELD = "not final static field"; static { System.out.println("[original static initialization]"); } } } ``` ```text:実行結果 【JVM による初期化】 [original static initialization] Fuga.NOT_FINAL_STATIC_FIELD = not final static field 【MockUp による宣言後】 Fuga.NOT_FINAL_STATIC_FIELD = not final static field ``` - JVM による初期化が先に実行されると、 `MockUp` による宣言をしても static 初期化ブロックはモック化されない(当たり前といえば当たり前)。 ## `Invocation` ```java package sample.jmockit; import mockit.Invocation; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { new MockUp<Hoge>() { @Mock public void method(Invocation inv, String parameter) {} }; } } ``` - `@Mock` を付けたメソッドの第一引数には、任意に `Invocation` 型の引数を受け取る用に宣言できる。 - `Invocation` には、そのモックメソッドが実行されているときのコンテキスト情報が入っている。 ### モックメソッド内で、実行時のモックオブジェクトを取得する ```java package sample.jmockit; import mockit.Invocation; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { new MockUp<Fuga>() { @Mock public void method(Invocation inv) { Fuga fuga = inv.getInvokedInstance(); fuga.value = "mock"; } @Mock public void classMethod(Invocation inv) { Fuga fuga = inv.getInvokedInstance(); System.out.println("クラスメソッド内で取得した fuga = " + fuga); } }; Fuga fuga = new Fuga(); fuga.method(); System.out.println("fuga.value = " + fuga.value); Fuga.classMethod(); } public static class Fuga { private String value; public void method() {} public static void classMethod() {} } } ``` ```text:実行結果 fuga.value = mock クラスメソッド内で取得した fuga = null ``` - `getInvokedInstance()` メソッドを使うと、そのモックメソッドを実行しているモックオブジェクトを取得できる。 - クラスメソッドの中で `getInvokedInstance()` を使うと、 `null` が返される。 ### モックメソッドが実行された回数を取得する ```java package sample.jmockit; import mockit.Invocation; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { new MockUp<Fuga>() { @Mock public void method(Invocation inv) { System.out.println(inv.getInvocationCount()); } }; Fuga fuga = new Fuga(); fuga.method(); fuga.method(); fuga.method(); } public static class Fuga { public void method() {} } } ``` ```text:実行結果 1 2 3 ``` - `getInvocationCount()` メソッドを使うと、そのモックメソッドが実行された回数が取得できる。 ### モックメソッド内で、オリジナルの実装を呼び出す ```java package sample.jmockit; import mockit.Invocation; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitTest { @Test public void test() { new MockUp<Fuga>() { @Mock public void method(Invocation inv) { System.out.println("mock"); inv.proceed(); } }; Fuga fuga = new Fuga(); fuga.method(); } public static class Fuga { public void method() { System.out.println("fuga"); } } } ``` ```text: mock fuga ``` - `proceed(Object...)` メソッドを実行すると、オリジナルのメソッドを呼び出すことができる。 # リフレクションを利用したユーティリティ `Deencapsulation` というクラスがあり、リフレクション API を使って private なフィールドやメソッドにアクセスするためのユーティリティメソッドが用意されている。 ```java package sample.jmockit; import mockit.Deencapsulation; import org.junit.Test; public class JmockitTest { @Test public void test() { Fuga fuga = Deencapsulation.newInstance(Fuga.class); Deencapsulation.invoke(fuga, "method"); Deencapsulation.setField(fuga, "instanceField", "fugaaa"); System.out.println("fuga.instanceField = " + Deencapsulation.getField(fuga, "instanceField")); } public static class Fuga { private String instanceField = "instance field"; private Fuga() { System.out.println("called Fuga constructor"); } private void method() { System.out.println("called fuga.method()"); } } } ``` ```text:実行結果 called Fuga constructor called fuga.method() fuga.instanceField = fugaaa ``` | メソッド | 機能 | | :---------------------- | :------------------------------------ | | newInstance() | コンストラクタを使ってオブジェクトを生成する | | setField(), getField() | フィールドにアクセスする | | invoke() | メソッドを実行する | 使い方は、メソッドのシグネチャを見ればだいたい想像できる([Deencapsulation (JMockit Toolkit API)](http://jmockit.googlecode.com/svn/trunk/www/javadoc/mockit/Deencapsulation.html))。 # 参考 - [The JMockit testing toolkit](http://jmockit.org/index.html) - [Getting started with the JMockit Testing Toolkit の邦訳(前編) - A Memorandum](http://etc9.hatenablog.com/entry/20110215/1297783809) - [Getting started with the JMockit Testing Toolkit の邦訳(後編) - A Memorandum](http://etc9.hatenablog.com/entry/20110216/1297876841) - [JMockitは理想的なモックフレームワーク - じゅんいち☆かとうの技術日誌](http://d.hatena.ne.jp/j5ik2o/20110205/1296922274) - [次世代のモックフレームワークであるJMockitの基本的な使い方 - 達人プログラマーを目指して](http://d.hatena.ne.jp/ryoasai/20110107/1294427245) |
|
| 374位 |
|
|||
|
21:16:43 |
(FaithCreates Inc. 所属) |
|
[zsh Advent Calendar](http://qiita.com/advent-calendar/2012/zsh) もいよいよ最終日なった。便利な使い方とかがいっぱい載ってるので、まだの人は見てみると良いと思う。
こういう Advent Calendar とか便利な Tips とかの記事なんかは、読んでると自分なりにカスタマイズしたくなってくる。それに、単純にコピペするだけじゃなくて、何をやってるのかちゃんと理解したいって人も多いと思う。 でも、そういうときに困るのが、なんか変な記号みたいなのがいっぱいあって何やってるか分かんなくなること。 例えばこんな感じ。 ```sh path=(${JAVA_HOME:+${JAVA_HOME}/bin}(N-/) $path) ``` 意味わかんないし、`(N-/)` とかググってもうまく検索できない。 この手の記号とかはいっぱいあるし、知らないことがあるのはしょうがない。でも「調べ方が分からない」ってのはだいぶまずい。「知らなかったとしても必要なときに調べられる」ことが大事だと思う。 というわけで zsh の分かりにくい記号、用語をまとめてみた。なんかよくわかんない時でもここで探せば、どうやって調べればよいか分かるようになると思う。 これを参考にしてもっと zsh を楽しんでみてください! ## 履歴 | 名前(日本語) | 名前(英語) | man ページの該当箇所 | 内容 | 例 | |:-------------|:-----------|:---------------------|:-----|:---| | 履歴展開 | history expansion | man zshexpn の「HISTORY EXPANSION」 | 過去に入力したコマンドラインを再度実行する |`!!` | | 履歴展開のイベント指定 | history designator | man zshexpn の「HISTORY EXPANSION」->「Event Designators」 | コマンドライン履歴を参照する指定方法 |`!ls` | | 履歴展開の引数の選択 | word designators | man zshexpn の「HISTORY EXPANSION」->「Word Designators」 | 履歴展開で対象となるコマンドラインの一部の引数だけを展開する | `vim !cp:2` | | 履歴展開の編集子 | modifiers | man zshexpn の「HISTORY EXPANSION」->「Modifiers」 | 履歴展開で取り出した内容を一部削除、置換して使用する | `cd !!:$:h` | ## ファイル名生成系 | 名前(日本語) | 名前(英語) | man ページの該当箇所 | 内容 | 例 | |:-------------|:-----------|:---------------------|:-----|:---| | ファイル名展開 | filename expansion | man zshexpn の「FILENAME EXPANSION」 | ファイル名、パス名に置き換わる記号 | `ls ~` | | グロブ記号 | glob operator | man zshexpn の「FILENAME GENERATION」->「Glob Operators」 | ファイル名のパターンとみなされるメタ文字 | `grep HOGE **/*.rb` | | グロブフラグ | globbing flag | man zshexpn の「FILENAME GENERATION」->「Globbing Flags」 | ファイル名パターン指定で指定するフラグ | `rm (#i)*.bak` | | グロブ修飾子 | glob qualifier | man zshexpn の「FILENAME GENERATION」->「Glob Qualifiers」 | ファイル名パターン指定で、ファイルの種類で絞り込みを行うための記号 | `mv *(.) ../tmp` | | ブレース展開 | brace expansion | man zshexpn の「BRACE EXPANSION」 | `{}` を使用して複数の名前を指定する方法 | `ls README.{txt,bak}` | | 整数範囲展開 | brace expansion (every number) | man zshexpn の「BRACE EXPANSION」 | 数値の連番を作成する記法 | `wget http://site.com/{1..28}.jpg` | | 算術展開 | arithmetic expansion | man zshexpn の「ARITHMETIC EXPANSION」。 計算の記法については man zshmisc の「ARITHMETIC EVALUATION」| 計算結果に置き換えるための記法 | `echo $((1+2))` | また、ファイル名生成では履歴展開の modifiers と同じものが使える。(`echo *(:e)` など) ## 変数 | 名前(日本語) | 名前(英語) | man ページの該当箇所 | 内容 | 例 | |:-------------|:-----------|:---------------------|:-----|:---| | 変数展開 | parameter expansion | man zshexpn の「PARAMETER EXPANSION」 | 変数を値に置き換える際の規則 | `${PWD##*/}` | | 変数展開フラグ | parameter expansion flags | man zshexpn の「PARAMETER EXPANSION」->「Parameter Expansion Flags」 | 変数展開の際に追加で指定するフラグ | `${(U)foo}` | | 配列の添字用変数展開フラグ | subscript flags | man zshparamの「ARRAY PARAMETERS」-> 「Subscript Flags」 | 配列の値を取り出すときの添字に追加で指定するフラグ | `${array[(I)pattern]}` | | シェルによって設定される変数 | PARAMETERS SET BY THE SHELL | man zshparam の「PARAMETERS SET BY THE SHELL」 | zsh によって自動的に設定される変数 | $? | | シェルによって使用される変数 | PARAMETERS USED BY THE SHELL | man zshparam の「PARAMETERS USED BY THE SHELL」 | zsh の動作を指定するために設定する変数 | $PROMPT | また、変数展開では履歴展開の modifiers と同じものが使える。(`${PWD:t}` など) ## コマンドライン編集 | 名前(日本語) | 名前(英語) | man ページの該当箇所 | 内容 | 例 | |:-------------|:-----------|:---------------------|:-----|:---| | デフォルトキーバインド | default bindings | man zshzle の「STANDARD WIDGETS」 | emacs モード, vi コマンドモード、 vi インサートモードで使用可能なデフォルトキーバインド | `^A` | | ユーザー定義ウィジェット | user-defined widgets | man zshzle の「USER-DEFINED WIDGETS」 | コマンドライン編集操作を独自に定義する | `function myfunc1 { ... }; zle -N myfunc1; bindkey '^Y' myfunc1` | | 文字ハイライト | character highlighting | man zshzle の「CHARACTER HIGHLIGHTING」 | コマンドラインで表示する文字に色を付けるなどして強調する || ## プロンプト表示 | 名前(日本語) | 名前(英語) | man ページの該当箇所 | 内容 | 例 | |:-------------|:-----------|:---------------------|:-----|:---| | プロンプト文字列の展開 | expansion of prompt sequences | man zshmisc の「SIMPLE PROMPT ESCAPES」 | プロンプトの表示をカスタマイズする | `PROMPT="[%n@%m] %~ ` | | プロンプト文字列の条件文字列 | conditional substrings in prompts | man zshmisc の「CONDITIONAL SUBSTRINGS IN PROMPTS」 | プロンプト文字列の一部を現在の状態に応じて変化させる | PROMPT="%(?..[%?]) %~ " | | プロンプトテーマ | prompt themes | man zshcontrib の「PROMPT THEMES」 | プロンプトテーマを選択して使用する | `autoload -Uz promptinit; promptinit; prompt -p adam2` | ## 補完 | 名前(日本語) | 名前(英語) | man ページの該当箇所 | 内容 | 例 | |:-------------|:-----------|:---------------------|:-----|:---| | スタイル | style | man zshcompsys の「COMPLETION SYSTEM CONFIGURATION」->「Standard Styles」 | どのように補完するかを指定するために定義された名前。zstyle *CONTEXT* *STYLE* *VALUE* の STYLE の部分。| `zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'` | | コンテクスト | context | man zshcompsys の「COMPLETION SYSTEM CONFIGURATION」 | どのような文脈で補完が行われているかを指定する文字列。zstyle *CONTEXT* *STYLE* *VALUE* の CONTEXT の部分。 | `zstyle ':completion:*:cd:*' group-name ''` | | タグ | tag | man zshcompsys の「COMPLETION SYSTEM CONFIGURATION」->「Standard Tags」 | 何を補完しているかを指定する文字列。CONTEXT は :completion:*FUNCTION*:*COMPLETER*:*COMMAND*:*ARGUMENT*:*TAG* という形式で書くが、その TAG の部分。 | `zstyle ':completion:*:processes' command 'ps x -o pid,s,args'` | ## リダイレクト | 名前(日本語) | 名前(英語) | man ページの該当箇所 | 内容 | 例 | |:-------------|:-----------|:---------------------|:-----|:---| | リダイレクト | redirection | man zshmisc の「REDIRECTION」 | コマンドの入出力を別のファイルに切り替えるための記号(ヒアドキュメント、ヒアストリングもここに含む) | `ls ~ > list.txt` | | プロセス置換 | process substitution | man zshexpn の「PROCESS SUBSTITUTION」| コマンドの入出力を読み取るファイル名に置き換えるための記号。本来一時ファイルが必要な箇所で、コマンド名を指定してそこにファイルがあるものとして記述できる。 | `diff <(ls dir1) <(ls dir2)` | | マルチIO | multio | man zshmisc の「MULTIOS」 | 1つのコマンドの入出力を複数指定する | `sort <foo <bar` | ## 構文 | 名前(日本語) | 名前(英語) | man ページの該当箇所 | 内容 | 例 | |:-------------|:-----------|:---------------------|:-----|:---| | 条件式 | conditional expressions | man zshmisc の「CONDITIONAL EXPRESSIONS」 | ファイルの状態チェック、文字列の比較などを行うための記法 | `[[ -f foo.zsh ]] && source foo.zsh` | | 制御構文 | complex commands | man zshmisc の「COMPLEX COMMANDS」 | 複数のコマンドを組み合わせて実行する方法。if, while などの制御構文 | `if foo; then bar fi` | ## 内部関数 | 名前(日本語) | 名前(英語) | man ページの該当箇所 | 内容 | 例 | |:-------------|:-----------|:---------------------|:-----|:---| | 組み込みコマンド | builtin commands | man zshbuiltins | zsh 内部で実装しているコマンド | `alias` | | 特殊関数 | special functions | man zshmisc の「SPECIAL FUNCTIONS」 | zsh でとく別な機能を持つ関数 | chpwd | ## その他 | 名前(日本語) | 名前(英語) | man ページの該当箇所 | 内容 | 例 | |:-------------|:-----------|:---------------------|:-----|:---| | オプション | options | man zshoptions | zsh の動作をON/OFFで指定する | `setopt AUTO_CD` | | コマンド前置 | precommand modifiers | man zshmisc の「PRECOMMAND MODIFIERS」 | コマンドの前に指定して実行するコマンドの動作を変更する | `nocorrect cp foo bar` | |
|
| 375位 |
|
|||
|
22:43:15 |
|
|
動くところまでできたので共有します。
```ruby:Gemfile group :development, :test do gem 'capistrano', :require => false gem 'capistrano-rails', :require => false gem 'capistrano-rbenv', :require => false gem 'capistrano-bundler', :require => false end ``` ```ruby:Capfile # Load DSL and Setup Up Stages require 'capistrano/setup' # Includes default deployment tasks require 'capistrano/deploy' require 'capistrano/rbenv' set :rbenv_type, :user set :rbenv_ruby, '2.0.0-p247' require 'capistrano/bundler' require 'capistrano/rails/assets' require 'capistrano/rails/migrations' # Loads custom tasks from `lib/capistrano/tasks' if you have any defined. Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r } ``` ```ruby:lib/capistrano/tasks/unicorn.cap namespace :unicorn do task :environment do set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid" set :unicorn_config, "#{current_path}/config/unicorn/#{fetch(:rails_env)}.rb" end def start_unicorn within current_path do execute :bundle, :exec, :unicorn, "-c #{fetch(:unicorn_config)} -E #{fetch(:rails_env)} -D" end end def stop_unicorn execute :kill, "-s QUIT $(< #{fetch(:unicorn_pid)})" end def reload_unicorn execute :kill, "-s USR2 $(< #{fetch(:unicorn_pid)})" end def force_stop_unicorn execute :kill, "$(< #{fetch(:unicorn_pid)})" end desc "Start unicorn server" task :start => :environment do on roles(:app) do start_unicorn end end desc "Stop unicorn server gracefully" task :stop => :environment do on roles(:app) do stop_unicorn end end desc "Restart unicorn server gracefully" task :restart => :environment do on roles(:app) do if test("[ -f #{fetch(:unicorn_pid)} ]") reload_unicorn else start_unicorn end end end desc "Stop unicorn server immediately" task :force_stop => :environment do on roles(:app) do force_stop_unicorn end end end ``` ```ruby:config/deploy.rb set :application, 'app' set :repo_url, 'git@github.com:your/app.git' set :deploy_to, '/var/www/app' set :log_level, :info set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/assets} set :ssh_options, { keys: [File.expand_path('~/.ssh/id_rsa')], forward_agent: true, auth_methods: %w(publickey) } namespace :deploy do desc 'Restart application' task :restart do invoke 'unicorn:restart' end end after 'deploy:publishing', 'deploy:restart' ``` ```ruby:config/deploy/staging.rb set :stage, :staging set :branch, 'development' set :rails_env, 'staging' set :migration_role, 'db' server 'staging.example.local', user: 'deploy', roles: %w{web app db} ``` |
|
| 376位 |
|
|||
|
23:07:58 |
|
|
オブジェクトを初期化するイニシャライザ、*init* で始まるメソッドですね。
*init* とか *initWithString* とか。他言語でいうところコンストラクタ代わりです。 iOS開発している人なら、**「指定イニシャライザ」** という単語は聞いたことある人多いと思いますが、これの意味と役割を理解せずに組んでいる人も多いのではないでしょうか? そしてイニシャライザではいくつか守ったほうがいいルールがあります。 3 Rules for initializers --------------- 結論から書くと、以下の3つルールをイニシャライザでは守ると効率的になります。 **(1)必ず一つの指定イニシャライザを持つ(基本的に複数はナシ)** **(2)指定イニシャライザ以外のイニシャライザは、必ず指定イニシャライザ経由で初期化する** **(3)親クラスの指定イニシャライザは必ずオーバーライドする** 理由はこの記事で説明します。(※長いです) class Rectangle ---------------- まず四角形を表すRectangleというクラスを考えてみましょう。幅高さを初期値で渡します ```objc:Rectangle.m - (id) initWithWidth:(float)width height:(float)height { if (self = [super init]) { self.width = width; self.height = height; } return self; } ``` ただObjective-Cの構造上、 **直接親クラスのイニシャライザを呼び出すことができてしまいます** ```objc:Main.m - (void) main { //Rectangleにinitはないが、NSObjectにinitがあるのでビルドは通る Rectangle *rect = [[Rectangle alloc] init]; } ``` これでは **幅と高さが渡らず正常に初期化できません。** その他の変数も初期化できていないので、まず間違いなく不具合を起こすでしょう。 override init -------------------- そのため、 **initをオーバーライドしておきます。** 方法は2つあり、「適当な値で初期化する」「どうしても初期化不可能ならnilを返す(最悪例外)」です。 ```objc:Rectangle.m //先ほどのコードは省略 //適当な値で初期化するとき - (id) init { if (self = [super init]) { self.width = 1; self.height = 2; } return self; } //どうしても初期化できないとき - (id) init { return nil; ///奥の手として @throw [NSException exceptionWith...]; } ``` こうすることでinitが呼び出されても大丈夫になりました。ただまだ問題があります。 ※ビルドエラーを出す方法をコメントでいただきました。ありがとうございます。 initが呼び出されたくない場合のやり方はコメント欄を参照してください。 class Square extends Rectangle ------------------ さらにRectangleを継承したSquareクラスを考えてみましょう。 この場合、イニシャライザにはlengthのみ渡せばOKなのでそういうイニシャライザを作ります。 ```objc:Square.m - (id) initWithLength:(float)length { self = [super initWithWidth:length height:length]; if (self) { /*その他初期化処理*/ } return self; } ``` これだけでは先ほど同様、 initWithWidth:height: や、initが呼び出されたとき問題が発生します。 確実に正方形になる保証がないからです。 ではその二つをオーバーライドすればいい、となりますが、**2つもオーバーライドしたくないですよね?** 2つならがんばれるかもしれませんが、10個あったら? 親クラスのイニシャライザ全部を初期化するのはしんどいですし非常に手間がかかります。 Designated initializer ------------------- そこで登場するのが、**指定イニシャライザ** です。 そのオブジェクトを初期化するのに必要十分な引数を持ったイニシャライザです。 すべてのクラスは、**「1つの指定イニシャライザをもち、 その他のイニシャライザはすべて指定イニシャライザ経由で初期化」** させるようにします。 Rectangleはこうなります。 ```objc:Rectangle.m - (id) initWithWidth:(float)width height:(float)height { if (self = [super init]) { /*先ほど同様なので省略*/ } return self; } - (id) init { return [self initWithWidth:1 height:2]; //指定イニシャライザを通す } - (id) initWithSize:(CGSize) size { self = [self initWithWidth:size.width height:size.height]; //これも指定イニシャライザを通す if (self) { /* 他に必要ならここにかく */} return self; } ``` こうすれば、すべて指定イニシャライザを通すので **サブクラスは指定イニシャライザをオーバーライドするだけで全イニシャライザが呼び出されたときの処理を記述できます** よってSquareはこうなります ```objc:Square.m - (id) initWithLength:(float)length { //省略 } //親クラス指定イニシャライザは必ずオーバーライドする - (id) initWithWidth:(float)width height:height { //今回は幅高さの平均を正方形の大きさとする float length = (width + height) / 2; return [self initWithLength:length]; //このクラスの指定イニシャライザを通す } ``` こうすることですべてのパターンでinitWithLengthを通り、予想どおり初期化ができるようになります。 ためしに *[[Square alloc] init]* が呼び出された場合だと > (1) Rectangleのほうのinitがマッチ。 initWithWidth:height: が呼び出される > (2) initWithWidth:height: はSquareでオーバーライドしているので、Squareのほうのメソッドがマッチする。 > (3) SquareのほうのinitWithWidth:height: から指定イニシャライザ、initWithLength が呼び出される > (4) 指定イニシャライザが呼び出されて無事初期化完了 このように、ちゃんと指定イニシャライザの意味を理解し、実装すればSquare にinitがなくてもちゃんと初期化できるのです。 ただし **initWithCoder:(NSCoder *)は特殊なので例外です。** これは親クラスの initWithCoder を呼び出すようにすれば正常に初期化できるはずです。 ぜひこのルールを守ってください。もう一度3つのルールを書きます 3 Rules for initializers ---------------------- **(1)必ず一つの指定イニシャライザを持つ(基本的に複数はナシ)** **(2)指定イニシャライザの以外のイニシャライザは、必ず指定イニシャライザ経由で初期化する** **(3)親クラスの指定イニシャライザは必ずオーバーライドする** |
|
| 377位 |
|
|||
|
23:19:59 |
(Moneyforward 所属) |
|
Vim Advent Calendar 2012 136日目
さて、便利でカッコいいpowerlineさんを使う時が来たようだ! この記事を読んでカッコいいVimにしよう! ## この記事で分かること。 ・最新のpowerline化 ・TabLineもpowerline化 ・powerlineを使う人のTmux ## powerlineとはなんぞ? 「CUIってダサいよねー。どうにかしたいねー。」 「Vimってダサいよねー。」 はい、powerlineっと。  *お..お、ぉぉ...サロメ。(※1 神の雫より)* ### 最新のpowerline化 tmux, zsh, vimのpowerlineが元々ありましたが、最近ひとつに統合されました。 多くの人がvim-powerlineを使っていますが、個人的には新しいpowerlineの方がおすすめです。 あと、この前遊んでたらtablineもpowerline化出来ました。やった! ### powerlineのインストール NeoBundleで一発。 Vundleとか未だに使ってる人は、なんというか、、残念です。 ```vim:.vimrc NeoBundle 'alpaca-tc/alpaca_powertabline' NeoBundle 'Lokaltog/powerline', { 'rtp' : 'powerline/bindings/vim'} NeoBundle 'Lokaltog/powerline-fontpatcher' ``` ### フォントにパッチをあてます powerlineは既存のフォントを拡張して、かっこ良くしてるんですよね。 パッチを当てないと文字化けするので、さくっとフォントにパッチを当てましょう。 **10分ぐらい**で終わるかと思います。 *[インストール中はアルパカを見て癒されましょう(10分)](http://www.youtube.com/watch?v=t0EhLtEMg5Q)* fontforgeのインストール。 大抵のパッケージ管理で入れれます。こんな感じでしょうか。 自分の使っているパッケージ管理で入れましょう。 - `brew install fontforge --with-python` - see https://github.com/FontCustom/fontcustom/issues/170 - `port install fontforge` - `yum install fontforge` - `apt-get install fontforge` #### パッチを当てる フォントを取り出してきて、パッチを当てます。 Macならこんな感じ。 パスは各々読み替えてください。 `sudo cp /System/Library/Fonts/Ricty-Regular.ttf $HOME/.font/` `fontforge -lang=py -script $HOME/.bundle/powerline-fontpatcher/scripts/powerline-fontpatcher $HOME/.font/Ricty-Regular.ttf` **完了! 楽勝ですね。** ## powerlineを使う人のTmux ### tmux-powerline .tmux.confに下記追加。 パスは書き換えてね。 `source ~/.bundle/powerline_master/powerline/bindings/tmux/powerline.conf` 個人的にはtmux1.8から追加されたこのセンタリングオプションもおすすめ。 `set-option -g status-justify "centre"` ### 修正版tmux - iTerm2 1.0.0 20130319 - tmux-for-iTerm2-20130319 iTerm2でpowerlineを使うと崩れちゃう、vimの文字が崩れる...。 という経験のある方はiTerm2のDouble-Width Charactersにチェックを入れたことがあるかと思います。 ただ、そうするとtmuxの分割線が崩れるんですよね...。 tmuxのこの問題、パッチで解消されてました。 [iTerm2のHP](https://code.google.com/p/iterm2/downloads/list)より、tmux-for-iTerm2-20130319をダウンロードしてインストールしてください。 こんな感じになるのかな。 ```sh # tmuxのbuildに必要なパッケージのインストール wget https://iterm2.googlecode.com/files/tmux-deps-v2.tar.gz tar xvf tmux-deps-v2.tar.gz tar xvf autoconf-2.69.tar.gz tar xvf automake-1.13.1.gar.gz tar xvf gettext-0.18.2.tar.gz tar xvf libevent-2.0.19-stable.tar.gz tar xvf pkg-config-0.27.tar.gz # それぞれのディレクトリで ./configure --prefix=$YOUR_BIN_PATH make && make install ``` ```sh wget https://iterm2.googlecode.com/files/tmux-for-iTerm2-20130319.tar.gz tar xvf tmux-for-iTerm2-20130319.tar.gz cd tmux-for-iTerm2-20130319 ./configure --prefix=$YOUR_BIN_PATH make && make install ``` ちなみに、最新のiTermとTmuxはclipboard連携が出来たり、分割後のディレクトリが同じディレクトリだったり、 便利ですよ、色々と。 ### おわり お次...も@tyruさんです。笑 よろしくお願いします。 |
|
| 378位 |
|
|||
|
09:07:58 |
(FaithCreates Inc. 所属) |
|
zsh で Git 使ってる人はプロンプトにブランチ名とかを表示してる人も多いと思う。
zsh に標準で入ってる vcs\_info っていうのを使うとだいたいいい感じにできるんだけど、できないことも当然ある。 例えば stash した数の表示には対応していないので、自分で無理矢理な感じで Git コマンドを呼び出してプロンプトに表示してる人もいると思う。 でも zsh 4.3.11 ぐらいから vcs\_info に Hooks というのが追加されて、元の機能に自分で処理を追加できるようになってる。これを使うと好きなようにカスタマイズできるようになるので紹介する。 ## この記事でできるようになること こんなことがプロンプトに表示できるようになる。 - 使用しているバージョン管理システムの名前(svn, git, hg, ...) - 現在のブランチ名 - マージ失敗のエラー表示 さらに Git の場合は以下が表示できる - ステージしていない修正があるか - ステージした修正があるか - untracked なファイル(バージョン管理に含まれていないファイル)があるか - masterからリモートに push していないコミットが何件あるか - master にマージしていないコミットが何件あるか - stash が何件あるか ## zshrc の例 こんな風に zshrc に書いておけば OK。 ```sh # vcs_info 設定 RPROMPT="" autoload -Uz vcs_info autoload -Uz add-zsh-hook autoload -Uz is-at-least autoload -Uz colors # 以下の3つのメッセージをエクスポートする # $vcs_info_msg_0_ : 通常メッセージ用 (緑) # $vcs_info_msg_1_ : 警告メッセージ用 (黄色) # $vcs_info_msg_2_ : エラーメッセージ用 (赤) zstyle ':vcs_info:*' max-exports 3 zstyle ':vcs_info:*' enable git svn hg bzr # 標準のフォーマット(git 以外で使用) # misc(%m) は通常は空文字列に置き換えられる zstyle ':vcs_info:*' formats '(%s)-[%b]' zstyle ':vcs_info:*' actionformats '(%s)-[%b]' '%m' '<!%a>' zstyle ':vcs_info:(svn|bzr):*' branchformat '%b:r%r' zstyle ':vcs_info:bzr:*' use-simple true if is-at-least 4.3.10; then # git 用のフォーマット # git のときはステージしているかどうかを表示 zstyle ':vcs_info:git:*' formats '(%s)-[%b]' '%c%u %m' zstyle ':vcs_info:git:*' actionformats '(%s)-[%b]' '%c%u %m' '<!%a>' zstyle ':vcs_info:git:*' check-for-changes true zstyle ':vcs_info:git:*' stagedstr "+" # %c で表示する文字列 zstyle ':vcs_info:git:*' unstagedstr "-" # %u で表示する文字列 fi # hooks 設定 if is-at-least 4.3.11; then # git のときはフック関数を設定する # formats '(%s)-[%b]' '%c%u %m' , actionformats '(%s)-[%b]' '%c%u %m' '<!%a>' # のメッセージを設定する直前のフック関数 # 今回の設定の場合はformat の時は2つ, actionformats の時は3つメッセージがあるので # 各関数が最大3回呼び出される。 zstyle ':vcs_info:git+set-message:*' hooks \ git-hook-begin \ git-untracked \ git-push-status \ git-nomerge-branch \ git-stash-count # フックの最初の関数 # git の作業コピーのあるディレクトリのみフック関数を呼び出すようにする # (.git ディレクトリ内にいるときは呼び出さない) # .git ディレクトリ内では git status --porcelain などがエラーになるため function +vi-git-hook-begin() { if [[ $(command git rev-parse --is-inside-work-tree 2> /dev/null) != 'true' ]]; then # 0以外を返すとそれ以降のフック関数は呼び出されない return 1 fi return 0 } # untracked ファイル表示 # # untracked ファイル(バージョン管理されていないファイル)がある場合は # unstaged (%u) に ? を表示 function +vi-git-untracked() { # zstyle formats, actionformats の2番目のメッセージのみ対象にする if [[ "$1" != "1" ]]; then return 0 fi if command git status --porcelain 2> /dev/null \ | awk '{print $1}' \ | command grep -F '??' > /dev/null 2>&1 ; then # unstaged (%u) に追加 hook_com[unstaged]+='?' fi } # push していないコミットの件数表示 # # リモートリポジトリに push していないコミットの件数を # pN という形式で misc (%m) に表示する function +vi-git-push-status() { # zstyle formats, actionformats の2番目のメッセージのみ対象にする if [[ "$1" != "1" ]]; then return 0 fi if [[ "${hook_com[branch]}" != "master" ]]; then # master ブランチでない場合は何もしない return 0 fi # push していないコミット数を取得する local ahead ahead=$(command git rev-list origin/master..master 2>/dev/null \ | wc -l \ | tr -d ' ') if [[ "$ahead" -gt 0 ]]; then # misc (%m) に追加 hook_com[misc]+="(p${ahead})" fi } # マージしていない件数表示 # # master 以外のブランチにいる場合に、 # 現在のブランチ上でまだ master にマージしていないコミットの件数を # (mN) という形式で misc (%m) に表示 function +vi-git-nomerge-branch() { # zstyle formats, actionformats の2番目のメッセージのみ対象にする if [[ "$1" != "1" ]]; then return 0 fi if [[ "${hook_com[branch]}" == "master" ]]; then # master ブランチの場合は何もしない return 0 fi local nomerged nomerged=$(command git rev-list master..${hook_com[branch]} 2>/dev/null | wc -l | tr -d ' ') if [[ "$nomerged" -gt 0 ]] ; then # misc (%m) に追加 hook_com[misc]+="(m${nomerged})" fi } # stash 件数表示 # # stash している場合は :SN という形式で misc (%m) に表示 function +vi-git-stash-count() { # zstyle formats, actionformats の2番目のメッセージのみ対象にする if [[ "$1" != "1" ]]; then return 0 fi local stash stash=$(command git stash list 2>/dev/null | wc -l | tr -d ' ') if [[ "${stash}" -gt 0 ]]; then # misc (%m) に追加 hook_com[misc]+=":S${stash}" fi } fi function _update_vcs_info_msg() { local -a messages local prompt LANG=en_US.UTF-8 vcs_info if [[ -z ${vcs_info_msg_0_} ]]; then # vcs_info で何も取得していない場合はプロンプトを表示しない prompt="" else # vcs_info で情報を取得した場合 # $vcs_info_msg_0_ , $vcs_info_msg_1_ , $vcs_info_msg_2_ を # それぞれ緑、黄色、赤で表示する [[ -n "$vcs_info_msg_0_" ]] && messages+=( "%F{green}${vcs_info_msg_0_}%f" ) [[ -n "$vcs_info_msg_1_" ]] && messages+=( "%F{yellow}${vcs_info_msg_1_}%f" ) [[ -n "$vcs_info_msg_2_" ]] && messages+=( "%F{red}${vcs_info_msg_2_}%f" ) # 間にスペースを入れて連結する prompt="${(j: :)messages}" fi RPROMPT="$prompt" } add-zsh-hook precmd _update_vcs_info_msg ``` ## 表示の例 こんな感じに表示されるっていうスクリーンショットを貼っておく。 ### 普通の状態  ### ステージしていない修正がある状態  ### ステージした修正がある状態  ### untracked なファイルがある状態  ### リモートに pushしていないコミットが1件ある状態  ### stash が1件ある状態  ### いろいろいっぱいある状態  ### fix-bug ブランチから master にマージしていないコミットが1件ある状態  ### マージでコンフリクトした状態  いい感じ。 ## カスタマイズする 自分で見た目を変えたい人もいると思うので説明する。 ### 色を変える `function _update_vcs_info_msg()` の中で `%F{green}` とかやってるので、それを変えればOK。 ### 書式、表示する文言を変える `zstyle ':vcs_info:git:*' formats '(%s)-[%b]' '%c%u %m'` `zstyle ':vcs_info:git:*' actionformats '(%s)-[%b]' '%c%u %m' '<!%a>'` というところで書式を指定してるので好きなように変えよう。 こんなふうに `formats` の後ろに文字列を2つ渡しておけば、`vcs_info` を実行すると1つ目が `$vcs_info_msg_0_` 2つ目が `$vcs_info_msg_1_` に代入されるようになる。 `actionformats` の方も同じで、今回は3つ指定してるので `$vcs_info_msg_0_` から `$vcs_info_msg_2_` までに代入される。 `':vcs_info:git:*'` は Git 専用の設定で、`':vcs_info:*'` はそれ以外のデフォルト設定という感じになるので、 `':vcs_info:hg:*'` とかやって「hg はもっと別のを出したい」とかもできる。 書式で使う主な「%なんとか」の意味は以下のとおり。 | フォーマット| 内容 | set-message hooks 内で参照する変数名(後述) | |:---|:---------------------------------------------------|:----------------------| | %s | バージョン管理システム名(git, svn, hg, ...) | $hook\_com[vcs] | | %b | ブランチ情報 | $hook\_com[branch] | | %i | リビジョン番号またはリビジョンID | $hook\_com[revision] | | %r | リポジトリ名 | $hook\_com[base-name] | | %R | リポジトリのルートディレクトリのパス | $hook\_com[base] | | %S | リポジトリルートから見た今のディレクトリの相対パス | $hook\_com[subdir] | | %a | アクション名(mergeなど) actionformats のみで指定可 | $hook\_com[action] | | %c | stagedstr 文字列 | $hook\_com[staged] | | %u | unstagedstr 文字列 | $hook\_com[unstaged] | | %m | その他の情報 | $hook\_com[misc] | 詳細は man zshcontrib(1) を参照。 ### 表示する情報を追加 stash の件数とかを表示するところは vcs\_info の Hooks という機能を使って実装している。これを使うと元の vcs\_info の中に自分で処理を追加することができる。 上で書いたの以外にも自分で独自の処理を追加したい人のために方法を紹介する。 ちなみに、ここまでけっこう書いたけど、ここから先も普通のエントリ1つ分ぐらいの量があるので注意。 今回は「現在のブランチにまだマージしていないコミットが master 上にあるかどうか」を表示することにしよう。もしマージ済でないなら misc (%m) に (R) と表示することにする。 もしこれが出ていたら、master にマージするときは fast forward ではマージできない、という意味になる。 ### hook 関数登録 hook 関数の登録は `zstyle ':vcs_info:git+set-message:*' hooks ...` でやってるので、こんな感じで追加すればOK。 ```sh zstyle ':vcs_info:git+set-message:*' hooks \ git-hook-begin \ git-untracked \ git-push-status \ git-nomerge-branch \ git-nomerge-master \ # <= 追加 git-stash-count ``` `git-nomerge-master` という名前で Hook を追加することにした。これで「git の set-message hook に対して関数を追加する」という意味になる。 「set-message」というのは、`vcs_info_msg_N_` 変数の値を設定する直前の処理のこと。 ここに登録した関数はメッセージを作成する回数分呼ばれる。 今回は `zstyle ':vcs_info:git:*' formats '(%s)-[%b]' '%c%u %m'` と指定してるので、'(%s)-[%b]'、 '%c%u %m' と2つメッセージが作成される。なのでこの hook 関数も全部2回ずつ呼びだされる。 「set-message」以外にもいっぱい hook はあるんだけど、とりあえず「set-message」だけでたいてい足りると思う。 関数本体はこんな感じで書く。 ```sh function +vi-git-nomerge-master() { # この中に処理を書く } ``` さっき名前を `git-nomerge-master` として登録したけど、関数名としては頭に `+vi-` を付けるってとこに注意。 ### hook 関数の中身を書く この関数の中身はこんな感じに書く。 ```sh # とりあえず実装 function +vi-git-nomerge-master() { # master ブランチにいる時はなにもしない if [[ "${hook_com[branch]}" == "master" ]]; then return 0 fi # 現在のブランチにまだマージしていないブランチ一覧を取得する。 # その中に master が含まれていた場合、 master は現在のブランチにマージ済みでないとみなす if command git branch --no-merged 2>/dev/null | command grep 'master' > /dev/null 2>&1 ; then hook_com[misc]+="(R)" fi } ``` 今回は「master ブランチは今のブランチにマージ済みか」を表示したいので、今 master にいるときは何も表示するものがない(master から master にマージするものは無い)。 なので何もせずに終了してる。 このときに 0 以外を返すとこれ以降の hook 関数が実行されなくなってしまうので、0 を返すようにする。 その次がメインの処理。普通にシェルスクリプトとして処理してる。 `hook_com[misc]` 変数に代入すれば misc の値が上書きされて、結果としてそれが %m に表示される。 書式がどの変数名で参照できるかは上の表にまとめておいた。 もし他の関数でも misc を設定していたらそれはそのまま残しておきたいので、 += で後ろに文字列を連結してる。 基本的にこれでいいんだけど1つハメがあって、これをそのまま実行すると (R)(R) みたいに2つ出てしまう。さっきhook 関数はメッセージを作成する回数分呼び出されるって書いたけど、それが原因。 += で misc の後ろに追加していて、今回の場合は2回呼び出されるので (R) が2つ出てくる。 これを避けるのはこんな感じ。 ```sh # 完成版 function +vi-git-nomerge-master() { # vcs_info_msg_1_ を設定する場合のみ処理の対象とする if [[ "$1" != "1" ]]; then return 0 fi if [[ "${hook_com[branch]}" == "master" ]]; then return 0 fi if command git branch --no-merged 2>/dev/null | command grep 'master' > /dev/null 2>&1 ; then hook_com[misc]+="(R)" fi } ``` set-message の hook 関数には引数が2つある。1つめは何番目のメッセージかを表す数値で、0 から始まって1, 2, と増えていく。2つ目は formats, actionformats で指定した書式文字列。今回の場合は '(%s)-[%b]' とか '%c%u %m' とかが渡ってくる。 %m は2つ目のメッセージにあるので、第1引数を使って判定して2番目のときだけ処理するようにした。こうやっておけばさっきの2重になるやつは解決する。 第2引数は必要ないので特に使ってない。 これで「今のブランチにまだマージしていないコミットが master 上にあるかどうか」が表示できるようになった。rebase する派の人は、(R) が出たときは rebase が必要って分かるようになる。 こんな感じでどんどん自分で処理を追加できるので、元々無い機能でも好きなようにカスタマイズできて便利。色々試してみると良いと思う。 ## 参考ドキュメント もっと色々やってみたい人はこういうのを読めばOK。 - [man zshcontrib(1)](http://zsh.sourceforge.net/Doc/Release/User-Contributions.html#Version-Control-Information) 基本的に今回言ったことは全部 man に書いてある。 - [Misc/vcs_info-examples](http://zsh.git.sourceforge.net/git/gitweb.cgi?p=zsh/zsh;a=blob_plain;f=Misc/vcs_info-examples;hb=HEAD) ソースコードに添付されてる vcs\_info の例。今回紹介した Hooks の例も載ってる。 |
|
| 379位 |
|
|||
|
19:35:07 |
|
|
「Pythonの遅い部分をCで書き直すと実行速度が100倍になりました!(神奈川県・主婦・30代)」といった広告をよく週刊誌で見かける。しかし、型ゆとり世代にとってCはいささかハードルが高い。一方Python並列化ならば追加の手間はかなり少なくて済み、100倍とは言わないが数倍程度の高速化ができる。 # 並列化する計算 言うまでもないが、HTTP通信が律速になっているようなPythonでは並列化しても高速化されない。並列計算を要するのは大体、巨大なforループ計算である。例として以下のようなものを考える。 ```python L = 20000 total = 0 for i in range(L): for j in range(L): total += i*j print (total) ``` 手元のマシンでの実行時間は25.663秒。これを並列化によって高速化する。 # Thread並列とProcess並列 並列化は主にThread並列(メモリ共有)とProcess並列(非共有)の2方式があり、Pythonではそれぞれのモジュールが提供されているが、諸般の事情 (Global Interpreter Lock) により、Pythonでは **Thread並列化による高速化はできない**。したがって本稿ではProcess並列について述べる。 Process並列では、それぞれ独立したメモリ領域で計算が行われるため、同一の`total`変数にアクセス出来ない。従って上のような総和計算では * 計算範囲を分割する * それぞれの部分和を計算する * それらの総和をとる という手順になる。 # multiprocessingモジュールによる並列化 まず部分和を計算するための関数を定義する。 ```python L = 20000 proc = 8 # 8並列とする # 各プロセスが実行する計算 def subcalc(p): # p = 0,1,...,7 subtotal = 0 # iの範囲を設定 ini = L * p / proc fin = L * (p+1) / proc # 計算を実行 for i in range(ini, fin): for j in range(L): subtotal += i * j return subtotal ``` 次にこれらを並列で実行する。 ここでは `multiprocessing` モジュールを使う。 ```python #!/usr/bin/env python # -*- coding: utf-8 -*- import multiprocessing as mp L = 20000 proc = 8 # 8並列とする # 各プロセスが実行する計算 def subcalc(p): # p = 0,1,...,7 subtotal = 0 # iの範囲を設定 ini = L * p / proc fin = L * (p+1) / proc # 計算を実行 for i in range(ini, fin): for j in range(L): subtotal += i * j return subtotal # 8個のプロセスを用意 pool = mp.Pool(proc) # 各プロセスに subcalc(p) を実行させる # ここで p = 0,1,...,7 # callbackには各戻り値がlistとして格納される callback = pool.map(subcalc, range(8)) # 各戻り値の総和を計算 total = sum(callback) print (total) ``` 手元のPC (Intel Core i7 870) は物理4コア・仮想8コアであるため、8プロセス並列とした。 計算時間は **6.235秒** で、4倍以上高速化された。 # 並列数と速度 計算時間はこのようになった。  CPUが物理4コアであるため、4個まではほぼコア数に比例して加速するが、5並列にすると4コアのうち1個のみ2プロセスを実行する事になるため、その段階が律速になり、4並列のときよりも計算時間が増えてしまう。 このような事情もあって、最速は8並列の場合である。 他のCPUでも、恐らく仮想コア数の数に合わせるのが最速と思われる。 # その他 `mp.Pool()`を用いた並列化はシンプルに記述できるが、細かい制御ができない点や、pickle化できない関数には適用できないらしい(classのmethodなどを並列化したいときなどに問題になるらしい)という欠点がある。 より複雑な並列処理をしたい場合、`mp.Process()`で個別にプロセスを作成して実行し、`mp.Queue()`でデータのやりとりをするといった方法もある。 以下にサンプルを載せる。各プロセスが`queue`にデータを送り、それをマスターが受け取るという形になっている。 ```python #!/usr/bin/env python # -*- coding: utf-8 -*- import multiprocessing as mp L = 20000 # 各プロセスが実行する計算 def subcalc(queue, p): subtotal = 0 # iの範囲を設定 ini = L * p / proc fin = L * (p+1) / proc for i in range(ini, fin): for j in range(L): subtotal += i * j # キューにデータを送る queue.put(subtotal) # キューを作成 queue = mp.Queue() # 8個のプロセスを用意 ps = [ mp.Process(target=subcalc, args=(queue, 0)), mp.Process(target=subcalc, args=(queue, 1)), mp.Process(target=subcalc, args=(queue, 2)), mp.Process(target=subcalc, args=(queue, 3)), mp.Process(target=subcalc, args=(queue, 4)), mp.Process(target=subcalc, args=(queue, 5)), mp.Process(target=subcalc, args=(queue, 6)), mp.Process(target=subcalc, args=(queue, 7)) ] # すべてを開始 for p in ps: p.start() # キューから結果を回収 total = 0 for i in range(8): total += queue.get() # キューに値が無い場合は、値が入るまで待機になる print(total) ``` |
|
| 380位 |
|
|||
|
02:40:09 |
(University of Tokyo 所属) |
|
JavaScriptの型にまつわるややこしい仕様について、簡単にまとめてみたいと思います。
## 5つのプリミティブ型を判定 ################################### JavaScriptにおける型とは、数値(number)、文字列(string)、ブール値(boolean)、null、undefinedの5つのプリミティブ型を指します。これらはtypeof演算子を用いて判別する事が出来ます。ただし、nullだけは仕様に反して`'object'`を返します。 ```javascript typeof 'str'; // 'string' typeof 1; // 'number' typeof true; // 'boolean' typeof null; // 'object' (ECMAScript標準規格によれば独自の型のはずだが...) typeof undefined; // 'undefined' ``` その他は全てオブジェクトです。ただし、関数オブジェクトだけは特別に`'function'`を返します。 ```javascript typeof {}; // 'object' typeof []; // 'object' typeof function() {}; // 'function' 関数オブジェクトは特別扱い ``` ## オブジェクトの[[Class]]を用いた判定 ########################### プリミティブ型は判別できましたが、これでは様々なオブジェクト(Object、Array等)の判別は出来ません。しかし、「型」の判定をしたいと言う場合、実際にはArrayやObjectの区別をしたい場合が多いかと思います。 そこで、`Object.prototype.toStging`を用いて、オブジェクトの`[[Class]]`内部プロパティを取得する方法がしばしばとられます。出力は、`[object [[Class]]]`という形式になります。 [参考: Qiitaページ](http://qiita.com/Layzie/items/465e715dae14e2f601de) ```javascript var toString = Object.prototype.toString toString.call({}); // [object Object] toString.call([]); // [object Array] toString.call(function() {}); // [object Function] toString.call(new Error()); // [object Error] toString.call(new Date()); // [object Date] toString.call(JSON); // [object JSON] toString.call(Math); // [object Math] toString.call(new RegExp()); // [object RegExp] toString.call(new String('str')); // [object String] toString.call(new Number(1)); // [object Number] toString.call(new Boolean(true)); // [object Boolean] ``` 最後の3つは、それぞれ文字列(string)、数値(number)、ブール値(boolean)のラッパーオブジェクトになります。ラッパーオブジェクトというのはstring, number, booleanの3つのプリミティブな値に対してメソッドを呼び出した際に、メソッド実行用に一時的に生成されるオブジェクトです。その為、コンストラクタを使わずに`toString`を呼び出す事が出来ます。 ```javascript toString.call('str'); // [object String] toString.call(1); // [object Number] toString.call(true); // [object Boolean] ``` また、ECMAScript5からは、`null`や`undefined`も`[[Class]]`を返すようになりました。 [参考: MDN公式ドキュメント toString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString) ```javascript toString.call(null); // [object Null] toString.call(undefined); // [object Undefined] ``` これで、標準コンストラクタで生成されたオブジェクトは判別出来る事が分かりました。 ## constructorプロパティを用いた判定 ########################### ただし、問題はまだあります。自分で実装したコンストラクタから生成したオブジェクトは、`Object.prototype.toString`を使っても`[object Object]`にしかなりません。 ```javascript function MyClass() {} var obj = new MyClass(); Object.prototype.toString.call(obj); // [object Object] ``` これを判別する一つの方法として、constructorプロパティを用いるといった方法が考えられます。 ```javascript obj.constructor === MyClass; // true ``` ただし、文字列としては取得出来ず、さらに`constructor`プロパティが上書きされる危険性もあります。また、コンストラクタの`prototype`プロパティを書き換えていると、おかしな事になる場合もあります。 ```javascript obj.constructor === 'MyClass'; // false obj.constructor = Array; // constructorプロパティを上書き obj.constructor === MyClass; // false function MyClass2() {} // 新しいコンストラクタを定義 MyClass2.prototype = {}; // コンストラクタのprototypeを書き換え var obj2 = new MyClass2(); obj2.constructor === MyClass2; // false obj2.constructor === Object; // true ``` 確実な判定方法とは言えないかもしれません。 ## instanceof演算子を用いた判定 ################################ instanceof演算子は、オブジェクトがどのコンストラクタから生成されたかを判別する為に使われます。オブジェクトの「広義の型」の判定に使えそうです。 ```javascript function MyClass() {} var obj = new MyClass(); obj instanceof MyClass; // true obj instanceof Array; // false ``` ここで、instanceof演算子の挙動について考えてみます。instanceof演算子は、内部的にはオブジェクト自身のプロトタイプチェーンにコンストラクタの`prototype`が含まれているかをチェックしています。 [参考: MDN公式ドキュメント instanceof](https://developer.mozilla.org/ja/docs/JavaScript/Reference/Operators/instanceof) ```javascript obj instanceof MyClass; // true obj.__proto__ === MyClass.prototype; // true obj instanceof Object; // true obj.__proto__.__proto__ === Object.prototype; // true ``` その為、オブジェクトのプロトタイプが書き換えられた場合には`instanceof`の返り値も変わってしまいます。 ```javascript obj.__proto__ = Array.prototype; obj instanceof MyClass; // false obj instanceof Array; // true ``` よって、これも確実な判定方法とはならないでしょう。 ## 結論 ##################################################### 標準コンストラクタから生成されたオブジェクトの判定には、`Object.prototype.toString`を使うと良いでしょう。自前のコンストラクタから生成されたオブジェクトの判定にはいくつか方法がありますが、上であげた2つはどちらも確実な方法とはいえません。\_\_proto\_\_プロパティの書き換えは決してしない等の規約を決めた上で、判定を行うと良いでしょう。 |
|
| 381位 |
|
|||
|
14:38:44 |
|
|
Jenkinsは [jenkins-ci.org](http://jenkins-ci.org/) からMac OS X用のインストーラも配布されている。公式のインストーラはシステム、Homebrewの場合はユーザ環境で使うことを想定している。どちらも一長一短あるが特に致命的で回避できない問題はないように思えるのでセットアップが楽なHomebrewを使っている。 ## インストール ```bash brew install jenkins ``` jekinsをhomebrewでアップデートすると起動ファイルも更新されてしまうのでシンボリックリンクを張るのではなくコピーを ``~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist`` に置くため以下を実行する。 ```bash cp -p /usr/local/opt/jenkins/*.plist ~/Library/LaunchAgents ``` ## サーバの起動コマンドのカスタマイズ ``~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist`` を編集し、起動時のパラメータを指定する。 起動引数: https://wiki.jenkins-ci.org/display/JENKINS/Starting+and+Accessing+Jenkins 変更例 ```xml:~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>homebrew.mxcl.jenkins</string> <key>ProgramArguments</key> <array> <string>/usr/bin/java</string> <string>-Xms512m</string> <string>-Xmx1024m</string> <string>-XX:MaxPermSize=128m</string> <string>-Dfile.encoding=utf-8</string> <string>-jar</string> <string>/usr/local/opt/jenkins/libexec/jenkins.war</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist> ``` ### 文字コードの変更 GitのUTF-8のコメントが化ける場合があるのでエンコードを設定している。 ``<string>-Dfile.encoding=utf-8</string>`` ### 外部から接続させる場合 外部からアクセスさせるため ``<string>--httpListenAddress=127.0.0.1</string>`` の行を削除している。 ### メールサーバの証明書が不完全な場合 社内メールサーバなどでメールサーバの証明書が完全に信頼できない場合でもメールを送信できるように ``<string>-Dmail.smtp.starttls.enable=true</string>`` の行を削除している ※Gmailなどを使う場合は関係なし ### ポートを変える場合 例では入れていないが ``<string>/usr/local/opt/jenkins/libexec/jenkins.war</string>`` の次の行あたりに以下のように追加すると良い。 ``<string>--httpPort=80</string>`` ### メモリの変更 適当。 ``` <string>-Xms512m</string> <string>-Xmx1024m</string> <string>-XX:MaxPermSize=128m</string> ``` 参考: https://wiki.jenkins-ci.org/display/JENKINS/Builds+failing+with+OutOfMemoryErrors ## 自動起動の設定 以下を実行しておく。 ```bash launchctl load ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist ``` ## 自動起動の解除 ```bash launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist ``` ## launchctlで再起動したり 自動起動が設定済みであれば以下のコマンドを利用できる。 ```bash launchctl stop homebrew.mxcl.jenkins launchctl start homebrew.mxcl.jenkins ``` ## 参考 * Mountain Lion環境への「Jenkins」のインストール手順: http://qiita.com/skinoshita/items/6862b4a6726fc3b24944 * Jenkins を Mac で使う: http://tech.hazi.jp/2012-08/13/ |
|
| 382位 |
|
|||
|
13:40:44 |
(コロプラ 所属) |
|
最近Unityを触り始め、さらにC#の勉強も同時にやっているので、色々知ったことを細々とまとめていきます。なので、随時更新していきます。 --------------------------------- ##Table of Contents * transformのコントロール * タイムステップによる操作 * 生成 * 取得・検索 * エディタ * Leapmotionを使う * デバッグ * コリジョン(Collision) * マウス操作 * スマホ関連 * 通信関連 * テクスチャ操作 * Tips --------------------------------- ## transformのコントロール ### 回転をQuaternionでがんばる 通常は `transform.Rotate` などを使って回転させるのが早いですが、物理演算にまかせているオブジェクトを `transform` から直接操作するのはあまりよくありません。 そこで利用するのが `Rigidbody.MoveRotateion(quaternion)` です。 これは引数に渡した角度に「変更する」ものなので、徐々に回転、みたいな操作ができません。 なので、自分でQuaternionを計算してやる必要があります。 Quaternionのx, y, z, wのそれぞれの要素は角度がそのまま入っているわけではありません。 Quaternionの各要素は以下の構成になっています。 ```math (x, y, z, w) = (x \sin(\frac{\theta}{2}}), y \sin(\frac{\theta}{2}}), z \sin(\frac{\theta}{2}}), \cos(\frac{\theta}{2})) ``` x, y, zはそれぞれ軸となるベクトルです。 それに、半分の角度の $sin$ を掛けたものがx, y, z、そして半分の角度の $cos$ がwになります。 ということで、前置きはこれくらいにして実際に計算すると以下のようになります。 ```csharp float angleSpeed = 0.5f; Quaternion rot = transform.rotation; float angle = angleSpeed * Mathf.Deg2Rad; float y = Mathf.Sin(angle * 0.5f); float w = Mathf.Cos(angle * 0.5f); rot *= new Quaternion (0f, y, 0f, w); GetComponent<Rigidbody> ().MoveRotation (rot); ``` 上の例では、Y軸だけ徐々に回転する、という操作です。 説明の通り、Y軸に対して半分の角度の値を与えているのが分かるかと思います。 あとは生成したクォータニオンを、もともとの `rotation` に掛けてやることで目的の回転を表したクォータニオンを手に入れることができます。 それをあとは指定してやるだけです。 --------------------------------- ## タイムステップによる操作 ### タイムステップの時間を得る タイマーなどを自作する場合、今、開始からどれくらい時間が経ったのかを知りたいはずです。 その場合は以下のように`Time.deltaTime`を使うことで実現できます。 ```c# float timer; float waitTime = 5000.0f; void Update() { timer += Time.deltaTime; if (timer > waitTime) { //do something. } } ``` ###一定時間後に処理をする ```c# void Start() { Invoke("MethodName", 5); //5秒後にMethodNameメソッドを実行する } ``` ###一定時間後にオブジェクトを削除する 弾丸とか、生成したあと消えてほしいオブジェクトがあると思います。 消えるタイミングが時間駆動の場合は`Destroy`メソッドを使うことで実現できます。 ```c# void Start() { Destroy(gameObject, 3); //3秒後にGameObjectを削除 } ``` ###コルーチンを使った遅延処理 遅延処理など、時間の処理はコルーチンを使っても実現できます。 ```c# void Start() { StartCoroutine(WaitSomething()); } IEnumerator WaitSomething() { yield return new WaitForSeconds(2.0f); //do something. } ``` ##生成 ###空のGameObjectを作る 単純にGameObjectクラスにnameを与えて生成するだけ。 ```c# GameObject emptyObj = new GameObject("object name"); ``` ###プリミティブなGameObjectを作る 専用のメソッドから作成する。 ```c# //Cubeオブジェクトを生成 GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube); ``` PrimitiveTypeはenum型で、以下が定義されている。 ```c# public enum PrimitiveType { Sphere, Capsule, Cylinder, Cube, Plane, Quad } ``` ### prefabから複製を作る ```c# [SerializeField] private GameObject prefab; void Start() { //InstantiateはObject型が返るので、GameObject型にキャストする GameObject clone = Instantiate(prefab, new Vector3(0, 0, 0), Quaternion.identity) as GameObject; } ``` ### 動的にMaterialを生成する 動的に生成する場合は、`UnityEngine.Material`クラスを使う。 ```c# using UnityEngine; // 中略 Material material = new Material(Shader.Find("Diffuse")); material.color = Color.red; ``` Materialクラスの引数は、シェーダをstringで与える必要がある。 ただ、基本的なシェーダはいくつか準備されているようなので、Shaderクラスから探して設定してやることで生成できる。 Findメソッドに渡すテキストは、UnityのMaterialのInspectorで指定できるもの(と思われる)。 --------------------------- ## 取得・検索 ### GameObjectのサイズを取得する ```c# float width = gameObject.renderer.bounds.size.x; ``` renderer.bounds.sizeから取得する ###マテリアルなど、Unityのライブラリに入っているデータを取得する 例えば、動的にマテリアルを変更する場合。 ```c# GameObject obj = GameObject.Find("obj name"); Material material = Resources.Load("path/to/item") as Material; obj.renderer.material = material; ``` `path/to/item`の部分は、Assetsフォルダ内に`Resources`フォルダを作成し、その中に入っている要素への相対パスとなる。  仮に、`HandPrimary`にアクセスする場合は ```c# Material material = Resources.Load("Materials/HandPrimary") as Material; ``` となる。 ###GameObjectにアタッチされたスクリプトへの参照を得る 参照を得たいケースとしては、別のGameObjectにアタッチされたスクリプト内で定義された変数を参照したい、という例。 static変数(グローバル変数)経由はNG。 取得方法は該当GameObjectを見つけ、さらにアタッチされたスクリプトのコンポーネントを取得することで参照を得る。C#で書くと以下。 ```c# GameObject targetObj = GameObject.Find("someObjectName"); TargetScriptName script = targetObj.GetComponent<TargetScriptName>(); Debug.Log(script.targetVariable); //=>なにがしかの値 ``` ここで、`TargetScriptName型`はスクリプトの名前。 具体的にはファイル名。(多分) もし「HogeScript.cs」がファイル名だとしたら`HogeScript script = …`という書き方になる。 詳細は以下を参照 [必見!Unity初心者が学ぶ「別スクリプトの変数やメソッドへの参照」](http://gamesonytablet.blogspot.jp/2012/11/unity.html) ###GameObjectの階層構造を検索して該当オブジェクトを得る  上記のような構造になっている場合、rootにスクリプトがアタッチされているとすると、 ```c# Transform mixTransform = transform.Find("body/arm"); ``` とすることで、armオブジェクトを取得することができる。 ###FindChildメソッドを使って子のGameObjectを得る ```c# //子供のGameObjectのparticle systemを取得する例 ParticleSystem particle = transform.FindChild("ChildGameObjectName").GetComponent<ParticleSystem>(); ``` --------------------------- ##エディタ ###private変数をエディタから操作する 通常、publicにするとエディタから編集することができる。 しかし、プログラム上はやはりprivateにしたいときもある。 そんなときは`属性`を使って、以下のようにすることでエディタから編集させつつ、privateなメンバを宣言できる。 ```c# [SerializeField] private float hoge = 1; ``` --------------------------- ##Leapmotionを使う UnityのFree版でLeapmotionの開発をしていると、dllが読み込めない?ため、通常のPlayモードではライブラリが読み込めず動作しない。 以下の記事を参考に色々やってみることでPlayモードでも指の検出ができるようになった。 http://pierresemaan.com/getting-the-leap-to-work-with-unity-free-version-not-pro/ Leapmotionのサンプルに入っているUnitySandboxの中身を解説した記事。色々参考になります。 http://d.hatena.ne.jp/hecomi/20130723/1374598279 --------------------------- ##デバッグ ブレークポイントなどで停止させるには、MonoDevelop側からUnityを起動しないとならないらしい。 MonoDevelopを起動し、Unityを閉じたら「実行>デバッグ」を実行するとUnityが立ち上がりブレークポイントでブレークするようになる。 --------------------------- ##コリジョン(Collision) ###OnCollisionEnterを利用する Collisionの検出には、ColliderとRigidbodyのコンポーネントをそれぞれ検出させたいもの同士に追加しないと検出されない。参考: http://gamesonytablet.blogspot.jp/2012/11/unityontriggeroncollision.html --------------------------- ##マウス操作 ###マウスの当たり判定 マウスと該当オブジェクトとの衝突検出。 考え方は2次元に存在するマウスを3D空間に逆投影変換する。 まず、マウスの位置からカメラ視点方向に向かって真っ直ぐな光線を飛ばし(概念的な意味で)、その光線と接触しているかを判定することで行う。 計算方法など詳しくはこちらを参考に→ [その48 スクリーン座標でワールド空間の地面を指す](http://marupeke296.com/DXG_No48_PointGroundInScreen.html) Unityではそのへんのめんどくさいことをやってくれるメソッドがあるので、以下のようにして判定できる。 ```c# void Update () { Ray ray; RaycastHit hit; float distance = 1000.0f; //光線を伸ばす距離 //メインカメラのスクリーン上のポイントを光線に変換 ray = Camera.main.ScreenPointToRay(Input.mousePosition); //光線がhitしているオブジェクトがあるかチェック //もし該当のオブジェクトがあればhitに格納される if (Physics.Raycast(ray, out hit, distance)) { //該当オブジェクトの判別にtagを利用。ここはやりたいことに応じて適宜変更する。 if (hit.collider.gameObject.tag == "Touchable") { selectedObject = hit.collider.gameObject; } else { selectedObject = null; } } else { selectedObject = null; } } ``` ###クリックされた位置を取る 本を参考にさせてもらった、マウスの位置に応じてオブジェクトを移動する処理のサンプル。 ```c# public bool UnprojectMousePosition(out Vector3 world_position, Vector3 mouse_position) { bool ret; float depth; //チェック用のPlaneオブジェクトを生成する Plane plane = new Plane(Vector3.up, new Vector3(0.0f, transform.position.y, 0.0f)); //当たり判定用のRay(光線)を生成 Ray ray = Camera.main.ScreenPointToRay(mouse_position); //planeとの当たり判定をチェック if (plane.Raycast(ray, out depth)) { //マウスがあたっていたら位置を補正してその値を返す world_position = ray.origin + ray.direction * depth; ret = true; } else { world_position = Vector3.zero; ret = false; } return ret; } ``` ###マウスの位置にオブジェクトを移動する カメラの`ScreenToWorldPoint`を使えば比較的簡単に実装できるよう。 ```c# public void MoveToMouseposition() { //マウスの位置を取得 Vector3 screen_point = Input.mousePosition; //マウス位置のZ座標を2.0fに設定 screen_point.z = 2.0f; //上記マウス位置を元に、ワールド空間の位置を取得 Vector3 world_point = Camera.main.ScreenToWorldPoint(screen_point); //取得した位置をGameObjectのpositionに設定する transform.position = world_point; } ``` ポイントは、マウス位置に明示的にZ座標の値を設定しているところ。 元々スクリーン上のマウス座標はZ軸の値を持っていないので(2次元なので)、表示したい位置を明示的に指定してやる必要がある。 カメラの位置から見てなので、基本的にマイナス方向に値を設定すると画面内に映らなくなるので注意。 --------------------------- ##スマホ関連 ###スマホのジャイロセンサにアクセス ```c# Quaternion gyro = Input.gyro.attitude; ``` で取れるらしい。まだ使ったことないけどいずれ使いそうなのでメモw --------------------------- ##通信関連 ###HTTP通信を利用する HTTP通信は、`UnityEngine.WWW`クラスを使う。 ```c# private string res; // Use this for initialization void Start () { StartCoroutine (Download()); } IEnumerator Download() { WWW www = new WWW("http://css-eblog.com/"); yield return www; res = ""; Debug.Log(www.text); } ``` 読み込みは非同期になるため、コルーチンを利用する必要がある。 --------------------------- ##テクスチャ操作 ###テクスチャでスプライトアニメーション(の準備) テクスチャのオフセットをいじるには、`renderer.material.mainTextureOffst`をいじる。 あるいは`renderer.material.SetTextureOffst("_MainTex", offset)`のようにしてもいい。 ここで`_MainTex`はUnityで作成したマテリアルが自動で生成するシェーダ内に定義されているテクスチャの変数ぽい。 これに値を設定することで、アニメーションを実現している。 ```c# using UnityEngine; using System.Collections; public class SpriteAnimScript : MonoBehaviour { private Material material; private float offsetX = 0.0f; private float offsetSpeed = 0.1f; // Use this for initialization void Start () { this.material = renderer.material; } // Update is called once per frame void Update () { offsetX += offsetSpeed * Time.deltaTime; Vector2 offset = new Vector2(offsetX, 0.0f); this.material.SetTextureOffset("_MainTex", offset); //こっちでも動く //this.material.mainTextureOffset = offset; } } ``` ※offsetの値は整数を与えると100%の移動になるので画面上はまったく動いていないように見えるので注意。UV座標かな。 こちらの公式Wikiを参考にしました → [Animating Tiled texture](http://wiki.unity3d.com/index.php?title=Animating_Tiled_texture) ##Tips ###通常のPlaneをBillboardにする パーティクルシステムはビルボードになっているので常にカメラ方向に面を向けてくれます。 しかしどうやらUnityにはデフォルトでビルボードを実行する機能がないらしいので、自前で書く必要がある。 コード自体は非常にシンプルで、以下のものをビルボード化したいオブジェクトにアタッチするだけ。 ```billboard.cs Camera m_Camera = Camera.main; //以下でrotationを常にカメラ方向に向ける this.transform.LookAt (this.transform.position + m_Camera.transform.rotation * Vector3.back, m_Camera.transform.rotation * Vector3.up * 270); //Planeの場合、デフォルトの回転は面がup方向を向いているので追加でX軸に90度回転させてやる this.transform.Rotate(90, 0, 0); ``` --------------------------- ##役立ちリンク * [LineRendererの使い方](http://d.hatena.ne.jp/shinriyo/20121117/p1) * [ライン レンダラ / Line Renderer](http://docs-jp.unity3d.com/Documentation/Components/class-LineRenderer.html) |
|
| 383位 |
|
|||
|
14:11:49 |
(フリーランス 所属) |
|
Rubyでソースコードを読む時の小技について書いてみようと思う。
この投稿も参考になる。 Rubyでメソッドの定義場所を見つける方法 #Ruby - Qiita http://qiita.com/items/fc8a61b421d026a23ffe ちなみに、私はVimmerなので、Vimに寄った話です。 emacsについては身近にemacsユーザーに聞きましょう。 ## ctagsを活用する ctagsでtagsファイルを出力しておけば、メソッドの定義元に飛ぶのが非常に楽になります。 Railsで開発しているなら、Railsのプロジェクトルートで以下のようなコマンドを打ちます。 ```sh ctags --langmap=RUBY:.rb --exclude="*.js" --exclude=".git*" -R . ``` (bundlerでプロジェクト内にGemがある場合を想定) langmapとかは別に無くても大丈夫だと思いますが。 --excludeを付けずにctagsを実行するとjavascriptのライブラリもタグ付けされて、 ノイズが多くなるので除外しておきます。 VimでRubyのコードを開きます。 この時、カレントディレクトリはtagsのあるディレクトリに揃えておきます。 定義元を知りたいメソッドにカーソルを合わせて、`<CTRL-]>`をタイプすると、 定義元にジャンプできます。 戻りたい時は、`<CTRL-T>`をタイプします。スタックを辿って戻っていけます。 マッチ対象が複数ある場合は、最初にヒットした箇所へ飛ぶので、 選択して飛び先を決定したい場合は、tjumpコマンドを使います。 CTRL-Tで飛んでから場所が違うなと思ったら、そのままtjumpコマンドで、 今飛んできたタグで飛べるリストが出てきます。 また、unite-tagを使うことで、uniteのインターフェースからtagsを検索できます。 ただし、最初に検索する時にtagsを読み込んでキャッシュするため、 tagsのファイルサイズが大きいと初回の読み込みにかなり時間がかかります。 ## vim-refを活用する vim-refというvimでリファレンスマニュアルを参照するためのプラグインがあります。 vim-refには、デフォルトでrefeを見るための定義があるのですが、 refeを入れてPATH通すのが若干面倒なので、最近はmyruremaを使ってます。 ただ、myruremaはrefeと出力が一緒なので、実はvim-refで普通に使うことができます。 ```vim " ref.vim let g:ref_open = 'vsplit' let g:ref_refe_cmd = "rurema" let g:ref_refe_version = 2 nnoremap ,rr :<C-U>Ref refe<Space> ``` こんな感じで設定しておけば、Rubyのリファレンスをvimからすぐに確認できます。 しばしば出力の形式の問題か、上手く表示できないメソッドがあったりしますが。 vim-refの画面では、Enterで関連するrefにジャンプしたり、 CTRL-Tで前のrefに戻ったりすることが可能です。 また、uniteのソースにも対応しているので、uniteインターフェースからクラス名を検索して、 メソッドの一覧を出したりできます。 vim-ref-riというプラグインもあり、インストールされているriドキュメントをvimから検索して読むことができます。 ref-refeよりもこっちの方がいいかもしれません。こちらもuniteに対応してます。 ## pryやdebuggerを活用する ctags等で定義元のgemを調べている時に動作がいまいち良く分からんということがあれば、 gemを直接いじって確認用のコードを埋めこんでしまいます。 `pry`の`binding.pry`メソッドや`ruby-debug`の`debugger`メソッド、後は`tapp`や`awesome_print`等のgemが便利です。 変に触って戻せなくなっても、bundlerなら入れ直すのは簡単ですし、 システムに入ってるgemも、`gem pristine`というコマンドでレストアできます。 なので、調査中のgemはガンガンいじっても大丈夫です。 ## 参考 taka84u9/vim-ref-ri https://github.com/taka84u9/vim-ref-ri thinca/vim-ref https://github.com/thinca/vim-ref tsukkee/unite-tag https://github.com/tsukkee/unite-tag |
|
| 384位 |
|
|||
|
04:12:20 |
(None 所属) |
|
# セクション関連要素 ## section セクション(章や節)を表す。 ```HTML:HTML5 <section> <h1>見出し</h1> <p>本文</p> </section> ``` ### こんなsectionは○○だ #### 1.見出しが無い - 見出しが無い/用意できない = 章や節ではない = sectionじゃない #### 2.段組みのためだけに使う - sectionはdivの代わりじゃない - セマンティクスを意識しよう ## nav サイトナビゲーションセクションを表す。 ```HTML:HTML5 <nav> <ul> <li><a href='/'>サイトトップ</li> <li><a href='/about'>このサイトについて</li> <li><a href='/contact'>お問い合わせ</li> </ul> </nav> ``` ### こんなnavは○○だ #### 1.nav要素を大量に使っている - nav要素を使うことができるのは、そのサイトにとって主要なナビゲーションだけ。 - 多くても3つくらい - たとえば、ヘッダメニュー、サイドメニュー、ぱんくずリストはnav - 検索ボックス、リンク集は非nav #### 2.リストじゃない - セマンティクス的に考えるとnavの中身はリスト要素であるべき - 見た目はCSSでごにょごにょしよう ### [TIPS] ひとつのnav要素の中に複数のセクションを置く場合 section要素で分割するか、複数のnav要素を定義してaside要素で囲みます。 ```HTML:section要素で分割 <nav> <section> <h3>カテゴリーから選ぶ</h3> <ul> <li><a href='/category/all'>全て</li> <li><a href='/category/book'>書籍</li> <li><a href='/category/game'>ゲーム</li> </ul> </section> <section> <h3>売り上げランキング</h3> <ul> <li><a href='/ranking/all'>全て</li> <li><a href='/ranking/book'>書籍</li> <li><a href='/ranking/game'>ゲーム</li> </ul> </section> </nav> ``` ```HTML:aside要素で囲む場合 <aside> <nav> <h3>カテゴリーから選ぶ</h3> <ul> <li><a href='/category/all'>全て</li> <li><a href='/category/book'>書籍</li> <li><a href='/category/game'>ゲーム</li> </ul> </nav> <nav> <h3>売り上げランキング</h3> <ul> <li><a href='/ranking/all'>全て</li> <li><a href='/ranking/book'>書籍</li> <li><a href='/ranking/game'>ゲーム</li> </ul> </nav> </aside> ``` ## article ページのメインコンテンツを表す要素 ```HTML:HTML5 <article> <h1>記事タイトル</h1> <p>本文</p> <section> <h2>この記事へのコメント</h2> <article> <h3>名無しさん</h3> <p>...</p> </article> </section> </article> ``` ### [TIPS]article要素にするかどうか決めるたったひとつの基準 そのコンテンツが **単独のフィードのエントリー** として、ふさしいかどうか。 (実際にフィードを配信しているかどうかは別として) ### こんなarticleは○○だ #### 記事一覧をarticle要素で囲ってしまう - トップページなどの記事一覧は個々の記事をそれぞれarticle要素で囲みます ## aside メインコンテンツと関係が薄く、仮に無くなってもそのコンテンツには影響が無い(少ない)ものを指します。 ややつかいどころのむずかしい要素。 ### こんなコンテンツはasideだ - 広告 - 参考リンク - アイキャッチ画像 - 補足記事 - この商品を買った人はこんな商品も買っています ### [TIPS]Q.asideにするか迷ったら - 自分がしたいようにすればOK! - 結局、そのコンテンツが自分のメインコンテンツにとって重要かどうか。 # ページの構造化要素 ## header セクションのヘッダーに当たる部分を表します。 ```HTML5 <article> <header> <h1>見出し</h1> </header> <p>...</p> </article> ``` ## footer セクションのフッターに当たる部分を表します。 ```HTML5 <article> <header> <h1>見出し</h1> </header> <p>...</p> <footer> ※1 注釈 </footer> <section> <h2>コメント</h2> ... </section> </article> ``` ### [TIPS] footerはセクションの末尾以外で使ってもいい 上の例ではフッターの後にコメント一覧をマークアップしています。 ## main ページの主要な部分であることを明示する。 >文書内で、main 要素が出てきていいのは、1回まで コンテンツモデルは Flow content ただし、main 要素を、article、aside、footer、header、nav 要素の子孫として使わないでね セクショニング・コンテンツではないので、アウトラインは生成しません *via [HTML5 勧告候補に main 要素が追加、hgroup 要素は予定通り削除 | WWW WATCH](http://hyper-text.org/archives/2013/04/add_main_html5_cr.shtml)* ```HTML:HTML5 <main role='main'> <article> ... </article> </main> ``` # グループ化要素 ## figure/figurecaption 画像/動画やコードなどのコンテンツとキャプションをまとめます。 ```HTML:HTML5 <figure> <img src="..." alt="..."> <figurecaption>画像のキャプション</figurecaption> </figure> ``` ###[TIPS] figure要素は自己完結型のコンテンツ - figure要素はメインコンテンツから参照されるコンテンツ - たとえ別の場所にあってもメインコンテンツに影響しないこと # マルチメディア関連要素 ## video 動画の読み込みと再生を行う。 対象コーデックはブラウザによって異なる。 ```HTML:srcを指定 <video src='sample.mp4'> <p>ご利用のブラウザでは再生できません。</p> </video> ``` source要素を使用した場合、複数のコーデックに対応できます。 (上から順に評価し、再生可能なものを使用。) ```HTML:source要素を使用 <video> <source src='sample.ogg' type='video.ogg'></source> <source src='sample.mp4' type='video.mp4'></source> <p>ご利用のブラウザでは再生できません。</p> </video> ``` ### 属性 - controls ブラウザ標準の再生コントローラを表示 - autoplay 自動再生 - autobuffer 事前バッファ - loop ループ再生 - poster='ファイル名' サムネイルの指定 - width/height サイズ指定 ## audio 音声の読み込みと再生を行う。 対象コーデックはブラウザによって異なる。 ```HTML:srcを指定 <audio src='sample.mp3'> <p>ご利用のブラウザでは再生できません。</p> </audio> ``` source要素を使用した場合、複数のコーデックに対応できます。 (上から順に評価し、再生可能なものを使用。) ```HTML:source要素を使用 <audio> <source src='sample.ogg' type='video.ogg'></source> <source src='sample.mp3' type='video.mp3'></source> <p>ご利用のブラウザでは再生できません。</p> </audio> ``` ### 属性 - controls ブラウザ標準の再生コントローラを表示 - autoplay 自動再生 - autobuffer 事前バッファ - loop ループ再生 ### [Tips] video/audio要素をJavascriptから操作 [video要素、audio要素をJavaScriptから操作する-HTML5のAPI、および、関連仕様](http://www.htmq.com/video/) 早送りや、巻き戻しも実装可能。 [JavaScript を使って HTML5 ビデオ プレーヤーを制御する (Windows)](http://msdn.microsoft.com/ja-jp/library/ie/hh924823(v=vs.85).aspx) ## source videoやaudioのメディアファイルを指定。 type属性に詳細なコーデックを指定することもできる。 メディアクエリの指定も。 ```HTML:HTML5 <source src='sample.mp4' type='video/mp4;codecs="avc1.42E01E, mp4a.40.2"' media='handheld'> ``` ## track audio要素や、video要素に対して、字幕、キャプション、チャプターなどのトラック情報を指定する際に用いられます。 ``` <video src="brave.webm"> <track kind="subtitles" src="brave.ja.vtt" srclang="ja" label="Japanese"> <track kind="subtitles" src="brave.en.vtt" srclang="en" label="English"> <track kind="subtitles" src="brave.de.vtt" srclang="de" label="Deutsch"> </video> ``` ## canvas Javascriptで動的にイメージを生成するための要素。 ```HTML:HTML5 <canvas width='300' height='150'> <!-- フォールバック用コンテンツ --> </canvas> ``` # テキスト関連要素 ## mark - ハイライトを表す - 他の場面で言及されていること - 引用文の中でも使える(セマンティクス的には、引用もとではなく、引用主が強調していることを表す) ```HTML:HTML5 <blockquote cite='http://ja.wikipedia.org/wiki/スティーブ・ジョブズ'> <p>当初、ジョブズは700万ドルをNeXTに投資し、1987年までには新しい製品が投入できるともくろんでいたが、実際に、NeXTの製品 (NeXTcube) を発表できたのは1988年秋で、最終版の出荷は、1989年になってのことだった。ジョブズはそれでも「5年は先取りしている」と語ったが(結果的にはMac OS Xの12年の先取り)、NeXTのロゴデザイン(ポール・ランドに依頼)に10万ドルを投じたり、OS (<mark>NEXTSTEP</mark>) の凝った仕様を開発するべく膨大な時間をかけたり、NeXTcubeの筐体デザインをフロッグデザインに依頼するなどして、過剰に資金を浪費した。1987年にはゼネラル・モーターズで成功していたロス・ペローから2000万ドルの出資を、1989年には、キヤノンから1億ドルの出資を引き出した。</p> </blockquote> <cite>Wikipedia - スティーブ・ジョブズ</cite>より <p>このNEXTSTEPこそ、後のOSXやiOSの元となったOSなのです。</p> ``` 表示例: <blockquote cite='http://ja.wikipedia.org/wiki/スティーブ・ジョブズ'> <p>当初、ジョブズは700万ドルをNeXTに投資し、1987年までには新しい製品が投入できるともくろんでいたが、実際に、NeXTの製品 (NeXTcube) を発表できたのは1988年秋で、最終版の出荷は、1989年になってのことだった。ジョブズはそれでも「5年は先取りしている」と語ったが(結果的にはMac OS Xの12年の先取り)、NeXTのロゴデザイン(ポール・ランドに依頼)に10万ドルを投じたり、OS (<mark>NEXTSTEP</mark>) の凝った仕様を開発するべく膨大な時間をかけたり、NeXTcubeの筐体デザインをフロッグデザインに依頼するなどして、過剰に資金を浪費した。1987年にはゼネラル・モーターズで成功していたロス・ペローから2000万ドルの出資を、1989年には、キヤノンから1億ドルの出資を引き出した。</p> </blockquote> <cite>Wikipedia - スティーブ・ジョブズ</cite>より <p>このNEXTSTEPこそ、後のOSXやiOSの元となったOSなのです。</p> ## time 日時を表します。 pubdate属性を指定すると、ページあるいはコンテンツの投稿/更新日時を表します。 ```HTML:HTML5 <article> <h1>タイトル</h1> <time datetime="2014-03-10T09:03:24" pubdate>1時間前に更新</time> ... </article> ``` ## data >コンピューターに理解してほしいデータを表す。 データたる内容テキストは人間に読める自然言語で書き、value属性値にはコンピューターに理解できるように特定の型・規格等にそった書式に変換して当該データを埋め込む。 *via [data要素:HTML5.1各要素別メモ](http://mtrootyy.web5.jp/htmllint/html5memo/data.html)* ## ruby/rt/rp/rb/rtc テキストにルビをふります。 ### ruby ルビを振るテキストを囲む ### rp ルビが使えないブラウザへのフォールバック (使えるブラウザではこの中は無視されます) ### rb ルビ定義のルビを振られる部分を明示する。 ### rtc ルビ定義のうち、ルビ注記部分のコンテナとして使用。主に二重ルビで用いる。 ```HTML:HTML5 <ruby> 佐村河内<rp>(</rp><rt>さむらごうち</rt><rp>)</rp> 守<rp>(</rp><rt>まもる</rt><rp>)</rp> </ruby> ``` 表示例: <ruby> 佐村河内</rb><rp>(</rp><rt>さむらごうち</rt><rp>)</rp> 守<rp>(</rp><rt>まもる</rt><rp>)</rp> </ruby> ## wbr 改行しても良い位置を示す。 - ただしwbrの前後は同一の単語とはみなされない - このためセマンティクス上は長い単語を分割するために使うことはできない ## bdi bidirectional(双方向)の略で、英語とヘブライ語が混ざるような双方向テキストにおいて、周囲の方向と独立して存在する区分を示すのに用いられます。 # インタラクティブ(WEBアプリケーション関連)要素 ## details/summary 開閉可能な詳細情報の表示。 summaryには要約を入れます。 ```HTML:HTML5 <details open> <summary>詳細情報</summary> <p>ここに詳細情報が入ります。</p> </details> ``` 表示例: <details open> <summary>詳細情報</summary> <p>ここに詳細情報が入ります。</p> </details> # template 再利用を前提とした断片。 デフォルトではレンダリングされず、Scriptから利用する。 # dialog >ダイアログボックス、インスペクタ、またはウィンドウといったユーザーがタスクを実行すると相互作用するアプリケーションの一部を示す。 *via [dialog要素:HTML5.1各要素別メモ](http://mtrootyy.web5.jp/htmllint/html5memo/dialog.html)* # フォーム関連要素 ## progress 進歩を表します。 ```HTML:HTML5 <progress value='70' max='100'> 70% <!-- フォールバック --> </progress> ``` 表示例: <progress value='70' max='100'> 70% <!-- フォールバック --> </progress> ## meter 限定された範囲における値を表す。 low/high属性を指定した範囲ごとで領域を分割することができる。 - low 低領域 - hight 高領域 - optimum 最適値 ```HTML:HTML5 <meter value='10' min='0' max='100' low='30' high='70' optimum='100'> 4/5 <!-- フォールバック --> </meter> ``` 表示例: value='10' <meter value='10' min='0' max='100' low='30' high='70' optimum='100'> 4/5 <!-- フォールバック --> </meter> 表示例: value='50' <meter value='50' min='0' max='100' low='30' high='70' optimum='100'> 4/5 <!-- フォールバック --> </meter> 表示例: value='90' <meter value='90' min='0' max='100' low='30' high='70' optimum='100'> 4/5 <!-- フォールバック --> 表示例: value='100' <meter value='100' min='0' max='100' low='30' high='70' optimum='100'> 4/5 <!-- フォールバック --> </meter> ## output フォームの計算結果を表す。 valueの値を直接出力する特性を持つ。 ``` <form onsubmit="return false" oninput="o.value = a.valueAsNumber + b.valueAsNumber"> <input name=a type=number step=any value=5> + <input name=b type=number step=any value=3> = <output name=o>8</output> </form> ``` 表示例: <form onsubmit="return false" oninput="o.value = a.valueAsNumber + b.valueAsNumber"> <input name=a type=number step=any value=5> + <input name=b type=number step=any value=3> = <output name=o>8</output> </form> ## keygen フォームデータのセキュリティ強化に用いるための鍵ペアを作成する。 秘密鍵はブラウザ側に保存され、公開鍵はサーバに送られます。 ``` <keygen name="key" challenge="123456789"> ``` 表示例: <keygen name="key" challenge="123456789"> ## datalist optionと組み合わせて入力項目の選択肢を定義します。 ``` <label> 好きなスポーツは? <input type="text" name="fav_sports" list="sports_list"> <datalist id="sports_list"> <option value="サッカー"> <option value="野球"> <option value="ゴルフ"> </datalist> </label> ``` 表示例: <label> 好きなスポーツは? <input type="text" name="fav_sports" list="sports_list"> <datalist id="sports_list"> <option value="サッカー"> <option value="野球"> <option value="ゴルフ"> </datalist> </label> # 参考URL [HTML 要素 - HTML5タグリファレンス - HTML5.JP](http://www.html5.jp/tag/elements/index.html) [HTML5.1各要素別メモ](http://mtrootyy.web5.jp/htmllint/html5memo/0mokuji.html) [HTML5プロフェッショナル認定試験 無料セミナー - Qiita](http://qiita.com/budougumi0617/items/5be51d12411df79efdff) |
|
| 385位 |
|
|||
|
06:39:21 |
(シナプス株式会社 所属) |
|
# コメント欄の議論に関するまとめ
以下,**XSS脆弱性が存在しない**前提.この脆弱性があるとあらゆるCSRF対策がほとんど意味をなさなくなるので,まずここから潰しておくこと. ## セッション固定攻撃に対する対策 - ログイン後に`session_regenerate_id`を必ず実行する. - ログアウト後に`session_destroy`を必ず実行する. ## CSRF攻撃に対する対策 - セッションIDを抜かれることは原則的には無いが,セッションIDをそのままトークンに用いるのは避けたほうが無難. - ワンタイムトークンにはF5リロードでの誤作動を防止する意味合いもあるが,必ずしもワンタイム性が求められるわけではない.固定トークンでも十分なセキュリティは保証される. - トークンによる対策が施されていない場合,GETであってもPOSTであってもCSRFは実行可能なので,それは議論の対象ではない. # 実装 以前はワンタイムトークン推奨にしていましたが,意向が変わってきたので固定トークンのサンプルに差し替えておきます. ```php:クラス定義 class CsrfValidator { const HASH_ALGO = 'sha256'; public static function generate() { if (session_status() === PHP_SESSION_NONE) { throw new \BadMethodCallException('Session is not active.'); } return hash(self::HASH_ALGO, session_id()); } public static function validate($token, $throw = false) { $success = self::generate() === $token; if (!$success && $throw) { throw new \RuntimeException('CSRF validation failed.', 400); } return $success; } } ``` # 使い方 ```html+php:フォームに埋め込むとき <input type="hidden" name="token" value="<?=CsrfValidator::generate()?>"> ``` ```php:検証するとき(返り値チェックタイプ) <?php if (!CsrfValidator::validate(filter_input(INPUT_POST, 'token'))) { header('Content-Type: text/plain; charset=UTF-8', true, 400); die('CSRF validation failed.'); } // 続きの処理 ?> <!DOCTYPE html> ... ``` ```php:検証するとき(例外タイプ) <?php try { CsrfValidator::validate(filter_input(INPUT_POST, 'token'), true); // 続きの処理… } catch (\RuntimeException $e) { header('Content-Type: text/plain; charset=UTF-8', true, $e->getCode() ?: 500); die($e->getMessage()); } ?> <!DOCTYPE html> ... ``` |
|
| 386位 |
|
|||
|
00:19:07 |
(フリーランス 所属) |
|
結論を先に書くと、app/assets/javascripts/application.js.erb を修正します。
```js:application.js.erb //= require jquery //= require jquery_ujs //= require_tree . ``` こんな風になってるはず。 どのページでも共通に読み込みたい JavaScript が以下の二つだった場合。 app/assets/javascripts/hogehoge/fugafuga.js app/assets/javascripts/areare/korekore.js.erb `//= require_tree . ` を削除してこんな風に修正します。 ```js:application.js.erb //= require jquery //= require jquery_ujs //= require hogehoge/fugafuga //= require areare/korekore ``` **でもちょっと待った。** この状態では、app/assets/javascripts/ 配下に置かれているJSファイル群はAssets Pipeline の対象外になってしまいます。 application.js の一部としてまとめられることはないものの、ダイジェスト化(ファイル名がMD5ハッシュ付きになるアレ)なども行われなくなるので、できれば以下の行を追加しましょう。 ```rb:production.rb config.assets.precompile += ['*.js'] ``` JSファイル全部じゃなくて特定のファイルだけAssets Pipelineの対象にしたい場合は、このように書きます。 ```rb:production.rb config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js'] ``` 特定のディレクトリだけとかだとこうなるみたい(未検証)。 ```rb:production.rb config.assets.precompile += ['directory/*.js'] ``` あとは、必要に応じて View で呼ぶとよいでしょう。 その際は、`javascript_include_tag` を使います。 例えば以下のようなJSファイルを View で読み込みたい場合。 app/assets/javascripts/kokohadoko/watashihadare.js.erb ```rb: <%= javascript_include_tag 'kokohadoko/watashihadare' %> ``` ってのを書けばいいです。MD5ハッシュ付きのアレなJSファイルをちゃんと読み込んでくれます。 細かいことを言うと、位置はerb ファイルの中でもなるべく下の方に書いてください。 CSSでも↑をまるっと読み替えてもらえば対応できます。 JSとCSS両方って場合のproduction.rbはこんな感じ。 ```rb:production.rb config.assets.precompile = ['*.js','*.css'] ``` stylesheet_link_tag を使って個別のCSSを読み込むことになりますが、JSと違って位置は上の方にしてください。 SASSを使っている場合、AssetPipelineのコンパイル順の関係でエラーが発生することがあります(コメント参照)。 --------------------------------------- 某所で質問に上がっていたので、もしかして需要があるのかな、と記事にしてみました。 app/assets/javascripts 配下にファイルを置くとひとつにまとまるのは Rails3.1 で導入された Assets Pipeline の既定の動作ですが、単にテンプレートがそうなっているだけの話です。 上で書いたように application.js.erb が配下のJSファイルをすべて読み込んでひとつにする記述になっているのと、 `app/views/layouts/application.html.erb` に以下の記述があるってのと。 ```rb:app/views/layouts/application.html.erb <%= javascript_include_tag 'application' %> ``` Assets Pipeline は何気にいろいろ難しいですね。 私も必要に応じてかいつまんで調べているので、間違いなどあったら指摘してください。 manifest ファイルがうんたらとか言い出したらちょっと深みにはまってる気がしないでもないですが、静的ファイルを効率よく提供できるかどうかはサイトのパフォーマンスに結構影響するんですよね。。。 Assets Pipeline。 開発者に負担を強いることなくページの読み込み速度を速くするためのいい仕組みなのは間違いないのですが、やはり何をどうしているのか知っておいた方がよさそうです。 YSLOW などを動かしながら計測すると分かりますが、1ページで読み込むファイル数が多いと表示速度は遅くなりますし、ブラウザのキャッシュの有効利用を考えた場合、あまり細かくページごとの JS ファイル読み込みにこだわるのも却ってパフォーマンスダウンにつながります。 ほんとに一部のページでしか使われていないとか、管理ページで使っている JS ファイルなので一般ページではちょっと・・・とかじゃなければ、素直に application.js にいっしょくたでいい気もしています。 --------------------------------------- `config.assets.precompile` の設定変更なども、これをやると確実に rake assets:precompile の処理にかな~りの時間がかかるようになります。デフォルトでは application.js.erb と application.css.erb が対象だったのが、すべてに変わるのである意味当然です。 heroku を使っている場合(かつ、manifest ファイルを事前に用意していない場合)は deploy がそれ以前に比べてかなり遅くなると思います。まあ、日本で heroku 使うなら assets_sync も使うだろうし、不可避というか必然ではあるんですけどね。 |
|
| 387位 |
|
|||
|
01:02:52 |
|
|
よく使うものを逆引きできるように。 ## アンチパターンを知る http://www.slideshare.net/JulianDunn/beginner-chef-antipatterns http://www.creationline.com/lab/3080 1. すべてのChefデータを1つの巨大なGitレポジトリに入れてしまう * cookbooksにはバージョンがあるが、environmentsやrolesにはない * cookbooksとして分割すべきものはリポジトリを分けるべき 1. 会社名つきの巨大なCookbookを作ってしまう * 本来組み合わせるべきでないものが混ざる危険がある * プロジェクト別に分けるべき 1. Environmentsを論理的な環境以上の目的で使ってしまう * developmentとかproductionとか論理的な環境で使い、クラスタとかデータセンターといった割り当てに使わない 1. Community Cookbookをフォークしてしまう * 開発元のバグ修正や機能追加といった恩恵を受け取れない、なるべくラップする方が良い 1. Role内でrun_listを管理してしまう * roleはバージョンが付かない、代わりに ``include_recipe`` を使いrecipeでまとめる 1. 無秩序なdata bagを作ってしまう * data bagとdata bag itemという2階層なので作る前に計画しておく 1. chef-shellを知らない、使わない * Cookbook開発、デバッグに適している 1. LWRPを怖がってしまう * 言うほどLWRPの実装は難しくない 1. 外部発祥だから利用しない症候群に陥ってしまう * まず使えるものがあるか探し、変更が必要ならコントリビュートする 1. 孤独なChef使いになってしまう * あるセットアップに自分より詳しいメンバーがいるなら任せ、チームで責任を持つ ## レシピをチェックする http://acrmp.github.io/foodcritic/ ```bash gem i foodcritic foodcritic -C . ``` [FC001](http://acrmp.github.io/foodcritic/#FC001)など警告の内容についてはサイトに載っている。 ### FC001以外をチェックする ``FC001``はdeprecatedになった。``FC001``を除外して検証するには以下のようにする。 ```bash foodcritic -t ~FC001 site-cookbooks ``` ## ログを出す [log](http://docs.opscode.com/resource_log.html)リソースを使う。でもレシピの一時的なデバッグならrubyの``puts``や``p``でもいいかもしれない。 ```ruby log "a debug string" do level :debug end log "message" do message "This is the message that will be added to the log." level :info end ``` ## Vagrantのプロビジョニングでログレベルを変更する ``log_level``属性を使う。 http://docs.vagrantup.com/v2/provisioning/chef_common.html ```ruby config.vm.provision :chef_solo do |chef| chef.log_level = :debug ``` ## nodeの属性を出力する とりあえずjsonで ```ruby puts JSON.pretty_generate(node) ``` またはレシピ内で使いそうなものだけ確認。 ```ruby [:platform, :platform_version, :ipaddress, :fqdn, :hostname, :domain].each do |key| puts "node['#{key}'] = #{node[key]}" end ``` ## 置き換わったファイルのバックアップを見る ``template``リソースなどでファイルを置き換えた場合に、元のファイルはリモートサーバの ``/var/chef/backup/`` に保存されている。 ## パスワードのハッシュを生成する Unixではパスワードを ``$<ハッシュ方式>$<salt>$<ハッシュ後のパスワード>`` で表現している。以下のコマンドでそれに対応したMD5のhashを出力できる。 ```bash openssl passwd -1 'password' ``` ## Environmentで分岐する ``node.chef_environment`` を見る ```ruby if node.chef_environment == 'development' end ``` ## OSで分岐する ### OS ```ruby case node["platform"] when "debian", "ubuntu" # do debian/ubuntu things when "redhat", "centos", "fedora" # do redhat/centos/fedora things end ``` ### OSカテゴリ xx系で分岐する場合。yumかaptでpackage名が変わったりパスが変わったりするときなどで使う。 ```ruby case node["platform_family"] when "debian" # do things on debian-ish platforms (debian, ubuntu, linuxmint) when "rhel" # do things on RHEL platforms (redhat, centos, scientific, etc) end ``` ## テンポラリファイルのパスを組み立てる foodcritic の [FC013](http://acrmp.github.io/foodcritic/#FC013)で ``/tmp`` ではなく ``Chef::Config[:file_cache_path]`` を使えとある。 ```ruby remote_file "#{Chef::Config[:file_cache_path]}/large-file.tar.gz" do source "http://www.example.org/large-file.tar.gz" end ``` ## 事前に用意した静的ファイルを配置する [cookbook_file](http://docs.opscode.com/resource_cookbook_file.html)リソースを使う。 デフォルトではファイルは上書きになる。上書きが困る場合は ``action: create_if_missing`` や ``not_if {File.exist?}`` などを使う。 ```ruby # files/default/testfile cookbook_file "/#{Chef::Config[:file_cache_path]}/testfile" do source "testfile" mode 00644 owner 'root' group 'root' end ``` 配置する元ファイルは以下の順番で ``files`` のサブディレクトリから検索される。 1. ホスト名 ``host-#{node[:fqdn]}`` 1. OS-フルバージョン ``#{node[:platform]}-#{node[:platform_version]}`` 1. OS-メジャーバージョン ``#{node[:platform]}-#{version_components}`` 1. OS ``node[:platform]`` 1. ``default`` ``` files/ host-foo.example.com ubuntu-10.04 ubuntu-10 ubuntu default ``` ## テンプレートを使ってファイルを配置する [template](http://docs.opscode.com/resource_template.html)リソースを使う。 [cookbook_file](http://docs.opscode.com/resource_cookbook_file.html)リソースとの違いは用意できるファイルがerbテンプレートであり動的に出力を変えられるところ。テンプレートのデフォルトのコンテキストは``node``オブジェクトで、``variables``属性で任意のhashをテンプレートに渡すことも可能。 ```ruby template "/etc/apache2/ports.conf" do source 'apache/ports.conf.erb' mode '0644' end ``` ソースのテンプレートファイルは ``files`` ディレクトリではなく、``templates`` ディレクトリに配置する。 ``` templates/ host-foo.example.com ubuntu-10.04 ubuntu-10 ubuntu default ``` ## 書き換えてしまったファイルの前のバージョンを見る [cookbook_file](http://docs.opscode.com/resource_cookbook_file.html)リソースや[template](http://docs.opscode.com/resource_template.html)リソースで既存のファイルを上書きしてしまった場合に世代バックアップが``/var/chef/backup`` にある。 ## 空のファイルを作成する [file](http://docs.opscode.com/resource_file.html)リソースの``: create_if_missing``アクションを使う。(ファイルが無い時限定) ```ruby file "/tmp/something" do action :create_if_missing end ``` ## ファイルを削除する [file](http://docs.opscode.com/resource_file.html)リソースを使う。 ```ruby file "/tmp/something" do action :delete end ``` 削除対象がシンボリックリンクの場合は警告がでるので、[link](http://docs.opscode.com/resource_link.html)リソースを使うのがエレガント。 ```ruby link "/tmp/mylink" do action :delete only_if "test -L /tmp/mylink" end ``` ## ディレクトリを作成する [directory](http://docs.opscode.com/resource_directory.html)リソースを使う。 ```ruby directory "/var/lib/foo" do owner "root" group "root" mode 00755 action :create end ``` 親ディレクトリが存在しないと失敗する。``recursive true``をつけると親ディレクトリを再帰的に作成する。 ## シンボリックリンクを作成する [link](http://docs.opscode.com/resource_link.html)リソースを使う。 ```ruby link "/tmp/passwd" do to "/etc/passwd" end link "/tmp/passwd" do to "/etc/passwd" link_type :hard end link "/tmp/mylink" do action :delete only_if "test -L /tmp/mylink" end ``` ## ファイルがなければリソースを実行する ``not_if``でファイルをチェックする。 ```ruby execute "create file hoge" do command "touch /tmp/hoge" not_if {File.exist?("/tmp/hoge")} end ``` ``Dir.exist?`` も使える(rubyの世界) ## コマンドを実行する [execute](http://docs.getchef.com/resource_execute.html)リソースを使う。 ```ruby execute 'bundle install' do cwd '/myapp' not_if 'bundle check' # This is not run inside /myapp end ``` ## シェルファイルを実行する 正確には以下の例は[bash](http://docs.opscode.com/resource_bash.html)リソースからコマンドを実行。 ```ruby bash "install_ruby_build" do cwd "#{Chef::Config[:file_cache_path]}/ruby-build" user "rbenv" group "rbenv" code <<-EOH ./install.sh EOH environment 'PREFIX' => "/usr/local" end ``` ## Gitリポジトリからファイルを取得する [git](http://docs.opscode.com/resource_git.html)リソースを使う。``revision``属性にタグなどを指定できる。 ```ruby git "/home/#{node[:user]}/redmine" do repository "git://github.com/edavis10/redmine.git" revision '2.5.1' user node[:user] group node[:user] end ``` ## ウェブにあるアーカイブファイルを展開する [remote_file](http://docs.opscode.com/resource_remote_file.html)リソースでダウンロード。[bash](http://docs.opscode.com/resource_bash.html)リソースで展開。 ```ruby # the following code sample is similar to the ``upload_progress_module`` recipe in the ``nginx`` cookbook: https://github.com/opscode-cookbooks/nginx src_filename = "foo123-nginx-module-v#{node['nginx']['foo123']['version']}.tar.gz" src_filepath = "#{Chef::Config['file_cache_path']}/#{src_filename}" extract_path = "#{Chef::Config['file_cache_path']}/nginx_foo123_module/#{node['nginx']['foo123']['checksum']}" remote_file src_filepath do source node['nginx']['foo123']['url'] checksum node['nginx']['foo123']['checksum'] owner 'root' group 'root' mode 00644 end bash 'extract_module' do cwd ::File.dirname(src_filepath) code <<-EOH mkdir -p #{extract_path} tar xzf #{src_filename} -C #{extract_path} mv #{extract_path}/*/* #{extract_path}/ EOH not_if { ::File.exist?(extract_path) } end ``` ## hostsファイルにエントリを追加する ``hostsfile`` cookbook を追加し、[hostsfile_entry](https://github.com/customink-webops/hostsfile) リソースを使う。 ```ruby hostsfile_entry '1.2.3.4' do hostname 'www2.example.com' aliases ['foo.com', 'foobar.com'] comment 'Append by Recipe X' action :append end ``` ## ファイルを置き換えたのでサービスを再起動する [Notifications](http://docs.getchef.com/resource_common.html#notifications)を使う。 ``` template '/etc/httpd/conf/httpd.conf' do source 'httpd/httpd.conf.erb' notifies :restart, 'service[httpd]' end service 'httpd' do action [ :enable ] end ``` ``notifies``で指定するリソース名が存在しないといけないので [service](http://docs.getchef.com/resource_service.html)リソースで ``:enable`` や ``:nothing``などのActionで記述しておく。 ## Ohaiで取得して使える属性 |属性|説明| |:--|:--| |node['platform']|nodeのプラットフォーム| |node['platform_version']|プラットフォームのバージョン| |node['ipaddress']|IPアドレス。nodeがデフォルトルートを持っている場合はIPv4アドレス、もっていない場合はnil| |node['macaddress']|MACアドレス| |node['fqdn']|完全修飾ドメイン名| |node['hostname']|ホスト名| |node['domain']|ドメイン| |node['recipes']|関連付けられたレシピのリスト| |node['roles']|関連付けられたroleのリスト| |node['ohai_time']|Ohaiが最後に実行された時刻。knife statusサブコマンドでサーバに保存し参照できる| |
|
| 388位 |
|
|||
|
10:06:21 |
(Increments inc. 所属) |
|
[jQuery.textcomplete](https://github.com/yuku-t/jquery-textcomplete)([デモ](http://yuku-t.com/jquery-textcomplete))
 [GitHubのようなtextareaの補完機能を実装する - カーソル位置の取得](http://qiita.com/yuku_t/items/fb92e173120d7b2e49ed) を書いたのも今は昔、いつか続きを書こう書こうと思いながら気がつけば5ヶ月が過ぎました :bow: なんか続きを書くのが面倒くさくなったのと、某日本最大レシピ共有サイトの技術部長の人から「OSSにして欲しい」という要請を人伝に受け取ったこともあって、OSS化した次第です。 # ライセンス MITライセンス # 簡単な使い方 簡単に説明します。詳しくは [README](https://github.com/yuku-t/jquery-textcomplete/blob/gh-pages/README.md) を読んでください。 まず jQuery.textcomplete は名前からも分かるように jQuery プラグインになっているので、別途 jQuery が必要です。 ```html <script src="path/to/jquery.js"></script> <script src="path/to/jquery.textcomplete.js"></script> ``` TEXTAREA 要素の jQuery オブジェクトに対して `textcomplete` メソッドを実行します。 ```js $('textarea').textcomplete({ // mentionは単なる名前で意味はありません。 // 分かりやすい名前をつけてください。 mention: { // 必須設定 match: /(^|\s)@(\w*)$/, search: function (term, callback) { // callback には文字列の配列を渡す $.getJSON('/search', { q: term }) .done(function (resp) { callback(resp); }) .fail(function () { callback([]); }); }, replace: function (value) { return '$1@' + value + ' '; }, // 任意設定(下記はいずれもデフォルト値) index: 2, maxCount: 10, template: function (value) { return value; } }, // 複数設定することもできます emoji: { … } }); ``` `match` は各 Keyup イベントで TEXTAREA の先頭からカーソル位置までの文字列に対して適応される正規表現を指定します。この正規表現がマッチした場合、 `index` 番目のグループが `search` の第一引数 `term` として渡されます。 `search` では与えられた `term` に対応する文字列の配列を作って `callback` を実行します。この例では `jQuery.getJSON` を使ってリモートからデータを取ってきています。今回の例のように `search` の中に非同期処理が入っていたとしても、 `search` は同時に1回しか実行されないことが保証されます。また配列に100個の要素が入っていたとしても先頭から `maxCount` 個だけが表示されます。 `callback` が実行されると与えられた配列の個々の要素に対して `template` が実行されます。この関数の返り値が補完の各要素になります。上の方に貼り付けられた gif の場合、補完メニューに画像が埋め込まれていますが、これを実現するには例えば下記のようにします。 ```js template: function (value) { return '<img src="path/to/' + value + '.png"/>' + value; } ``` 最後に補完が確定したときに `replace` に選択された値が与えられて実行されます。この関数の返り値は次のように利用されるので、サンプルを参考に、条件を満たすように指定してください。String.replaceの詳しい仕様については [String.replace - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/replace) を参照してください。 ```js textarea.value = textarea.value.replace(matchRegExp, replaceFunc(value)); ``` # デザイン 出力される補完メニューはBootstrapのDropdownと同じHTML構造をしています。なので、Bootstrapを使っている環境では特に何もしなくてもいい感じにスタイルが勝手に適応されます。 Bootstrapを使っていない場合は、BootstrapのDropdownを参考によしなにスタイルを書いてください。リポジトリに jquery.textcomplete.css というファイルがあるので、それを土台に使うといいかも知れません。 # 今後実装される予定の機能 - あいまい検索機能 各利用者が `search` 関数を実装しなければいけませんが、その中で使えるあいまい検索用の組み込み関数を用意しようと思っています。 ```js search: function (term, callback) { var data = this.fuzzySearch(term, /* データ配列 */); callback(data); } ```` こんな感じで書くと勝手にいい具合に検索してくれる、みたいなイメージです。 |
|
| 389位 |
|
|||
|
01:52:37 |
|
|
## .gitignoreとは?
`.gitignore`はGitのリポジトリのルート(.gitフォルダと同じ位置)にあるときに使えます。 ## Railsの場合 Railsアプリとかだと、logディレクトリはバージョン管理したくないから ``` .gitignore log/ ``` とか書いてある。 同じように、バージョン管理の必要性がなかったり、していると不都合がおきる、 - `sqlite3`のDBファイル - キャッシュが入る`tmp`フォルダ - プロセス監視の`pid`ファイル 等はignoreしておくべきです。(デフォルトで.gitignoreに書かれていますが) ## .gitignore自体はignoreにするべき? それを ignore するなんて とんでもない! .gitignoreはプロジェクト全体で共有しておくと、環境が変わっても(パソコンが変わっても)ignoreされるファイルやフォルダは 一緒な"はず" なので、ignoreはしなくて大丈夫です。 ## 一緒な"はず" ? 環境が変わるってことはもしかしたら他人が使うこともあるのです。 たとえば、僕みたいにEmacsとVimとSublimeを併用するときには、同じ人でありながら違う環境でコードを書くことさえあります。一般的にあまりないですが。 ## 環境が違うとは 例えば使っているエディタが違うときがありますね。 これは、Vimにおける`*.swp`ファイルやEmacsにおける`*~`ファイルが該当しますね。 こういうのはもちろん ignore しておくべきですが、 プロジェクト毎、リポジトリ前に `.gitignore`へ ``` .gitignore *~ *.swp ``` と書くのは面倒ですしおすし、 さらにプロジェクトメンバーにEmacs使いとVim使いが共存している中で、共通で見ることができる `.gitignore` に`*.swp`や`*~`などを記述してそれぞれのエディタの痕跡が残ってしまうと戦争になりかねません。 ## 毎回、同じファイルを ignore するのは面倒、でも戦争も起こしたくはない 基本的に、DRY(Don't repeat yourself)という考え方で世の中は回っています。 つまり同じ事は一回だけにしようってことですね。GitでもDRYな `.gitignore` の書き方ができます。 まずはホームフォルダに`.gitignore`と`.gitconfig`ファイルをつくります。 それぞれ中身は ``` .gitignore .DS_Store *~ *.swp ``` こんな感じで書いて ``` .gitconfig [core] excludesfile = ~/.gitignore ``` と書いておきます。 すると、Gitさんは基本的にホームフォルダにある`.gitconfig`を見に行くので。 1. `excludefile`で指定された.gitignoreをみる 2. リポジトリのルートにある.gitignoreをみる という順序で見ていくので、 自分の環境に依存して、かつGitではバージョン管理をしたくない時にはホームフォルダの`.gitignore`にファイルを記述して、 プロジェクトでバージョン管理したくないファイルをリポジトリ内にある`.gitignore`に記述してメンバー全員で共有する というのがベストプラクティではないでしょうか。 ## あわせて読みたい [Gitの設定](http://qiita.com/items/f86740dacb909a244b14 "gitの設定") |
|
| 390位 |
|
|||
|
22:55:38 |
(Akatsuki Inc. 所属) |
|
## 3行まとめ - Twilioなら、自分だけの電話番号を簡単取得。 - SMS受信のタイミングで任意のAPIをキックしてくれます。送信者の情報も取得できるので、それを用いて認証機能を実現。 - コストは1通(1認証)あたり1円!(ただし最初にチャージが必要なので初期投資2000円から) ## 概要 ### SMS認証とは ユーザ登録を行うメディアにおいて、 不正なユーザ(いわゆる複アカなど)の防止策として有効な対策の一つです。 例えばこちら等。 FullCourt https://www.fullcourt.co/ja/docs/samplecode/smsauthentication ざっくり言うと、 1. ユーザは認証画面で電話番号を入力 1. メディアサーバは、SMS認証サービスへSMS送信依頼 1. SMS認証サービスはユーザのスマホ/携帯電話にSMSを送信 1. ユーザはSMSで送られてきた認証トークン(パスワード)を見て、認証画面に入力 1. サーバはユーザからの入力内容と、SMS送信時にDBに保管しておいたトークンを突き合わせ、認証  こんな流れです。 電話番号が取得出来るので、以後の複アカを防ぐこともできます。 導入事例の有名どころだとアメーバピグさん等。 【お知らせ】SMS認証について http://ameblo.jp/pigg-staff/entry-11522530986.html ですが、実際の導入にはなかなかのネックが存在します。 ### 問題は・・ コスト。 1通(1認証)10円前後が相場だと思います。 上記のFullCourtさんだと[8円/日本国内1通](https://www.fullcourt.co/ja/sms)とのことでした。 けっこうなお値段ですが、SMS送信自体の価格を各キャリアが定めているので、 どうしても最低限そこはコストとなってしまいます。 ### 対策 ということで、今回は電話・SMSを気軽に開発者が扱えるサービスTwilioを使って もっと安く仕上げられそうな仕組みを実装してみました。 ## Twilioとは 電話やSMSを扱えるAPIサービス。 http://twilio.kddi-web.com/ シリコンバレーの企業のサービスですが、日本ではKDDIが 代理店になっており、国内でも気軽に扱えます。 ### 価格 料金(Price) http://twilio.kddi-web.com/price/index.html 最初の表の下の方ですね。 送信料はやはり10円弱(Softbankだけ安いですが)。 しかし、<strong>受信料は1円</strong>です。 ということで、<strong>受信のみで成り立つSMS認証</strong>を考えてみたいと思います。 ## 受信型認証のイメージ これでどうでしょう。 1. 認証画面で、ユーザに電話番号とユーザ識別ID、認証トークン(パスワード)を表示 - ユーザはスマホ/携帯電話から、指定の電話番号宛に、ユーザ識別IDと認証トークンをSMS送信 - TwilioサーバはMediaAPIをキック - サーバはTwilioから渡された認証トークンより、ユーザを認証。  2の部分が<strong>受信</strong>になっていますね。これなら<strong>1通(1認証)1円</strong>です! ということで、こちらで実装をすすめていきます。 ## Twilioアカウントの作成と電話番号の取得 まずは、以下記事を参考にアカウントの作成を行います。 Twilio(トゥイリオ)を触ってみた http://www.casleyconsulting.co.jp/blog-engineer/twilio/twilio%EF%BC%88%E3%83%88%E3%82%A5%E3%82%A4%E3%83%AA%E3%82%AA%EF%BC%89%E3%82%92%E8%A7%A6%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F/ 記事内までで、トライアルの電話番号で電話利用ができるようになりますが、 <strong>SMSは利用できません。</strong> 次はSMSを利用できる電話番号を取得します。 ## SMS用電話番号の取得 注:ここからはお金がかかります! (電話番号維持費は月1ドル。ただしはじめに2000円のチャージが必要なため、実質2000円) ※有料アカウントの体系を詳しく知っておきたい方は一度以下に目を通しておくと良いかもしれません。 よくある質問(FAQ)/課金 & 料金 https://twilioforkwc.zendesk.com/forums/22006727-%E8%AA%B2%E9%87%91-%E6%96%99%E9%87%91 それでは行きましょう。 まず、「電話番号」のページから、「電話番号を購入」をクリック。  国は「アメリカ」を選択(※)し、特に何も入力せず「検索」をクリック。  ※現在日本の電話番号ではSMSは使えないようです。 しかも海外の番号の方がずっと安いので、迷わず取得しましょう。 この後、一覧に出てきた番号をクリック(どれでも良いです。)すると、 「アップグレードが必要」と言われます。 そのまま指示に従い、クレカ情報を入力すればokです。 ※途中で自動チャージのON/OFFを選択しますが、とりあえずOFFの方が無難でしょう。 この後、自分はだいぶハマったのですが、 しばらく時間が立たないとアップグレードされず、電話番号を購入できるようになりません。 しかもあるページではアップグレード済みの表示(※)なのに、 電話番号購入ページではトライアルアカウント表示のままになっていたり。 ※グローバルナビ右の登録アドレスの下に、 トライアルアカウントの場合は「トライアル」、 アップグレードした場合はチャージ分の金額が表示されます。 ちなみに自分はブラウザを変えて閲覧したら上手くいきました。 セッション管理等の実装に不具合があるのかもしれません。 ともかく、これでSMS用電話番号が取得できました! ## SMS送信時にキックするAPIを用意する Twilioでは、取得した電話番号にSMSが届いたタイミングでキックするAPIを設定することができます。 取得電話番号のページから簡単に設定できます。  緑の枠内にキックするAPIを設定すればok。 テストしてみましょう。 取得した電話番号宛にSMSを送信します。  キックしたサーバのログを確認すると・・  来ています! たったこれだけで、以下の図で言う2,3の箇所が実現出来てしまいました。  あとは、投げられてくるパラメータを料理して、1,3,4の箇所を実装しましょう。 取得出来るパラメータはこうです。 ```ruby { "AccountSid"=>"hogehoge", "MessageSid"=>"SMbfea2d641027f6f3434bf656c0b55d97", "Body"=>"Hello, Qiita!", "ToZip"=>"60081", "ToCity"=>"FOX LAKE", "FromState"=>"", "ToState"=>"IL", "SmsSid"=>"SMbfea2d641027f6f3434bf656c0b55d97", "To"=>"+18xxxxxxxx", "ToCountry"=>"US", "FromCountry"=>"JP", "SmsMessageSid"=>"SMbfea2d641027f6f3434bf656c0b55d97", "ApiVersion"=>"2010-04-01", "FromCity"=>"", "SmsStatus"=>"received", "NumMedia"=>"0", "From"=>"+8180xxxxxxxx", "FromZip"=>"" } ``` ユーザの送信内容や電話番号などがPOSTパラメータとして取得できる訳ですね。 ```ruby params[:From] # ユーザの電話番号 params[:Body] # ユーザ入力値。ユーザidと認証トークンを入力してもらう。 ``` ※ユーザ入力値(params[:Body])は、今回は ```ruby user_id + '_' + token ``` の形式を指定(認証ページでユーザに依頼)すると都合が良さそうです。 さて、これらがあれば1,3,4は実装できそうですね。 こんなイメージでしょうか。 ```ruby class SmsAuthController < ApplicationController TWILIO_NUMBER = '+81xxxxxxxx' # 電話番号 # 認証ページ(GET) # viewでは@sender_num, @user.sms_token, @user.idを表示 def index @user = User.find(current_user.id) @sender_num = TWILIO_NUMBER @user.sms_token = generate_token @user.save! end # 認証処理(POST) def verify begin params_check;format_params;user_check;token_check; logger.error("valid user!") render :json => { code: 0 } rescue => e logger.error("error: #{e.message}") render :json => { code: 400, message: e.message } return end end private # 認証token生成 def generate_token ((0..9).to_a + ("a".."z").to_a + ("A".."Z").to_a).sample(4).join end # パラメータチェック def params_check raise 'Body is invalid' if params[:Body].nil? raise "Body isn't contain underscore" if !params[:Body].include?("_") raise 'From is invalid' if params[:From].nil? end # パラメータ整形 # params[:Body]は「user_id + '_' + token」の形式を想定 def format_params body = params[:Body].split("_") @user_id = body[0] @token = body[1] @user.mobile_number = params[:From].sub(/^\+81/, '0') raise 'user_id is nil' if @user_id.nil? raise 'token is nil' if @token.nil? raise 'mobile_number is nil' if @user.mobile_number.nil? end # ユーザチェック def user_check @user = User.find_by_id(@user_id) raise 'user is invalid' if @user.nil? end # 認証トークンチェック def token_check raise 'token is invalid' if @user.sms_token == @token @user.verify = true @user.save! end end ``` indexが1、verifyが3,4の工程にあたりますね。  ひとまず、これにて完成です。 Twilioを用いることで、本当に「1認証1円」が実現できました。 これまでコストの面でSMS認証の導入を見送っていた方も、ぜひ検討してみてください! ## 最後に 以下の点をブラッシュアップしていきたいなと思っています。 ・ユーザビリティの検討(ユーザに一定の情報量(ユーザ識別ID/認証token)を入力してもらうことになるため)。 ・電話番号を偽装してSMS送信されることはないか?その他不正行為の検討&対策。 ・ちょっと捻れば、「受信型SMS認証サービス」として第三者へ提供することも・・ 今回は完全に手探りでの検討だったので、その他仕組みの不足点など お気づきの点ありましたら突っ込み頂けますと幸いです! |
|
| 391位 |
|
|||
|
14:31:33 |
(Nepula,Inc. 所属) |
|
新年あけましておめでとうございます。 今年も Xamarin 推しで参ります、よろしくお願いします。 2013年大晦日の紅白歌合戦、NHK が iPhone/Android 用のアプリを配信していました。 * [紅白アプリ|第64回NHK紅白歌合戦](https://play.google.com/store/apps/details?id=jp.or.nhk.kouhaku.android&hl=ja) * [紅白で「イェーガー!」と叫ぶために曲を見逃さないiPhoneアプリ、NHK紅白](http://weekly.ascii.jp/elem/000/000/192/192769/) なんとこのアプリ、Xamarin 製だったとのこと。 紅白あんまり興味なかったのでノーチェックでしたわー。 <blockquote class="twitter-tweet" lang="ja"><p>紅白アプリXamarinなんか</p>— ゆたか (@tmyt) <a href="https://twitter.com/tmyt/statuses/413092620567470080">2013, 12月 17</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script> <blockquote class="twitter-tweet" lang="ja"><p>iPhoneの紅白アプリ、MvvmCross使ってるってことは、Xamarinで作ってるってこと?! <a href="http://t.co/cTWPz2cp9E">pic.twitter.com/cTWPz2cp9E</a></p>— 菊池紘 (@kikuchy) <a href="https://twitter.com/kikuchy/statuses/417977438597959680">2013, 12月 31</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script> このツイートを RT した後、ソッコーで Android 版を入れてみましたら、確かにクレジットに MvvmCross やら ActionBarSherlock for Xamarin やらならんでいました。 ## 開発してるのは… 開発は、[スレイプニル](http://www.fenrir-inc.com/) で有名なフェンリルさんのようですね。 * [フェンリル株式会社 | スマートフォンアプリ開発 実績 NHK 紅白](http://biz.fenrir-inc.com/application_development/casestudy_app/nhk_kouhaku.html) 2年連続で作っておられるようですが、おととしから Mono(MonoTouch/Mono for Android) 製だったのかな?いやスゴいです! ## 使われているライブラリ せっかくなので、Android/iOSアプリ両方の著作権表示から、使われている OSS ライブラリを列挙してみます。([こちらのエントリ](http://nkzn.hatenablog.jp/entry/2013/12/30/010956)にインスパイアされました) 当然ですが、すべて .NET/Mono で動作するライブラリばかりです。(ActionBar と Nimbus を除く) ### MvvmCross * https://github.com/MvvmCross/MvvmCross * クロスプラットフォームMVVMフレームワーク。[こちらでも](http://qiita.com/amay077/items/c4227663b5a5e540dc13) 紹介しました ### Json.NET * http://james.newtonking.com/json * .NET/Mono で JSON を扱うための事実上標準ライブラリ ### SocketIO4Net.Client * http://socketio4net.codeplex.com/ * WebSocket4Net と組み合わせて使うっぽい?ライブラリ ### WebSocket4Net * http://websocket4net.codeplex.com/ * .NET で WebSocket 使うためのライブラリ。 ### SuperSocket.ClientEngine * http://clientengine.codeplex.com/ * ソケット通信用ライブラリっぽい。 ### MvxSettings * https://github.com/jamesmontemagno/Mvx.Plugins.Settings かな? * 設定情報をストアするための、MvvmCross のプラグイン ### ActionBarSherlock for Xamarin * http://components.xamarin.com/view/XamarinActionBarSherlock * スライドメニュー(NavigationDrawer) を実現するライブラリ。そういえばちょっと変わったスライドメニューでしたね。 ### AsyncOAuth * http://neue.cc/2013/02/27_398.html * C#/LINQ の神であらせられる [@neuecc](http://neue.cc/2013/02/27_398.html) さん作の 非同期OAuthライブラリ ### Nimbus * http://nimbuskit.info/ * iOS の UIパーツがいろいろ拡張されてる的なライブラリ?Xamarin.iOS で Binding して使ってるのかなあ? ### Html Agility Pack * http://htmlagilitypack.codeplex.com/ * HTMLパーサライブラリ 生放送のテレビ番組向けアプリということで、リアルタイム通信に注力された様子が、使用されたと思われるライブラリからも伺えます。 ## これは強力すぎる Xamarin 導入事例ですね 日本の最も有名なテレビ番組のスマホアプリに Xamarin が使われていたというのは大きな導入事例になること必至です。 アプリの性質上、期間限定となる可能性もあります。できればこのまま公開しつづけて欲しいですが、Xamarin を上司や提案先に紹介されたい場合は、お早めに、またキャプチャを多く撮っておかれる事をおすすめします。 最後に、今年が Xamarin 普及元年とならん事を近くの神社にお祈りして、新年最初のエントリの締めとします。 |
|
| 392位 |
|
|||
|
02:06:10 |
(Cryptocurrencies 所属) |
|
node.js(0.10.x)はepelを使えるようにしておけばyumで簡単にインストールできる
#リポジトリ追加 ```bash yum install epel-release ``` #yumでインストール ```bash yum install nodejs npm --enablerepo=epel ``` #c++を入れる npmで使うパッケージもあるのでc++を入れる ```bash yum install gcc gcc-c++ ``` # centos7へのインストールはこちら http://qiita.com/you21979@github/items/4c9c382b9536effc590d |
|
| 393位 |
|
|||
|
15:44:28 |
|
|
# はじめに iOSアプリ開発でInterfaceBuilderやStoryboardを使うか使わないかというのは宗教論争の火種の一つとなっていますが、自分の場合なるべく使いたくない派です。 画面回転やiPhone/iPadユニバーサル対応、そしてiOS7のステータスバー周りの仕様変更など、やり方を調べるよりささっとコードで調整できる、そして現状アプリがどういう仕様になっているかが一目瞭然なのはコードの方ではないかと思います。 コードでレイアウト調整をする場合、UIViewのサブクラスをViewController毎に用意し、その中でlayoutSubviewsをオーバーライドするとわかりやすく書くことができます。 # ViewControllerでレイアウトする良くない例 自分は3年間くらいこのやり方でやっていたのですが(恥ずかしながらlayoutSubviewsを知らなくてもiOSアプリは書けちゃうものです…) 経験上ViewControllerに対するViewクラスは面倒臭がらずに定義した方がいいということがわかりました。 ViewControllerでViewの制御、回転時の対応や全画面表示、アニメーション等いろいろやっていくと、どんどん複雑で読みにくいコードになってしまいます。 ```objc:HogeViewController.m -(void) loadView { UIView *view = [[UIView alloc] init]; _subView = [[UIView alloc] init]; // サブビューのレイアウト調整 _subView.frame = CGRectMake(50,50,50,50); [view addSubview:_subView]; self.view = view; } // 画面回転時 -(void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; // こんなことしてたら後々大変なことに... _subView.frame = CGRectMake(...); } ``` # ViewControllerからViewのコードを切り離すべし ViewControllerは理想的には極限まで薄くするのがいいと思います。 ビジネスロジックはModelに書く、汎用処理はヘルパークラスや関数にする、DataSourceは切り離す、などいろいろありますが、Viewも別クラスに切り離しましょう。 HogeViewControllerにはHogeViewを対応させ、ViewControllerがViewのインスタンスを持つようにします。 そしてViewControllerでサブビューのframeをいじっていた部分を、ViewのlayoutSubviewsに移動します。 ```objc:HogeViewController.m @implementation HogeViewController -(id) init { if (self = [super init]) { // ViewControllerに対応するViewを保持 _hogeView = [[HogeView alloc] init]; } return self; } -(void) loadView { self.view = _hogeView; } @end ``` ```objc:HogeView.m @implementation HogeView -(id) init { if (self = [super init]) { _subView = [[UIView alloc] init]; } return self; } -(void) layoutSubviews { [super layoutSubviews]; // レイアウト調整はここでやる _subView.frame = CGRectMake(50,50,50,50); } @end ``` イベントハンドラの繋ぎ部分をどうするかは好みがわかれるかと思いますが、自分の場合はイベント定義が必要なコンポーネントはパブリックプロパティにしてしまって、ViewControllerから直接イベント登録することが多いです。 # layoutSubviewが呼ばれるタイミング ViewをaddSubviewした時、Viewのframeを変更した時(親ビューのlayoutSubviews経由、画面回転時など)に呼ばれます。 大抵意識することなく必要な時に呼ばれるますが、任意のタイミングでsetNeedsLayoutを呼べば手動で実行できます。 Viewに動的にサブビューを追加する場合などはサブビューをaddSubviewした後にViewのsetNeedsLayoutを呼びます。 ```objc:HogeView.m -(void) setContentsView:(UIView*)contentsView { [_bodyView removeFromSuperview]; _contentsView = contentsView; [self addSubview:contentsView]; // layoutSubviewsを明示実行 [self setNeedsLayout]; } ``` # 上下左右中央の寄せ サブビューのレイアウトを親ビューのサイズを使って相対的に定義することで、画面回転による縦横にもスムーズに対応することができます。 ```objc: -(void) layoutSubviews { [super layoutSubviews]; float width = self.bounds.size.width; float height = self.bounds.size.height; // 間隔(padding)を空ける float pad = 10; // 左上 _leftTopView.frame = CGRectMake(pad, pad, 100, 100); // 中上 _centerTopView.frame = CGRectMake((width - 100) / 2, pad, 100, 100); // 右上 _rightTopView.frame = CGRectMake(width - 100 - pad, pad, 100, 100); // 左中 _leftMiddleView.frame = CGRectMake(pad, (height - 100) / 2, 100, 100); // 中中 _centerMiddleView.frame = CGRectMake((width - 100) / 2, (height - 100) / 2, 100, 100); // 右中 _rightMiddleView.frame = CGRectMake(width - 100 - pad, (height - 100) / 2, 100, 100); // 左下 _leftBottomView.frame = CGRectMake(pad, height - 100 - pad, 100, 100); // 中下 _centerBottomView.frame = CGRectMake((width - 100) / 2, height - 100 - pad, 100, 100); // 右下 _rightBottomView.frame = CGRectMake(width - 100 - pad, height - 100 - pad, 100, 100); } ``` # よくあるヘッダー/コンテンツ/フッターというレイアウト 上にナビゲーションバー等のヘッダー部分があって、下にタブバー等のフッター部分があって、残りはコンテンツ、というよくあるレイアウトは以下のように書けます。 ```objc: -(void) layoutSubviews { [super layoutSubviews]; float width = self.bounds.size.width; float height = self.bounds.size.height; // 固定の高さのヘッダー(UINavigationBar等) _headerView.frame = CGRectMake(0, 0, width, 44); // 固定の高さのフッター(UITabBar,UIToolbar等) _footerView.frame = CGRectMake(height - 44, 0, width, 44); // 可変の高さのコンテンツ(UITableView,UIScrollView等) _contentView.frame = CGRectMake( _headerView.bounds.size.height, 0, width, height - _headerView.bounds.size.height - _footerView.bounds.size.height ); } ``` # iPhone/iPadユニバーサル用のレイアウト調整 iPhoneサイズに適したレイアウトとiPadサイズに適したレイアウトは異なるので、分岐が必要になっていきます。IBを使う場合はiPhone用とiPad用のxibファイルを用意したりメンテナンス性に問題がありましたが、layoutSubviewsはシンプルに対応できます。 ```objc:HogeView.m -(void) layoutSubviews { [super layoutSubviews]; float width = self.bounds.size.width; float height = self.bounds.size.height; BOOL isPad = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad); // iPadの場合は高さ300、iPhoneの場合は高さ100 float hogeH = (isPad) ? 300 : 100; _hogeView.frame = CGRectMake(x, y, hogeH, width); } ``` # iOS6以前/iOS7共存のレイアウト調整 iOS7対応でステータスバーの分ビューが上にずれる!とか面倒な自体が発生しても落ち着いて対応することができます。 ```objc:HogeView.m -(void) layoutSubviews { [super layoutSubviews]; float width = self.bounds.size.width; float height = self.bounds.size.height; BOOL isOS7Later = [[UIDevice currentDevice].systemVersion floatValue] >= 7.0f //注: 縦画面専用です float sbarH = [UIApplication sharedApplication].statusBarFrame.size.height; // iOS7でステータスバーに重ならないようにする float navbarY = isOS7Later ? sbarH : 0; self.navbar.frame = CGRectMake(0, navbarY, width, 44); } ``` # 脱autoresizingMask layoutSubviewsでレイアウト調整を行うと決めたら、autoresizingMaskは混乱の元になるので削除する。 単純な例の場合はautoresizingMaskでも十分可能ですが、layoutSubviewsを使う方法もさほどコストはかからない上に柔軟性・拡張性に優れるため、layoutSubviews方式で統一しています。 # そしてAutoLayoutへ… (iOS6以降) 諸事情によりiOS5も面倒を見る必要があるため、今のところAutoLayoutは使っていません。 試しにStoryboardで適当にいじってみましたが何が起こってるのかよくわからない…orz |
|
| 394位 |
|
|||
|
05:11:18 |
(Consensus Base Inc. 所属) |
|
## 前提知識 Gitは、分散レポジトリ だから 「どのレポジトリ」の「どのブランチ」かを指定しないとわからないよ! ## origin と master - origin: レポジトリの場所(URL)の別名 - master: ブランチの名前 つまり、"git pull origin master" は、originという名前のレポジトリのマスターブランチから、git pull しろと命令している。 ## デフォルト origin と master はデフォルトだよ。 つまり、 "git pull" = "git pull origin master" ## 別名はどこを指してるんだよ? ``` % git config --list ``` 以下みたいにどこを指しているかわかる! ``` remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* remote.origin.url=https://github.com/ユーザ名/レポジトリ名.git ``` ## 俺だって別名つけてーよ GitHub のレポジトリに名前つけちゃおう。 > git remote add 別名 https://github.com/ユーザ名/レポジトリ.git 例 > git remote add upstream https://github.com/user/repositry.git あちこちのレポジトリを追加して、あちこちに push すればいいさ。 ## 別のレポジトリに push しちゃうぜ GitHub から持ってきたソースを Bitbucket にpush しちゃおう。 Bitbucketのサイトでレポジトリを新規作成してから ``` git remote add bitbucket https://ユーザ名@bitbucket.org/ユーザ名/レポジトリ名.git ``` で、 git config --list >remote.bitbucket.url=https://ユーザ名@bitbucket.org/ユーザ名/レポジトリ名.git >remote.bitbucket.fetch=+refs/heads/*:refs/remotes/gitbreak/* push する git push はデフォルトでは、同じブランチ名がリモート上にある場合、全部のブランチを push しちゃう? ``` git push -b bitbucket master ``` |
|
| 395位 |
|
|||
|
13:55:19 |
(Increments inc. 所属) |
|
意外と認知度が低い感じですが、Google Analyticsがそろそろ新しくなりますよ!
現在パブリックベータとして提供されているUniversal Analyticsが **予定では** [7月中](http://news.mynavi.jp/articles/2013/04/30/ga1/001.html) に正式リリースになります。つまり今月! 世の中見渡していると非エンジニアなウェブマスター向けの紹介記事が多い印象なので、ここではJavaScriptのインタフェースがどう変わったか、それを使ってエンジニアはどういうことができるようになるか、みたいな、よりエンジニア向けの話題を中心に書いてみたいと思います。 ソースは[公式ドキュメント](https://developers.google.com/analytics/devguides/collection/analyticsjs/)なので、より詳しく知りたい方はそちらを参照してください。 # 簡単なまとめ Universal Analytics(analytics.js)は... - これまでのやつ(ga.js)よりJavaScriptのインタフェースがイケてる - APIは存在するけどWebインタフェースが未実装の機能がまだまだ多い - ソーシャルボタンの効果測定とか、クライアント上でのエラー情報の集積とかする専用の機能がある - 古いデータを引き継ぐ機能は未実装だがその内提供される ## この記事を読むと分かること - これまでのやつ(ga.js)と比べて新しいやつ(analytics.js)がどう変わったか - Universal Analyticsの新機能で **個人的に** 気になってるもの ## この記事を読んでも分からないこと - iOSやAndroid向けSDKの話 - Universal Analyticsの新機能で **個人的に** 気になってないもの - いつUniversal Analyticsが公開されるか(僕が教えて欲しい) ---- # ところで… [Qiitaに投稿した自分の記事にはGoogle Analytics埋め込めるようになりました。](http://blog.qiita.com/post/53841405700/google-analytics-is-available) 送られてくるのはページビューだけですが、これ設定すると自分の記事がどれくらい読まれているか、とか、Twitterやはてブでどれくらい共有されてるか、とかが分かって楽しいです。 あと大雑把にQiitaを利用している人の情報が分かるので「Chromeユーザ多すぎワロス」とか(なんとなく)分かって楽しいです。 以上お知らせでした ---- # Universal Analyticsとは何か これまでのGoogle Analytics(以下ga.js)では端末をまたいでユーザを識別することができませんでした。 例えば、同じアカウントでログインしているにも関わらず、会社PC、自宅PC、携帯でそれぞれアクセスしたら3人の別の人がアクセスしたと判断してました。 1番の違いは、この点に関して、同じアカウントなら同じ人として扱えるようになる点だろうと思います。 加えて、ga.jsは(PC向け)Webアプリケーション向け機能が多いですが、Universal Analyticsはモバイルサイト向けの機能の充実や、iOSとAndroid向けのSDKが提供されるなど、よりモバイル向けに機能が強化されているのも大きな違いです。 というわけで様々な環境からのアクセスを解析できるから Universal Analytics なわけですね。 ## 読み込むJavaScriptが変わってインタフェースが変わった 今までは ga.js ってやつでしたが、今度からは analytics.js になります。名前なんてどうでもいいんですが、インタフェース的に結構変わってます。 以前のga.jsはなかなか酷くて、ページビューを送信する場合次のように書きました。 ```js:ga.jsでアカウント設定&ページビュー _gaq.push(['_setAccount', 'UA-XXXX-Y']); _gaq.push(['_trackPageview']); ``` グローバルに露出している `_gaq` という名前はいいとしても、なんで `_setAccount` とか `_trackPageview` みたいな内部APIが `_` で始まるんだよ、とか `push` ってなんだよ直感的じゃねーな、とか色々思うところがあります。 特に酷いのが送信後に何かをしたい場合で、例えばユーザがformにsubmitした、というイベントをga.jsでトラッキングしようとすると、イベント通知をGoogleに終えてからsubmitイベントを実行しないといけませんが、それを実現するには次のように書きます。 ```js:ga.jsでイベント送信後に何かする方法 _gaq.push(['_trackEvent', 'form', 'submit']); _gaq.push(function () { // _trackEventが終わったあとに実行される関数 }); ``` 非同期処理といえばコールバック関数、もしくは[Deferredインタフェース](http://qiita.com/yuku_t/items/3d1cf51d7ae91305eaaa)を使うのがJavaScript的にはよくある方法なんじゃないでしょうかね。サーバサイドではキューの方が一般的かも知れないけど。(ga.jsでも `hitCallback` が使えるという情報がありますが、公式ドキュメントでは上記の方法しか紹介されていませんでした。) それが analytics.js では下記のように変更されます。だいぶ分かりやすくなった気がします。 ```js:analytics.jsの場合 ga('create', 'UA-XXXX-Y'); ga('send', 'pageview'); ga('send', 'event', 'form', 'submit', { hitCallback: function () { // イベント送信が終わったら実行されるコールバック }); ``` まず断っておきたいのは、グローバル関数である `ga` はデフォルト名で、問題がある場合は変更できます。あと、イベント完了後に何かをするにもコールバックを使うようになってます。 ## 端末をまたいでログインユーザを追跡する `clientId` というのがあって、おそらく、これを使います。 ```js:ユーザの識別 ga('create', 'UA-XXXX-Y', { clientId: 'ログインユーザID' }); ``` 何も指定しないとanalytics.jsが何か適当に埋めます。これをログインしているユーザのIDとかにすれば、端末が変わっても、ログインしていれば同じ値になるので、同じユーザとして追跡できることになります。 # Hitとは何か Hitってのは ```js: ga('send', 'pageview'); ``` でいうところの `pageview` の部分のことで、`pageview` `event` 以外に `appview` `transaction` `item` `social` `exception` `timing` があります。というか、これら以外は指定できないので注意。 それぞれ意味が違うので、目的に合わせて利用します。それぞれ引数が若干違うので、使う場合は[公式ドキュメント](https://developers.google.com/analytics/devguides/collection/analyticsjs/)を確認してください。 ここでは `social` と `exception` について取り上げようと思います。 ## ソーシャルボタンの効果測定 - [Social Interactions - Web Tracking (analytics.js) - Google Analytics — Google Developers](https://developers.google.com/analytics/devguides/collection/analyticsjs/social-interactions) `social` hitは、ページにTweetボタンとかLikeボタンとか置く場合が増えていると思いますが、それらが実際どれくらい押されているのかをトラッキングする専用のイベントです。 もちろん `event` を使ってもいいんですが、こっちを使っておくと何かとよしなにやってくれそうです。 現時点では専用のページがあって、通常のイベントとは区別して見れるというだけなんですが、今後もっと色々と面白いことができるようになっていくんじゃないかと期待しています。 ### Tweetボタンをトラッキングする Tweetボタンをクリックされたり、実際にツイートされた回数を計測する方法です。詳しくは [Twitterのドキュメント](https://dev.twitter.com/docs/tfw-javascript)を参照してください。 ```js:Tweetボタンをトラッキングする twttr.ready(function (twttr) { twttr.events.bind('click', function (e) { ga('send', 'social', 'twitter', 'click', 'http://example.com'); }); twttr.events.bind('tweet', function (e) { ga('send', 'social', 'twitter', 'tweet', 'http://example.com'); }); }); ``` FacebookのLikeボタンをトラッキングする方法は、上でリンクを貼った公式ドキュメントに書いてあるので、それを参考にしてください。 はてブボタンには、LikeボタンやTweetボタンのようにユーザの行動をフックするAPIが用意されていないので、トラッキングできないです。 あと、当たり前ですが、これで分かるのはページに埋め込まれているボタンを経由して行動をした場合のみです。 ## ブラウザ上でのエラーの計測 - [Exceptions - Analytics.js Field Reference - Google Analytics — Google Developers](https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#exception) サーバ上で起こるエラーは把握できますが(ログがサーバに残るので)、ブラウザでJavaScriptがエラーをおこしたとしても、それを開発者が知ることは基本的にできません。 規模が大きな会社ではエラーが起きたら、その情報を自分のサーバに送ってあとで見れるようにしたりしているのかも知れませんが、そんなことにリソースを割く余裕は無い、というところも多いはず。 `exception` hitは恐らくこういう用途で使うためのものです。恐らく、と言っているのは、現時点ではまだWebインタフェースが存在していなくて、データを送っても何も見れない状態だからです。 あと1つだけ注意があって、 `exception` で送れるデータが150Bまでなので、スタックトレースを全部送る、みたいなことはできそうにないです。 # Custom Dimensions & Metrics とは何か ga.jsにはCustom Variablesというのがあって、これを使うと独自に定義した属性みたいなのをユーザに付与することができますが、それをもうちょっと便利にしたものです。 Dimentionsは文字列でmetricsは数値になります。DimensionsもMetricsも20個までしか作れないし、一回作ったら削除できないので注意が必要。 Mixpanelみたいにとりあえず送りつけたらその場でトラッキング開始してくれる、みたいな感じじゃなくて、事前にこちら側で、Custom Dimensionsの1番目はログインしてるかどうかを表します、というのを設定しておかなければならないのがちょっと面倒。(ga.jsのCustom Variablesもそんな感じでしたね) 設定ページにいくとスコープをどうするか聞かれます。スコープというのはその情報が、毎回異なるのか、セッション毎に異なるのか、ユーザに対して不変なのか(例えば性別は基本的に変わらないはず)で決まります。詳しくは [Custom Dimensions & Metrics - Feature Reference - Google Analytics — Google Developers](https://developers.google.com/analytics/devguides/platform/features/customdimsmets) に書いてあります。 簡単な話が、例えば「ログインしてるかしてないか」を1つのdimentionにして、アクセスユーザに対して適切に設定してやれば、おそらく、簡単にアクセス全体に対するログインユーザの割合が分かったり、ログインユーザと非ログインユーザの行動の違いとかを簡単に比較できたりするんだと思います。 おそらく、とここで言っているのは、これまたドキュメントにはあるけどWebインタフェースからアクセスできない状態だからです。Universal Analyticsを有効にしていてもWebインタフェースにはga.js用のCustom Variablesが表示されます。ひどい ## 使ってみる Webインタフェースはないんだけどデータを送るだけなら既にできます。 ```js+erb:ログインユーザかどうか設定する ga('create', 'UA-XXXX-Y'); ga('set', 'dimension1', '<%= logged_in? %>'); ga('send', 'pageview'); ``` 仮にdimensionの1番目をログインしているかどうかに設定しているとすると、上記のようにしておけば、各ページビューがログインユーザによるものか、それとも非ログインユーザによるものかが判別できるようになります。 # ga.jsからanalytics.jsへの移行 いまはまだデータを移行することはできません。Googleによればそのうち提供するらしいです。 なのですでにga.jsを使っている人は現状このままga.jsを使い続けて、移行する機能が提供された段階でanalytics.jsに移るというのがよさそう。 ちなみにga.jsとanalytics.jsが混在しても問題無い(とGoogleが明言している)ので、とりあえずUniversal Analyticsを導入しておく、というのもOKです。 # 合わせて読みたい - [GoogleAnalytics - Google Analyticsの利用時に注意すること - Qiita [キータ]](http://qiita.com/tatata111/items/a6e2574cb654986ed3f0) Google Analyticsの利用規約で気をつけるべき点についての解説記事 - [GoogleAnalyticsを使ったメールマガジン等からのトラフィック分析 - Qiita [キータ]](http://qiita.com/kengos@github/items/1998a3f71e5b702d36c8) キャンペーン機能の使い方。キャンペーン機能はga.jsとanalytics.jsのどちらでも利用できます。(違いがあるかまでは見てないです) - [2012年Google Analyticsサミット報告|コラム アユダンテ株式会社](http://www.ayudante.jp/column/2012-11-05/14-48/) 昨年末に行われたGoogle Analyticsサミットでのお話。今回紹介しなかったUniversal Analyticsの新機能について網羅的に紹介されています。 |
|
| 396位 |
|
|||
|
14:08:11 |
(サイボウズ株式会社 所属) |
|
よそからcloneしてきたリポジトリでは、remotes/origin/xxxという名前でoriginのブランチが参照される。
```bash $ git branch -a * master remotes/origin/hoge remotes/origin/fuga ``` だが、他の人がhogeブランチを削除してoriginにpushしても、既にそのブランチが手元にある人のローカルリポジトリからは普通にpullしても消えてくれない。 そういった既にリモートで削除されているブランチを消したい場合は、以下のどちらかの操作を実行すればちゃんと消えてくれる。 ## git fetchもしくはgit pullにpruneオプションをつける --pruneオプションをつけると、fetchやpullする際に自動的にリモートで削除されているリモートブランチを削除してくれる。 ```bash $ git fetch --prune x [deleted] (none) -> origin/hoge ``` ## git remote pruneを実行する ```bash $ git remote prune origin x [deleted] (none) -> origin/hoge ``` |
|
| 397位 |
|
|||
|
03:29:44 |
|
|
Rubyといえば動的型付け言語なので、IDEサポートがほとんど皆無です。
それはしょうがないんですが、やはりIDEサポートはけっこうなアドバンテージに思うわけで。 例えばC#+Visual Studioは素晴らしいです。膨大なBCLも、IntelliSenseがあると結構なんとかなったりします。 もちろんruby環境にも補完機能がないわけではないですが、やはり弱いです。 なのでどうしてもtypo→エラー→ググるというプロセスをよく歩んでしまいます。 **そこでpryですよ** pryはirb(rubyの標準インタプリタ)の上位版です。 irbで出来ることはpryでも大抵は可能です。 pryにはシンタックスハイライト機能とかもありますが、一番はlsコマンドとcdコマンドです。 例えば適当なstringオブジェクトをlsしてみると  `"hello pry!"`オブジェクトのメソッドが一覧されます。 また、同じstringオブジェクトの中に入ってみます。  同じlsでも内容が変わってますね。そう、lsの内容が先ほどの`ls "hello pry!"`と同じ内容になりました。 つまり、cdで中に入ったわけです。 しかし、メソッドだけわかったところであまり旨くないですね。 pryは`?`コマンドでドキュメントを、$コマンドで定義元のコードを見ることができます。   また、pryのlsは--grepオプションで*正規表現で*grep可能です。  最後に、pry内で.の後にシェルコマンドが使えます。 また、railsを使う際も、pryならrailsの独自拡張メソッドを全てドキュメントとソースを一緒に見れます。 railsでpryを使うには以下をGemfileに書いて`bundle install`するだけです。 `rails console`がirbでなくpryで起動します。 ```file:Gemfile group :development, :test do gem 'pry-rails' gem 'pry-doc' end ``` Enjoy Cording! |
|
| 398位 |
|
|||
|
12:51:05 |
(株式会社キュリオシティソフトウェア 所属) |
|
iOS7から変わったUITabBarのデザインよりのカスタマイズについて。と言ってもレンダリングモードだけiOS7からなのでそこだけ注意すればいい。 ## UITabBarのテキストフォントとカラーを変更する appearanceのsetTitleTextAttributes:forStateメソッドを使う。タブバーの画像はデフォルト。  選択中が赤色、他は非選択(通常時)の青色を設定。 ```objectivec: UIFont *font = [UIFont fontWithName:@"HelveticaNeue-Bold" size:10.0f]; //タブ選択時のフォントとカラー NSDictionary *selectedAttributes = @{NSFontAttributeName : font, NSForegroundColorAttributeName : [UIColor redColor]}; [[UITabBarItem appearance] setTitleTextAttributes:selectedAttributes forState:UIControlStateSelected]; //通常時のフォントとカラー NSDictionary *attributesNormal = @{NSFontAttributeName : font, NSForegroundColorAttributeName : [UIColor blueColor]}; [[UITabBarItem appearance] setTitleTextAttributes:attributesNormal forState:UIControlStateNormal]; ``` ## UITabBarの画像を作成する i0S7になっても変わらず画像は30x30px(Ratina 60x60px)で作成する。このサイズはドキュメントで決められたサイズではないのか公式の記述がないのでだいたいこんなもんだろうという感じだと思う。 元画像は http://icons8.com/free-ios-7-icons-in-vector/ のサイトのpng画像を使った例(brickというキューブな画像を適用)。  画像の作りとして、brickをダウンロードして見ればわかるが、黒色(#000)の線のみで作られた画像がデフォルトでは選択時は薄い青っぽくなり、非選択時(通常時)は灰色になる。 ### 選択時の色を変える 選択時の色を替える場合はtintColorを変更するだけ ```objectivec: [[UITabBar appearance] setTintColor:[UIColor greenColor]]; ``` tintColor原色緑を指定すると薄い緑になる。これはレンダリングモードが画像をテンプレートとして使用しているからだろうと思う。  iOS7からレンダリングモードはUIImageで設定するようになった。画像をテンプレートとしてマスクして使っている。 AppleのiOS7移行ガイド日本語訳参照 https://developer.apple.com/jp/devcenter/ios/library/documentation/UserExperience/Conceptual/TransitionGuide/Bars/Bars.html ### 画像をそのまま表示したい 画像をそのまま使う場合、レンダリングモードをオリジナルにする。 ``` UIImage *image = [[UIImage imageNamed:@"brick"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; item.selectedImage = image; ``` ちょっとわかりづらいのは、RenderingModeはオブジェクトのプロパティで自由に設定変更できるわけではなく、設定したらUIImageを再生成するところ。UIImageのインスタンスに対してimageWithRenderingMode:メソッドで生成した戻り値がモードの変更されたUIImageになっているのでそれを利用する。  レンダリングモードをオリジナルにすると画像をそのまま使う。brickの画像はこのような黒い線で構成される画像だったのでそのまま表示される。 ## UITabBarの背景を変更したい ### 背景色を指定する barTintColorを設定することでバー自体の色を変更できる。 ```objectivec: [UITabBar appearance].barTintColor = [UIColor brownColor]; ``` ブラウンカラーに設定する  iOS7からbarTintColorに従って半透明になっていたが、iOS7.0.3からbarTintColorを設定すると半透明にならなくなったので、どうしても半透明にしたければUIColor自身に透明度を指定する (http://qiita.com/yimajo/items/4781d5d2712e34677db2 )。 ### 背景画像を変更する 背景画像は320x49px (Ratina 640x96px)の画像を用意し、backgroundImageに設定する。iOS7になってもこの部分は変わってない。 ```objectivec: [UITabBar appearance].backgroundImage = [UIImage imageNamed:@"tab_background"]; ```  |
|
| 399位 |
|
|||
|
14:53:47 |
(Increments inc. 所属) |
|
### 閲覧上の注意
この記事で対象としているバージョン0.5.3は結構古いので注意してください。この記事でいえば、`bind`は無くなり、現在では`on`や`listenTo`が使われています。 その他の割りと新しい情報は [Backbone.js Advent Calendar 2012](http://www.adventar.org/calendars/15) などにあります。 (追記ここまで) ---- ネタ切れ感が否めないBackbone.js Advent Calendarですが、今回から何回かに分けて懇切丁寧な入門記事を書いていこうと思います。 以下のように書き進めていく予定です。 1. Events 2. View 3. Model 4. ViewとModelの連携 5. Collection 6. ViewとModelとCollectionの連携 7. RouterとHistory なおここで扱うBackbone.jsのバージョンは0.5.3です。 #Backbone.js入門 「Events」 [Backbone.jsのドキュメント](http://documentcloud.github.com/backbone/)を開くとまず最初に解説されているのが `Events` です。 `Events` を直接使うことは無いかも知れません。それというのも `Model` `Collection` `Router` `View` は `Events` を拡張しており、しかも用途に特化した様々な機能を持っているため、これらを使った方が便利だからです。 しかしながら、上記のオブジェクトを用いる中で間接的に `Events` を利用しているということができます。 Backbone.jsでのプログラミングは、この `Events` が提供するインタフェースを用いて **[オブザーバ・パターン](http://ja.wikipedia.org/wiki/Observer_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)** を上手く使い、オブジェクト間の疎結合を促進することが基本的なコーディング方針です。 ##オブザーバ・パターン JavaScriptに限らず、A.aが行われたら、B.aを行いたい、というような場面があります。 ```js A.a = function () { // do something B.a(); }; ``` これで問題無いように思えますが、AはBがaメソッドを持っていることを知っていなければなりませんし、後になってC.aを追加したくなるかも知れません。それに、動的にB.aが実行されるかどうかを切り替えたい場合は、さらにゴチャゴチャしてきます。 このような場面でオブザーバ・パターンを用いることで、プログラムの見通しをよくすることができます。(Aのようなオブジェクトを *出版者* や *Subject* 、Bを *購読者* や *Observer* と呼びます) ```js A.bind("doA", B.a); // A に対して doA が trigger されたら B.a を実行する A.a = function () { // do something this.trigger("doA") // => B.a が実行される } ``` あとからC.aも実行したくなったら `A.bind("doA", C.a)` を実行すればよい訳です。 ##Eventsの使い方 ここでAを `Events` を用いて定義すると以下のようになります。 ```js A = {}; _.extend(A, Backbone.Events); ``` `Events` を拡張したAには`bind`, `unbind` そして `trigger` の3つのメソッドが追加されています。 ###bind(eventName, callback, [context]) 他のオブザーバ・パターンの実装では **register** と呼ばれることもあるメソッドで、`A.trigger(eventName)` によって `callback` が起動されるようになります。`context` はオプションの引数で、`callback` の `this` に束縛されます。`context` は後述する *Eventsを用いる時の注意点* でもう一度扱います。 ###unbind([eventName], [callback]) 他のオブザーバ・パターンの実装では **unregister** と呼ばれることもあるメソッドで、既に束縛( *bind* )されているコールバック関数を解除することができます。`eventName` と `callback` は両方省略可能で、両方省略すると全てのコールバック関数が解除されます。 ###trigger(eventName, [*args]) 他のオブザーバ・パターンの実装では **notify** と呼ばれることもあるメソッドで、束縛されているコールバック関数を起動します。第二引数以降はそれぞれコールバック関数に引数として渡されます。 ここで、`eventName` が *"all"* だった場合、このオブジェクトに束縛されている全てのコールバック関数が実行されます。 ##Eventsを用いる時の注意点 `Events` に限らず、コールバック関数内では `this` が何を指すのか気をつける必要があります。 `bind` において、`context` を指定しなかった場合、コールバック関数内で `this` は *Subject* オブジェクトになります。つまり ```js B = { name: "B", b: function () { console.log(this.name) }; } A.name = "A"; A.bind("foo", B.b); A.trigger("foo"); // => "A" ``` これは意図しない動作でしょう。ここで、`this` をBにする方法はいくつかあります。まずは、`context` を使う方法です ```js A.bind("foo", B.b, B); A.trigger("foo"); // => "B" ``` もう一つは、`_.bind` を使って事前に `this` を束縛する方法です。(紛らわしいですが、この `bind` は `Events#bind` とは全くの別物です) ```js B.b = _.bind(B.b, B); A.bind("foo", B.b); A.trigger("foo"); // => "B" ``` この方法の利点は、金輪際、どのような状況で呼び出されようと、B.b() が "B" と出力するようになることです。B.b を何度もコールバック関数として使う場合は、先に `_.bind` しておくとよいでしょう。 しかしながら、Bのメソッドが増えてくると、いちいち `_.bind` を呼ぶのが面倒です。 ```js B = { b1: function () {}, b2: function () {}, b3: function () {}, b4: function () {}, b5: function () {}, b6: function () {}, b7: function () {}, b8: function () {}, b9: function () {}, b10: function () {} }; B.b1 = _.bind(B.b1, B); // ... 面倒 B.b10 = _.bind(B.b10, B); ``` こういう場合は `_.bindAll` が便利です。`_.bind` を10回呼ぶ代わりに、次のように書くことができます。 ```js _.bindAll(B, "b1","b2","b3","b4","b5","b6","b7","b8","b9","b10"); ``` ##次回は 次回は「View」です。お楽しみに --- 1. Backbone.js入門 Events 2. [Backbone.js入門 MVC](http://qiita.com/items/03f49c64d6c4bacdbb26) 3. [Backbone.js入門 View](http://qiita.com/items/8ee2ed1949cd7c337f06) 4. [Backbone.js入門 Model](http://qiita.com/items/5acef8dd49f67fd7813c) 5. [Backbone.js入門 ViewとModelの連携](http://qiita.com/items/7ce4b1ceb5d18c9cef08) 6. [Backbone.js入門 Collection](http://qiita.com/items/c19d2fff0dc23e4015ff) 7. [Backbone.js入門 ViewとModelとCollectionの連携](http://qiita.com/items/7095a32cc5e438172844) 8. [Backbone.js入門 RouterとHistory](http://qiita.com/items/13f3d1f71d31f3e78123) - [BackboneJS 日本語メーリングリスト](https://groups.google.com/forum/?fromgroups#!forum/backbonejs-ja) |
|
| 400位 |
|
|||
|
22:37:57 |
(私立リリアン女学園高等部 所属)
|
|
プログラミングをしたことがあるひとなら、誰でも1度くらい自分の理想の言語を作ってみたいと思うのではないでしょうか。このテキストは、オリジナルのプログラミング言語のコンパイラ作成を通して、パーサコンビネータの使い方を紹介していくものです。
## 2分でわかる、俺の俺による俺のためのプログラミング言語を作る大まかな手順 自分のオリジナルなプログラミング言語を作るには、典型的には次のような手順を踏みます。 1. 既存のプログラミング言語を使ってみる 2. その既存の言語の気に入らないところを徹底的になじる(ただし心のなかで) 3. 己の内に秘める中二力を卍解し、最強プログラミング言語の仕様を妄想する 4. コンパイラを作る 5. その言語を教典とする宗教団体を設立し、慈悲深き終身の独裁者を名乗る 6. 自分の言語が思ったよりしょぼいことに気付く 7. 桶屋が儲かる このテキストではこのうち手順 4 だけ、特にコンパイラのパーサ部分を解説します。 * このテキストを読むにあたって、構文解析についての知識はとくに不要です。正規表現を知っていると理解の助けになるかもしれません。 * このテキストで作ってみるプログラミング言語は、最近のお茶っぽいネーミングの流行に乗っかって BanchaScript と名づけました。あまり変な言語を作っても参考にならないので、言語仕様は C/Java/JavaScript の系統の比較的オーソドックスなものを採用しています。 * コーディングには JavaScript/TypeScript を使い、パーサを作成するのに Parsect というライブラリを利用します。これは Haskell の Parsec という構文解析を行うためのライブラリを筆者が JavaScript/TypeScript へといい加減に移植したりしなかったりしたもので、このテキストはそれの紹介も兼ねています。本文中のソースコードのほとんどは、JavaScriptとTypeScriptのどちらでも有効なコードで書かれています。 ## まずは3分でわかるコンパイラの構造 コンパイラの工程は、典型的には次のような段階を踏みます。らしいです。 1. 字句解析 2. 構文解析 3. 中間コード生成 4. 最適化 5. コード生成 筆者は大変な面倒くさがり屋なので、今回作るコンパイラではこれらの工程を大きく簡略化します。最適化については面倒くさいので省略します。最終的に出力するコードはJavaScriptのソースコードとします。このようなコンパイル形態をもつ言語は [AltJS](http://altjs.org/) などと呼ばれていて、実行に関するあらゆる機能をJavaScriptエンジンに丸投げできるので、パーサさえうまくできればコンパイラを作るのがとても簡単になります。また、このような手法を採用することから中間的なデータ構造を作らなくても済むので、構文解析と同時に直接最終的なコードを出力していきます。コード生成部分に関しては、ほぼ等価なJavaScriptソースコードを文字列として生成しているだけなので、特に説明する内容はありません。 ## というわけで、4分くらいでわかる字句解析器と構文解析器とはなにか 今回作るコンパイラでは、工程としては字句解析器と構文解析器に分類はしますが、実はコード上では両者に区別はありません。字句解析器も構文解析器も、どちらも一種の関数とみなすことができます。どちらの解析器でも、入力する情報は次のふたつだけです。 * 構文解析の対象としている文字列 * 現在何文字目まで読み終えているか また、解析器は入力に応じて次の情報を返してきます。 * 読み取りが成功したかどうか * 成功した場合は、何文字目まで読み終えたかと、読み取った内容 * 失敗した場合は、何文字目まで読み終えたかと、期待していた内容 基本的にはたったこれだけです。コンパイルする対象の文字列の最小単位である『識別子』『演算子』などのトークンを読み取るのが字句解析器で、『関数の定義』『if文』など複数のトークンが組み合わさったものを読み取るのが構文解析器なのです。まずはトークンを読み取る字句解析器を作り、それらの字句解析器を組み合わせて構文解析器を作成していくことになります。ここでは、字句解析器も構文解析器をまとめて『パーサ』や『解析器』などと呼ぶことがあります。それぞれの解析器は基本的には状態を持ちません。入力に応じて決まった出力を行う、単なる関数です。 ## 概念は5分くらいでわかるけど、実際に作るのには何時間もかかるんじゃないかと思えてくる字句解析器 **字句解析器** (Lexer, Scanner, Token Parserなどともいいます)は、その言語の最小単位である『識別子』『リテラル』『予約語』などのトークンごとの読み取りを行うパーサです。注意しなければならないのは、トークンとトークンの間には空白文字やコメント文があるときもありますし、そのような区切りがなくて直後にいきなり次のトークンが現れるときもあることです。字句解析器はひとつトークンを読んだら、つねに次のトークンの直前まで読まなくてはならないものとします。たとえば、次のようなソースコードを解析しているものとします。 var hoge /* この変数はほげ */ = 42; ここで、現在の読み取り位置は 4、つまり `hoge` の直前で、ここから識別子をひとつ読み取るパーサを作りたいとします。識別子の `hoge` の直後にはブロックコメントがありますが、コメントはトークンにはならないので、一度の読み取りでこれらのブロックコメントも含めて読み飛ばして `=` の直前まで来なければなりません。つまり、識別子の字句解析器は * 入力文字列: `var hoge /* この変数はほげ */ = 42;` * 現在の位置: 4 という入力から、 * 成功 * 現在の位置: 23 * 読み取った情報: `"hoge"` というような結果を返すようプログラムされていなければなりません。また、次のように識別子と次のトークンに空白などの区切りがないときもあります。 var hoge=42; このような場合でも、識別子の字句解析パーサは次のトークンである `=` の直前まで読み取り、以下のような結果を返さなければなりません。 * 成功 * 現在の位置: 8 * 読み取った情報: `"hoge"` また、現在の位置に識別子が存在しない場合もあります。先ほどと同じ `var hoge=42;` というソースコードの9文字目、`42`の直前の位置で識別子の字句解析器を使ったなら、そこには識別子は存在しないので、次のような結果を返さなければなりません。 * 失敗 * 現在の位置: 9 * 期待していたもの: a-z, A-Z, _, $ のいずれかひとつ こういった機能をもつ字句解析パーサを、『識別子を読み取るパーサ』『数値リテラルを読み取るパーサ』『文字列リテラルを読み取るパーサ』『予約語を読み取るパーサ』『括弧を読み取るパーサ』などと、その言語のソースコードに出現しうるすべてのトークンの種類ごとに定義しなければなりません。それほど難しくはなさそうですが、これでは作業量としてはかなりのの量になりそうです。やれやれ。 ## 実際には10数行で書ける字句解析器 というわけで字句解析器を構成するのはとても大変なので、ライブラリに付属している **字句解析器を自動で構成してくれる機能** を使います。関数 `makeTokenParser` は『コメントを開始する記号/終了する記号』『予約語』などの情報をオブジェクトのプロパティとして定義して渡すだけで自動で字句解析器を構成してくれます。ほとんどのプログラミング言語の字句解析はこの機能で十分構成できます。BanchaScriptの場合は次のようになっています。 var lexer = Parsect.makeTokenParser({ commentStart: '/*', // ブロックコメントの開始記号 commentEnd: '*/', // ブロックコメントの終了記号 commentLine: '//', // 行コメントの開始記号 nestedComments: true, // ネストされたブロックコメントを許可するかどうか identStart: /[_$a-zA-Z]/, // 識別子の最初の文字 identLetter: /[_$a-zA-Z0-9]/, // 識別子の2文字目以降 opStart: /[+\-*\/=!$%&\^~@?_><:|\\.]/, // 演算子の最初の文字 opLetter: /[+\-*\/=!$%&\^~@?_><:|\\.]/, // 演算子の2文字目以降 reservedNames: ["def", "return", "operator", "infix", "infixl", "infixr", "prefix", "postfix", "var"], // 予約語 reservedOpNames: [], // 予約された演算子 caseSensitive: true // 大文字小文字を区別するかどうか }); これだけでBanchaScriptの字句解析器は **完成** です。この一覧表をいじれば、プログラミング言語の見た目を自由に調節できます。たとえば行コメントの開始を `#` にしたければ、`commentLine` プロパティの値を `#` に変えるだけです。 `makeTokenParser` が返してくるオブジェクト `lexer`はいろいろな字句解析パーサをプロパティとして持っています。たとえば、`lexer.identifier` は識別子の字句解析を行ってくれるパーサです。たとえば、 var hoge /* この変数はほげ */ = 42; というソースコードの4文字目まで読み終わった状態から `lexer.identifier` を実際に使ってみましょう。この字句解析器を実際に使うには、`parse` という関数を使います。`parse` はパーサと入力となる `State` オブジェクトを渡すと、そのパーサの解析の結果を `Reply` オブジェクトとして返してくれます。ソースコードは次のようになります。 var state = new Parsect.State("var hoge /* この変数はほげ */ = 42;", 4); // state は現在の状態 var reply = Parsect.parse(lexer.identifier, state); // reply は解析の結果 console.log("成功したかどうか: " + reply.success); console.log("現在の位置: " + reply.state.position); console.log("読み取った情報: " + reply.value); これを実行すると、次のような出力を得られます。 成功したかどうか: true 現在の位置: 23 読み取った情報: hoge さきほどコメントして `/*` から `*/` までの間をコメントにするように指定しましたが、識別子 `hoge` を読み取ったあと、このコメント部分もちゃんと読み飛ばし、次のトークン `=` の直前で停止したことがおわかりになると思います。他にも数値リテラルを字句解析する `lexer.naturalOrFloat` や、文字列リテラルを読み取る `lexer.stringLiteral` 、予約語を読み取る `lexer.reserved` や任意の単純なパーサをこの言語における字句解析パーサに変換する `lexer.lexeme` などが自動的に作成されます。便利便利。これじゃあ全然構文解析の勉強になりませんが、とにかく楽なのでよしとしましょう。カレーを作るのにジャガイモの育て方から知る必要はない、みたいなのと同じです。 ## 8行で書ける変数宣言の構文解析 字句解析器ができたので、手始めに次のような変数の宣言の文を構文解析してみましょう。 var hoge = 42; この式を読み取って、読み取った情報を `var hoge=42;` というようなJavaScriptのソースコードとして返すようなパーサを構成してみます(BanchaScriptの変数宣言とJavaScriptの変数宣言はほぼ同じなので、ほぼ同じコードにコンパイルされます)。このような文を字句解析器で構文解析するには、『予約語varを読み取るパーサ』『識別子を読み取るパーサ』『`=`を読み取るパーサ』などの各種のパーサを `lexer` から呼び出して、順番に適用していきます。このようにいろんなパーサを順番に使用するときは、Parsect では `Parsect.seq` という関数を使うと次のように直感的に書くことができます。 var varStatement = Parsect.seq(function(s){ // 変数の宣言とは s( lexer.reserved("var") ); // まず予約語 var があって、 var name = s( lexer.identifier ); // 変数名となる識別子があり(その識別子をnameとする)、 s( lexer.reservedOp("=") ); // 代入を示す = の記号、 var value = s( lexer.naturalOrFloat ); // 代入する値(その値をvalueとする)、 s( lexer.semi ); // 最後にセミコロン。 return "var " + name + "=" + value + ";"; // return で結果の値を返す }); `seq`から受け取った `s` という関数に順に実行したいパーサを渡すのがポイントです。このとき、変数 `name` には `lexer.identifier` で読み取った文字列が代入されます。これは先ほど `parse` 関数を使って返ってきた `Reply` オブジェクトの `reply.value` の値と同じもので、`seq`が内部で適当に`parse`を呼び出してくれているのです。また、`value` には `lexer.naturalOrFloat` で読み取った数値が代入されます。あとは `return` でこのパーサ `varStatement` で返したいオブジェクトを返せば OK です。この新たなパーサ `varStatement` は次のように使うことができます。 var state = new Parsect.State("var hoge = 42;", 0); var reply = Parsect.parse(varStatement, state); console.log("成功したかどうか: " + reply.success); // true console.log("現在の位置: " + reply.state.position); // 14 console.log("読み取った情報: " + reply.value); // "var hoge=42;"" (seqが渡してくる `s` という謎の関数オブジェクトは、パーサを実行しつつ値を取り出すための Parsect 特有の仕組みです。本家 Parsec だと Haskell 特有の言語機能(do記法)を使ってすっきりと書くことができるのですが、この言語機能は他の言語には存在しないので、このあたりは移植版それぞれで違う書き方になっています。Parsect では Parsec での書き方に似せるためにこの `seq` 関数を導入していますし、Java版 jparsec やRuby版 rparsec はまた違った方法で書くようになっています。Parsectと同じく JavaScript版の jsparsec も Parsec に似せようとはしてるようですが、Parsect とはまったく違う設計になっています。 この `seq` 関数を使うパースですが、このコールバック関数内にブレークポイントを仕掛けると、その時点でパースを一時停止することができたりします。このとき、`name`や`arg`に代入されている値を調べたり、`s`関数の `s.suceess` で現在パースが成功しているかどうか、`s.peek`で現在読み取ろうとしている文字列部分の情報を取得してデバッグに使うことができます。`s.peek` をウォッチしながらステップ実行していくと、入力が少しづつ読まれている様子が確認できます。) ## 何日かかっても書けそうにない、演算子式の解析 (ここの節は意味がわからなくていいです。演算子式の構文解析はやたらと難しいということだけわかってください。なるほど、まったくわからん、となるのが目標です) プログラミング言語というからには、計算ができてなんぼのものでしょう。こんどは加算や減算などの演算子が組み合わさった式をどのように処理するかを見ていきます。まずはもっとも単純に、加算と数値しかない演算子式を考えます。『式』とはどんなものかというと、数値は『式』ですし、『式』と『式』を `+` でつないだものもやはり『式』です。このとき、数値を `NUMBER`、『ふたつのうちどちらか』というのを `|` という記号で表すとすると、式 `expr` は次のように表現することができます。 expr = NUMBER | expr + expr 『式は、数値か、式と式を + で結んだもの』ということです。これを対応するソースコードにすればいいのでしょうか?この形のパーサで `1 + 2` という式を解析しようとすると、まず `1` というトークンが `NUMBER` と `expr + expr` のどちらかに一致するかを調べますが、この定義では `NUMBER` のほうが左側にあるのでこっちを先に試します。すると `1` は `NUMBER` に一致するので `1` が式だということがわかり、めでたく解析は成功……といいたいところですが、実はまだ `+ 2` という部分が残っています。これでは `1 + 2` という式を読み取ったことになりません。ここで解析が終わってしまってはまずいのです。 先程は `expr + expr` より先に `NUMBER` であるかどうかを調べてしまったのが失敗でした。では、今度は `NUMBER` と `expr + expr` を入れ替えて、次のような定義で解析してみてはどうでしょうか。 expr = expr + expr | NUMBER この場合は、`NUMBER` より先に `expr + expr` にトークン `1` が一致するかを調べます。この `expr` は `expr + expr | NUMBER` のことですから、`1` が `expr + expr` に一致するかどうかを調べるには、`|` の左側の `expr + expr` に一致するかを調べないといけないわけで、その `expr + expr` に一致するかを調べるにはその `expr` が何かを調べなければならないわけで…… expr = expr + expr = (expr + expr) + expr = ((expr + expr) + expr) + expr ... というように、`1` が `expr` に一致するかを調べていこうとしてもひたすら展開を繰り返してしまうばかりで、`1` が `expr` に一致するのかどうかは一向にわかりません。このように無限に再帰し続けてしまうような形式を **左再帰** といいます。本テキストで採用している『再帰下降構文解析』では加算のような単純な式でも、『式 + 式』という直感的な文法では構成できないのです。そのため、このような左再帰を含む文法をどうやって含まない文法に変形するか、というのが再帰下降構文解析では必須の話題になります。演算子式の構成が難しいのは再帰下降構文解析最大の欠点だとも言えるかもしれません。他の解析法だと左再帰を含んでも処理できる場合があるのですが、それはそれでまた別に面倒なアルゴリズムが必要になります。 さらに、`*` は `+` よりも優先順位が高いので、優先して結合するように処理しなくてはなりません。また、同じ順位でも結合の方向によって結合のしかたが変わってきます。さらに、演算子は中置演算子だけじゃなくて前置演算子や後置演算子もあります。さらには三項演算子や関数呼び出しのような構文、キャストの構文も入り混じり、とにかくもう複雑で、構成するのがひたすらに難しくて面倒くさいのが、この演算子式なのです。もはや何が面倒くさいのかを説明するのすら面倒くさいというレベル。思いつきでパーサを書き始めた人は、 **だいたいここで挫折** します。 ## 実際は10行にも満たない、演算子式の解析器 というわけで式の解析はとても大変なので、ライブラリに付属している **演算子式のパーサを自動的に構成してくれる機能** を使います。使い方は簡単で、演算子の一覧表と作るのと、式の木構造における末端の項を解析するパーサを用意するだけです。まずは演算子の表をどのように定義するかを見てみます。字句解析器で作った `lexer` の `lexer.prefix`、`lexer.postfix`、`lexer.binary` は、それぞれ前置演算子、後置演算子、中置演算子を表すオブジェクトを作成する関数です。最初の引数には演算子の記号を文字列で、ふたつめの引数は、その演算子の両辺の式の値(両辺の位置の式をパーサで解析したときの値)を受け取ってその演算子式の値を返す関数です(紙幅の都合で、無名関数リテラルの代わりに `=>` というアロー関数式を使って書いています。) 。中置演算子はさらに3つ目の引数として結合方向を表す引数もとります。 var opTable = [ [lexer.prefix("-", x=>"-"+x), lexer.prefix("+", x=>x)], [lexer.binary("*", (x,y)=>x+"*"+y, Parsect.Assoc.Left), lexer.binary("/", (x,y)=>x+"/"+y, Parsect.Assoc.Left)], [lexer.binary("+", (x,y)=>x+"+"+y, Parsect.Assoc.Left), lexer.binary("-", (x,y)=>x+"-"+y, Parsect.Assoc.Left)], [lexer.binary("<", (x,y)=>x+"<"+y, Parsect.Assoc.Left), lexer.binary(">", (x,y)=>x+">"+y, Parsect.Assoc.Left)], [lexer.binary("==", (x,y)=>x+"=="+y, Parsect.Assoc.Left), lexer.binary("!=", (x,y)=>x+"!="+y, Parsect.Assoc.Left)] ]; この演算子の一覧表 `opTable` のうち、先頭の方の配列に含まれる演算子ほど優先順位が高くなります。上記の表の場合、単項の `+`, `-` がもっとも優先順位が高く、次に `*`, `/` 、その次に `+`, `-` という順序になることがわかります。演算子を追加したければ、この表にさらに追加すればいくらでも演算子を増やすことができます。この実装では、それぞれの演算子式は単に同じ意味の文字列を作って返すようになっています。 次に、各演算の末端の項となるパーサを用意します。ここでは簡単のために数値と識別子のみが項となるとしましょう。あるふたつのパーサのうちどちらかということを表すには、`Parsect.or` を使います。 var term = Parsect.or( lexer.naturalOrFloat, // 数値リテラルか、 lexer.identifier // 識別子 ); ここでは説明のためにかなり簡略化していますが、他にもこの `term` を他の種類の項に対応させれば、文字列リテラルや括弧で囲まれた式、関数呼び出しの式なども項として書けるように拡張できます。詳しくはあとで紹介する実際のソースコードを見てください。 最後に、関数 `buildExpressionParser` に演算子の表と項のパーサを渡すだけです。 var expr = Parsect.buildExpressionParser(opTable, term); これで任意の演算子式を解析できるパーサ `expr` が完成しました。先ほどの変数宣言のパーサにこれを組み込み、変数を初期化するときに任意の式を書けるようにしてしまいましょう。 var varStatement = Parsect.seq(function(s){ s( lexer.reserved("var") ); var name = s( lexer.identifier ); s( lexer.reservedOp("=") ); var value = s( expr ); // NEW! s( lexer.semi ); return "var " + name + "=" + value + ";"; }); これで四則演算などの式でも変数を初期化することができるようになりました。 var state = new Parsect.State("var hoge = 1 + 2 * 42;", 0); var reply = Parsect.parse(varStatement, state); console.log("成功したかどうか: " + reply.success); // true console.log("現在の位置: " + reply.state.position); // 22 console.log("読み取った情報: " + reply.value); // var hoge=1+2*42; やっぱり全然構文解析の勉強にはなりませんが、とりあえず簡単なのはいいことです。左再帰の除去が必要になる言語仕様は普通はこの演算子式くらいなので、ここさえ凌げばあとの構文解析は素直に書いていくだけでなんとかなります。これでパーサ最大の山は越えました。`buildExpressionParser` という抜け穴で回避しただけですけど。 ## 5分でわかるパーサコンビネータ BanchaScriptでは、先ほどの変数宣言の文を次のように何度でも繰り返すことができます。 var hoge = 42; var piyo = 10; var foo = 256; このようなある構文の繰り返しというのは `many` という関数にパーサを渡すことで構成する事ができます。例えば、さきほどの `varStatement` が任意の個数繰り返すことができるなら、 var statements = Parsect.many(varStatement); とするだけで、その変数宣言が任意の個数繰り返されるテキストを解析するパーサを作れるのです。正規表現の `*` 量化子みたいなものです。このとき、パーサ全体が読み取った値は、`varStatement` が読み取った値の配列になります。このように、他のパーサを受け取って別のパーサを作り出す関数を **パーサコンビネータ** といいます。先ほど出てきた `seq` や `or` もコンビネータですが、他にもコンビネータは次のようにたくさんあります。 * `many(p)` pの0回以上の繰り返し(正規表現の `p*` に相当) * `many1(p)` 1回以上の繰り返し(正規表現の `p+` に相当) * `optional(p)` 0回か1回 (正規表現の `p?` に相当) * `or(p, q, r)` p, q, r のうちどれかひとつ (正規表現の `p|q|r` に相当) * `lookAhead(p)` 肯定先読み(正規表現の `(?=p)` に相当) * `notFollowedBy(p)` 否定先読み(正規表現の `(?!p)` に相当) * `repeat(n,m,p)` pのn回以上m回以下の繰り返し(正規表現の `p{n,m}` に相当、Parsecにはない) これらは正規表現にも見られるようなものばかりです。さらに、 * `sepBy(p, q)` qで区切られたp。 `sepBy("p", ",")` なら `p,p,p,p` などにマッチ * `between(p, q, r)` p,q,rで順番に読み取るが、真ん中のqの値だけ返す (Parsecと引数の順序が異なる) などなどの各種のコンビネータがあります。 パーサコンビネータは Haskell や OCaml のような関数型言語の界隈でよく使われてきましたが、けっして関数型言語の専売特許というわけではありません。メジャーな手続き型言語でももちろん使うことができますし、正規表現で手におえないような複雑な構文解析をするときにはとても役に立つものです。また、昔から構文解析に使われてきた yacc/lex (bison/flex) や ANTLR のようなパーサジェネレータよりずっと柔軟でお手軽です。なにしろただのライブラリなので、構文定義専用の特別な DSL を覚えたり、事前にツールを実行してパーサのソースコードを生成したりという手順が必要ないからです。 ## 100行ちょっとで書けるBanchaScriptコンパイラの完成 ここまでで変数の宣言の式や演算子式を構文解析できるようになりました。あとは、基本的には同じ調子で関数定義やら何やらの文のパーサを定義して、`Parsect.or` や `Parsect.many` のコンビネータでつなぎ合わせるだけです。BanchaScript の関数定義はつぎのようなものです。 def hoge(k){ var x = k * 2; return x; } これを読み取る関数定義のパーサも、先ほどの変数宣言と同じように `seq` を使ってトークンや式が出現する順番でひとつづつパーサを適用して読み取っていくだけです。まず`def`という予約語があって、識別子(関数名)、括弧で囲まれてカンマで区切られた識別子(引数)の列、中括弧で囲まれた文の列。これを `seq` で順番に読んでいきます。 // 関数の定義 var func = p.seq(s=>{ // 最初に予約語 def があって、 s( lexer.reserved("def") ); // 関数名 var name = s( lexer.identifier ); // コンマで区切られた識別子が、括弧で囲まれたものがあって(これが引数 args) var args = s( lexer.parens(p.sepBy(lexer.identifier, lexer.comma)) ); // 複数の文が中括弧で囲まれたものがある var body = s( lexer.braces(p.many(p.or(func, returnStatement, varStatement, exprStatement))) ); // 対応するJavaScriptコードを返す return s.success && ["function ", name, "(", args.join(','), "){", body.join(""), "}"].join(''); }); 筆者が BanchaScript のためにこれまでに実装したコードは 100行強ですが、以下の様な機能が実現できました。 * 関数の定義 * 関数の呼び出し * 変数の定義 * if文 * 四則演算 * ネストできるコメント まだ for 文などの制御文すらありませんが、ひとまず四則演算はできますし、関数だけあればあとはなんとかなる気がしないでもないので、ひとまずこれだけの言語仕様に留めておきます。 ## 10分くらいでわかるBanchaScriptのユーザ定義演算子 せっかくなのでBanchaScriptにはあまり他の AltJS 系の言語では見られない珍しい機能をつけようと思い、 **演算子のユーザ定義** を盛り込むことにしました。これは C++ や C# にあるような『演算子オーバーロード』ではなくて、優先順位と結合方向を指定した演算子をユーザが任意に記号を組み合わせて定義できるというものです。HaskellやSML、OCaml、Scalaあたりにはこの機能があります。BanchaScriptでは前置、後置、中置や優先順位、結合方向をすべて指定できますが、ここまでできる言語はかなり稀だと思います。たとえば、BanchaScriptでは次のような文でベクトルの加算を行う新たな演算子 `=+=` を定義することができます。 operator =+= infixl 2 = addVectors; `infixl 2` というのは新たな演算子 `=+=` が優先順位が2で左結合の中置演算子であることを示します。このとき、関数 `addVectors` の別名として `=+=` が定義され、 `a =+= b` という式は `addVectors(a, b)` という式へとコンパイルされます。BanchaScriptでのユーザ演算子は単なる他の関数の別名です。ベクトル演算をするときはかなり複雑な計算式になることが多く、これを関数呼び出しやメソッド呼び出しで書こうとすると括弧だらけになってとてもつらいのです。 BanchaScriptではもっとフリーダムな演算子も定義できます。次の前置演算子 `|^_^|<` は標準出力する関数 `log` の別名です。 // 演算子の定義 operator |^_^|< prefix 5 = log; // こうやってつかいます |^_^|< "わたしです"; ユーザ定義演算子があるということは事前に構文を書き下すことができないので、この機能がある言語は構文解析がかなり難しくなってしまいます。特に、事前にパーサを生成してしまうパーサジェネレータの類でこれを実現するのは困難です。BanchaScriptコンパイラでは、演算子の定義を見つけると、それと同時にパーサの一部をその場で書き換えて次の行から新しいパーサで構文解析を継続することで、1パスで構文解析を済ませるようになっています。BanchaScriptコンパイラには、次のような `topLevelStatement` という変数があり、現在の演算子テーブルからパーサを構成する `buildStatementParser` という関数を呼び出すことで初期化されています。 var topLevelStatement = buildStatementParser(); パーサ全体はトップレベルのステートメントをパースしようとするたびに適宜この変数を参照し、この変数に代入されているパーサのオブジェクトを呼び出しながらながらパースを行っています。関数 `buildStatementParser` は現在の演算子テーブルをもとにパーサを構成しますが、そのパーサは演算子の定義を見つけると次のように演算子テーブルに演算子を追加し、新たなパーサを再構築して先ほどの `topLevelStatement` に代入しています。 opTable[precedence].push(newOperator); topLevelStatement = buildStatementParser(); パーサを動的に書き換えるといっても、単に変数をひとつ書き換えているだけで難しいことは何もしていません。パーサコンビネータでは動的にパーサの構成を変化させることができるので、このような無茶な言語仕様でも簡単に構文解析できるのです。 ## ソースコード BanchaScriptコンパイラは [Parsect のサンプル](https://github.com/kontan/Parsect/blob/master/example/bancha/bancha.ts) として一緒に github で公開してますので、覗いてみたい方はどうぞ。Parsectにはまだあまりちゃんとマニュアルが書かれていないので、同梱されているサンプルコードを参考にしたり、本家 Parsec の解説を参考にしてみるといいかもしれません。かなり古くなっていますが、readmeも多少は参考になります。 [kontan/Parsect](https://github.com/kontan/Parsect) おまけですが、BanchaScriptのコンパイルと実行をブラウザで試せる Playground を作ってみました。でも機能が足りなさすぎてアレです。 [BanchaScript Playground](http://phyzkit.net/banchascript/bancha.html) ## その他のパーサコンビネータ実装 先程も簡単に触れましたが、パーサコンビネータの実装は案外たくさんあります。 * Haskell, [Parsec](http://hackage.haskell.org/package/parsec-3.1.3) * Java, [jparsec](http://jparsec.codehaus.org/) * Python, [pyparsing](http://pyparsing.wikispaces.com/) * Ruby, [rparsec](http://docs.codehaus.org/display/JPARSEC/Ruby+Parsec) * Perl, [Parse::RecDescent](http://search.cpan.org/~dconway/Parse-RecDescent-1.94/lib/Parse/RecDescent.pod) * JavaScript, [jsparsec(jshaskell)](https://code.google.com/p/jshaskell/) * C++, [boost Spirit](http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/index.html) * Objective-C, [Parcoa](https://github.com/brotchie/Parcoa) * Scala, [scala.util.parsing.combinator](http://www.scala-lang.org/api/current/index.html#scala.util.parsing.combinator.Parsers) * C#, [NParsec](http://jparsec.codehaus.org/NParsec+Tutorial), [Parseq](http://www.nuget.org/packages/Parseq/) * PHP, [Loco](http://qntm.org/loco) * Clojure, [fnparse](https://github.com/joshua-choi/fnparse) 探せばどの言語でもひとつくらいは見つかりそうです。Perl使いやRuby使いは正規表現(?)で何でも構文解析してそうですけど、パーサコンビネータも意外とあるものです。自分のよく使う言語のものを探して使ってみると良いのではないでしょうか。`makeTokenParser` や `buildExpressionParser`相当の機能があるライブラリは特に便利です。筆者のおすすめはやはり遅延評価やdo記法があるHaskellのParsecです。切れ味が違います。 ## まとまっていないまとめ * 自分の理想の言語を作る → 楽しい三(^q^)三 * パーサコンビネータを使えば複雑な構文解析も楽勝 * よく訓練されたParsec使いは[Perl6の完全なパーサを15分で書ける](http://d.hatena.ne.jp/kazu-yamamoto/20090309/1236590230)らしい。なにそれこわい ## 関連資料 * [プログラミング言語を作る](http://kmaebashi.com/programmer/devlang/) 昔ながらの yacc/lex による手法 * [「なぜ作ったのか?」、オレ様言語作った人々](http://www.atmarkit.co.jp/news/200708/07/ll3.html) * [コンパイラの構造を解説](http://www.gadgety.net/shin/tips/unix/compiler.html) |
|
| 401位 |
|
|||
|
22:18:47 |
|
|
#作業ディレクトリにGitを使う宣言をする
``` >> git init ``` #ステージングエリアにあげる ``` >> git add (ファイル名) or git add . ``` #コミットする ``` >> git commit "コミット名" ``` #コミットメッセージを1行以内に納める ``` >> git commit -m "コミット名" ``` #一つ前のコミットと統合する ``` >> git commit --amend -m "コミット名" ``` #gitのログを確認する ``` >> git log ``` #Gitのlogを簡潔にまとめて表示する ``` >> git log --oneline ``` #オプション git log --oneline git log -p(変更した場所を見たい場合) git log --stat(より詳しく変更した場所を見たい場合) #現在の状態 ``` >> git status ``` #前の状態に戻る ``` >> git checkout --(file名) ``` #何処を編集したのか知りたい場合 ``` >> git diff ``` #ステージングエリアにあげた場合、コミットで変更されるファイルが分かる ``` >> git diff --cached ``` #コミットした後の編集されたファイルが分かる ``` >> git diff -r (ID名) ``` #git addを取り消す ``` >> git reset HEAD または git reset HEAD (ファイル名) ``` #直前に戻る ``` >> git reset --hard HEAD ``` #1つ前に戻る ``` git reset --hard HEAD^ ``` #指定されたlogに戻る ``` >> git reset --hard (ID) ``` #マージを始めた頃のブランチに戻る ``` >> git reset --hard ORIG_HEAD ``` #git 修正したファイルの変更を取り消す事はできるけど、特定のバージョンに戻したい場合 ``` >> git checkout コミット ファイル ``` #ブランチの一覧表示 ``` >> git branch ``` #ブランチを作る ``` >> git branch ``` #ブランチを削除 ``` >> git branch -d (ブランチ名) ``` #リポジトリの複製 ``` >> git clone git@(アドレス名) ``` #新規ブランチを作りそれをカレントブランチにする ``` >> git checkout -b (ブランチ名) ``` #リモートリポジトリにPushする前に登録するコマンド ``` >> git remote add origin(リモートリポジトリ名) git@~ ``` #リモートリポジトリ削除 ``` >> git remote rm origin ``` #ローカルリポジトリをリモートリポジトリに同期する ``` >> git fetch origin ``` #リモートブランチと同期したデータ、追跡ブランチをローカルリポジトリに取り込む ``` >> git merge origin / (ブランチ名) ``` #mergeとfetchをまとめて行う ``` >> git pull origin (ブランチ名) ``` #ローカルブランチのデータをリモートブランチに送る(最新のもの) ``` >> git push origin (ブランチ名) ``` #他のブランチを現在のカレントブランチに取り込む ``` >> git merge (branch name) ``` #rebaseをする(履歴を綺麗にする、まとめてコミットを取り込む) ``` >> git rebase (branch name) ``` #rebaseインタラクティブモード ``` # intの所にはなにか任意の数字を入れる >> git rebase -i HEAD~int ``` #cherry-pickをする(直接コミットを取り込みたいとき) ``` >> git cherry-pick (コミットID) # コミットIDはgit logで確認 ``` #git の履歴を削除する ``` >> git filter-branch -f --index-filter 'git rm --ignore-unmatch filename' HEAD # -rfを付けるとディレクトリを削除 >> git filter-branch -f --index-filter 'git rm -rf --ignore-unmatch dirname' HEAD ``` #名前の登録 ``` >> config --global user.name ”(your name)" ``` #メールの登録 ``` >> config --global user.email "(your email)" ``` #メッセージの色分け ``` >> config --global color.ui true ``` #設定一覧 ``` >> config -l ``` #ヘルプを見る ``` >> config --help >> help config ``` #git省略コマンド 参考ページ http://tobysoft.net/wiki/index.php?git%2F%A5%B3%A5%DE%A5%F3%A5%C9%A4%CE%BE%CA%CE%AC(alias)%C0%DF%C4%EA%A4%F2%A4%B9%A4%EB%CA%FD%CB%A1 git config --global alias.co checkout git config --global alias.st 'status' git config --global alias.ci 'commit -a' git config --global alias.di 'diff' git config --global alias.br 'branch' .bashprofile,bashrcなどに記入 alias gco="git checkout" alias gst="git status" alias gci="git commit -m" alias gdi="git diff" alias gbr="git branch" |
|
| 402位 |
|
|||
|
02:51:00 |
(dotstudio株式会社 所属) |
|
Node.jsでのセッションの使い方の勉強です. jadeでやってるサンプルが多いですが,jadeはイマイチ直感的に書けない(慣れない)のでテンプレートエンジンはejsを使います. 勉強中なので自分が理解出来るように解説してみます. 参考: [Expressでログイン機能を作る - uchida75cmの日記](http://uchida75cm.hatenablog.com/entry/20110712/1310469113) 参考: [Node.js + Express でログイン認証機能を実装する - Devlog](http://kkurahar.github.io/blog/2013/05/10/node-express-session/) ##バージョン node v0.10.23 express 3.4.8 npm 1.3.17 MongoDB shell version: 2.4.8 ##まずはプロジェクト作成 $ express -e login $ cd login $ npm install $ node app.js とりあえずブラウザから`http://lobalhost:3000`にアクセスして確認してみましょう.エラーが無く,Expressの文字が表示されればOKです.  ##MongoDB関連モジュールのインストール MongoDBを利用するためのモジュールをインストールしておきましょう.調べると出てきますがmongooseはよく使われているみたいです. ``` $ npm install mongoose $ npm install connect-mongo ``` ##MongoDB起動 ターミナルをnode.jsとは別プロセス(別なタブやウィンドウ)で起動しましょう. プロジェクトフォルダ内にdbフォルダを作成し,`--dbpathオプション`でdbフォルダを保存先に指定するようにしました. ``` $ mkdir db $ mongod --nojournal --noprealloc --dbpath db ``` ##app.js ###connect-mongoの読み込み app.jsの上部のrequire()の部分に以下を追記します. ``` var MongoStore = require('connect-mongo')(express); ``` ###セッションを使う準備 sessionを使うためにはcookieを読み込む必要があるらしいので`app.use(app.router);`の前に以下を追記します. ```js: app.use(express.cookieParser()); //追加 app.use(express.session({ secret: 'secret', store: new MongoStore({ db: 'session', host: 'localhost', clear_interval: 60 * 60 }), cookie: { httpOnly: false, maxAge: new Date(Date.now() + 60 * 60 * 1000) } })); //追加 ``` ###認証用のバリデーター関数 セッションに値が無ければ/loginにリダイレクトさせます.ルーティング設定の前に記述しています. ```js: var loginCheck = function(req, res, next) { if(req.session.user){ next(); }else{ res.redirect('/login'); } }; ``` ###ルーティング設定 ルーティング設定はこんな感じです.↑で記述したloginCheck関数を咬ますことで認証必須のページと認証無しで見れるページを切り分けます. ```js: app.get('/', loginCheck, routes.index); app.get('/login', routes.login); app.post('/add', routes.add); app.get('/logout', function(req, res){ req.session.destroy(); console.log('deleted sesstion'); res.redirect('/'); }); ``` ##model app.jsと同じ階層にmodel.jsを作成しましょう. Model定義をしています. -db名:user -collection名:info(UserSchema) 最後の行の`exports.User`で他のファイルからModelを呼び出せるようにしています. ```js: var mongoose = require('mongoose'); var url = 'mongodb://localhost/user'; var db = mongoose.createConnection(url, function(err, res){ if(err){ console.log('Error connected: ' + url + ' - ' + err); }else{ console.log('Success connected: ' + url); } }); // Modelの定義 var UserSchema = new mongoose.Schema({ email : String, password : String },{collection: 'info'}); exports.User = db.model('User', UserSchema); ``` ##controller ###routes/index.js `var model = require('../model.js')`で↑で定義したmodelの情報を読み込みます. `User = model.User;`とすることで↑のUserSchemaが`routes/index.js`でも使えるようになります. また,sessionの情報は`req.session`に入っているのでconsole.log()などで確認してみるとわかると思います. ```js: /*モデル読み込み*/ var model = require('../model.js'), User = model.User; /*ログイン後ページ*/ exports.index = function(req, res){ res.render('index', { user: req.session.user}); console.log(req.session.user); }; /*ユーザー登録機能*/ exports.add = function(req, res){ var newUser = new User(req.body); newUser.save(function(err){ if(err){ console.log(err); res.redirect('back'); }else{ res.redirect('/'); } }); }; /*ログイン機能*/ exports.login = function(req, res){ var email = req.query.email; var password = req.query.password; var query = { "email": email, "password": password }; User.find(query, function(err, data){ if(err){ console.log(err); } if(data == ""){ res.render('login'); }else{ req.session.user = email; res.redirect('/'); } }); }; ``` ##view あとはフォームなどを作ればOKです. ###views/login.ejs ログイン前のページです. ```html: <!doctype html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>ログイン前</title> </head> <body> <h2>ログイン</h2> <form action="/login" method="GET"> <input type="text" name="email" placeholder="Email"/> <input type="password" name="password" placeholder="Password"/> <input type="submit">ログイン</button> </form> <h2>新規登録</h2> <form action="/add" method="POST"> <input type="text" name="email" placeholder="Email"/> <input type="password" name="password" placeholder="Password"/> <input type="submit">新規登録</button> </form> </body> </html> ``` ###画面例 /login  ###views/index.ejs ログイン後のページです. ```html: <!doctype html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>ログイン後</title> </head> <body> <h1>My Site</h1> <p>Welcome to My Site</p> <%= user %> さんようこそ。 <p> <a href="/logout">ログアウト</a> </p> </body> </html> ``` ###画面例 /index  ##動作確認 ログイン前に`/` or `/index`にアクセスしようとするとリダイレクトで`/login`が表示されると思います.新規登録(特に何も表示されませんが)をしてから新規登録したメールアドレスとパスワードでログインをすると`/`を見れるはずです.また,ログアウトのリンクを押すとsessionが破棄され`/logoin`に戻ると思います. お疲れ様でした〜. |
|
| 403位 |
|
|||
|
19:31:23 |
|
|
GitHubのdiffがメソッド名表示されて見やすかったので、ローカルでも出来ないか調べたのでそのメモ。
例えばGitHubのdiffはメソッド名`def is_repo?`が出る  https://github.com/github/hub/commit/87050ce94a97b0c382b99c975bde0c833332b38e 普通にしてるときのローカルのdiffはこんなかんじ。メソッド名出ない:confounded:  ローカルでもGitHubのようにdiffにメソッド名を出すようにするにはプロジェクトルートに ```.gitattributes *.rb diff=ruby ``` を置けば:ok_hand: ローカルはこんなかんじなる:heart_eyes:  `diff`で選択できるパターンは http://git-scm.com/docs/gitattributes#_defining_a_custom_hunk-header に掲載されているので、必要に応じて変える。 **2013/11/22 13:40 追記** グローバルな.gitattributesを指定したい場合は ```~/.gitconfig [core] attributesfile = ~/.gitattributes_global ```` ```~/.gitattributes_global *.rb diff=ruby ``` のように設定すれば:ok_hand: |
|
| 404位 |
|
|||
|
19:53:52 |
|
|
いつもコマンドを忘れるので覚書。
```sh: git clone /var/cvs/test ``` とかって感じでリモートのリポジトリをcloneしてきたとする。 で、`git branch`すればわかるんだけど、このままだとmasterブランチしかローカルにcloneできてない。別のブランチ(developmentとする)もローカルにcloneしたい。 という時はまず ```sh: git branch -r ``` でリモートのブランチ名を調べる。 そうすると以下のような感じで表示される。 > $ git branch -r > origin/HEAD -> origin/master > origin/development > origin/master で、この中からお目当てのブランチ名を探し(この場合は「origin/development」)、こいつを以下のようにしてcloneすればOK。 ```sh: git checkout -b development origin/development ``` checkoutの第一引数にローカルリポジトリでのブランチ名を、第二引数にリモートの落としていたいブランチ名を指定する。 |
|
| 405位 |
|
|||
|
20:25:07 |
(Magnet Inc 所属) |
|
こんにちは、 rosylilly です。すっかり Advent Calendar のことを忘れていて、大急ぎで記事を書いています。ちゃんと予定は管理しておかないといけませんね……
ということでいつも使っているコマンドの中で 3 つほど紹介されていなかったものがあったので紹介しようと思います。 ## git grep コマンド 特定の語句が含まれているファイルを検索し、そのファイルでその語句が含まれている行数などを調べるコマンドです。 ```shell $ git grep "検索単語" ``` 通常の `grep` コマンドなどを利用してもいいのですが、コマンドがわかりにくいのと、対象として指定するファイルを指定するのが面倒です。 その点 `git grep` だと、 git が管理しているファイルだけが検索対象になるので、手軽にリポジトリから検索することが出来ます。 また、オプションを使うことでより柔軟な検索も実現出来ます。一部を紹介しておきますので、`man git-grep` してより便利な使い方を探してみてください。 - `-i / --ignore-case` : 大文字小文字を区別せずマッチします - `-I` : バイナリファイルを無視します - `-w / --word-regexp` : 検索文字が単語としてマッチする時のみマッチするようになります。このオプションが指定されているとき `a` は `abc` にマッチしません - `-v / --invert-match` : 実際にマッチした単語がない行も表示します - `-h / -H` : マッチしたファイル名を行頭に表示するか否かの指定です。 - `--full-name` : ファイル名を表示する時、詳細なファイルパスを出力するようにします(デフォルトでは basename のみ) - `-E / --extended-regexp` : 検索に POSIX の拡張正規表現を利用します - `-G / --basic-regexp` : 検索に POSIX の標準正規表現を利用します - `-P / --perl-regexp` : 検索に Perl の正規表現を利用します - `-F / --fixed-strings` : 検索に正規表現を利用しません - `-n / --line-number` : マッチしたファイル名の後ろにマッチした行数を表示します - `-l / --files-with-matches / --name-only` : マッチしたファイルのファイル名だけを表示します - `-L / --files-without-match` : マッチしなかったファイル名だけを表示します ## git commit --amend --reset-author 会社のリポジトリを clone してきて、とりえずトピックブランチを切って、さっくり適当にコミットして……という時に、うっかり個人アカウントのメールアドレスと名前でコミットしてしまった!という時などに使います。 ```shell $ git ci -m "さっくりコミット" $ git config user.name "Sho Kusano" # --global はつけない $ git config user.email "sho_kusano@kaisya.com" $ git ci --amend --reset-author ``` さすがに個人用アドレスで会社のリポジトリにコミット、というのはマズイので、気づいたら直しておくのが吉です。 ## git blame 「誰がバグをコミットしたか」という犯人探しは良くないのですが、実際にバグが仕込まれてしまった場合、原因究明する時に多くの場合は「どのコミットでバグが入ったか」より「どこにバグが入ったか」の方が先に見つかります。 そういったとき、特定の行のバグがどのコミットで仕込まれたかを探すには、 `git blame` が便利です。 ```shell $ git blame src/bug.js ``` とすると、左から順に 1. (その行に最後に変更を与えた)コミットハッシュ 2. ファイル名 3. コミット者の名前 4. コミットした時間 5. 行数 6. ファイルの内容 という具合に表示されます。あとは目的の行までジャンプして、コミットハッシュを特定すれば解決、という具合です。 ## まとめ いかがでしたでしょうか?すこしでも日々の開発のお役に立てば幸いです。 git にかぎらず多人数で開発するときは「自分が書いていないソースコードをいかに高速に把握するか」という能力も重要になってくると思っています。そんな中で、git には今回紹介した以外にも様々な便利なサブコマンドがあるので、ぜひぜひ探してみてください。 もし便利なものがあったら僕にも教えていただけると幸いです! また、git で困った時に助けてくれる便利なコマンドラインツール、[git-tasukete](https://github.com/rosylilly/git-tasukete) のコミッタも募集中です! それでは! |
|
| 406位 |
|
|||
|
03:30:38 |
(Mercari, Inc. 所属) |
|
説明するほどのでもない気がするけど、書いてとせがまれたので書いてみる。
適当に書いたので、細かい説明とか用語の使い方がおかしいのは大目に見てもらう方向で。 ssh-agentは、sshの鍵をssh-agentデーモン(?)に保持させておいて、使い回せるようにするツール。 使い方は、ssh-agentを起動して、そのシェル内でssh-add でkeyを追加するだけ。 % ssh-agent bash % ssh-add /path/to/my-private-key とやっておく。 ちなみに、ssh-agent使うなら普通はagent forwardingした方がらく。 agent forwardingは、多段sshする際にsshの鍵の認証情報をforwardする機能で、たとえば A(local) -> B(DMZ) -> C(App server) -> D (DB server) みたいな多段構成でsshをわたっていくとき、Dに入るための秘密鍵をいちいちBやCに置くのは面倒だしセキュリティ的にモアレで気持ち悪い。 そんなときにagent forwardingを使うと、Aで持っている秘密鍵をつかってBだけでなくCやDも認証できてしまう。 ssh-agentつかってれば、パス入力なしで全部ばんばんわたっていける。 やり方は単純で、ssh-agentの起動しているところ(ここでいうA) からBにsshでアクセスする際に % ssh -A B と、-Aオプションをつけてやるだけ。B->CやC->Dのときにはもう-Aはいらない。 (Bはサーバの名前。れいの名前のつけ方が良くなかった。。) こんな感じでやると、色々さくさくできて便利。 ### 追記: agent forwardingを常時使いたい場合は~/.ssh/configで ForwardAgent yes を書いておけば-Aいらない。 |
|
| 407位 |
|
|||
|
13:43:07 |
(Sansan株式会社 所属) |
|
CapistranoはオープンソースのRuby製ソフトウェアデプロイメントツールです。
 複数のサーバへのソフトウェアのデプロイメントを自動化する事ができます。 railsを対象によく用いられますが、railsなどのフレームワークや言語に限らずデプロイする事ができます。 現在最新版は3.0.1となります。(2014/1/12現在) なおCapistranoは2から3への変更時にパラメータの持ち方などが変わり互換性がなくなっています。今回は3以降の記述で行います。 [公式ページ](http://capistranorb.com/) |今回の環境 | |---------| |CentOS6.4 | |Ruby 2.1 | |rbenv | ###1.インストール gemを使ってcapistranoをインストールします。 rootでない場合は```sudo```も付けてください。 railsじゃない場合はcapistrano-railsは必要ありません。 ``` gem install capistrano gem install capistrano-rails gem install capistrano-rbenv #またはcapistrano-rvm ``` bundlerを使っている場合は下記の記述をGemfileに追記してbundler installを実行してください。 ``` gem 'capistrano', '~> 3.0.1' gem 'capistrano-rails' gem 'capistrano-bundler' ``` ###2.設定ファイル作成 設定ファイルを作成します。railsのルートディレクトリで下記コマンドを実行してください。 ``` cap install ``` これで下記のファイルが作成されます。 ``` railsルート ├─ Capfile ├─ config │ ├─ deploy │ │ ├─production.rb │ │ └─staging.rb │ └─deploy.rb └─ lib └─capistrano └─tasks ``` ###3.Capfile記述 まずCapfileを記述します。 初期ではコメントアウトがあるので必要なものをコメントアウトから外します。 ```ruby:Capfile # Load DSL and Setup Up Stages require 'capistrano/setup' # Includes default deployment tasks require 'capistrano/deploy' # Includes tasks from other gems included in your Gemfile # # For documentation on these, see for example: # # https://github.com/capistrano/rvm # https://github.com/capistrano/rbenv # https://github.com/capistrano/chruby # https://github.com/capistrano/bundler # https://github.com/capistrano/rails/tree/master/assets # https://github.com/capistrano/rails/tree/master/migrations # # require 'capistrano/rvm' #rvmならこちらを外す require 'capistrano/rbenv' #rbenvならこちらを外す set :rbenv_type, :system # or :system, depends on your rbenv setup set :rbenv_ruby, '2.1.0' #rubyのバージョンを指定 # require 'capistrano/chruby' require 'capistrano/bundler' require 'capistrano/rails/assets' require 'capistrano/rails/migrations' # Loads custom tasks from `lib/capistrano/tasks' if you have any defined. Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r } ``` ###4.deploy/{環境}.rb記述 deployフォルダ配下にproduction.rbやstagingなどが初期ではあります。 追加したければdevelopment.rbなどを追加します。 ```ruby:development.rb set :stage, :production #環境名 #各サーバの役割を記述 role :app, %w{deploy@example.com} #アプリケーションサーバ role :web, %w{deploy@example.com} #webサーバ role :db, %w{deploy@example.com} #DBサーバ #サーバ情報記述 server 'example.com', #サーバ名 user: 'deploy', #実行ユーザ roles: %w{web app db}, # サーバの役割 ssh_options: { keys: %w(~/.ssh/xxxxx/private_key), auth_methods: %w(publickey), # 認証方法 passwordも可能 #password: 'xxxxx' #password指定 } ``` ###4.外部ファイルにタスクを記述する deploy.rbの外部ファイルにタスクを記述する事ができます。 lib/capistrano/tasks配下にXXXX.capファイルを作成します。 今回はunicornの起動タスクunicorn.rbとして記述します。 ```ruby:unicorn.rb namespace :unicorn do task :environment do set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid" set :unicorn_config, "#{current_path}/config/unicorn/#{fetch(:rails_env)}/unicorn.rb" end def start_unicorn within current_path do execute :bundle, :exec, :unicorn_rails, "-c #{fetch(:unicorn_config)} -E #{fetch(:rails_env)} -p 8080 -D" end end def stop_unicorn execute :kill, "-s QUIT $(< #{fetch(:unicorn_pid)})" end def reload_unicorn execute :kill, "-s USR2 $(< #{fetch(:unicorn_pid)})" end def force_stop_unicorn execute :kill, "$(< #{fetch(:unicorn_pid)})" end desc "Start unicorn server" task :start => :environment do on roles(:app) do start_unicorn end end desc "Stop unicorn server gracefully" task :stop => :environment do on roles(:app) do stop_unicorn end end desc "Restart unicorn server gracefully" task :restart => :environment do on roles(:app) do if test("[ -f #{fetch(:unicorn_pid)} ]") reload_unicorn else start_unicorn end end end desc "Stop unicorn server immediately" task :force_stop => :environment do on roles(:app) do force_stop_unicorn end end end ``` ##5.deploy.rb記述 deploy.rbにデプロイ時のタスクを記述していきます。 ここでは各環境共通の処理、変数を記述します。 ```set :対象変数```で変数に値を設定します。この値は```fetch :対象変数```で使用する事ができます。 ```ruby:deploy.rb #アプリケーション名 set :application,'test' #レポジトリURL set :repo_url, 'git@bitbucket.org:xxxxxx/test.git' #対象ブランチ masterに固定 set :branch, 'master' #デプロイ先ディレクトリ フルパスで指定 set :deploy_to, '/var/www/test' #バージョン管理方法 subverion, git, mercurial, cvs, bzrなど set :scm, :git #情報レベル info or debug set :log_level, :debug #sudoに必要 これをtrueにするとssh -tで実行される set :pty, true #sharedに入るものを指定 set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets bundle public/system public/assets} #capistrano用bundleするのに必要 set :default_env, { path: "/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH" } #5回分のreleasesを保持する set :keep_releases, 5 #タスク定義 namespace :deploy do #タスクnamespace #desc 'タスク説明' #task :restart do #タスク定義 #ここにタスク処理内容を記述 #end #after :finishing, 'deploy:cleanup' #task実行タイミングを指定できます。詳細は下記 #http://capistranorb.com/documentation/getting-started/flow/ #サンプルにunicorn再起動タスク desc 'Restart application' task :restart do invoke 'unicorn:restart' #lib/capustrano/tasks/unicorn.cap内処理を実行 end after :finishing, 'deploy:cleanup' end ``` ###6.実行 実行します。 ``` cap <対象環境名> deploy ``` これで対象サーバに対してデプロイメントが実行されます。 Capistranoは実行すると下記のような配置がされます。 ``` railsルート ├─current #releases内最新のシンボリックリンク ├─ releases #配下に現在時刻に基づく名前のサブディレクトリが作成され最新コードがチェックアウトされる └─ shared #リリース間で共用するファイルを置いておく ``` |
|
| 408位 |
|
|||
|
09:59:19 |
(Increments Inc 所属) |
|
(おそらく)git v1.8から以下のようなメッセージが出るようになった人は多いと思います.この意味を解説します.
``` $ g push warning: push.default is unset; its implicit value is changing in Git 2.0 from 'matching' to 'simple'. To squelch this message and maintain the current behavior after the default changes, use: git config --global push.default matching To squelch this message and adopt the new behavior now, use: git config --global push.default simple See 'git help config' and search for 'push.default' for further information. (the 'simple' mode was introduced in Git 1.7.11. Use the similar mode 'current' instead of 'simple' if you sometimes use older versions of Git) ``` ## push.default: simple? matching? `push.default`は,`git push`時にrefspec(ブランチ名やタグ名)を指定しなかったときの挙動を設定する.(例えば`git push`, `git push origin`としたとき) `push.default`のデフォルト設定がGit 2.0でmatchingからsimpleに代わる. ### simple 現在のブランチをupstreamのブランチにpushする.ただしupstreamのブランチ名が現在のブランチと異なる場合はpushしない. "This is the safest option and is well-suited for beginners."とのこと.あまり起きなさそうだけど,間違ったブランチをtrack設定してしまったときなどはpushできないので安全? ### matching `git push`時に同名のブランチ(=マッチするブランチ)をそれぞれpushする.いままでのデフォルト. ### その他 push.defaultに設定できる値としては以下がある. - nothing - pushしない.つまり必ずgit push origin masterのようにrefsを指定する必要がある. - current - 現在のブランチを同名のリモートブランチにpushする. - upstream - 現在のブランチをupstreamのブランチにpushする. ## 結論 warningの通り,いまのgitの挙動を保ちたいならmatching,現在のbranchのみ安全にpushしたいならsimpleにするとよい. ````sh git config --global push.default matching # or git config --global push.default simple ``` |
|
| 409位 |
|
|||
|
10:27:07 |
|
|
[Dash](https://itunes.apple.com/jp/app/dash-docs-snippets/id458034879) は様々な API リファレンスを高速に検索できる Mac 用アプリケーション。
 特徴は * 主要なライブラリ、フレームワークの API リファレンスをまとめて検索・閲覧できる。 * ドキュメントのダウンロードと更新が簡単。 * 検索が非常に高速。 * IDE 連携やショートカットなど、キー操作に最適化。 * ダウンロードは無料だけど、アプリ内課金しないと使いものにならない(笑) ~~ちなみに時々値引きセールをやっているので、そのタイミングで購入するとお得。~~ 最近はあまり値引きの話を聞かない... ### 基本設定  * Launch Dash at login: ログイン時に Dash を起動。必ず ON にしましょう。 * Global search shortcut: Dash の検索画面を呼び出すショートカット。お好みのキーを設定。 * Search using selected text: 選択中のテキストを Dash で検索するショートカット。クリックするとシステム環境設定が起動するので、`Look Up in Dash` をチェックの上、お好みのキーを入力。 > `Search using selected text` のショートカットを入力しても Dash の検索が行われない場合、以下を試してみる。 > > * 利用したいアプリケーション上で、適当なテキストを選択 > * アプリケーションメニューにある `サービス(Services)` にマウスカーソルを当てる > (サブメニューに `Look Up in Dash` の項目が表示されていればOK)  > Android Studio や RubyMine 等の Intelli J 系 IDE の場合、サービスメニューに `Look Up in Dash` が表示されない。 > その代わりに `Dash` プラグインが提供されているので、これをインストールして IDE のショートカットを設定するとよい。 ### ドキュメントのダウンロードと設定 `Preferences` から各種設定を行う。 #### Downloads タブで使いたいドキュメントをダウンロード  * All Docsets: 各種プラットフォームやフレームワークのドキュメント一覧。 * Cheat Sheets: [新機能]様々のチートシートの一覧。自作もできる。 * Cocoa Docsets: CocoaDocs でドキュメントを検索してダウンロードできる。オープンソース系を探すのに便利。 * Ruby Docsets: RubyGems にあるドキュメントを検索してダウンロードできる。 * Java Docsets: (私は使ってませんので割愛) * Scala Docsets: (私は使ってませんので割愛) #### Docsets タブで検索対象のドキュメントを選択  > このリストの 2 列目に書かれている "iphoneos:" といったキーワードは検索の絞り込みに利用できるので覚えておくとよい。 > ("iphoneos:" → "ios:"、"javascript:" → "js:" と短くしておいた方がさらに便利) あとは `General` タブの `global search shortcut` を設定しておけばどこからでも呼び出せる。 ### 検索とショートカットキー * 検索ボックスにキーワードを入力すると即座に候補と内容が表示される。 * "iphoneos:nsobject" のように初めに docset のキーワードを入力すると対象ドキュメントを絞り込める。 * "nsobject init" のようにスペース区切りでキーワードを入力すると、2 語目でページ内検索を行う。 その他、覚えておくと便利なショートカット | ショートカットキー | 機能 | |:-------------------|:-----| |↓ or ↑|検索候補の選択| |Control+↓ or Control+↑|ページスクロール| |ESC|入力キーワードのクリア| |(2 語目を入れた状態で)Enter or Shift+Enter|次の検索結果、前の検索結果にジャンプ| ### スニペット #### 登録  * Dash のウィンドウを開く。 * 左ペインにある `{}` アイコンを選択。 * `+` アイコンをクリックして `New snippet` を選択。 * 右ペインの `Abbreviation` にスニペットの挿入キーワードを入力。 * 右ペインの中央にコードを入力。 * `__name__` のように記述すると、挿入時に入力を行うプレースホルダになる。 * 日付や時間を挿入する `@date` や `@time` のようなプレースホルダもある。 #### 利用 まず Mac OS の設定を変更。 * システム環境設定の「セキュリティとプライバシー」を開く。 * 「プライバシー」タブの「アクセシビリティ」項目にある `Dash` にチェックを入れる。  あとはテキストエディタで登録したスニペットのキーワードを入力すると、以下のように挿入内容が表示される。  ### CheatSheet を自作する https://github.com/Kapeli/cheatset#readme にほとんど説明されているが、 * cheatset という gem をインストール。 * エディタでチートシートを作成。 * cheatset generate で docset を生成。 * 生成した docset を Finder 上でダブルクリックしてインストール。 インストールするとこんな感じで閲覧できる。  |
|
| 410位 |
|
|||
|
05:58:34 |
(SonicGarden Inc. 所属) |
|
## はじめに: 関連(association)って何? 関連とはModel(データ)同士のつながりのことです。 単純な例で言うと、ブログの投稿(Post)とそれに対するコメント(Comment)は **関連** しています。 たとえば以下のような1件の投稿と2件のコメントを考えてみます。 ````text 新しいMacBook Proを買いました!(投稿) |-- わー、いいなあ!(コメント) `-- うらやましすぎる!!(コメント) ```` 上のデータを取得するRailsのコードはこんな感じになります。 ````ruby post.text #=> 新しいMacBook Proを買いました! post.comments.size #=> 2 post.comments[0].text #=> わー、いいなあ! post.comments[1].text #=> うらやましすぎる!! ```` コメントから投稿を参照することもできます。 ````ruby comment.text #=> わー、いいなあ! comment.post.text #=> 新しいMacBook Proを買いました! ```` 親子関係で言い換えると、投稿が **親** で、コメントが **子** です。 また、1件の投稿に対し、コメントは複数(0件以上)付けることができます。 これをModelの定義で表現するとこのようになります。 ````ruby class Post # 投稿は複数のコメントを持つ。投稿から見るとコメントは子 has_many :comments end class Comment # コメントから見ると投稿は親 belongs_to :post end ```` ついでにクラス図も載せておきましょう。  ## 親子関係を保存する方法あれこれ さて、データを取得するコードはわかりましたが、この親子関係を保存するときはどんなコードを書けば良いのでしょうか? もしこんなコードを書いたとしても関連をうまく保存することはできません。 ````ruby post = Post.create(text: "新しいMacBook Proを買いました!") comment = Comment.create(text: "わー、いいなあ!") # 投稿にコメントが1件も付いていない post.comments.size #=> 0 ```` 親子関係を保存する方法はいくつかバリエーションがあるので、それを今から紹介していきます。 ### 1. parent.children.create / parent.children.build `parent.children.create`の形式で子のデータを作ると関連が設定できます。 ````ruby post = Post.create(text: "新しいMacBook Proを買いました!") comment = post.comments.create(text: "わー、いいなあ!") post.comments.size #=> 1 post.comments[0].text #=> わー、いいなあ! ```` `create`の代わりに`build`を使うこともできます。 この場合、データは保存されません。 ````ruby post = Post.create(text: "新しいMacBook Proを買いました!") comment = post.comments.build(text: "わー、いいなあ!") post.comments.size #=> 1 post.comments[0].text #=> わー、いいなあ! # buildを使ったのでコメントは保存されていない comment.persisted? #=> false ```` ### 2. parent.children << child `parent.children << child`という形式で関連を設定することもできます。 ````ruby post = Post.create(text: "新しいMacBook Proを買いました!") comment = Comment.new(text: "わー、いいなあ!") post.comments << comment post.comments.size #=> 1 post.comments[0].text #=> わー、いいなあ! ```` `<<`を使うと、データが同時に保存されてしまう点に注意してください。 ````ruby comment.persisted? #=> false post.comments << comment # コメントが保存される comment.persisted? #=> true ```` #### [備考] parent.children = children 同じような考え方で、`parent.children = children`という形式もあります。 配列を渡すので複数のコメントを同時に設定できますが、既存のコメントは削除されるのが特殊な点です。 使用する場合はこの副作用に十分注意してください。 ````ruby post = Post.create(text: "新しいMacBook Proを買いました!") comment_old = Comment.new(text: "やった、コメント1番乗り!!") post.comments << comment_old post.comments.size #=> 1 post.comments[0].text #=> やった、コメント1番乗り!! comment_1 = Comment.new(text: "わー、いいなあ!") comment_2 = Comment.new(text: "うらやましすぎる!!") # 新しい関連が保存されるのと同時に既存のコメントは削除される post.comments = [comment_1, comment_2] post.comments.size #=> 2 post.comments[0].text #=> わー、いいなあ! post.comments[1].text #=> うらやましすぎる!! comment_1.persisted? #=> true comment_2.persisted? #=> true comment_old.destroyed? #=> true ```` ### 3. child.parent = parent `child.parent = parent`というように、「子に親を設定する」という形式もあります。 ````ruby post = Post.create(text: "新しいMacBook Proを買いました!") comment = Comment.new(text: "わー、いいなあ!") comment.post = post comment.save # 関連を確定するためには子のsaveが必要 post.comments.size #=> 1 post.comments[0].text #=> わー、いいなあ! ```` #### Child.new(parent: parent) / Child.create(parent: parent) さらに、このバリエーションとして`new`や`create`の引数に親を渡す形式も考えられます。 ````ruby post = Post.create(text: "新しいMacBook Proを買いました!") comment_1 = Comment.new(text: "わー、いいなあ!", post: post) comment_1.save comment_2 = Comment.create(text: "うらやましすぎる!!", post: post) post.comments.size #=> 2 post.comments[0].text #=> わー、いいなあ! post.comments[1].text #=> うらやましすぎる!! ```` ### 4. child.parent_id = parent_id オブジェクトの代わりに親のidを子に設定する方法もあります。 ````ruby post = Post.create(text: "新しいMacBook Proを買いました!") comment = Comment.new(text: "わー、いいなあ!") comment.post_id = post.id comment.save # 関連を確定するためには子のsaveが必要 post.comments.size #=> 1 post.comments[0].text #=> わー、いいなあ! ```` #### Child.new(parent_id: parent_id) / Child.create(parent_id: parent_id) idを設定する場合でも`new`や`create`を使うことができます。 ````ruby post = Post.create(text: "新しいMacBook Proを買いました!") comment_1 = Comment.new(text: "わー、いいなあ!", post_id: post.id) comment_1.save comment_2 = Comment.create(text: "うらやましすぎる!!", post_id: post.id) post.comments.size #=> 2 post.comments[0].text #=> わー、いいなあ! post.comments[1].text #=> うらやましすぎる!! ```` 親子関係を保存する主な方法はこんな感じです。 ## 上の説明をRSpecで表現する 最後に、上の説明をRSpecで表現してみましょう。 これは自分の説明が間違っていないか実際に動かして確認する目的で作成しました。 動作確認に使ったRailsアプリケーションと実際のSpecはこちらにあります。 - https://github.com/JunichiIto/association-sandbox - https://github.com/JunichiIto/association-sandbox/blob/master/spec/models/comment_spec.rb ````ruby require 'spec_helper' describe Comment do let!(:post) { Post.create(text: "新しいMacBook Proを買いました!") } context "not associated" do specify do Comment.new(text: "わー、いいなあ!") expect(post.comments).to be_empty end end describe "parent.children.create" do specify do comment = post.comments.create(text: "わー、いいなあ!") expect(post.comments).to eq [comment] expect(comment).to be_persisted end end describe "parent.children.build" do specify do comment = post.comments.build(text: "わー、いいなあ!") expect(post.comments).to eq [comment] expect(comment).to_not be_persisted end end describe "parent.children << child" do specify do comment = Comment.new(text: "わー、いいなあ!") expect(comment).to_not be_persisted post.comments << comment expect(post.comments).to eq [comment] expect(comment).to be_persisted end end describe "parent.children = children" do specify do comment_old = Comment.new(text: "やった、コメント1番乗り!!") post.comments << comment_old expect(post.comments).to eq [comment_old] expect(comment_old).to be_persisted comment_1 = Comment.new(text: "わー、いいなあ!") comment_2 = Comment.new(text: "うらやましすぎる!!") post.comments = [comment_1, comment_2] expect(post.comments).to eq [comment_1, comment_2] expect(comment_1).to be_persisted expect(comment_2).to be_persisted expect(comment_old).to be_destroyed end end describe "child.parent = parent" do specify do comment = Comment.new(text: "わー、いいなあ!") comment.post = post comment.save expect(post.comments).to eq [comment] end end describe "Child.new(parent: parent) / Child.create(parent: parent)" do specify do comment_1 = Comment.new(text: "わー、いいなあ!", post: post) comment_1.save comment_2 = Comment.create(text: "うらやましすぎる!!", post: post) expect(post.comments).to eq [comment_1, comment_2] end end describe "child.parent_id = parent_id" do specify do comment = Comment.new(text: "わー、いいなあ!") comment.post_id = post.id comment.save expect(post.comments).to eq [comment] end end describe "Child.new(parent_id: parent_id) / Child.create(parent_id: parent_id)" do specify do comment_1 = Comment.new(text: "わー、いいなあ!", post_id: post.id) comment_1.save comment_2 = Comment.create(text: "うらやましすぎる!!", post_id: post.id) expect(post.comments).to eq [comment_1, comment_2] end end end ```` ## 役に立つ情報源 関連についてもっと深く知りたい場合はRails Guidesをじっくり読みましょう。 - http://guides.rubyonrails.org/association_basics.html RSpecでRailsでテストする方法を学習するためにはこちらの電子書籍がオススメです!(宣伝) - [Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)  |
|
| 411位 |
|
|||
|
15:31:08 |
|
|
ransackはrails用の検索機能を実装するためのgemです。 比較的シンプルなコードで複雑な検索を実装することができます。 ransackの概要と使い方については[Ransackのススメ](http://qiita.com/items/9a95d91f2b97a08b96b0 "Ransackのススメ")を参照してください。 ここでは実際に使用するまでのサンプルを作ってみます。 ####プロジェクトを作成 ```terminal rails new ransack_study -T --skip-bundle cd ransack_study ``` localeとtimezoneを設定 ```config/application.rb require File.expand_path('../boot', __FILE__) 〜〜 ( 中略 ) 〜〜 module RansackStudy class Application < Rails::Application 〜〜 ( 中略 ) 〜〜 # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. # config.time_zone = 'Central Time (US & Canada)' config.time_zone = 'Tokyo' config.active_record.default_timezone = :local # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de config.i18n.default_locale = :ja # Configure the default encoding used in templates for Ruby 1.9. config.encoding = "utf-8" 〜〜 ( 中略 ) 〜〜 end end ``` ####ransackのインストール Gemfileに追記 ```terminal vi Gemfile ``` ```Gemfile source 'https://rubygems.org' gem 'rails', '3.2.11' # Bundle edge Rails instead: # gem 'rails', :git => 'git://github.com/rails/rails.git' gem 'sqlite3' gem 'rails-i18n' # この行を追加(ransackには関係ないけどdate_select用) gem 'ransack' # この行を追加 〜〜( 以下 略 )〜〜 ``` bundle installを実行 ```terminal bundle install ``` ####今回のサンプル用にscaffold ユーザーモデルと、ユーザーが紐づく掲示板モデルを作成します。 ```terminal rails g scaffold user name:string rails g scaffold topic title:string content:text user:references ``` 掲示板モデルでユーザーを登録するためにちょろっと修正 ```app/models/topic.rb class Topic < ActiveRecord::Base belongs_to :user attr_accessible :content, :title, :user_id #この行を修正 end ``` ユーザーをselectタグで選択できるように ```app/views/_form.html.erb <%= form_for(@topic) do |f| %> 〜〜 ( 中略 ) 〜〜 <div class="field"> <%= f.label :user %><br /> <%= f.select :user_id, User.all.map { |u| [u.name, u.id] } %><!--この行を修正--> </div> <div class="actions"> <%= f.submit %> </div> <% end %> ``` 一覧からcontentを削除し、created_atを追加、ユーザー名を表示するように変更 ```app/views/index.html.erb <h1>Listing topics</h1> <table> <tr> <th>Title</th> <th>User</th> <th>Created At</th><!--この行を修正--> <th></th> <th></th> <th></th> </tr> <% @topics.each do |topic| %> <tr> <td><%= topic.title %></td> <td><%= topic.user.name %></td><!--この行を修正--> <td><%= topic.created_at.strftime('%Y/%m/%d %H:%M') %></td><!--この行を修正--> <td><%= link_to 'Show', topic %></td> <td><%= link_to 'Edit', edit_topic_path(topic) %></td> <td><%= link_to 'Destroy', topic, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </table> <br /> <%= link_to 'New Topic', new_topic_path %> ``` 詳細でユーザー名を表示するように変更 ```app/views/topic/show.html.erb 〜〜 ( 中略 ) 〜〜 <p> <b>User:</b> <%= @topic.user.name %><!--この行を修正--> </p> 〜〜 ( 中略 ) 〜〜 ``` とりあえず実行してみる ```terminal rake db:migrate rails s ``` [http://localhost:3000/users](http://localhost:3000/users)でユーザーを適当に登録 [http://localhost:3000/topics](http://localhost:3000/topics)で掲示板を何件か登録 今の画面はこんな感じ。   ここまでで準備は完了です。 ここからは実際にransackの機能を使って検索フォームを作成してきます。 ####ransackの基本的な使い方 ransackは基本的にはsearchメソッドで条件を指定し、resultで結果を返します。 ```ruby User.search(name_cont: 'ほげ').result ``` 上の処理を実行すると次のようなSQL文を発行し、ActiveRecord::Relationオブジェクトを返します。 ``` SELECT "users".* FROM "users" WHERE ("users"."name" LIKE '%ほげ%') ``` また、ransackには検索フォーム用のフォームビルダーも用意されています、 今回はそれを利用して掲示板の検索フォームを作成していきます。 ####search_form_forの利用 TopicsControllerでsearchオブジェクトを生成 ```app/controllers/topics_controller.rb class TopicsController < ApplicationController # GET /topics # GET /topics.json def index @search = Topic.search(params[:q]) # この行を追加 @topics = @search.result #この行を修正 respond_to do |format| format.html # index.html.erb format.json { render json: @topics } end end 〜〜 ( 中略 ) 〜〜 end ``` search_form_forを利用して検索フォームを作成 まずはtopicのtitleを前後あいまい検索する場合index.html.erbを以下のように修正 ```app/views/topics/index.html.erb <h1>Listing topics</h1> <%= search_form_for @search do |f| %> <%= f.label :title_cont, 'タイトル' %> <%= f.text_field :title_cont %> <div> <%= f.submit '検索する' %> </div> <% end %> 〜〜 ( 以下 略 ) 〜〜 ``` ###以上 これだけtitleの曖昧検索が実装されます。 動かしてみるとこんな感じになってると思います。  ここからはindex.htmlを追加するだけで検索処理を実装することが可能です。 試しに作成日の範囲指定を追加してみます。 index.html.erbを以下のように修正します。 ```app/views/topics/index.html.erb <h1>Listing topics</h1> <%= search_form_for @search do |f| %> <%= f.label :title_cont, 'タイトル' %> <%= f.text_field :title_cont %> <!--ここから追加--> <br /> <%= f.label :created_at, '作成日' %> <%= f.date_select :created_at_gteq, include_blank: true %>〜 <%= f.date_select :created_at_lteq, include_blank: true %> <!--ここまで追加--> <div> <%= f.submit '検索する' %> </div> <% end %> 〜〜 ( 以下 略 ) 〜〜 ``` 動かしてみると以下のようになり、作成日での絞込みが実装されました。  日付の指定をした場合に発行されるSQLを確認してみます。 ``` SELECT "topics".* FROM "topics" WHERE (("topics"."title" LIKE '%について%' AND "topics"."created_at" >= '2013-05-01 00:00:00.000000' AND "topics"."created_at" <= '2013-05-09 00:00:00.000000')) ``` このままではtoのほうの日付の条件が指定した日付の 00:00:00までしか対象になりません。 日付で絞り込むなら 23:59:59までを検索対象にしたいですよね。 なので、以下のようなファイル(config/initializers/ransack.rb)を作成します。 ```config/initializers/ransack.rb Ransack.configure do |config| config.add_predicate 'lteq_end_of_day', :arel_predicate => 'lteq', :formatter => proc { |v| v.end_of_day }, :compounds => false end ``` formatterを指定し、index.html.erbも以下のように修正します。 ```app/views/topics/index.html.erb <h1>Listing topics</h1> <%= search_form_for @search do |f| %> <%= f.label :title_cont, 'タイトル' %> <%= f.text_field :title_cont %> <br /> <%= f.label :created_at, '作成日' %> <%= f.date_select :created_at_gteq, include_blank: true %>〜 <%= f.date_select :created_at_lteq_end_of_day, include_blank: true %><!--この行を修正--> <div> <%= f.submit '検索する' %> </div> <% end %> 〜〜 ( 以下 略 ) 〜〜 ``` ここでサーバーを一度再起動し、再度検索をし発行されるSQLを確認します。 ``` SELECT "topics".* FROM "topics" WHERE (("topics"."title" LIKE '%について%' AND "topics"."created_at" >= '2013-05-01 00:00:00.000000' AND "topics"."created_at" <= '2013-05-08 23:59:59.999999')) ``` いい感じですね♪ 最後に、関連するテーブルの項目での絞込みも試してみます。 topicsにひもづくユーザの名前で前後あいまい検索を行います。 index.html.erbを以下のように修正します。 ```app/views/topics/index.html.erb <h1>Listing topics</h1> ```app/views/topics/index.html.erb <h1>Listing topics</h1> <%= search_form_for @search do |f| %> <%= f.label :title_cont, 'タイトル' %> <%= f.text_field :title_cont %> <br /> <%= f.label :created_at, '作成日' %> <%= f.date_select :created_at_gteq, include_blank: true %>〜 <%= f.date_select :created_at_lteq_end_of_day, include_blank: true %> <!--ここから追加--> <br /> <%= f.label :user, 'ユーザー' %> <%= f.text_field :user_name_cont %> <!--ここまで追加--> <div> <%= f.submit '検索する' %> </div> <% end %> 〜〜 ( 以下 略 ) 〜〜 ``` 実行すると以下のようになり、ユーザー名での絞込みが実装できましたね。  ####ソートの実装 ransackにはソート用のhelperも用意されています。 それらを利用して掲示板一覧にソートを実装してみます。これもviewの修正だけで実装することが可能です。 index.html.erbのtableのヘッダーを以下のように修正します。 ```app/views/topics/index.html.erb <h1>Listing topics</h1> 〜〜 ( 中略 ) 〜〜 <table> <tr> <th><%= sort_link(@search, :title, 'タイトル') %></th><!--この行を修正--> <th><%= sort_link(@search, :user_name, 'ユーザー') %></th><!--この行を修正--> <th><%= sort_link(@search, :created_at, '作成日時') %></th><!--この行を修正--> <th></th> <th></th> <th></th> </tr> 〜〜 ( 以下 略 ) 〜〜 ``` これで実行すると、ヘッダーがリンクになりクリックするとソートが実装されていることがわかります。  ####まとめ ransackとransackのフォームビルダーを利用することで、比較的複雑な検索条件もほぼviewを修正するだけで実装することができました。動的に検索条件を作成することもview側を動的に作成するだけで可能ですので比較的簡単に実装できるかと思います。それらのチュートリアルがRailsCasts([#370 Ransack](http://railscasts.com/episodes/370-ransack?language=ja&view=asciicast))にありますので、そちらを参照してみてください。 ####今回のソース 今回のソースは以下で公開しています。 LuckOfWise/ransack_study https://github.com/LuckOfWise/ransack_study |
|
| 412位 |
|
|||
|
21:02:28 |
|
|
1. 開発中とリリースビルドをわけて考える。
* 開発中はとにかく速度重視。テスト重視。 * リリースビルドのほうに、自動化できる便利なタスクを出来るだけ突っ込んでおく。 * 開発中に使用するソースファイルから、リリースビルドに余計なファイルを混ぜない。 こんな考え方でGruntfileを書いてみました。 プロジェクトごとに最適化したGruntfileを作るということも魅力的な挑戦なのですが、私は開発者一人で短納期な案件をいくつもこなさないといけないので、そのために導入したタスクランナーのはずなのにGruntfileの開発やメンテに時間を取られるのは本末転倒になってしまうので、出来る限り汎用的に使えるように気をつけて書きました。 さらに何か特定の開発スタイルやフレームワークに依存しないように、特殊なディレクトリ構成などを必要としない点も気を付けました。 普段は開発は私一人ですが、デザイナーやコーダーや開発者などスキルの異なる複数人でコーディングを同時進行する場合も多々あるからです。 Gruntを導入することによって自分や誰かのスキルや開発スタイルに依存したり変更することがないよう、こころがけています。 CoffeeScript、Sass対応。 Lessは対応しているはずだけど自分はまだ使ってません。 次の段階でTypeScriptにも対応させるつもり。 Grunt0.4.1対応。 プラグインは管理が面倒臭いので、grunt-contribを全部入りで入れています。 ```Coffeescript:Gruntfile.coffee #除外ファイル exclude = [ '!**/.DS_Store' '!**/Thumbs.db' '!**/*.coffee' '!**/*.map' '!**/*.scss' '!**/*.less' '!**/*.s.css' '!**/*.l.css' '!**/<%= dir.cssCompile %>/' '!**/coffee/' '!**/sass/' '!**/less/' '!**/_notes/' '!**/.idea/' '!**/.gitignore' '!**/*.mno' '!**/Templates/' '!**/Library' '!**/*.dwt' '!**/*.lbi' '!**/*.fla' ] module.exports = (grunt) -> pkg = grunt.file.readJSON 'package.json' grunt.initConfig #ディレクトリ設定 dir : src : 'src' dist : 'dist' test : '<%= dir.src %>/test' doc : 'docs' js : 'js' css : 'css' cssCompile : '<%= dir.src %>/<%= dir.css %>.compile' #package.jsonの読み込み pkg : pkg #クリーン clean: js: src : '<%= dir.src %>/<%= dir.js %>/*' css: src : '<%= dir.src %>/<%= dir.css %>/*' build : src : ['<%= dir.dist %>/**', '<%= dir.doc %>/**'] #CoffeeScriptコンパイル coffee: options: sourceMap : true #通常はルートディレクトリのcoffeeディレクトリに置かれたスクリプトをメインとする main : src : '<%= dir.src %>/coffee/*.coffee' dest : '<%= dir.src %>/<%= dir.js %>/<%= pkg.name %>.js' #サブディレクトリのCoffeeScriptもコンパイル all : expand : true ext : '.js' src : ['<%= dir.src %>/**/*.coffee', '!<%= coffee.main.src %>'] #Sassコンパイル sass: #通常はルートディレクトリのsassディレクトリに置かれたCSSをメインとする main : src : '<%= dir.src %>/sass/*.scss' dest : '<%= dir.cssCompile %>/<%= pkg.name %>.s.css' #サブディレクトリのSassもコンパイル all : expand : true ext : '.css' src : ['<%= dir.src %>/**/*.scss', '!<%= sass.main.src %>', '!<%= dir.src %>/<%= dir.css %>/<%= pkg.name %>.css', '<%= concat.css.dest %>'] #Lessコンパイル less: #通常はルートディレクトリのlessディレクトリに置かれたCSSをメインとする main : src : '<%= dir.src %>/less/*.less' dest : '<%= dir.cssCompile %>/<%= pkg.name %>.l.css' #サブディレクトリのlassもコンパイル all : expand : true ext : '.css' src : ['<%= dir.src %>/**/*.less', '!<%= less.main.src %>', '!<%= dir.src %>/<%= dir.css %>/<%= pkg.name %>.css', '<%= concat.css.dest %>'] #結合 concat: #コンパイルされたCSSを結合 css : src : '<%= dir.cssCompile %>/*.*.css' dest : '<%= dir.src %>/<%= dir.css %>/<%= pkg.name %>.css' #画像最適化 imagemin: dev : optimizationLevel: 3 files : [ expand: true src: '<%= dir.src %>/**/*.{png,jpg,jpeg}' ] dist : optimizationLevel: 3 files : [ expand: true src: '<%= dir.dist %>/**/*.{png,jpg,jpeg}' ] #監視ファイル watch: coffee: files : '<%= coffee.main.src %>' tasks : 'coffee:main' coffeeAll: files : '<%= coffee.all.src %>' tasks : 'coffee:all' sass: files : '<%= sass.main.src %>' tasks : 'sass:main' sassAll: files : '<%= sass.all.src %>' tasks : 'sass:all' less: files : '<%= less.main.src %>' tasks : 'less:main' lessAll: files : '<%= less.all.src %>' tasks : 'less:all' css: files : '<%= concat.css.src %>' tasks : 'concat:css' #コピー copy: build : expand : true filter: 'isFile' cwd : '<%= dir.src %>/' src : ['**'].concat exclude dest : '<%= dir.dist %>/' #htmlのminify htmlmin: all : options : removeComments : true removeCommentsFromCDATA : true removeCDATASectionsFromCDATA : true collapseWhitespace : true removeRedundantAttributes : true removeOptionalTags : true expand : true src : '<%= dir.dist %>/**/*.html' #JSのminify uglify: options : banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n' main : expand : true src : '<%= dir.dist %>/<%= dir.js %>/<%= pkg.name %>.js' all : expand : true src : ['<%= dir.dist %>/**/*.js', '!<%= uglify.main.src %>'] #CSSのminify cssmin: main : expand : true src : '<$= dir.dist %>/<%= dir.css %>/<%= pkg.name %>.css' all : expand : true src : ['<%= dir.dist %>/**/*.css', '!<%= cssmin.main.src %>'] #yuidoc yuidoc: dist: name : '<%= pkg.name %>' description : '<%= pkg.description %>' version : '<%= pkg.version %>' options : paths : '<%= dir.src %>/' outdir : '<%= dir.doc %>' syntaxtype : 'coffee' extension : '.coffee' for taskName of pkg.devDependencies when taskName.substring(0, 6) is 'grunt-' grunt.loadNpmTasks taskName #どういった用途で使用するか明示的にするためにも、冗長なエイリアスも指定しておく grunt.registerTask 'default', 'watch' grunt.registerTask 'w', 'watch' grunt.registerTask 'c', ['clean:js', 'clean:css'] grunt.registerTask 'main', ['coffee:main', 'sass:main', 'less:main'] grunt.registerTask 'compile', ['coffee:all', 'sass:all', 'less:all'] grunt.registerTask 'img', 'imagemin:dev' grunt.registerTask 'build', ['clean:build', 'copy', 'imagemin:dist', 'htmlmin', 'uglify', 'cssmin', 'yuidoc'] ``` プロジェクトディレクトリは次のようになります。 + src 開発用ディレクトリ。ここでごりごり開発していきます。 + dist リリースビルド用ディレクトリ。ビルドすると自動的に作られます。 + doc ドキュメントが入るディレクトリ。これもビルドすると自動的に作られます。 開発用ディレクトリとリリースビルドの他にもうひとつ、開発中ビルドのディレクトリ「dev」も欲しかったのですが、LiveReloadなどリロード系監視システムでdevフォルダを監視してしまうと、開発ビルドが行われるたびに何十回、何百回とリロードしまくってしまうので、srcをソースディレクトリ兼開発用ディレクトリとして、LiveReloadなどの監視対象としています。 さらにデザイナー系コーダーの方と同時開発する場合でも、srcさえ共有しておけば相手はGruntを使っていなくても**LiveReloadやCodeKitなどで同じように**SassやCompass、LessやCoffeeScriptなども導入できるという利点もあります。 ## 使い方 開発中のときとリリースビルドするときで動作が違います。 順番に説明していきます。 ## 開発中 プロジェクトディレクトリで次のように`grunt`してsrcディレクトリを監視開始します。 ``` $ grunt ``` srcディレクトリを監視して、ファイルの更新があった場合次のような動作をします。 ### `/src/coffee/*.coffee`に変更があった場合 /src/coffee/ 内の *.coffee ファイルを全て結合して `/src/js/<%= pkg.name %>.js`として保存します。 (<%= pkg.name %>はパッケージ名) なのでHTML側では ```html <script src="/js/< パッケージ名 >.js"></script> ``` このようにパッケージ名.jsファイルを読み込むようにしておけばいいことになります。 パッケージ名は、プロジェクト毎にGruntをインストールするときに使う、`package.json`の ```javascript:package.json { "name": "パッケージ名" }; ``` この部分です。 /src/coffee はリリースビルドの時にはビルドされません。 リリースビルドの動作は後でも詳細に書きます。 ### その他 `*.coffee` に変更があった場合 自動でCoffeeを結合してくれるのもいいんですが、場合によっては単体のCoffeeScriptを単体のままJavaScriptとして出したいときもあります。 そこで、 `/src/coffee` 以外のどこに書いた `*.coffee` でも、同一ディレクトリに `< 同名 >.js` としてコンパイルする機能も備えています。 つまり /src/hoge/fuga.coffee は /src/hoge/fuga.js としてコンパイルされます。 fuga.coffeeのほうは、リリースビルドのときにはビルドされないので安心です。 ### SassやLessも基本的には同じ ただしちょっとだけ複雑なので順番に説明します。 ### `/src/sass/*.scss`に変更があった場合 `/src/sass`内の`*.scss`ファイルに変更があった場合、一旦 `/src/css.compile/` というディレクトリに `<%= pkg.name %>.s.css` というファイル名でコンパイルします。 つまりHTML側では /css.compile/< パッケージ名 >.s.css をリンクすればいいのだなと言うのはちょっと待ってください。 ### `/src/less/*.less`ファイルに変更があった場合 sassのときと似たように、 `/src/css.compile/` というディレクトリに `<%= pkg.name %>.l.css` というファイル名でコンパイルします。 つまり /src/css.compile/ディレクトリには Sassで作った < パッケージ名 >.s.css と、Lessで作った < パッケージ名 >.l.css の二つのファイルが入る可能性があるということになります。 このディレクトリ内ファイルをHTML側からリンクするのはちょっと待ってください。 **/src/css.compile/はリリースビルドに含まれません。** もう一段階Gruntが自動化処理します。 ### `/src/css.compile/` 内ファイルに変更があった場合 つまりSassなりLessなりがコンパイルされた場合ですが、Gruntにさらにそれをwatchさせていて、 `/src/css/<%= pkg.name %>`として結合します。 なのでHTML側では ```html <link rel="stylesheet" href="/css/< パッケージ名 >.css"> ``` としておけばいいということになります。 ### その他`*.scss`や`*.less`に変更があった場合 CoffeeScriptのときと事情が一緒ですが、全部をまとめてひとつのファイルに結合したくないとき、つまり個別にSassを書いたら個別のcssファイルとしてコンパイルしたい場合もあります。 `/src/sass/`や`src/less/`以外の場所にある`*.scss`や`*.less`ファイルはそのまま単体のcssファイルとしてコンパイルさせる機能も持っています。 つまり /src/hoge/fuga.scss は /src/hoge/fuga.css としてコンパイルされます。 もちろんfuga.scssのほうはリリースビルドには含まれないので安心です。 ### 画像最適化を手動で行う リリースビルドの時に画像最適化は自動で行いますので、ふだん開発中は気にしなくてもいいのですが、コマンドラインで ``` $ grunt img ``` と打つことで開発中の/src/ディレクトリ内画像を最適化処理することもできます。 開発中に画像のファイルサイズなどが気になった場合に使えます。 基本的な開発中の機能は以上です。 次はリリースビルド時にどういうことをするかの説明に入ります。 ## リリースビルド リリースビルドをするには、コマンドラインで ``` $ grunt build ``` と入力します。 プロジェクトディレクトリに、 /dist/ というディレクトリを自動的に作り、その中にリリースビルドが書き出されます。 ## 除外ファイル リリースビルドに含ませたくないソースファイルや無駄なファイルは、 Gruntfile.coffeeの一番最初の ```Coffeescript:Gruntfile.coffee #除外ファイル exclude = [ '!**/.DS_Store' '!**/Thumbs.db' '!**/*.coffee' '!**/*.map' '!**/*.scss' '!**/*.less' '!**/*.s.css' '!**/*.l.css' '!**/<%= dir.cssCompile %>/' '!**/coffee/' '!**/sass/' '!**/less/' '!**/_notes/' '!**/.idea/' '!**/.gitignore' '!**/*.mno' '!**/Templates/' '!**/Library' '!**/*.dwt' '!**/*.lbi' '!**/*.fla' ] ``` この部分で指定できます。 .DS_StoreやThumbs.db、CoffeescriptやSassのソースファイルやソースディレクトリ、その他flaファイルやpsd素材ファイル、Dreamweaverのテンプレートやデザインノートファイルなど、リリースに含ませたくないファイルは最初から除外しています。 他に除外したいファイルがあれば追加してください。 ## minify HTML、Javascript、CSSファイルをminifyします。 さらに画像はプロジェクト内にあるpngとjpegをimageminで最適化を行っています。 ## ドキュメント さらに /doc/ディレクトリにYUIDocのドキュメント化も行っています。 まだちょっと自分でも使っていませんが、いずれ便利に使える日が来ることを信じて。 ## さいごに 既にリリースビルドが終わっていて、更新作業のときに`grunt build`し忘れて古いファイルをアップしてしまったという、Gruntを導入したことによって増えたヒューマンエラーがありましたが、リリースビルドを習慣づけるという人的対応、あと将来的に`grunt watch`のタイミングでリリースビルドを削除してしまうと言うシステム対応も考えていて、そのへんでどうにか切り抜けようと思います。 いちおう3案件ほど実戦投入してきて、だいぶこなれてきたので思い切って晒してみました。 最後に思ったんだけど、これアップグレードしていくんでGitHubに置いたほうがよかった。 |
|
| 413位 |
|
|||
|
16:36:09 |
(Increments inc. 所属) |
|
2016年2月現在、JSHintよりESLintの利用を奨励します。
---- **全て** の JavaScript ファイルは JSHint ないしその他のソース解析ツールで管理されるべきだと思っている。 今回は JSHint の基本的な使い方を説明する。 ##インストール JSHint は Node.js で実装されているのでまずはそれをインストールする。Mac なら `brew install node` でサクッとインストール可能。また、インストールには npm を使うのでそれもいれる。そして npm をつかって JSHint をインストールする。 `-g` オプションはシステムにインストールするという意味。インストールディレクトリを PATH に追加するのを忘れない(デフォルトで追加されたかどうか記憶が曖昧)。 ```sh % brew install node % curl https://npmjs.org/install.sh | sh # npm のインストール % npm install -g jshint ``` ##.jshintrcと.jshintignoreをプロジェクトルートに設置する `.jshintrc` は `jshint` の設定を書く。`jshint` は個々のファイル単位や、関数単位での設定もできる。その方法は後述する。詳しいオプションの説明は [JSHint -Documentation](http://www.jshint.com/docs/) 参照。 **Relaxing Options** はデフォルトで禁止されているものを許可する項目なので、注意して使うようにする。 ここでは自分の設定を紹介する。ちなみに JSON 形式。 ```js:.jshintrc { "strict" : true, // "use strict" を強制 "indent" : 2, // インデントの深さ "maxlen" : 80, // 一行の最大長 "unused" : true, // 宣言したきり使っていない変数を検出 // グローバル変数へのアクセスの管理 "undef" : true, // グローバル変数へのアクセスを禁止 "browser" : true, // ブラウザ用のやつは許可 "dojo" : true, // dojo tool kit用のやつは許可 "devel" : true, // consoleやalertを許可 "debug" : true, // debugger を許可 "globals": { "_": false, "Backbone": false }, // Relaxing Options - 危険性を認識した上で設定すること "eqnull" : true, // == null を許可 "expr" : true // x || (x = 1); とかができるようにする } ``` *グローバル変数のへのアクセスの管理* に並んでいる `browser` `dojo` `devel` によって具体的に許可される項目は [/src/shared/vars.js](https://github.com/jshint/jshint/blob/master/src/shared/vars.js) を読めば分かる。ちなみに `dojo` を入れているのは `require.js` を使っていて `define` と `require` を許可したかったため。 `globals` で任意のグローバル変数を追加できる。個々の `globals` が何故 `false` なのかは後述。 `.jshintignore` は名前から分かるように `jshint` コマンドで無視するファイルと記述する。ファイル名ではなく、ここではプロジェクトルートからの相対パスで記述する。現時点でグロブは使えない。プロジェクトで使うライブラリなどを指定する。 ``` js/require-jquery.js tests/vendor/qunit.js ``` ##実行してみる 実行する時はプロジェクトルートで実行する。 ```sh % jshint js/**/*.js tests/**/*.js ``` この時 `.jshintignore` で指定されたファイルは無視される。 ##ファイルローカル、関数ローカルな設定をする どうしても全体より条件を緩くしたいファイルや関数は出てくる。逆に厳しくしたい時もあるかも知れないが、基本的には全体としては厳しく、必要に応じて緩和するようにした方がよい。それはさておき、このようなときは先頭に次のようなコードを入れる。 `/*` と `jshint` の間は **くっつけて書く** 。スペースが入ると単なるコメントとして扱われてしまうので注意。 ```js /*jshint strict: false */ ``` これがファイルの先頭にあれば、そのファイルでは `use strict` の使用は強制されない。関数の先頭ならその関数で `use strict` を使わなくてよい。ちなみにこれをファイルの先頭においたとしてもグローバルに `use strict` するには `globalstrict: true`が別途必要である(グローバルで使うとサードパーティのライブラリが動かなくなる可能性が高いのでおすすめしない)。 グローバル変数へのアクセスの管理には `/*global */` を使う。`.jshintrc` で `"jquery": true` とするのは、全てのファイルの先頭で次のように宣言するのと等しい。 ```js /*global jQuery: false $: false */ ``` ここで `false` の意味は、ローカルで `jQuery` と `$` を **上書きできない** という意味。 `true` で上書きできるようになるが、基本的に `false` 以外設定するべきではない。 ##Vim使いはsyntasticをインストールしカレントディレクトリをプロジェクトルートにする [scrooloose/syntastic - GitHub](https://github.com/scrooloose/syntastic) をインストールする。デフォルトで JavaScript の保存時に jshint がインストールされていれば実行してくれるようになる。この時カレントディレクトリにある `.jshintrc` はちゃんと考慮してくれるので(より正確に言えば設定ファイルを指定する `--config` オプションを使わずに `jshint` コマンドを起動してくれるのでデフォルトの動作であるカレントディレクトリの `.jshintrc` がそのまま使われる。)vim を起動する時は必ずプロジェクトルートで起動するようにする。そうすれば極論としては手で jshint コマンドを一度も叩く必要はない。 もし全ての JS ファイルで同じ `.jshintrc` を使いたいなら `g:syntastic_javascript_jshint_conf` で指定する。その場合、カレントディレクトリの `.jshintrc` が使われ無くなってしまうので [thinca/vim-localrc - GitHub](https://github.com/thinca/vim-localrc) とか使って設定を上書きする必要がある。 |
|
| 414位 |
|
|||
|
02:21:32 |
(Wantedly, Inc. 所属) |
|
ライブラリを使う時、バージョンが合わなくてうまく動かないという問題は、`Ruby`に限らず、どのプログラミング言語でも常に悩みの種である。特にDepedencyの多いライブラリをインストール/インクルードするときは、大体この問題で躓く。`C++`とかでGUI関係のライブラリを入れるときは何度も挫折したし、特に、進化が速く変更の多い`Perl`,`PHP`をはじめとしたスクリプト言語では普通にそういう問題に出くわす。
もちろん`Ruby`もその例外ではないのだが、より個人がライブラリ(gem)を作って、githubなどに上げ、公開していくという文化がある割に、あまりこの問題に悩まされないので、`Ruby(on Rails)`は比較的うまくやっていると思う。 これは、`Bundler`というツール自体の功績でもあるけれど、ライブラリを作る個人個人が、ちゃんと一定の規則に従ってバージョン付けして、依存関係を正しく`Gemfile`に書くという文化によるものが大きいと思う。なので、`Bundler`を支える文化のセマンティック・バージョニングと、`Gemfile`の書き方について紹介しようと思う。 今回は、AdventCalendarということで、あんまりRubyを使わない人にも参考になるテーマで書いてみたつもり。 他言語プログラマでも、特に、自分でライブラリを作ってみようかなと思っている人は絶対に知っておくべき。 ## Semantic Versioning (バージョン付けの規則) バージョンには付け方があり、基本的にはこの法則に従うべきというガイドラインが存在する。 ちゃんと意味のあるバージョン付けをしましょうということなのだが、それが、Semantic Versioningという名前で以下のURLで公開されている。 http://semver.org/ http://shijimiii.info/technical-memo/semver/ (日本語訳) ここでは、`3.2.8`のように、バージョンを常に3つの数字を`.`で区切った方法(`X.Y.Z`形式)で、指定する方法のみ説明する。なお、上のURLには`1.0.0-alpha.1`や`1.3.7+build.11.e0f985a`などのより細かい規則も載っている。 ### Semantic Versioningの大原則 まず、大原則として以下のルールに則る。 > *公開APIを定め、それを基にバージョンを付ける。* ### Semantic Versioningの詳細ルール あとは、3桁それぞれに意味を持たせ、以下のルールに従ってバージョン付けする。 - すべての公開される変更でバージョンを上げる - 3つの数字`X.Y.Z`の意味はそれぞれ`Major.Minor.Patch` - `Major`をあげるときは後方互換のない変更 - 新しいAPIの公開など - `Minor`をあげるときは後方互換のある変更 - APIの追加、サポートを外そうとしているAPIに警告を出すなど - `Patch`をあげるときはバグ修正 - バグ修正の定義は、正しくない挙動を直すこと - 安定した公式APIが定まるまでは、`0.y.z`形式にしておき、この状況では例外的にいつAPIを変えても良い ちなみにバージョンは少数ではないので、`1.10`と`1.9`は`1.10`の方が新しいバージョンである。 ### 具体例 バージョン付けの例としては、以下のような流れになる。 ``` 0.0.1 # Initial Version 0.0.2 0.1.0 # 0.0.2と比べて機能が大きく変わった。 1.0.0 # 多分Stableであろう、公式APIを定めた! 1.0.1 1.0.2 1.0.3 … 1.0.9 1.0.10 1.0.11 … 1.0.17 # バグだらけでいろいろ修正 1.1.0 # 追加APIを導入。ついでにバグも修正(Minorをあげるのと同時にバグ修正してもOK) 2.0.0 # 過去のしがらみを断ち切り、リニューアルしたAPIを公開 ``` ## GemfileでのVersion指定の仕方 例えば`hoge`という名前のgemがあったとする ### 安全に今のバージョンのみ使いたい場合 ```rb:Gemfile gem "hoge", "2.1.8" # 2.1.8のみ使う ``` ### 今後のバグ修正のみ受け入れたい場合 基本は、後方互換のある修正はすべて受け入れればよいので数字を2つだけ書いて指定する。 ```rb:Gemfile gem "hoge", "~> 2.1" # 2.1.0以上3.0.0未満の最新のものを使用 ``` 例えば`devise`などの大き目のgemだったり、なんだかんだでバグ修正以外の機能追加が入ると動かなくなりそうというようなものは数字3つで指定しておく。ちゃんとガイドラインにしたがって運用されていて、公開APIが変わってなくても、推奨されるディレクトリ構成だったり、設定の仕方だったりが変わるというのもありえる。 ```rb:Gemfile gem "hoge", "~> 2.1.8" # 2.1.8以上2.2.0未満の最新のものを使用 ``` ### 今後の変更は全て受け入れ最新に追従したい場合 基本は、以下のように何も指定しないでOK ```rb:Gemfile gem "hoge" ``` 公開するgemなどを作っていて、見知らぬユーザーの環境に古い`hoge`gemが入っていると困るなどという場合は以下のように`>=`を使う。 ```rb:Gemfile gem "hoge", ">= 2.1.3" # 2.1.3以上の最新のものを使う ``` `2.1.3`の部分は頑張って自分の書いたものが`hoge`gemのどのバージョンからなら動くかを調べて埋める。`">= 2.1"`かもしれないし、`">= 1"`かもしれない。 ### 参照 http://gembundler.com/gemfile.html |
|
| 415位 |
|
|||
|
23:57:35 |
|
|
最初に。内容に誤謬がありましたら申し訳在りません。訂正を歓迎します。
## tableView:heightForRowAtIndexPath: は rowHeight で置き換えるべきか [UITableViewCellの高さが常に一定の時はrowHeightを使う - Qiita](http://qiita.com/taka0125/items/eda7692034a68f7c0097) <del>この記事には正しいことが書いてあるのですけど、影響があるのは **表示されるセル数** が100や1000に到達するような稀有なケースです。<br /></del> <del>通常のテーブルビューでは、セルは一度に高々12程度しか表示しないため、`tableView:heightForRowAtIndexPath:`を`rowHeight`に置き換えることによる劇的なパフォーマンス良化はありません。</del> このことについて、`rowHeight`プロパティのリファレンスには次のように触れています。 >There are performance implications to using tableView:heightForRowAtIndexPath: instead of rowHeight. Every time a table view is displayed, it calls tableView:heightForRowAtIndexPath: on the delegate for each of its rows, which can result in a significant performance problem with table views having a large number of rows (approximately 1000 or more). <del>ここでは約1,000回以上`delegate`メソッドが呼び出されると、重篤なパフォーマンス悪化が起きると警告していますが、通常の利用ではまずありえない状況です。</del> **UPDATE:** コメントにてkishikawakatsumi様よりご指摘を頂きました。実際にパフォーマンスに影響を及ぼすのは、「表示されているセル数」ではなく「`dataSource`の全体の数」が正しいとのことです。というのも、スクロールバーのために`contentSize`の値を計算しなければならないため、一度`delegate`に対して全ての`indexPath`に対して高さを取得する必要があるからです。 よって`dataSource`の`row`の数によっては置き換えによるパフォーマンス良化はある、が正しいかと思われます。申し訳在りません。 ## 問題は動的なセルの高さの計算 `UITableView`の実際的な利用時には、主としてテキスト長の問題により、セルの高さを動的に決定しなければならないケースが多いかと思います。 そしてユーザー体験を向上させる上で重要なのは、 **セルの高さが動的となる`dataSource`の読み込み時に、セル高の計算量で発生する負荷を軽減する** ようにすること、です。 セルの高さを取得するには、`intrinsicContentSize`や`sizeWithThats:`、`boundingRectWithSize:options:attributes:context:`のような高コストのメソッドを使わざるを得ません。 もし可変長の長いタイムラインを読み込む必要性のあるアプリの場合、この呼び出しによる計算量は無視できなくなります。 ※もし **`sizeWithFont:`を未だ使っている人がいるとしたら、使用を即座にやめましょう** 。このメソッドはdeprecatedになりましたし、なによりiOS7 SDK上では正しい結果を返しません。 ## 高さの見積もりを返す そこでiOS7からは、`UITableViewDelegate`に **`tableView:estimatedHeightForRowAtIndexPath:`** が追加されました。 このメソッドを実装する場合、当該の`indexPath`のテーブルビューセルが必要とする高さを、見積もった値を返却するようにします。例えば文字数からおおよその行数を割り出すなどでしょうか。 すると`UITableView`はまずその値を利用し、実際にセルを描画する必要が生じるタイミングまで、 **実際の計算を遅延させることができる** のです。 当然ながら、`tableView:estimatedHeightForRowAtIndexPath`の実装は軽量である必要があります。そして、必ず`tableView:heightForRowAtIndexPath:`を同時に実装する必要があります。 また、推定の高さで固定長で扱って問題がない場合、`UITableView`の`estimatedHeight`プロパティをセットすることもできます。このプロパティのデフォルト値は0で、推測の高さを持たないことを意味しています。 ## 高さの見積もりを返さなくていいケース 高さ計算のコストが低く、見積もりを返さなくても問題ないと判断できるケースでは、`UITableViewAutomaticDimension`の定数を返却することができます。 この定数を返却された場合、通常通り高さを計算します。 何気に見逃している人が多いと思うのですが、この定数はiOS5から存在するもので、`tableView:heightForRowAtIndexPath`でも利用することができます。この場合は`rowHeight`の値が適用されます。 # まとめ `UITableView`が行の高さを必要としたときの流れをまとめます。 1. `delegate`が`tableView:estimatedHeightForRowAtIndexPath`に応答するかチェックします。した場合は、暫定的にその返却値を見積もりの高さとして利用します。 2. `UITableView`が`estimatedHeight`プロパティを持つかチェックします。0以外の値がセットされている場合、暫定的に見積もりの高さとして利用します。 3. `delegate`が`tableView:heightForRowAtIndexPath`に応答するかチェックします。応答し、かつ1.と2.が実装されていない場合、`UITableView`は`contentSize`の計算のために即座に高さを取得します。実装されている場合、呼び出しは実際にセルを表示されるタイミングまで遅延されます。 4. 1.および3.に応答しない場合、`rowHeight`プロパティを高さの値として利用します。 |
|
| 416位 |
|
|||
|
21:11:36 |
|
|
こんにちは、Qiita二回目の投稿です、 [yosuke_furukawa](https://twitter.com/yosuke_furukawa) と申します、Golang勉強中です。
Qiitaの[一回目の投稿もScrapingネタだったのですが、](http://qiita.com/yosuke_furukawa/items/c17c5aac348b6ed29294)二回目もGolang勉強中ということでQiitaのScrapingネタで行きます。 まず、goqueryの説明に行く前に単純なやり方でscrapingしてみます。 Golangで単純なスクレイピングをするためには、以下のモジュールを利用するとできます。 - [net/http](http://golang.org/pkg/net/http/) httpリクエストを送るためのモジュール - [code.google.com/p/go.net/html](http://godoc.org/code.google.com/p/go.net/html) html解析をするためのモジュール 超シンプルなサンプルとして、該当のurlのaタグのhrefにある値だけ取得する場合は以下の様な感じになるかと思います。 # go.net/htmlで頑張る ```go package main import ( "code.google.com/p/go.net/html" "fmt" "io" "net/http" ) type Result struct { Url string } func ParseItem(r io.Reader) []Result { results := []Result{} doc, err := html.Parse(r) if err != nil { fmt.Println(err) } var result Result var f func(*html.Node) f = func(n *html.Node) { // n.Typeでノードの型をチェックできる、ElementNodeでHTMLタグのNode。 // n.Dataでノートの値をチェックする、aタグをチェックしている if n.Type == html.ElementNode && n.Data == "a" { // n.Attrで属性を一覧する // ここでもう少し頑張るとparseできる for _, a := range n.Attr { if a.Key == "href" { result.Url = a.Val results = append(results, result) } } } for c := n.FirstChild; c != nil; c = c.NextSibling { f(c) } } f(doc) return results } func GetPage(url string) []Result { //http.GetでGetリクエストを発行する res, err := http.Get(url) if err != nil { fmt.Println(err) } // deferでやるとReaderを関数の終わりで必ずCloseしてくれる。便利!! defer res.Body.Close() results := ParseItem(res.Body) return results } func main() { url := "http://qiita.com/advent-calendar/2013/" results := GetPage(url) for _, result := range results { fmt.Println(result.Url) } } ``` # go-html-transformを使う また、単純にページから情報を抽出する、というよりもDOMの中から不要なものを削ったり、ページを加工するなら[code.google.com/p/go.net/html](http://godoc.org/code.google.com/p/go.net/html)よりも[code.google.com/p/go-html-transform/html/transform](http://godoc.org/code.google.com/p/go-html-transform/html/transform)を使うのがいいかと思います。 CSSセレクタが使えるので、いい感じにfilterを書けます。(ただ名前の通りhtmlの加工目的なので、htmlから必要な情報を抽出するのには向かない気もします。) ```go package main import ( "code.google.com/p/go-html-transform/html/transform" "fmt" "io" "net/http" ) type Result struct { Url string } func ParseItem(r io.Reader) { // transformのインスタンスを作る t, _ := transform.NewFromReader(r) // Applyメソッドで自分のDOMに反映する。 // Applyメソッド内では、TransformFuncを受け付けるようになっており、 // Replaceの他にもDOMを追加するAppendChildrenやPrependChildrenなどもある。 // ページを加工するならこっちのが便利。 // ちなみに以下の処理で不要なページを削っている t.Apply(transform.Replace(), "script") t.Apply(transform.Replace(), "footer") t.Apply(transform.Replace(), "meta") t.Apply(transform.Replace(), "ul") t.Apply(transform.Replace(), "li") t.Apply(transform.Replace(), "link") t.Apply(transform.Replace(), "div.day") t.Apply(transform.Replace(), "div.advent-calendar-breadcrumb") t.Apply(transform.Replace(), "a.post-user-icon") fmt.Println(t.String()) } func GetPage(url string) { //http.GetでGetリクエストを発行する res, err := http.Get(url) if err != nil { fmt.Println(err) } // deferでやるとReaderを関数の終わりで必ずCloseしてくれる。便利!! defer res.Body.Close() ParseItem(res.Body) } func main() { url := "http://qiita.com/advent-calendar/2013/" GetPage(url) } ``` # goqueryを使う と、色々と書きましたが、jQueryライクなセレクタを持ち、httpのリクエストの処理も兼ね備えているgoqueryを使うのがスクレイピングには一番向きます。Golangの勉強ということで、なるべくモジュール組み合わせて、試したかったのです。すいません。 ```go package main import ( "fmt" "github.com/PuerkitoBio/goquery" ) func GetPage(url string) { doc, _ := goquery.NewDocument(url) doc.Find("a").Each(func(_ int, s *goquery.Selection) { url, _ := s.Attr("href") fmt.Println(url) }) } func main() { url := "http://qiita.com/advent-calendar/2013/" GetPage(url) } ``` めちゃくちゃ短くできる!!!!!!!! しかも直感的!!!!!!!!! goqueryは既にmattnさんが記事にしてくれてるので、それも参考になると思います。 [Go言語で jQuery ライクな操作が出来る goquery を試した。](http://mattn.kaoriya.net/software/lang/go/20120914184828.htm) [goquery](https://github.com/PuerkitoBio/goquery ) 最後にgoqueryとgoroutine、channelで並列処理でQiitaのこれまでのエントリをscrapingして出力して終わります。コードはコチラ。 ```go package main import ( "fmt" "github.com/PuerkitoBio/goquery" "os" "sync" ) type Result struct { CalTitle string Title string Url string } func GetPage(url string) []Result { results := []Result{} doc, _ := goquery.NewDocument(url) doc.Find("a.calendar-name").Each(func(_ int, s *goquery.Selection) { url, exists := s.Attr("href") if exists { caltitle := s.Text() entryPage, _ := goquery.NewDocument("http://qiita.com" + url) entryPage.Find("div.body h1>a").Each(func(_ int, s *goquery.Selection) { url, exists := s.Attr("href") if exists { result := Result{caltitle, s.Text(), url} results = append(results, result) } }) } }) return results } func GoGet(urls []string) <-chan []Result { var wg sync.WaitGroup ch := make(chan []Result) go func() { for _, url := range urls { wg.Add(1) go func(url string) { ch <- GetPage(url) wg.Done() }(url) } wg.Wait() close(ch) }() return ch } func main() { args := os.Args if len(args) < 2 { panic("usage : goquery <url>") } urls := []string{} for index, arg := range args { if index != 0 { urls = append(urls, arg) } } ch := GoGet(urls) for { results, ok := <-ch if !ok { return } calTitle := "" for _, result := range results { if calTitle != result.CalTitle { fmt.Println("#" + result.CalTitle) calTitle = result.CalTitle } fmt.Println("[" + result.Title + "](" + result.Url + ")") } } } ``` リポジトリはコチラ: [https://github.com/yosuke-furukawa/goquery_sample ](https://github.com/yosuke-furukawa/goquery_sample ) 使い方: ``` $ goquery http://qiita.com/advent-calendar/2013 http://qiita.com/advent-calendar/2012 ``` # Qiita アドベントカレンダー一覧(2013一覧): #1分で実現できる有用な技術 [1分でPAGER環境変数を設定したあとページャーを知る](/tadsan/items/7658038487b7a82778ec) [1分で実現できるtmuxのTips x3 (ついでにinstall to Mac,CentOS,Debian/Ubuntu)](/nntsugu@github/items/107cbd6cd6db2aa46172) [1分で実現できるjavascriptにおけるconsoleまわりの有用なテクニック4つ](/puriketu99/items/92b83172268685fceaf2) #Amazon Web Service [AWSクレジットの引き換えエラーメッセージまとめ](/kawaz/items/a92a46293f03be4c1c1f) [AWS CLIの --query オプションが便利。](/kawaz/items/9bccd1d3f51ba46b25a0) [Cross-Zone Load Balancing を有効にしない理由がない件](/rch850/items/23c0743876ef5ab7bd30) [AWSを便利に使う為の情報・ツール](/camelmasa/items/9f03c1c04bace41532c3) #AngularJS Startup [AngularJSを使ったWebアプリのアーキテクチャ設計](/zoetro/items/46d2a8b57f2645bb5033) [AngularJSのTutorialのstep-1とstep-2よりData Bindingの仕組みをレポート](/matsuzan/items/d0c8b2948c4ad1ea7d68) [AngularJSのはじめの一歩(詳細)](/matsuzan/items/1fc80083496951e0406f) [AngularJSを選択する見解](/matsuzan/items/a37b29fe918ba0441d99) #Ansible [Ansibleちょっとしたメモ](/myaaaaa_chan/items/f3473ae27f8e1b2abcac) [Ansibleの事例とちょっとしたTips](/volanja/items/d38fe0678848bae6902f) #Ceylon [Ceylonモジュール作成やクラスなどの基本コード](/newta/items/05150ed45fe99829b3e4) [Ceylonの良いとこ説明コードその1](/newta/items/41d5bc6f25b0ba8d9cc6) [Ceylon開発環境セットアップ!](/newta/items/d84200874645fa5cef26) [Java大好きな人が作ったCeylon言語ってどんなもの?](/newta/items/2ca98051450c4751b4b1) #Civic Tech [Open Government Licenceの紹介](/nyampire/items/206559231a45ca884590) [21世紀型の都市が持つべき7つの戦略とCivic Hackerへのマインドシフト](/eguchishintaro/items/6f043c671b0337dbcd67) [Beyond Transparency の紹介](/shigeomi/items/db66430be97212924350) [地域課題解決の新しい形、Civic Tech と Code for Japan](/hal_sk/items/3f78031e7fcd9f9d02ec) #Clojure [Clojureで音楽組織プログラミングについて](/nobkz/items/aea2c70a5fb03003f375) [Clojureを練習するためのオンライン問題集](/esehara@github/items/4183f1dcee9a82ac2c6b) [clj-webdriverの紹介](/hash/items/30ea7aee76c8179e12e8) #cocos2d-x [【まとめ】cocos2d-Xmas Special by #gumistudy〜 Chukong Technologies(coco2d-x開発元)他登壇!](/hatchup/items/ecb3df8570788fa5d198) [簡単なカスタムボタンの作り方(2)](/fullfool/items/034402b13ce07224a242) [透過画像に対応したコリジョン判定](/syuhari/items/dbe29dcfb78a1f188645) [簡単なカスタムボタンの作り方](/t-kashima/items/adb7e823cc2269aed3f4) [cocos2d-xでのJSON利用方法](/tyama83/items/27d537b502934da3e656) #CodeIgniter [自作クラスでCodeIgniterオブジェクトを使用する](/kaneshinth/items/aa60d69eaac6581519ef) [【基礎】CodeIgniterでライブラリを作成する](/kaneshinth/items/0e99167169c85a361fc8) [【基礎】CodeIgniterでコアクラスを作成する](/kaneshinth/items/876e20b63f869c5d7335) #Corona [Corona SDK + Parse.com でプッシュ通知をする](/fakestarbaby/items/9b3a7b27f94170e191fb) [Kwikを買いました。](/CoronaSDK_Ambassador/items/c0c0abd5f795e81c3dd3) [今年のCoronaSDKの振返り](/CoronaSDK_Ambassador/items/fb09de093ff30e8ef26b) #Delphi [カメラの画角を変える方法](/luxidea/items/b36acbbcff904c8b7fd7) #Doc-ja [Translate Toolkitで翻訳ツールを作る](/jmatsuzawa/items/eaa40f51aa56f3854a63) [続・フリーなオフィススィートを翻訳すること](/naru0ga/items/3ec1fb04556277599932) [WordPressプラグインを題材にGettextでの翻訳方法を紹介](/iwaim@github/items/883e4aeb29085e39581a) [フリーなオフィススィートを翻訳すること](/naru0ga/items/a456e35c242881287488) #D言語 [D言語で、コンパイル時単体テスト](/youxkei/items/24aabd5d5b65df0dc2e1) [DustMiteで、バグ再現最小コードを作る](/youxkei/items/825f0437dfea8101f743) [D言語 Language Update 2013](/repeatedly/items/643a43b6407732f096e1) #Elixir [`mix new`で作られるファイル探索](/yaotti/items/a3b5b2ec4249064ab578) [elixir on Android](/modalsoul@github/items/4a35e6fe32fd884289a5) [fluent-logger-elixirのはなし](/mururu/items/8e2683bef8ceb7701838) #Fluentd [Fluentd のベンチマークテストに使える dummy_log_generator の紹介](/sonots/items/750da77a18e62852a02f) #G*(Groovy, Grails ..) [Cucumber-Groovyでfeatureの書き方を手順を追ってみる](/kyon_mm/items/7ea43a92be9225ae444b) [Cucumber-groovy設定編](/kyon_mm/items/97f1021432bba7d0a5cd) #Git [Gitのコミットログ再考](/esehara@github/items/87f7d227d515dc6fc37e) [プルリクエストを自動補完してcheckoutする](/yuku_t/items/f53a9d3ea92614b0927d) #Google Apps Script [Google Formでクイズして、自動で答え合わせして、メールまで送っちゃうお ](/soundTricker/items/c9f2cfe70bb73a67a3e2) [公式ガイドの「Dialogs and Sidebars in Google Apps」を制覇する!(その1)](/ttyokoyama/items/21a1938c1e460a9d308b) [2013年度版 5分で始めるWebアプリケーション(Google Apps Script)](/soundTricker/items/a4878d7e3100082576e4) #GorillaScript [GorillaScript簡易ガイドブック](/esehara@github/items/f215d27ac4db7e82ced2) #Grunt Plugins [CSSプロパティの重複を解析してくれるgrunt-csscssについて紹介するよ](/t32k/items/ba9e213d0253e369bf2e) [CSS書く人なら絶対入れとけのgrunt-contrib-csslintについて紹介するよ](/t32k/items/f2de0e934cf5e6d66058) [タスクが正常に終わったらチャットワークにメッセージを通知する grunt-chatwork を紹介するよ](/astronaughts/items/80b476ad1e592967b926) [散乱した@mediaをまとめてくれるgrunt-combine-media-queriesを紹介するよ](/hail2u/items/7bf2b830f2536c71666d) [フロントエンドだけじゃない! サーバサイドの開発も手助けしてくれる grunt-connect-proxy を紹介するよ](/kamiyam/items/7967405632c4527b3d87) [JSONファイルの管理をちょっとだけ楽にしてくれるgrunt-sync-versionを紹介するよ](/1000ch/items/1fdabd68ec9895c766d7) [なんでも開ける grunt-open について紹介するよ](/kamiyam/items/9e40f780e029ed35139f) [イケてるスタイルガイドを簡単に作れるgrunt-kssについて紹介するよ](/t32k/items/9e03e80061de21411765) [CSSプロパティをソートしてくれるgrunt-csscombについて紹介するよ](/t32k/items/e59cebb51825347689f9) [高いCSS圧縮率を誇るgrunt-cssoについて紹介するよ](/t32k/items/193c82e04e5383ead38f) [JSコードの品質チェックをしてくれるgrunt-platoについて紹介するよ](/shoito/items/cca3ae1cc6b0895c9455) [ページ表示速度の問題チェックをしてくれるgrunt-pagespeedについて紹介するよ](/shoito/items/aed36f56ae9a3e46c215) [Gitフックを仕込むgrunt-githooksについて紹介するよ](/shoito/items/0cd10e14f437a0cab9a9) #Haxe [Haxeの関数値について雑多なこと](/nobkz/items/aea57de73d95d887b329) [Haxeの文字列](/mandel59/items/0ac1fc6724126734abd0) [知ってると色々と世界が広がるかもしれないHaxeのコンパイルオプション](/k-motoyan/items/d9c516bba525379d3cda) #HDL [Verilog HDLでの数値リテラル(定数)の書き方](/marsee101/items/846ab0f59d99656f0176) [SFLのstageとNSLのseqを攻略する](/ksmakoto/items/90b38021b98700efd93f) [[SystemVerilog]即時アサーションでコネクティビティをチェックする。](/tethys_seesaa/items/90c9696a89e2d830aed0) [[HDL][雑記] 言語トレンドを見てみよう](/Kocha/items/10a1ca38cdcc39924311) #hubot-scripts [Hubot-scriptsでサイコロを投げる](/daxanya2/items/45045a2e87a0d7aa3846) [Hubot-scriptsのmsg.randomを学ぶ](/daxanya2/items/4d7a8c94fa0756c97b5d) [はじめてのHubot](/kmdsbng/items/fdc069048b5f0d07295e) [Hubot-scriptsの学びかた](/daxanya2/items/e382b32408f1f9aad641) #iBeacon [iBeacon開発ハマリどころポイントまとめ](/himara2/items/1d6c11a4d4839c3027d5) [iBeacon で忍者が密会する](/usamik26/items/563e94301ca150c3d0c3) [iBeaconを利用したアプリ開発でチェックしておきたい!良記事・ソースコードまとめ](/hedjirog/items/abd48a55387891cc8503) [1行もコードを書かずにiBeaconで遊んでみる](/yumu19/items/91f04ccc6c625876b22c) #IntelliJ IDEA [IntelliJ IDEA 13がリリースされました!](/todogzm/items/838fdc3a34be789689ed) [設定、プラグイン導入した後にこれだけはやっとけってやつ](/dvorak__/items/7de6ac29216336f50729) [IntelliJ IDEAをチームで導入するために私が行ったこと](/todogzm/items/e4332f15c3ec8d137289) [IntelliJ IDEAをインストールしたら設定してること(Java/Groovy編)](/kyon_mm/items/8e283d3295e7706c51c5) #Jenkins CI [ALMiniumをVirtualBox上のCentOSにインストールするのにJenkinsを使ってみたお話](/akiko-pusu/items/8b084107132572e5c73e) [Jenkinsビルド後の処理でRedmineにチケット登録ができるプラグインを作った話](/Kokawa_Takashi/items/6ffd7f51bdc95549e8ab) #JSX [JSX + npm](/shibukawa/items/7836426a7dfaa31cfb89) [ライブラリからみるJSXの特徴](/gfx/items/ee94661a80bea35d8f84) #Laravel [Model で Validation したい? それならば Ardent だ!](/localdisk/items/da4d658a921d11f073e3) [Laravel の現状と拡張の話(読み物)](/localdisk/items/dba5f9a725af76a9285a) [最小構成で始めるLaravel](/localdisk/items/b5ad297d3aebf14c00ad) [日本人に優しいLaravel](/razokulover/items/dd81420dd257e20191ec) #Lisp ['`'と','](/snmsts@github/items/ef625bd6be7e685843ca) #Max/MSP/Jitter [Max/MSPの様々なGUIパーツ](/shu223/items/bd9cb186bd3fcffbd3cf) [Max/MSPの便利な操作方法](/shu223/items/852e4ef06af4f07e381b) [Max/MSPの入門レシピいろいろ](/shu223/items/da5eb8602caa3a39fb05) [Max/MSP/Jitterをはじめるメモ](/shu223/items/38a14ad3efd260faafe0) #Mojolicious [Mojoliciousのテンプレートでレイアウトを自在に操る](/yusukebe/items/b1dffe22bd870b128aa9) [Mojoliciousのセッションの話 2013年年末版](/yusukebe/items/65486fc44b85b4415299) [Mojoliciousの様々な立ち上げ方](/yusukebe/items/c9fb34096e61976f062f) #MongoDB [Mongo shellの裏技色々](/crumbjp/items/eddd379d9bf4bfb3f65b) [MongoEngineでMongoDBを触ってみる基礎編](/key/items/b3894c64161a02d10649) [Ruby初心者がRuby on Rails + Mongoidを試してみた](/naru0ga/items/eaa5be47c10800c98656) [MongoDBとブラウザだけで旅の記録をつけてみるか その1](/syokenz/items/00b06ac01adeeaebada4) #mruby [GUIアプリケーションなどが保持するmrubyのオブジェクトのGC対策](/dycoon/items/fa8c66848bb37dc29454) [mruby-redisでランキングを実装](/suzukaze/items/b56de22618ba308d3f34) [mrubyのビルド方法](/masuidrive/items/e516c23b4feab73d139f) #NEET [Cryptocurrency Mining、始めてみませんか?](/nyarla/items/10708afd380fb9daa814) [NEET Advent Calendar作りました](/nyarla/items/ba00d2da3d172b9a046b) [NEETでGeekな情報収集術](/nyarla/items/a9ed13ed1804cc210206) [全国のNEET達に送る、プログラミングの始め方。](/nyarla/items/35966109fd10a50c5e0e) #openFrameworks [ofxFaceTracker で顔をリアルタイムにトラッキングする](/shu223/items/7846ef33a80a489dff93) [openFrameworks for iOS のサンプル一覧](/shu223/items/e28110bf729fdf8712e0) [openFrameworks for iOS ことはじめ](/shu223/items/1bf9c9cc6cafc58969bb) #OpenStreetMap [ライセンス表記に注意しよう(自戒の念を込めて)](/k_zoar/items/57414fd66d48a786b3c0) [ライトマッパーが1年を振り返るよ](/k_zoar/items/e42aca8f478e31568308) [Surveyorジャケットの日本版作成とか](/nyampire/items/672c984a0e705c12d93f) #Perl [普通のデーモンを 1) Server::Starterでホットデプロイ+ 2) slow-restart対応にする](/lestrrat/items/9eee9590d604dd183207) [Twiggy で応答の遅いサーバーを模倣する](/dayflower/items/e02e3e7a47629d3f210d) [HTTPクライアントとStream::Bufferedの合わせ技](/kazeburo/items/2f54ba4c39ad013d6e31) [HTTP::Message::PSGIでPSGIアプリのテストを書く](/hiratara/items/13d8ef65499b151bd79d) [Log::StringFormatter でログ文字列のフォーマット](/kazeburo/items/af99fb84e525b5788b57) #PostgreSQL [PostgreSQLのあまり知られていない型3種](/choplin/items/9d5e2ff8721fb9509bf8) #Python [LXCをPythonから操作する](/0xfffffff7/items/9f3e0cc326ec2053a694) [テキストファイルから指定した文字列を含む行を出力する](/qt-luigi/items/76f7955d68418f967efb) #Ruby on Rails [てめえらのRailsはオブジェクト指向じゃねえ!まずはCallbackクラス、Validatorクラスを活用しろ!](/joker1007/items/2a03500017766bdb0234) [websocket-railsで簡単なPush通知を実装する](/naoty_k/items/3a2b5d8cfc1619e6145b) [Rails4に対応したgem doorkeeperついて調べてみた。](/camelmasa/items/1c6d06713fc25eb7bddf) [Railsのコンソールをより便利にするpry-rails gem](/yaotti/items/c6e850010f36acedb0e1) #RubyMotion [Parse.com で Push Notification with Rubymotion](/amazedkoumei@github/items/45447b5f44e33c0e5369) [motion-modeの紹介とruby-modeについて](/ainame/items/e2d2cd3aa29341166211) [RubyMotionアプリ開発に、motion-mode + Rubocop を導入](/watson1978/items/debafdfc49511fb173e9) [Rubyist が RubyMotion で iOS アプリの開発を始める方法](/satococoa/items/05bd1be5bf948dba0fdc) #Rust [Rust Advent Calendar 12/2分を20分ででっちあげる](/kakkun61/items/f3fae6dfa19837e5f878) #TDD [ノーマルにMSTestを使おう](/moonmile/items/269295ad5758fa69d203) #WebPay [WebPayLiteを使ってiOSから簡単にクレジットカード決済する](/kyoro353/items/ae41a91ad2b56a829863) [少しのコードでWebPayを導入する PHP Ver.](/sowawa/items/576930dbb175e8b83ba5) [少しのコードでWebPayを導入する](/hmsk/items/744f9a7e37638340a992) #Windows Azure [Windows Azure Cloud ServiceでPython/Djangoを使おう!](/ishisaka/items/e3700808261e9ed31b4d) [Windows Azure仮想マシンのディスクをPowerShellでバックアップする ](/k1hash/items/f6553dbbdbfeb4cc9a53) [私が Windows Azure Web サイトを大好きな 7 つの理由](/shibayan/items/ceb287cab34ac50f614f) #Windows Store App [スタート画面っぽくいろんなサイズのタイルを GridView に表示しちゃうやり方](/matatabi/items/158c681b9849102b93b0) [WinJS.Bindingについて](/iwate/items/aeb077526ffdcf16a471) [ ストアアプリで手軽に画像を加工しよう](/moonmile/items/b3eea4225d18ff584b40) [markSupportedForProcessingについて調べたかった](/iwate/items/caaa5581264919cb3c68) #Xamarin [Xamarin.iOSでUITableViewの罠 / Xamarinの進化に望む事](/kochizufan/items/78b6b837beabcca7f362) [XamarinでParse SDKを利用する](/koji_yusa/items/a6878bef10577ee744b5) [Xamarin(ザマリン) とはなんぞや](/amay077/items/38ee79b3e3e88cf751b9) #zsh [zshにオプションや引数を補完できるキーバインドを設定しよう](/PSP_T/items/82b080920a4241e96aed) ["select-word-style bash"の不満を解消する](/syohex/items/ad8a707b4f4280aa4272) [bundle exec を打たなくて良くなる zsh プラグイン書いた](/Linda_pp/items/cb047fb1fe279f9d0b22) [zsh で find を使わずに簡単にファイルを絞り込む](/mollifier/items/1c4a4930a89aa75e5ced) #カーネル/VM [カーネル/VM Advent Calendar 2013 3日目:LinuxカーネルのlockrefというLock機能を試してみよう](/masami256/items/00449e22cb38bfc3ba76) [カーネル/VM Advent Calendar 2013 1日目](/katsyoshi/items/1b6b8f94476606cc599f) #ディストリビューション/パッケージマネージャー [aptitude検索パターンの紹介](/znz/items/a276db99cbb671e96455) [debian-goodies](/uwabami/items/77f920448ad73ce12aa2) [抄訳 games-misc/fortune-mod-gentoo-dev](/hiyuh/items/fa33aac2d7bab3d14009) [Gentoo Wikiの翻訳](/naota344/items/60709014309880d28475) #どう書く過去問 [等差数列(2013.5.17の過去問)](/hiratara/items/26ef713ac0182211e8ce) [ボールカウント:野球(2012.8.8の過去問)](/hiratara/items/6c6ae47037ff6cd22973) [のんびり座りたい (2013.2.2の過去問)](/hiratara/items/2e8dee9e6e26188e2671) #ドキュメンテーション [プロジェクト管理WebアプリBrabio!のガントチャートをプレゼン用に綺麗に出力する](/sky_y/items/7403d44d0abb31605d55) #マイナー言語 [明日から使えるasm.js - Low Level JavaScript - 「LLJS」 マイナー言語アドベントカレンダー・一日目](/mizchi/items/ccc1429ca6156233bdee) #みんなでやるRiak [Riak2を複数ノードで動かしてみる](/saisa6153/items/4efa1a2d0316c136e8aa) [Riak2にデータを出し入れしてみる](/saisa6153/items/4f1b676a579714f83dfd) [Riak2をインストールしてみる](/saisa6153/items/82a985bc20f458e8a451) #ラテン語プログラミング [あらこんな所にラテン語が。いつも見るあの語が実は・・・](/warabanshi/items/d787a7157e0f8bf7a16d) [自由に使えるラテン語辞書データ (I)](/kagamiwari/items/2967412e4a7ec9ccfb0e) [ラテン語文解析プログラムを書くことを目的としたラテン語学習(前編)](/naoya_t/items/438dbad5398d54c57f0f) [VB5で作ったラテン語アプリを発掘](/7shi/items/7b4b863af14ed2e1fb14) #全文検索エンジンGroonga [リポジトリを横断してコミットメッセージを眺めるツールgglogを使ってみた](/kenhys/items/fe2dec314c65526c7a7b) [GObject Introspectionを使っていろいろな言語からGroongaを使う方法](/groonga/items/71b145b37d77bd160bf2) #設定ファイル「dotfiles」の作り方 [vim-powerline (lightline.vim)](/PSP_T/items/1e9856599765de233a5c) [zsh-powerline](/PSP_T/items/8f57eb6f1e8cc11ea8b4) [tmux-powerline](/PSP_T/items/dc509f208b464838b948) # iOS Second Stage [iOS 7でバーコード・QRコードを読み取る方法と生成する方法(おまけもあるよ)](/hkato193/items/c36a940c2929a124e416) [複数のStoryboardを使ってタブの遷移を作成する](/hedjirog/items/1c074069e56157a4e54b) [iOSで使える日本語OKな音声読み上げエンジン8種(TTS,音声合成)](/shu223/items/223492e4f061032e652e) [Xcodeと自動化](/keroxp@github/items/de5b1982345cfb1e2320) #.emacs [emacs のリージョンや編集中のバッファを DayOne の Dropbox に保存する拡張をつくった](/mori-dev@github/items/05afe9a0fa5345bdbb7f) [自分流の .emacs管理](/shiba_yu36/items/5c4f0e72df7b16cd0579) [anzu.elの紹介](/syohex/items/56cf3b7f7d9943f7a7ba) [ぼくの.emacs 2013](/tadsan/items/c4a80f46623ea266f5c5) #Android [BeagleBone BlackにAndroidをインストールする](/kinneko/items/40865b3714316dddf664) [Gradleことはじめ](/wasabeef_jp/items/f9436e1c9b74536caaff) #Go [Androidでʕ ◔ϖ◔ʔGo~](/Ladicle/items/fc694c726a84f4c3b7ff) [goyaccを使う](/draftcode/items/c9f2422fca14133c7f6a) [Go言語で顔認識してみた](/tenntenn/items/63d52c01a6339eb392f0) #iOS [XIBからもコードからもカスタムViewインスタンスを生成する方法](/hosokawa0825@github/items/2a650cbca5a087518d57) [「顔以外」のものを画像認識する](/shu223/items/ffd2202eaf92d342f83d) [IB/Storyboard使わない派のlayoutSubviewsによるレイアウト調整](/yuch_i/items/b4612fae110254c816f4) #JavaScript - Client Side - [BakboneJSだってDOMとModelをBindingしたい!](/itoKami1123/items/e3d50ff40e8578adc732) [なぜクライアントサイドJavaScriptにはHTML/CSSの知識が必要か?](/YusukeHirao/items/bba3d05dda7f0459a913) [Ember.jsのコアな機能を学ぶためのサンプルプロジェクト](/puriketu99/items/e88d40d55d2f30e9ba1d) #Machine Learning [Random Forest とその派生アルゴリズムの紹介](/kazoo04/items/711142df77fed82d7bc0) [Dropoutの実装と重みの正則化(MLAC 2013 3日目)](/olanleed/items/c9d04904d46b6bba2ad0) [たぶん1分くらいでできる形態素解析とtfidf(テストコードつき)](/puriketu99/items/a6d8100165d576443938) [ベイズ線形回帰(PRML§3.3)の図版再現](/naoya_t/items/80ea108cebc694f5cd63) #PHP [PHPの開発に使えるVagrantfileのまとめ](/yando/items/10058ef292233d21f296) #PhpStorm [コードフォーマットを使いこなす](/Vexus2/items/7adbf5c00ddd1e570f45) [PhpStorm EAPを使ってみる](/Vexus2/items/bcd68648f27f4a4b78fb) [PhpStormをVimキーマップで使う](/Vexus2/items/1d84442f625233dce276) [PhpStormとは?](/Vexus2/items/f4f0e509a41aa0c547b0) #Ruby [websocket-client-simple でPush通信を使ってJenkinsさんと会話](/hayabusa333/items/70bcec06f2b47f0a4d46) [Ruby 2.1.0-preview2で追加されたException#causeの紹介](/znz/items/db187aec13004a56710c) [RailsとGrapeで行う最高のWeb API開発](/anoworl/items/756f01cc3d188ebad139) #Scala [Lift の RestHelper でサクサクAPI 開発](/shitai246_/items/5fe5b97fe828846ebad3) [2014年こそScalaを始めよう](/shoma2da/items/b131c53ffa958c9386f7) #Unity [Unity 途別2Dアセット](/kame0_0/items/8f8d3c397b314f6a12f4) [Transform拡張](/MunenaoMiyata/items/24d0675861744c827668) [MoveAssetToTrashでUndo/Redo](/kyusyukeigo/items/f66e4c1b0f155cb8c956) |
|
| 417位 |
|
|||
|
19:42:48 |
(Wantedly, Inc. 所属) |
|
gitのpush.defaultの設定に関して、他人のを設定してあげることもあり、毎回迷うのでまとめました。 push.defaultは、今までは`matching`というのがデフォルトでしたが、Git2.0から`simple`っていうのがデフォルトになります。なので、何らかの設定をしないと、以下の様な警告が出たりします。 >warning: push.default is unset; its implicit value is changing in Git 2.0 from 'matching' to 'simple'. To squelch this message and maintain the current behavior after the default changes, use: > >git config --global push.default matching > >To squelch this message and adopt the new behavior now, use: > >git config --global push.default simple > >See 'git help config' and search for 'push.default' for further information. (the 'simple' mode was introduced in Git 1.7.11. Use the similar mode 'current' instead of 'simple' if you sometimes use older versions of Git) ちなみに、設定できるオプションの詳しい説明については[git help config](http://git-scm.com/docs/git-config)に書いてあります。 >Defines the action git push should take if no refspec is given on the command line, no refspec is configured in the remote, and no refspec is implied by any of the options given on the command line. Possible values are: > > - nothing - do not push anything. > > - matching - push all branches having the same name in both ends. This is for those who prepare all the branches into a publishable shape and then push them out with a single command. It is not appropriate for pushing into a repository shared by multiple users, since locally stalled branches will attempt a non-fast forward push if other users updated the branch. + This is currently the default, but Git 2.0 will change the default to simple. > > - upstream - push the current branch to its upstream branch. With this, git push will update the same remote ref as the one which is merged by git pull, making push and pull symmetrical. See "branch.<name>.merge" for how to configure the upstream branch. > > - simple - like upstream, but refuses to push if the upstream branch's name is different from the local one. This is the safest option and is well-suited for beginners. It will become the default in Git 2.0. > > - current - push the current branch to a branch of the same name. > >The simple, current and upstream modes are for those who want to push out a single branch after finishing work, even when the other branches are not yet ready to be pushed out. If you are working with other people to push into the same shared repository, you would want to use one of these. ## 性格別おすすめpush.default設定 ただ訳してもつまらないので、どういう人が使うべきか解釈してみました。 ###nothing 何もしない。`git push`なんて省略形使わないで常に`git push origin master`みたいに指定するぜ!という堅実なあなた向き。 ###matching 今までのデフォルト。ローカルとリモートで同一の名前のリポジトリがあれば全てpushする。gitがgithubのような使い方じゃなくてP2Pのように完全分散型バージョン管理を目指した頃の、古き良き時代を愛すあなた向き。 ###upstream 現在のブランチにupstreamが設定されている場合、そこにpushする。これを指定すると`git push`が完全に`git pull`と逆の意味を表すようになる。規律や規則性を大事にするあなた向き。 ###simple これからのデフォルト。upstreamが設定されていて、それが同名のブランチ名であるときのみpushする。初心者でも安心して使える。初心を忘れないあなたと、デフォルトを愛するあなた向き。 ###current 現在のブランチをリモートに同名でpushする。例えupstreamを設定していなくてもpushされるので、めんどくさがりで効率重視なあなた向き。 ### まとめ 結局どれ使えばいいかわからないという人は、次世代のデフォルトの`simple`か、`current`を使うといいと思います。`current`はかなり楽なので、僕はこれを使っています。 ## おまけ1: upstreamの設定 上の記述に何回か登場したupstreamは以下の2つの方法で設定できます。 ### pushするときについでに設定 `git push`する際に`-u`オプションを付けると、push時に同時に設定できます。 ``` git push -u origin master ``` ### 明示的に設定 今いるブランチのupstreamを設定したい時: ``` git branch -u origin/current-branch-name ``` 別ブランチの設定したい時: ``` git branch -u origin/different-branch-name different-branch-name ``` なお、設定の確認は ``` git branch -vv ``` で出来ます。 ## おまけ2: git push のおさらい git pushは略さないで書くと以下のように指定することになっています。 ``` git push <リモートのレポジトリ名> <ローカルのブランチ名>:<リモートのブランチ名> ``` - <リモートのレポジトリ名>: `git remote`/`git remote -v`で確認可能。 - <ローカルのブランチ名>: `git branch`で確認可能。 - <リモートのブランチ名>: `git branch -r`で確認可能。 例えば、ステージング用のHeroku`heroku_staging`に、新たに機能を追加したブランチ`experiment`をデプロイしたい時は以下のようにします。 (Herokuは`master`にpushすればデプロイされます) ``` git push heroku_staging experiment:master ``` リモートのブランチ名がローカルと同じ場合は省略できます。例えば、よく使う ``` git push origin master ``` は、以下のように指定したのと同じ意味になります。 ``` git push origin master:master ``` さらにもっと省略して、 - リモートリポジトリ名だけを指定 ``` git push origin ``` - 全て省略 ``` git push ``` と記述しても、何らかの操作を行うことが可能で、その挙動を指定するのが今回の`push.default`の設定です。 ## おまけ3: git pushの挙動を実験したい いろいろ実験して確認したい人は、`git push`は`-n`(`--dry-run`)オプションを付けることで、何が起こるかだけを出力することができます。 お試しあれ。 |
|
| 418位 |
|
|||
|
01:45:46 |
(株式会社キュリオシティソフトウェア 所属) |
|
iOSアプリ開発で高速に写真や動画にエフェクトを加える事が出来る[GPUImage](https://github.com/BradLarson/GPUImage)の基本的なパターンと応用方法を理解するためのメモ。 GPUImageライブラリはOpenGL ES2.0をベースとしているため、大抵の場合は処理にCPUを使った場合よりも高速な動作が特徴。その上、複雑な概念を理解する必要があるOpenGLについて知る必要はなく、さらにカメラのためのAVFoundationのフレームワークを触る必要もない。 これはもう画像処理やカメラアプリに使わない理由がないんだけど案外日本語の説明がなかったので調べて行った過程とフィルタの作成方法についてを書いてみた。構成は下記の通り。 1. GPUImageの基本パターン 1. 用意した静止画像へのフィルタ適用で基本パターンを解説。 2. カメラでのリアルタイムフィルタリング利用のための基本パターン解説 3. GPUImageの応用 1. 複数フィルタを重ねがけする 2. オリジナルのフィルタを作るための方法 3. オリジナルフィルタをプログラマブルに調整するための解説 ## GPUImageの基本パターン 静止画像とカメラ動画へのエフェクトの適用(フィルタリング)の基本パターンを[GPUImageのリポジトリにあるiOSのデモ](https://github.com/BradLarson/GPUImage/tree/master/examples/iOS)のコードをベースに解説する。 ### GPUImageの基本パターン: 静止画像にフィルタリング [GitHubにあるデモプロジェクトSimpleImageFilter](https://github.com/BradLarson/GPUImage/tree/master/examples/iOS/SimpleImageFilter)では、プロジェクト内に用意された画像ファイル"WID-small.jpg"に対してフィルタリングしている。 WID-small.jpgはこんな感じの写真  デモプロジェクトではフィルタリングにSobelフィルタを使っていて、輪郭抽出を行っている。  #### 重要なこと GPUImageの静止画像フィルタリングで抑えておくべきなのは次の点のみ。 * GPUImagePictureクラスが元画像でそこに処理するフィルタを追加する * addTarget:メソッドでフィルタを指定 * GPUImageFilterクラスがフィルタになる * GPUImagePictureクラスを使い画像のフィルタリングを行う * processImageメソッドでフィルタリング処理を実行 * 表示のためのViewはGPUImageViewクラスのインスタンスをつかう #### 静止画フィルタリングデモのソースコードは次のようになっている 重要な事をおさえるとソースコードでやっていることが明確になるので見てみる ```objectivec: - (void)setupDisplayFiltering; { //元画像を用意 UIImage *inputImage = [UIImage imageNamed:@"WID-small.jpg"]; sourcePicture = [[GPUImagePicture alloc] initWithImage:inputImage smoothlyScaleOutput:YES]; //フィルタを用意 //GPUImageFilterクラスを継承したGPUImageSobelEdgeDetectionFilterで //Sobelフィルタをインスタンス化している。 sobelFilter = [[GPUImageSobelEdgeDetectionFilter alloc] init]; //表示するためのviewを用意。 GPUImageView *imageView = (GPUImageView *)self.view; [sobelFilter forceProcessingAtSize:imageView.sizeInPixels]; //GPUImagePictureでフィルタを追加 [sourcePicture addTarget: sobelFilter]; //フィルタに表示すためのビューを指定 [sobelFilter addTarget:imageView]; //フィルタリング処理を実行 [sourcePicture processImage]; } ``` ### GPUImageの基本パターン: 動画フィルタリング [GitHubにあるSimpleVideoFilter](https://github.com/BradLarson/GPUImage/tree/master/examples/iOS/SimpleVideoFilter)を使い、次はリアルタイムにカメラで撮影した動画にエフェクトを加えるコードを解説する。 #### 重要なこと 動画でのフィルタリングは静止画像とほぼ同じ。 * GPUImageVideoCameraクラスが元動画で処理するフィルタを追加する * addTarget:メソッドでフィルタを指定 * GPUImageFilterクラスがフィルタになる * GPUImageVideoCameraクラスを使い撮影とフィルタリングを行う * startCameraCaptureメソッドで撮影とフィルタリング処理を開始 * 表示のためのViewはGPUImageViewクラスのインスタンスをつかう #### 動画フィルタリングデモのコードは次のようになっている 重要な事を抑えた上でソースコードを見る。動画撮影では静止画像と比較してカメラ設定が細かいのが分かる。 ```objectivec: - (void)viewDidLoad { [super viewDidLoad]; //カメラの設定 videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack]; videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait; videoCamera.horizontallyMirrorFrontFacingCamera = NO; videoCamera.horizontallyMirrorRearFacingCamera = NO; //フィルタのカメラへの追加 filter = [[GPUImageSepiaFilter alloc] init]; [videoCamera addTarget:filter]; //表示用ビューの追加 GPUImageView *filterView = (GPUImageView *)self.view; [filter addTarget:filterView]; //撮影とフィルタリングの開始 [videoCamera startCameraCapture]; } ``` ### 基本パターンおさらい 基本パターンのおさらいをすると、静止画ならGPUImagePictureを使い、カメラならGPUImageVideoCameraを使うだけの違いになっている。 * GPUImagePicture/GPUImageVideoCameraクラスが元画像でそこに処理するフィルタを追加する * addTarget:メソッドでフィルタを指定 * GPUImageFilterクラスがフィルタになる * GPUImagePicture/GPUImageVideoCameraクラスを使い画像のフィルタリングを行う * processImage/startCameraCaptureメソッドでフィルタリング処理を実行 * 表示のためのViewはGPUImageViewクラスのインスタンスをつかう とても使いやすいライブラリってことがわかる。 ## GPUImageの応用 ###GPUImageの応用: 複数フィルタを重ねがけする 複数フィルタを重ねがけすることでより複雑なフィルタを作成することが出来る。例として最初に用いたエッジ検出された静止画像にセピア化させるフィルタで説明する。  #### 重要なこと * 複数フィルタはGPUImageFilterGroupクラスに追加 * 複数フィルタはフィルタリングする順序でフィルタ同士ターゲット追加 * エッジ検出したあとでセピア色にするならエッジにセピアを追加 ```objectivec: - (void)viewDidLoad { [super viewDidLoad]; //元画像を用意 UIImage *inputImage = [UIImage imageNamed:@"WID-small.jpg"]; sourcePicture = [[GPUImagePicture alloc] initWithImage:inputImage smoothlyScaleOutput:YES]; //フィルタ用意 GPUImageSobelEdgeDetectionFilter *sobelFilter = [[GPUImageSobelEdgeDetectionFilter alloc] init]; GPUImageSepiaFilter *sepiaImageFilter = [[GPUImageSepiaFilter alloc] init]; //フィルタグループを作ってそこに追加していく GPUImageFilterGroup *filterGroup = [[GPUImageFilterGroup alloc] init]; [filterGroup addFilter:sobelFilter]; [filterGroup addFilter:sepiaImageFilter]; [filterGroup setInitialFilters:@[sobelFilter]]; [filterGroup setTerminalFilter:sepiaImageFilter]; //フィルタはフィルタ同士追加していく [sobelFilter addTarget:sepiaImageFilter]; //表示するためのviewを用意 GPUImageView *imageView = (GPUImageView *)self.view; [filterGroup forceProcessingAtSize:imageView.sizeInPixels] //GPUImagePictureでフィルタグループ追加 [sourcePicture addTarget:filterGroup]; [filterGroup addTarget:imageView]; [sourcePicture processImage]; } ``` フィルタグループにフィルタを順番に追加することでフィルタの一元管理をしてもらいたいものだけど、元のフィルタに次のフィルタを追加する必要があるらしい。 これはおそらくさらに多くのフィルタがあった場合や同じフィルタかけ合わせる場合などを考慮しているためで、上記2つのフィルタだけでは冗長に見えてしまうのかもしれない。ここらへんの理由を知っている人がいたら教えて欲しい。 ### GPUImageの応用: オリジナルフィルタを作成する GPUImageには用意されたいくつかのフィルタがあるが、自前でフィルタを作ることも出来る。このオリジナルフィルタはOpenGLの頂点シェーダとフラグメントシェーダを用いて作成する。 これらシェーダについて大雑把に説明すると、頂点シェーダはテクスチャ位置を計算していて、フラグメントシェーダは、フラグメント(ピクセル)ごとに色を決めるための技術。2つのシェーダは"シェーディング言語 (GLSL: OpenGL Shading Language)"を使うことによって実現できる。 シェーダに関しては下記のサイトがとっかかりとしては最適かもしれない 『フラグメントシェーダー事始め』で勉強したメモ http://d.hatena.ne.jp/shu223/20130114/1358664861 iOSアプリ開発の場合、シェーダはOpenGL ES2.0で使うことになるが、GPUImageは内部でそれをやってくれるので、GLSLを記述するだけで良い。 #### フラグメントシェーダによる赤と緑の置換フィルタ 赤の要素が緑で、緑の要素が赤のフィルタを作る。これは色を置換しているだけなのでフラグメントシェーダだけで簡単にできる。  実際に記述していくにはまず、シェーディング言語を記述するファイルをプロジェクトに追加する。まずは赤と緑をピクセル上で入れ替えるためのフィルターを「ColorSwapFilter.fsh」として作成しプロジェクトに追加する。 このファイルはGPUImageが[NSBundle mainBundle]で読み込むため、Xcodeのターゲットから「Copy Bundle Resources」でリソースとして設定する必要がある(設定しないと読み込めず例外になる)。 実際のコードは次のようになる。テクスチャユニット番号を保持するinputImageTextureと座標値を保持するグローバル変数textureCoordinateが登場しているが、これらはGPUImageでオリジナルフィルタを使う上でのお約束だと思おう。これらの変数を用いてtexture2D関数でピクセルごとの色を取得し、赤と緑を置き換えている。 ```objectivec: //グローバル変数 /* inputImageTextureという変数名はGPUImageで指定されている。 GPUImage(OpenGL)側で初期化されテクスチャ情報がサンプリングされている。 uniformはシステムから渡される定数であることを示す。 sampler2Dは2Dのテクスチャをサンプリングするデータ型。uniformである必要がある。 */ uniform sampler2D inputImageTexture; /* textureCoordinateという変数名はGPUImageで指定されている。 GPUImage(OpenGL)側で頂点シェーダにより座標値が書き込まれている。 varyingは頂点シェーダで書き込まれていることを示す。 highpは精度が高いことを示す。 vec2は2Dベクトルを示す型。 */ varying highp vec2 textureCoordinate; //エントリポイントmainはC言語とは違い戻り値は整数を返さない void main() { //texture2Dを使い、指定のテクスチャユニットに対してピクセル単位で色を取得する //引数の1つ目でテクスチャユニットを指定 //引数の2つ目で頂点シェーダにより指定された座標値を取得 lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate); //出力するピクセルの色を設定する変数 lowp vec4 outputColor; outputColor.r = textureColor.g; //赤に緑 outputColor.g = textureColor.r; //緑に赤 outputColor.b = textureColor.b; //青は青 outputColor.a = 1.0; //アルファ1.0で不透明 //グローバルなgl_FragColorが出力するピクセルの色になる gl_FragColor = outputColor; } ``` 次に、作成したフラグメントシェーダを読み込むコードを書く。フィルタとして何度も登場しているGPUImageFilterのinitWithFragmentShaderFromFile:メソッドでフラグメントシェーダのファイル名を指定するのみ。 ```objectivec: - (void)filterFromShaderFile { //元画像を用意 UIImage *inputImage = [UIImage imageNamed:@"WID-small.jpg"]; sourcePicture = [[GPUImagePicture alloc] initWithImage:inputImage smoothlyScaleOutput:YES]; //フィルタ用意。ColorSwapFilter.fshを読み込ませる。拡張子はいらない GPUImageFilter *filter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"ColorSwapFilter"]; //表示するためのviewを用意 GPUImageView *imageView = (GPUImageView *)self.view; [filter forceProcessingAtSize:imageView.sizeInPixels]; //フィルタを対象として追加 [sourcePicture addTarget:filter]; [filter addTarget:imageView]; //フィルタリング処理を実行 [sourcePicture processImage]; } ``` これで実行できるはずだし、なんとなくフラグメントシェーダが分かってきたと思う。 理解を深めるために、フラグメントシェーダを読み込むGPUImageFilterのinitWithFragmentShaderFromFile:メソッドを見てみると、記述しなかった頂点シェーダがデフォルトで記述されていることがわかる。 ```c: NSString *const kGPUImageVertexShaderString = SHADER_STRING ( attribute vec4 position; attribute vec4 inputTextureCoordinate; varying vec2 textureCoordinate; //フラグメントシェーダに渡すxy頂点座標 void main() { gl_Position = position; //モデルビュー射影変換後の座標 textureCoordinate = inputTextureCoordinate.xy; } ); ``` まず、SHADER_STRING()はObjective-CのなかでGLSLを記述するマクロ。 textureCoordinateはフラグメントシェーダで利用した座標値だった。デフォルトの頂点シェーダでは単に座標値をグローバル変数にセットしてフラグメントシェーダに受け渡しをしているだけだ。 ### GPUImageの応用: GPUImageでシェーダと値をやりとりする シェーダの記述方法は分かったが、シェーダを自在に操れないのでは変化のないフィルタとなってしまう。条件によってシェーダを微調整したいこともあるはずだろう。Objective-Cの変数とシェーディング言語上の変数(厳密には定数)で値をやりとりする場合はどのようにすればよいかは、既存のGPUImageBrightnessFilterを読むと分かる。 #### GPUImageBrightnessFilterを読む GPUImageBrightnessFilterは明るさを調整するフィルタだ。明るさはGPUImageBrightnessFilterクラスのプロパティCGFloat brightnessを変更することで調整できるようになっている。 ヘッダーは次のようになっている。 ```objectivec: #import "GPUImageFilter.h" @interface GPUImageBrightnessFilter : GPUImageFilter { GLint brightnessUniform; } // Brightness ranges from -1.0 to 1.0, with 0.0 as the normal level @property(readwrite, nonatomic) CGFloat brightness; @end ``` 実装ファイルの初期化メソッドではメンバ変数brightnessUniformに対して、シェーディング言語で使う定数"brightness"のindexを渡している。indexはシェーディング言語中の変数番号であり、アクセスするためのindexになる。 ```objectivec: - (id)init; { if (!(self = [super initWithFragmentShaderFromString:kGPUImageBrightnessFragmentShaderString])) { return nil; } //メンバ変数に brightnessUniform = [filterProgram uniformIndex:@"brightness"]; self.brightness = 0.0; return self; } ``` 次に、brightnessプロパティのsetterを見るとシェーディング言語中の定数brightnessに対して、セットする値を代入している。 ``` - (void)setBrightness:(CGFloat)newValue; { _brightness = newValue; [self setFloat:_brightness forUniform:brightnessUniform program:filterProgram]; } ``` 同ファイルにはフラグメントシェーダも記述してありbrightnessを使って明度を調整していることが分かるだろう。 ``` NSString *const kGPUImageBrightnessFragmentShaderString = SHADER_STRING ( varying highp vec2 textureCoordinate; uniform sampler2D inputImageTexture; uniform lowp float brightness; void main() { lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate); gl_FragColor = vec4((textureColor.rgb + vec3(brightness)), textureColor.w); } ); ``` ここまでくるとuniformは定数で、シェーディング言語に渡された定数であるという説明もわかってくるのではないかと思う。 ### より良いフィルタを作るため より良いオリジナルフィルタを作るため、OpenGLについて知りたい場合は、古い本だけれど「OpenGLプログラミングガイド」が基本的なことについて抑えてある。 <a href="http://www.amazon.co.jp/gp/product/4894717239/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4894717239&linkCode=as2&tag=curiosityamazon-22">  </a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=curiosityamazon-22&l=as2&o=9&a=4894717239" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> <a href="http://www.amazon.co.jp/gp/product/4894717239/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4894717239&linkCode=as2&tag=curiosityamazon-22">Amazonへのリンク: OpenGLプログラミングガイド 原著第5版</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=curiosityamazon-22&l=as2&o=9&a=4894717239" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> Amazonのレビューでは最近の主流であるOpenGL4.0では古すぎて役に立たないと書かれているが、今のところGPUImageで利用するのはOpenGL ES2.0なので微妙に役に立つのではないかと思う。800ページ以上もあるので枕になるし。 最近の本だと「OpenGL+GLSLによる画像処理プログラミング」は3Dではなく画像処理に徹していてGPUImageを使って画像処理を行うには最適な一冊。 <a href="http://www.amazon.co.jp/gp/product/4777514765/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4777514765&linkCode=as2&tag=curiosityamazon-22">  </a> <a href="http://www.amazon.co.jp/gp/product/4777514765/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4777514765&linkCode=as2&tag=curiosityamazon-22">OpenGL+GLSLによる画像処理プログラミング―「OpenGL」と「シェーダ言語」で「レタッチ・ソフト」の仕組みを知る! (I・O BOOKS)</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=curiosityamazon-22&l=as2&o=9&a=4777514765" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> 工学社I/O Booksの本は、本自体が小さいにも関わらず文字サイズが大きく、ソースコードを載せる技術書としてかなり読みづらい。また行間の調整もイマイチで図表とテキストの行間が狭く印刷物としての余白の美しさみたいなものがないので敬遠してたんだけど内容は良かった。 ## なぜGLSLは高速に処理を行えるのか フラグメントシェーダや頂点シェーダはGPUを用い、画像の全体あるいは一部の領域の画素に対し画素値の計算を同時に実行する。GPUはSIMD演算ユニットで構成され、1回の命令で複数のデータに対する処理を同時に行う。 SIMD演算の概要 http://cell.fixstars.com/ps3linux/index.php/2.1_SIMD%E6%BC%94%E7%AE%97%E3%81%AE%E6%A6%82%E8%A6%81 ### if文の処理 GPUは並列に命令を処理する性質上if elseの命令両方をセットしなくてはならず、命令を実質的に実行するかどうかで処理が変わる。セットするだけでもコストかかるのでelse側の命令を少なくすべき Kepler GPUアーキテクチャとプログラム最適化 http://news.mynavi.jp/series/kepler_gpu/002/ ### iPhoneのGPUについて iPhoneのGPUはプロセッサ内にあり世代別に違いがある。 |iPhone | GPU |備考| |:-----------|:------------|:------------| | iPhone5s | iPowerVR Series 6 クアッドコア G6340 | | | iPhone5 | iPowerVR Series 5XT トリプルコア SGX543MP3|| | Phone4S | iPowerVR Series 5XT デュアルコア SGX543MP2 | | | Phone4 | iPowerVR Series 5 SGX535 | | PowerVR - Wikipedia http://en.wikipedia.org/wiki/PowerVR |
|
| 419位 |
|
|||
|
14:41:38 |
|
|
# 目的 Mac Terminal.app のカラー設定をSolarized にする。 vim とls コマンド結果を、Solarized カラーにする。 私は、TotalTerminal を利用しているが、本手順を行えばTotalTerminal 上でもSolarized カラースキーマが適用できる。 TotalTerminal の設定は、Terminal.app に依存しているため。 ## Solarized とは? * 目に優しく疲れにくい、けど見やすいカラースキーマ * Ethan Schoonover という人が考案 * カッコヨス ## で、どんな感じになる? [Solarized - Ethan Schoonover](http://ethanschoonover.com/solarized) を参照。  *** # 前提条件 ## 環境 ### 作業前 * OS: Mac OSX 10.8.4 ### 作業後(※差異部のみ記載) * coreutils(glsが必要) インストール ## 条件 * Homebrew がインストールされていること *** # 参考情報 * [Solarized - Ethan Schoonover](http://ethanschoonover.com/solarized) * [Issues with solarized and Terminal.app](http://stackoverflow.com/questions/11822081/issues-with-solarized-and-terminal-app) * [LS_COLORSを設定しよう](http://qiita.com/yuyuchu3333/items/84fa4e051c3325098be3) *** # 作業手順:概要 1. Solarized をダウンロード 1. Terminal のテーマに設定 1. vim カラーを設定 1. ls カラーを設定 *** # 作業手順:詳細 ## Solarized をダウンロード OSX 10.8用を以下より任意の場所へダウンロードする。 `$ git clone https://github.com/tomislav/osx-terminal.app-colors-solarized solarized.git` 初め[SolarizedのHP](http://ethanschoonover.com/solarized) から直接DLして設定したんだが、思った感じの色になってなかった。 どうすんのか、探してみたら[同じ悩みの人がいて、それ見たら解決した](http://stackoverflow.com/questions/11822081/issues-with-solarized-and-terminal-app)。 ## Terminal の設定でSolarized プロファイルを読み込む 1. メニューバー > ターミナル > 環境設定 1. タブ > 設定 1. プロファイル下部 > 歯車ボタン > 読み込む 1. ダウンロードしたSolarized(e.g. Solarized Dark.terminal)を選択してプロファイルにロードする(Dark, Light はお好みで) 1. ついでに、そのプロファイルをデフォルトに設定しておくといい ここまでで、基本設定は出来た。 ただ、vim と、ls 結果が、まだ設定出来てないので続けて設定する。 ## vim カラー設定 ※ Solarized Dark.terminal を設定する場合。 Light の場合は、以下設定を`set background=light` にすればいい。 `$ vi ~/.vimrc` ```vimrc syntax enable set background=dark colorscheme solarized let g:solarized_termcolors=256 ``` ## ls カラー設定 Mac デフォだと、BSD のls コマンドを使用しており、ls カラー設定するための`gdircolors` コマンドが使えないので、GNU版のls を入れることにする。 ### GNU 版ls(gls)とかが入ってるcoreutils をインストール `$ brew install coreutils` ### Solarized のディレクトリカラー設定ファイルを任意の場所へダウンロード この例だと、ユーザのホームディレクトリ直下にダウンロード `$ git clone https://github.com/seebi/dircolors-solarized.git ~/dircolors-solarized` ### ls コマンドをgls コマンドに置換え(※任意) 本作業以降、ls コマンドは、ずっとgls コマンドを使いたいならalias 設定しておく。 そうしないなら、この手順は不要。 `$ vi ~/.bash_profile` ```.bash_profile alias ls='gls --color=auto' ``` ### シンボリックリンクの付替えでSolarized の各テーマを変更できるようにする(※任意) Dark、Light を設定ファイルの修正なく変更したいということであれば、予めテーマ自体をシンボリックリンクとし、そのシンボリックリンク自体を設定ファイルに記載しておけば便利。 だが、ずっとかえねぇよ。ということであれば、この手順は不要。 `$ ln -fs ~/dircolors.ansi-universal .dircolors-solarized` ### .bash_profile を編集して`gdircolors` コマンドで、dircolors-solarized を読み込む設定にする `$ vi ~/.bash_profile` ```.bash_profile eval $(gdircolors ~/.dircolors-solarized) ``` 以上で、Terminal が美しい色になる。 |
|
| 420位 |
|
|||
|
01:38:33 |
(Wantedly, Inc. 所属) |
|
複数のリポジトリがあった時に、それをまとめた親レポジトリを作り、各レポジトリをサブディレクトリとしてまとめてしまう方法。 ファイルとして保存するだけじゃなく、ちゃんとコミットHistoryも保存される。一つにまとめたものを後で切り出すこともできる。 これでリポジトリ数の削減が可能になるので、GithubのPrivateリポジトリ数の上限などにお悩みの人は是非お試しを。現在、頻繁に使っているものをまとめてしまうのはおすすめしないが、古い使われてないものを歴史ごとアーカイブするのには持ってこいだと思う。 以下では、 - $ACCOUNT = githubのアカウント名 - $REPO = サブディレクトリに移したいレポジトリ名 - $ARCHIVE = 複数リポジトリをまとめておくアーカイブ用親リポジトリの名前 とする。 ## 複数リポジトリをまとめたアーカイブ用gitリポジトリをローカルに作成 ``` mkdir $ARCHIVE cd $ARCHIVE git init touch .gitignore README.md # 適当に編集 git add . git commit -m "Add .gitignore and README.md" ``` ここで、とりあえず一つ何かCommitしておくのは、失敗しても`git reset --hard HEAD~`で戻れたりと何かと都合がいいから。 ## 既存リポジトリをARCHIVEのサブディレクトリとして保存 ``` git remote add $REPO git@github.com:$ACCOUNT/$REPO.git git fetch $REPO git checkout -b $REPO $REPO/master git filter-branch -f --tree-filter "mkdir $REPO && git mv -k {,.[\!.],..[\!.]}* $REPO/" git checkout master git merge $REPO ``` うまくいってそうだったら、github上のREPOリポジトリを消す。 これをまとめたい各リポジトリに対して行う。 ### 参考 http://stackoverflow.com/questions/13337877/what-is-the-opposite-of-git-filter-branch-subdirectory-filter ## 複数リポジトリをまとめたアーカイブをGithubにPush Github上にARCHIVEという名前のリポジトリを作成する。 ``` git remote add origin git@github.com:$ACCOUNT/$ARCHIVE.git git push -u origin master ``` この順番でやれば、Githubのprivateリポジトリ数の上限に達していても、ちゃんと複数リポジトリをサブディレクトリとしてまとめたアーカイブを作ることができる。 ## おまけ:もう一度切り出したい時 逆にまとめてしまったものをもう一度切り出したいときは以下のようにする。 ``` git clone git@github.com:$ACCOUNT/$ARCHIVE.git $REPO cd $REPO git filter-branch --subdirectory-filter $REPO git remote add origin git@github.com:$ACCOUNT/$REPO.git git push -u origin master ``` |
|
| 421位 |
|
|||
|
19:53:09 |
(kayac.com 所属) |
|
gitレポジトリの状態を前に戻すコマンド```git reset```について、覚えるために自分用メモ。 ## 参考資料 - [git reset についてもまとめてみる - murankの日記](http://d.hatena.ne.jp/murank/20110327/1301224770) - 参考資料というか、これだけではよくわからなかった部分を今回整理してみました、という感じ ## usage ```git reset -h```で出てくるusageはこんな感じ。 ``` git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>] ``` いろいろオプションがあるけれど、普通に使う分には、だいたい以下のものを把握してればいいみたい。 ``` git reset [--soft | --hard] [HEAD | HEAD^] ``` 次の項から、この[HEAD | HEAD^]及び[--soft | --hard]がどういう意味を持つのか、まとめてみる。 ### HEADとHEAD^ この部分は、「状態をどこまで戻すか」の指定をしている。HEADとHEAD^はそれぞれ、最新のコミットの位置・そのひとつ前のコミットの位置を指す。 ただしHEADは、[ここ](http://d.hatena.ne.jp/murank/20110327/1301224770)の図では「どこまでコミットしているか」という意味で使われているので、すこしこんがらがった。そりゃぁ最新のコミットという意味なので、そういうことなんだけど。 なお、ここにはコミットのidを入れてももちろんOK。HEADとHEAD^は代名詞なんですね。 ### --softと--hard ```git reset```はともかく、状態を前に戻すコマンド。しかしgitにおける「状態」は、 - HEAD (現在の最新コミットの状態) - index (何をaddしたか・addした時点でのファイルの状態) - working tree (いま現在のファイルの状態) と3つ考えられる。このうちのどれを取り扱うか指定しているのが、この--soft・--hardなどのオプションだ。 どのオプションを付けると、何が操作対象になるかは、以下の表の通り。 | | HEAD | index | working tree | |:------------|:-----:|:-------:|:------------:| | --soft | o | | | | (no option) | o | o | | | --hard | o | o | o | 要するに、 - やってしまったコミット(だけ)をなかったことにしたかったら、--soft - addもなかったことにしたかったら、オプションなし - ファイルの変更自体をなかったことにしたかったら、--hard ということ。また言い換えると - 現在のファイルの中身が変わっちゃ困るなら、--hardはやっちゃダメ - addしたものを忘れられちゃこまるなら、オプションなしでは危ない。--softをつけよう。 - コミット位置を変えたくないなら、```git reset```使うな…ではなく、戻す先をHEADにしておけば安心ですね。 という感じ。 ## まとめ 分かってしまえば、 ``` git reset <何を戻す?> <どこまで戻す?> ``` というだけの話だったんですね。納得。 まぁ、ミスを修正するためのコマンドにしてはややこしいっすよね。「ええっ! コミットをなかったことにされると困るぞ…!? git resetこわっ!」とか思ってました。 でもこういう理解をしておけば、「何をミスったのか」「どこまで戻れば帳消しにできるのか」だけ考えればコマンド打てるので、使えそうな気がしてきました。 |
|
| 422位 |
|
|||
|
06:40:38 |
|
|
Java 8がリリースされたので早速インストールしたが、まだJava 7も残しておきたいし、切り替えることもあるので調べてみた。 Macでは、 **/usr/libexec/java_home** を使ってJAVA_HOMEを変えるのが一番スマートっぽい。 # インストールされているJDKのバージョン一覧を出す ```bash % /usr/libexec/java_home -V Matching Java Virtual Machines (2): 1.8.0, x86_64: "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home 1.7.0_45, x86_64: "Java SE 7" /Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home ``` # 現在有効なJDKのバージョンを出す ```bash % /usr/libexec/java_home /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home ``` # JDKのバージョンを切り替える ```bash % export JAVA_HOME=`/usr/libexec/java_home -v 1.7.0_45` % java -version java version "1.7.0_45" Java(TM) SE Runtime Environment (build 1.7.0_45-b18) Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode) % javac -version javac 1.7.0_45 % export JAVA_HOME=`/usr/libexec/java_home -v 1.8.0` % java -version java version "1.8.0" Java(TM) SE Runtime Environment (build 1.8.0-b132) Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode) ``` |
|
| 423位 |
|
|||
|
00:19:46 |
(コロプラ 所属) |
|
[Appleが提供しているプログラミングガイド](https://developer.apple.com/jp/devcenter/ios/library/japanese.html)は全部読んだほうがいいなーと思っています。 目下、興味のあるところから順次読み込み中。 今回は[[PDF] Core Animationプログラミングガイド](https://developer.apple.com/jp/devcenter/ios/library/documentation/CoreAnimation_guide.pdf)を読んだので、覚えておいたほうがいいところとかをピックアップし、自分なりの考察なんかを加えて書いています。 ------------------------------------------- ##UIView/CALayerの基本構造 画面になにかを表示しようとした時、使うのは`UIView`かそのサブクラスを使うのが通常です。 `addSubview:`メソッドなど使って階層構造を成して構築していきますね。 普通は`UIView`を使えば問題ありませんが、描画のパフォーマンスやちょっとした細かい演出などをしたい場合は`CALayer`を利用します。 やはりどういう構造になって画面に表示されているか、を知るのは大事なことですね。 `UIView`は通常、レンダリング周りを担当する`CALayer`クラスを`layer`プロパティに持っています。 `layer`はいわゆる`モデルオブジェクト`だと、ガイドラインには書かれています。 レンダリング自体を担当するのではなく、あくまでその情報を保持するモデルとして振る舞う、ということのようです。 ###3種類のレイヤツリー レイヤには、以下の3種類のツリー構造があります。 * モデルレイヤツリー いわゆる`layer`プロパティとそのサブレイヤの階層構造。アニメーションする際は目標値(つまりアニメーションのゴール)の値を保持します。(まさに「モデル」データ) * プレゼンテーションツリー アニメーション実行中の、「現在の値」を保持する。アニメーション中の値を参照したい場合は`presentationLayer`プロパティで参照することができます。(逆に、これにアクセスできるのはアニメーション実行中のみ) * レンダーツリー アニメーション処理のためにCore Animationが内部的に使います。 ###`UIView`に関連付けるレイヤーオブジェクトを変更する 通常は`CALayer`クラスが紐付けられていますが、ビューの`layerClass`クラスメソッドをオーバーライドし、変更したいCALayerのサブクラスを返すようにすることで変更することができます。 ```objc // `CAEAGLLayer`を返す例 + (Class) layerClass { return [CAEAGLLayer class]; } ``` ##レイヤにコンテンツを提供する レイヤはビットマップ画像を管理するモデルクラスです。 表示すべき内容は`contents`プロパティが保持しています。 ###提供する方法 * `contents`プロパティに直接画像オブジェクトを渡す方法 * デリゲートオブジェクトをレイヤに割り当て、デリゲートオブジェクトで描画する方法 * レイヤのサブクラスを作り、描画用メソッドをオーバーライドする方法 ####ビットマップ画像を提供する 通常の画像などを提供する場合は、上記の通り`contents`プロパティに`CGImageRef`型のデータを渡すことで提供することが可能です。 画像を直接`contents`プロパティに渡す場合、画像側の`contentsScale`プロパティに適切な値を設定する必要があります。 (いわゆるRetina対応。Retinaデバイスの場合は2.0を指定する) ####デリゲートを使う デリゲートを使う場合は、`displayLayer:`メソッドか、`drawLayer:inContext:`メソッドを実装して描画コンテンツを返すことで対応します。 デリゲートで対応する場合は、少なくともどちらか一方をオーバーライドする必要があります。 `drawLayer:inContext:`メソッドはその名の通り、現在のコンテキストに描画します。 Core Animation側で適切にコンテキストを生成した後にこのメソッドを呼び出し、メソッドは渡されたコンテキストに描画処理を行うことで対応します。 *※両方のメソッドをオーバーライドした場合は、`displayLayer:`メソッドが使われます。* #####Appleプログラミングガイドのサンプル ######`displayLayer:`メソッド `displayLayer:`メソッドは、アプリケーション側でビットマップ画像のロードや事前準備しておく場合などに有用です。 ```objc - (void)displayLayer:(CALayer *)theLayer { // 状態プロパティの値を検査 if (self.displayYesImage) { // 「Yes」画像を表示 theLayer.contents = [someHelperObject loadStateYesImage]; } else { // 「No」画像を表示 theLayer.contents = [someHelperObject loadStateNoImage]; } } ``` ######`drawLayer:inContext:`メソッド 一方、`drawLayer:inContext:`メソッドは実行時に動的に描画を行うことが可能です。 ```objc - (void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext { CGMutablePathRef thePath = CGPathCreateMutable(); CGPathMoveToPoint(thePath, NULL, 15.0f, 15.0f); CGPathAddCurveToPoint(thePath, NULL, 15.0f, 250.0f, 295.0f, 250.0f, 295.0f, 15.0f); CGContextBeginPath(theContext); CGContextAddPath(theContext, thePath); CGContextSetLineWidth(theContext, 5); CGContextStrokePath(theContext); // パスを解放 CFRelease(thePath); } ``` プログラミングガイドからの引用 > 自動レイヤつきビューに独自のコンテンツを表示したい場合は、当該ビューの描画メソッドをオーバーライドしてください。すると、ビューは自動的に、対応するレイヤのデリゲートを生成し、必要なメソッドを実装するようになっています。この仕組みは変更できません。したがって、コンテンツを描画するためには、ビューのdrawRect:メソッドを実装することになります。 *※自動レイヤ付きビューは、iOSの場合はすべてのViewです。* ####サブクラスで対応する サブクラスでは、以下の方法でコンテンツを提供することができます。 * `display`メソッドをオーバーライドし、直接`contents`プロパティにコンテンツを格納する * `drawInContext:`メソッドをオーバーライドし、引数のコンテキストに直接描画を実行する ------------------------------------------- ##レイヤの視覚スタイルや外観を調整する レイヤの視覚スタイルや外観については、以前[[iOS] CALayerが便利そう](http://qiita.com/edo_m18/items/56267b5108a79f2e1e5d)という記事にちょっとだけ書いたのでこちらを見てください。 ------------------------------------------- ##独自のプロパティをレイヤに追加する キー値コーディングの仕組みを利用して、独自のプロパティが追加できるようになっています。 また、これで設定したプロパティにアクションを関連付けて、任意のプロパティが変化した際に対応するアニメーションが動き出すようにすることも可能です。 ------------------------------------------- ##レイヤのプロパティ変化をアニメーション表示する レイヤのプロパティへの変更は、即時に変化しますが外観は変化しません。(即時に変化するのは保持する値です) 変化はアニメーションによって実行され、徐々に変化していきます。 これは暗黙のアニメーションです。 従って、ごく簡単な変化やあまりコードを記述したくない場合は暗黙のアニメーションを使うとコードがシンプルになります。 *※`CALayer`を新規で作成し、既存のUIViewのレイヤに`addSublayer:`で追加したレイヤの場合にアニメーションします。おそらくUIViewクラスがアニメーション機能をオフにするためでしょう。* 一方、明示的に簡単なアニメーションをさせたい場合は`CABasicAninmation`を利用します。 ###Appleのプログラミングガイドのサンプル ```objc CABasicAnimation *fadeAnim = [CABasicAnimation animationWithKeyPath:@"opacity"]; fadeAnim.fromValue = [NSNumber numberWithFloat:1.0]; fadeAnim.toValue = [NSNumber numberWithFloat:0.0]; fadeAnim.duration = 1.0; [theLayer addAnimation:fadeAnim forKey:@"opacity"]; // レイヤの実プロパティを最終値に変更 theLayer.opacity = 0.0; ``` 明示的なアニメーションの場合、実際の値は変更されません。あくまでアニメーション用の値として使われるだけです。 そのため、アニメーション後に変更を固定したい場合はサンプルの最終行のように変化後の値を設定しておく必要があります。 `addAnimation:forKey:`の`forKey`は、あとでアニメーションを途中で削除する場合などにアニメーションを指定する際に利用します。 (つまり任意の文字列・アニメーション名) ------------------------------------------- ##キーフレームアニメーションを使ってレイヤのプロパティを変更する `CAKeyframeAnimation`オブジェクトを使うことで、上記のシンプルな値の変化をさせるアニメーションだけではなく、非線形に変化するアニメーションも実現することができます。 また、レイヤの位置については指定したパスに沿って移動させることもできます。 ###Appleのプログラミングガイドのサンプル ```objc // 2つの弧(跳ね返りの軌跡)を表すパス(CGPath)を生成 CGMutablePathRef thePath = CGPathCreateMutable(); CGPathMoveToPoint(thePath, NULL, 74.0, 74.0); CGPathAddCurveToPoint(thePath, NULL, 74.0, 500.0, 320.0, 500.0, 320.0, 74.0); CGPathAddCurveToPoint(thePath, NULL, 320.0, 500.0, 566.0, 500.0, 566.0, 74.0); CAKeyframeAnimation *theAnimation; // positionプロパティをキーパスとして指定し、アニメーションオブジェクトを生成 theAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; theAnimation.path = thePath; theAnimation.duration = 5.0; // アニメーションをレイヤに追加 [theLayer addAnimation:theAnimation forKey:@"position"]; ``` ###キーフレーム値を指定する キーフレーム値はキーフレームアニメーションの要です。 キーフレーム値はオブジェクトの配列として指定するのが普通ですが、CGPoint型のデータを含む値(例えば`anchorPoint`や`position`プロパティなど)の場合はCGPathRef型のデータも指定可能です。 配列を準備する場合、いくつかの注意点があります。 プログラミングガイドから引用します。 > * CGRect型のプロパティ(bounds、frameなど)はNSValueオブジェクトにラップします。 * transformプロパティの場合は、CATransform3D型の行列をNSValueオブジェクトにラップします。この場合、レイヤに各変換行列を適用した結果がキーフレームになります。* borderColorプロパティの場合は、CGColorRef型の各データをid型にキャストして配列に追加します。* CGFloat型の値を取るプロパティについては、値をNSNumberオブジェクトにラップして配列に追加します。* レイヤのcontentsプロパティをアニメーション化する場合は、CGImageRef型の配列を指定します。 > CGPoint型の値を取るプロパティについては、(NSValueオブジェクトにラップした)点構造体の配列を生成するか、またはCGPathRefオブジェクトでパスを指定してください。点の配列で指定すると、各点を直線で結んだ折れ線のパスに沿って動くアニメーションになります。CGPathRefオブジェクトであれば、その起点からパスに沿って動くようになり、曲面に沿った動きにすることも可能です。また、開いたパスでも閉じたパスでも構いません。 ####`calculationMode`プロパティ `calculationMode`プロパティは、各キーフレーム間のタイミングの計算に用いるアルゴリズムを表します。 | mode | 意味 | |--------|----------| | kCAAnimationLinear | 指定されたtimingFunctionに応じて線形に補完する | | kCAAnimationCubic | 指定されたtimingFunctionに応じて補完する | | kCAAnimationPaced | 等速で補完する。指定された`keyTimes`や`timingFunctions`は無視される | | kCAAnimationCubicPaced | (調査中) | | kCAAnimationDiscrete | 離散(discrete)アニメーション。キーフレーム間は補完されずに一気に変化する | ####`keyTimes`プロパティ 各キーフレーム値を適用する時刻マーカを指定します。 (`calculationMode`が`kCAAnimationLinear`、`kCAAnimationDiscrete`、`kCAAnimationCubic`のときのみ有効) ####`timingFunctions`プロパティ 隣接するキーフレーム間に適用されるタイミング曲線を指定します。 (ease-inやease-outのような) ------------------------------------------- ##明示的アニメーションを実行中に停止する アニメーションを停止する方法がいくつかあります。 * `removeAnimationForKey:`メソッドで、指定したキーのアニメーションを削除する * `removeAllAnimations`メソッドですべてのアニメーションを削除する ※削除されたアニメーションはレイヤが保持している現在値を元に再描画します。 ------------------------------------------- ##`CAAnimationGroup`オブジェクトを使って複数のアニメーションを実行する 上記のアニメーションは単一のキーに対するアニメーションでした。 これを複数(例えば画面外に移動しながらフェードアウト、のような)のアニメーションを実装したい場合に使います。 `CAAnimationGroup`オブジェクトはグループ化に特化していて、仮に個別の`CAKeyframeAnimation`オブジェクトにタイミングなどを指定していても、グループ側の設定が優先されます。 ###Appleプログラミングガイドのサンプル ```objc // アニメーション1 CAKeyframeAnimation *widthAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderWidth"]; NSArray *widthValues = [NSArray arrayWithObjects:@1.0, @10.0, @5.0, @30.0, @0.5, @15.0, @2.0, @50.0, @0.0, nil]; widthAnim.values = widthValues; widthAnim.calculationMode = kCAAnimationPaced; // アニメーション2 CAKeyframeAnimation *colorAnim = [CAKeyframeAnimation animationWithKeyPath:@"borderColor"]; NSArray* colorValues = [NSArray arrayWithObjects:(id)[UIColor greenColor].CGColor, (id)[UIColor redColor].CGColor, (id)[UIColor blueColor].CGColor, nil]; colorAnim.values = colorValues; colorAnim.calculationMode = kCAAnimationPaced; // アニメーショングループ CAAnimationGroup *group = [CAAnimationGroup animation]; group.animations = [NSArray arrayWithObjects:colorAnim, widthAnim, nil]; group.duration = 5.0; [myLayer addAnimation:group forKey:@"BorderChanges"]; ``` ------------------------------------------- ##アニメーションの終了を検出する アニメーションには、開始/終了を検出する仕組みがあります。 アニメーションの状態を検出する方法は2通りあります。 * `setCompletionBlock:`メソッドで、アニメーションが終了した段階で呼ばれるblockを渡す * CAAnimationオブジェクトにデリゲートを設定し、`animationDidStart:`メソッドと`animationDisStop:finished:`メソッドを実装する ------------------------------------------- ##UIViewをアニメーションさせる UIViewは通常、プロパティを変更しても自動的にアニメーションされません。 UIViewをアニメーションさせる場合は、`UIView#animateWithDuration:animations:`クラスメソッドを利用します。 animations引数はblockを渡し、その中でアニメーションさせたいプロパティの値を変化させます。 これは、このblock内に限りアニメーションを有効にすることになるためです。 ###Appleプログラミングガイドのサンプル ```objc [UIView animateWithDuration:1.0 animations:^{ // 不透明度を暗黙に変更 myView.layer.opacity = 0.0; // 位置を明示的に変更 CABasicAnimation *theAnim = [CABasicAnimation animationWithKeyPath:@"position"]; theAnim.fromValue = [NSValue valueWithCGPoint:myView.layer.position]; theAnim.toValue = [NSValue valueWithCGPoint:myNewPosition]; theAnim.duration = 3.0; [myView.layer addAnimation:theAnim forKey:@"AnimateFrame"]; }]; ``` ------------------------------------------- ##レイヤ階層にレイヤを配置する レイヤは、UIViewと似たような階層構造を形成します。 子のレイヤを「サブレイヤ」、親のレイヤを「スーパーレイヤ」と呼びます。UIViewの関係と同じです。 表示順もUIViewと同様に、あとから追加したものが上に来ますが、`zPosition`プロパティが設定されている場合はそれを優先します。 ------------------------------------------- ##レイヤ階層がアニメーションに与える影響 `speed`プロパティはアニメーション速度を指定するプロパティです。 このプロパティを(例えば)2.0にした場合は速度が2倍のアニメーションになります。 そしてこのプロパティは子レイヤにも設定されている場合は累積となります。 つまり、スーパーレイヤで2.0を指定し、さらに子レイヤにも2.0を指定すると、子レイヤは累積4.0倍のスピードで動くことになります。 プロパティの中にはこうした子レイヤに影響を与えるものがあります。 ------------------------------------------- ##レイヤ間で座標値を変換する 各レイヤはいわゆる座標空間を持ち、子レイヤに影響を与えます。 (例えば、親レイヤが移動すれば自動的に子レイヤも移動する) つまり子レイヤは「相対位置」で表されます。 しかし、例えばタッチ位置などは画面全体の中での座標を示します。 この位置に調べたい子レイヤがあるか、といったチェックをする際に座標値を変換する必要が出てきます。 [座標変換については以前に記事を書いている](http://qiita.com/edo_m18/items/06e55b60f4d7adb9e0ea)のでそちらをご覧ください。 変換のためのメソッドだけ列挙しておきます。 * convertPoint:fromLayer: * convertPoint:toLayer; * convertRect:fromLayer; * convertRect:toLayer; ###レイヤの持つ時間空間 レイヤにはそれぞれ独自に時間空間を管理しています。 時間はアニメーションの開始や終了時刻を、システムの他の部分と同期するようになっています。 この独自管理の時間空間も、座標変換と同様に変換用のメソッドが用意されています。 * convertTime:fromLayer; * convertTime:toLayer; 通常はどの時間も同期していますが、あるレイヤでアニメーション速度を変更するなどすると時間にずれが生じます。 アニメーションの終わりを合わせたい場合などは、時間空間を変換することで時間をあわせることができるようになります。 ------------------------------------------- ##高度なアニメーション ###遷移アニメーション 遷移アニメーションオブジェクト(`CATransition`)を利用すると、いわゆる遷移アニメーションを実装することが出来ます。 また遷移アニメーションは、開始/終了時点の値(`startProgress`/`endProgress`)を設定することで、アニメーションを途中で終わらせることができます。 ####Appleプログラミングガイドのサンプル *myView1のみが可視状態となっている前提です。* ```objc CATransition *transition = [CATransition animation]; transition.startProgress = 0; transition.endProgress = 1.0; transition.type = kCATransitionPush; transition.subtype = kCATransitionFromRight; transition.duration = 1.0; // 2つのレイヤに遷移アニメーションを追加 [myView1.layer addAnimation:transition forKey:@"transition"]; [myView2.layer addAnimation:transition forKey:@"transition"]; // 最後に各レイヤの表示/非表示を更新 myView1.hidden = YES; myView2.hidden = NO; ``` `type`、`subtype`に指定できる定数は以下の通り。名前から推測できますね。 ####定数一覧 #####type * kCATransitionFade * kCATransitionMoveIn * kCATransitionPush * kCATransitionReveal #####subtype * kCATransitionFromRight * kCATransitionFromLeft * kCATransitionFromTop * kCATransitionFromBottom ------------------------------------------- ##アニメーションのタイミングを調整する ```objc CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime() fromLayer:nil]; ``` `fillMode`と`kCAFillModeBackwards` `beginTime`プロパティ `autoreverses`プロパティ `repeatCount`プロパティ … 1.5で終了値で終わる `timeOffset`プロパティ ------------------------------------------- ##アニメーションを一時停止/再開する アニメーションを一時停止したい場合、レイヤが`CAMediaTiming`プロトコルに準拠していることを利用し、速度を0.0にすることで擬似的に一時停止させることができます。 再開したい場合は速度を0以外にすれば再開することができます。 ###Appleプログラミングガイドのサンプル ```objc - (void)pauseLayer:(CALayer *)layer { CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil]; layer.speed = 0.0; layer.timeOffset = pausedTime; } ``` ```objc - (void)resumeLayer:(CALayer *)layer { CFTimeInterval pausedTime = [layer timeOffset]; layer.speed = 1.0; layer.timeOffset = 0.0; layer.beginTime = 0.0; CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; layer.beginTime = timeSincePause; } ``` ####サンプルに対してのメモ #####停止部分 上が停止のコード、下が再開のコードです。 やっていることは、停止時にまず、現在時刻を`timeOffset`プロパティに設定した上で、`speed`プロパティを0に設定します。こうすることでアニメーションが停止します。 *おそらく、speedを0にしつつ`timeOffset`に現在時刻を入れることで当分先になるようオフセットを設定しているのだと思います。* #####再開部分 続いて再開部分は、まず`speed`を1にし、`timeOffset`を0にします。 `beginTime`に設定しているのは「実際の」時間です。(とある時点からの秒数? つまり、相対時間ではなく、絶対時間的な感覚で時間を指定します。例えば「何時何分から開始」のような) *最初、オフセットのように「5秒後に開始」させようとして`layer.beginTime = 5;`としてもまったく動かず悩んだんですが、上で書いた通り実際の時間を指定するようです。つまり、「現在の時間+5秒」とする必要がある、ということです。* #####解説 ちなみに下のコードで`timeOffset`値を取り出し、とあるレイヤの現在時刻から引いているのは、`timeOffset`の設定により **現在時刻が未来になっている** ためです。 つまり該当行時点では現在時刻の倍の時間が進んだ状態になっている、ということです。(ローカル時間空間的に) そこで、その時間から現在時刻を引くことで、未来から現在に戻している、という処理になります。 ------------------------------------------- ##明示的なトランザクションでアニメーションパラメータを変更できる 複雑なアニメーションを管理するために、`CATransaction`クラスが用意されています。 (例えばグループ化や開始時間の管理など) 基本的に、レイヤに追加されたアニメーションは暗黙のトランザクションが生成され管理されます。 **明示的にトランザクションを生成するコード例(Appleプログラミングガイドから引用)** ```objc [CATransaction begin]; theLayer.zPosition = 200.0; theLayer.opacity = 0.0; [CATransaction commit]; ``` トランザクションに対するパラメータ(durationなど)を変更する場合は`setValue:forKey:`メソッドを使って変更する必要があります。 ```objc [CATransaction begin]; [CATransaction setValue:[NSNumber numberWithFloat:10.0f] forKey:kCATransactionAnimationDuration]; ``` ###トランザクションの入れ子 トランザクションは入れ子にすることができます。(`begin`メソッドと`commit`メソッドを対にして入れ子にする) 入れ子にすることで、トランザクションごとに設定を施すことができ、個別のアニメーションをさせることが可能になります。 ------------------------------------------- ##アニメーションに遠近感を与える レイヤは通常、単純化のため平行投影して表示されます。(要は遠近感(パース)がない表示モード) しかし、遠近感を表現する座標変換行列を用いることで、3D空間の遠近感を演出することが可能です。 この遠近感を出す方法は、スーパーレイヤの`sublayerTransform`プロパティに適切な変換行列を設定します。 (`sublayerTransform`プロパティは、自身には影響なく、子レイヤに対して変換行列が適用されます) 遠近感を出す設定は以下の通りです。 ###Appleプログラミングガイドのサンプル ```objc CATransform3D perspective = CATransform3DIdentity; perspective.m34 = -1.0 / eyePosition; // 変換を親レイヤに適用 myParentLayer.sublayerTransform = perspective; ``` `m34`は行列の3行4列目を表す要素です。 遠近感(パースペクティブ)を出すのはこの値を適切に設定することでいわゆる「パース」がつくようになります。 `eyePosition`の値を色々変えてみるとパースの付き方が確認できます。(500〜600が人間が見るのと同じくらいのパースだと思います) ------------------------------------------- ##レイヤのデフォルトの動作を変更する Core Animationは「アクションオブジェクト」を使ってレイヤの暗黙のアニメーション動作を実装しています。 アクションオブジェクトは`CAAction`プロトコルに準拠したオブジェクトで、レイヤ上で実行されるなにかしらの動作(アクション)を定義するオブジェクトです。 ###アクションオブジェクトは`CAAction`プロトコルに準拠する 独自のアクションオブジェクトを生成するためには、`CAAction`プロトコルに準拠し、`runActionForKey:object:arguments:`メソッドを実装する必要があります。 ###アクションを起動する方法を決める アクションオブジェクトを定義するだけでなく、その起動も決める必要があります。 アクションの起動が設定できるのは以下になります。(Appleプログラミングガイドから引用) > * レイヤのあるプロパティ値が変化したとき。アニメーション化可能なプロパティでなくても構いません(さらに、独自にレイヤに追加したプロパティであっても可)。このアクションを識別するキーは、当該プロパティの名前と同じです。* レイヤが可視になった、あるいはレイヤ階層に追加されたとき。このアクションを識別するキー はkCAOnOrderInです。* レイヤがレイヤ階層から削除されたとき。このアクションを識別するキーはkCAOnOrderOutです。* レイヤが遷移アニメーションに関与しようとしているとき。このアクションを識別するキーは kCATransitionです。 ###アクションオブジェクトを対象レイヤに組み込む 生成したアクションをレイヤに組み込むと、Core Animationはアクションオブジェクトを以下の順番で検索します。(Appleプログラミングガイドから引用します) > 1. レイヤにデリゲートがあってactionForLayer:forKey:メソッドが実装されていれば、これを呼び出します。デリゲートは次のいずれかを実行しなければなりません。 * 与えられたキーに対応するアクションオブジェクトを返す。 * nilを返す。アクションを処理できない旨を表し、この場合、検索を続行することになります。 * NSNullオブジェクトを返す。この場合、検索を直ちに打ち切ることになります。2. 与えられたキーで、レイヤのactions辞書を検索します。3. style辞書から、与えられたキーを含むactions辞書を検索します。詳しく言うと、style辞書には actionsキーがあり、その値は再び辞書になっています。レイヤはこの辞書から、与えられたキーに対する値を検索するのです。4. クラスメソッドdefaultActionForKey:を呼び出します。5. Core Animationに定義された、暗黙のアクションがあればそれを実行します。 ####Appleプログラミングガイドのサンプル ```objc - (id<CAAction>)actionForLayer:(CALayer *)theLayer forKey:(NSString *)theKey { CATransition *theAnimation = nil; if ([theKey isEqualToString:@"contents"]) { theAnimation = [[CATransition alloc] init]; theAnimation.duration = 1.0; theAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; theAnimation.type = kCATransitionPush; theAnimation.subtype = kCATransitionFromRight; } return theAnimation; } ``` このメソッドはレイヤのプロパティが変化した際などに呼び出されます。 上記サンプルでは、`contents`プロパティが変化した場合のみ、トランジションオブジェクトを生成してそれを返しています。 ------------------------------------------- ##CATransactionを使って一時的にアクションを無効にする 以下のようにすることで、暗黙のトランザクションによるアニメーションを無効化できます。 ```objc [CATransaction begin]; [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; [aLayer removeFromSuperlayer]; [CATransaction commit]; ``` ------------------------------------------- ##描画・アニメーション性能の改善 ###できるだけ不透明なレイヤを使う `layer.opaque`プロパティに`YES`を指定する ###同じ内容のレイヤにはコンテンツを明示的に設定する 同じ画像を表示するレイヤに関しては、レイヤの`contents`プロパティに画像を直接指定します。 すると、レイヤはバッキングストアとして設定された画像を直接使うため、メモリを節約できます。 ###レイヤの大きさは常に整数で設定する ###必要であれば非同期にレイヤをレンダリングする デリゲートの`drawLayer:inContext:`メソッドやビューの`drawRect:`メソッドは通常、メインスレッドで同期的に実行されます。 しかし同期的に処理すると重い場合などは`drawsAsynchronously`プロパティを有効にして、バックグラウンドスレッドで処理することができます。 |
|
| 424位 |
|
|||
|
14:45:33 |
|
|
まずこんなテーブルを作るとします。ここに毎月10万件以上のレコードが入ってくる予定です。
1レコードが57byteなので、月に5.7Mbyte、プライマリーキーを入れると60Mbyteくらいが入ってきます。 年間にすると720Mbyteなので、まぁデータ量的には余裕だと思うのですが、 100万レコードを超えるとレスポンスが鈍化するという印象があります。 というわけで、MySQLにあるパーティショニング機能を使い、データを振り分けたいと思います。 【参考】[DB設計時のサイズ見積もり | よねのはてな](http://d.hatena.ne.jp/yone098/20090512/1242088638) # テーブル作成 注意する点として、パーティショニングのキーにしたいカラムを、プライマリーキーに含める必要があるようです。 なので、オートインクリメントのカラムがあるテーブルだと辛い。構成を考えなおした方がいいかも。 ```sql:create_table CREATE TABLE `list_rtx` ( `member_id` varchar(40) CHARACTER SET sjis COLLATE sjis_bin NOT NULL, `platform` varchar(10) NOT NULL, `year` smallint(5) unsigned NOT NULL, `month` tinyint(2) unsigned NOT NULL, `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`member_id`,`year`,`month`,`platform`) ) ENGINE=InnoDB DEFAULT CHARSET=utf-8 ``` # パーティションの作成 1年毎にパーティションを分けたいと思います。 年を基準に範囲を決めて、振り分けます。 なので、今回はRANGEを使います。 ```sql:partitioning ALTER TABLE `list_rtx` PARTITION BY RANGE (YEAR(`year`)) ( PARTITION p2013 VALUES LESS THAN (2013) ENGINE = InnoDB, PARTITION p2014 VALUES LESS THAN (2014) ENGINE = InnoDB, PARTITION p2015 VALUES LESS THAN (2015) ENGINE = InnoDB, PARTITION p2016 VALUES LESS THAN (2016) ENGINE = InnoDB, PARTITION p2017 VALUES LESS THAN (2017) ENGINE = InnoDB, PARTITION p2018 VALUES LESS THAN (2018) ENGINE = InnoDB, PARTITION p2019 VALUES LESS THAN (2019) ENGINE = InnoDB, PARTITION p2020 VALUES LESS THAN (2020) ENGINE = InnoDB, PARTITION pmax VALUES LESS THAN MAXVALUE ); ```` パーティションを後から操作するのは、サービスが稼働している場合はメンテナンスを入れなくてはいけなくなリます。 なので、出来るだけ最初からユースケースを具体的に想定して、想定より少し多めのパーティションを作成します。 【参考】[今更だけどMySQLのパーティショニング機能を試してみた | (゚∀゚)o彡 sasata299's blog](http://blog.livedoor.jp/sasata299/archives/51882315.html) # パーティションの確認 ```sql:confirm_partition SELECT TABLE_SCHEMA, TABLE_NAME, PARTITION_NAME, PARTITION_ORDINAL_POSITION, TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 'list_rtx'; ``` どのパーティションが使用されているかは以下。EXPLAIN PARTITIONを上記に追加。 ```sql:confirm_used_partition EXPLAIN PARTITIONS SELECT TABLE_SCHEMA, TABLE_NAME, PARTITION_NAME, PARTITION_ORDINAL_POSITION, TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 'list_rtx'; ``` # パーティションの削除 そのパーティション内のデータも消えます/(^o^)\ ```sql:delete_partition ALTER TABLE list_rtx DROP PARTITION p2015; ``` # パーティションの追加・再構成 パーティションの追加は基本的に今あるパーティションの後ろにしか出来ない様です。 なので、maxvalueを用いて振り分けてイた場合、パーティションを追加出来なくなります。 その為、追加ではなく、パーティション全体を再構成させます。 *尚、データは消えません。* 例: 2013年より前のデータを保存することになったから、パーティションを追加したい ```sql:reorganize_partition ALTER TABLE mau_list_rtx REORGANIZE PARTITION p2013 INTO ( PARTITION p2010 VALUES LESS THAN (2010), PARTITION p2011 VALUES LESS THAN (2011), PARTITION p2012 VALUES LESS THAN (2012), PARTITION p2013 VALUES LESS THAN (2013) ); ```` 【参考】[第15章 パーティショニング](http://mysql.manual.php.to/partitioning.html#partitioning-management) 【参考】[MySQLパーティショニングでパフォーマンスアップ! | QuickKnowLedge](http://www.s-quad.com/wordpress/?p=62) # 複合パーティショニング 例えば「年月」でパーティションを分けたくて、テーブルには「年」と「月」が別のカラムにある場合。 サブパーティション(複合パーティショニング)を使います。 ```sql:sub_partitioning ALTER TABLE `list_rtx` PARTITION BY RANGE (YEAR(`year`)) SUBPARTITION BY HASH (MONTH(`month`)) SUBPARTITIONS 12 ( PARTITION p2013 VALUES LESS THAN (2013) ENGINE = InnoDB, PARTITION p2014 VALUES LESS THAN (2014) ENGINE = InnoDB, PARTITION p2015 VALUES LESS THAN (2015) ENGINE = InnoDB, PARTITION p2016 VALUES LESS THAN (2016) ENGINE = InnoDB, PARTITION p2017 VALUES LESS THAN (2017) ENGINE = InnoDB, PARTITION p2018 VALUES LESS THAN (2018) ENGINE = InnoDB, PARTITION p2019 VALUES LESS THAN (2019) ENGINE = InnoDB, PARTITION p2020 VALUES LESS THAN (2020) ENGINE = InnoDB, PARTITION pmax VALUES LESS THAN MAXVALUE ); ```` # その他 多分、パーティションを作成するSQLではいちいち、ENGINE = hogehoge, を指定しなくても問題ないんじゃないかな?と思います。 【参考】[15.2.1. RANGE パーティショニング | MySQL](http://dev.mysql.com/doc/refman/5.1/ja/partitioning-range.html) |
|
| 425位 |
|
|||
|
19:28:35 |
|
|
そろそろiOSで開発本腰入れようと思って、便利なライブラリたちを色々挙げてみました。 ##フィルタライブラリ:[GPUImage](https://github.com/BradLarson/GPUImage) 爆速のフィルターライブラリ ##画像選択:[ELCImagePickerController](https://github.com/elc/ELCImagePickerController) UIImagePickerでかつ複数の画像を選択できるライブラリ ##通信:[AFNetworking](https://github.com/AFNetworking/AFNetworking) 便利なHTTPクライアント的なやつ [AFNetworkingの通信内容が更新されない問題](http://d.hatena.ne.jp/unlimitedtas/20130108/1357678405) ##プログレスダイアログ:[SVProgressHUD](https://github.com/samvermette/SVProgressHUD) プログレスダイアログ 単純なものならこちらの方が楽です。 ##プログレスダイアログ:[MBProgressHUD](https://github.com/jdg/MBProgressHUD) プログレスダイアログ ##レビュー:[Appirater](https://github.com/arashpayan/appirater) レビュー書いてね!というものを適度なタイミングで自動で出してくれるライブラリ ##ユーザーフィードバック:[AAMFeedback](https://github.com/fladdict/AAMFeedback) ユーザーからの問い合わせフォームを作成してくれる ##プルリフレッシュ:[EGOTableViewPullRefresh](https://github.com/enormego/EGOTableViewPullRefresh) TableViewにおいて、引っ張ったら更新されるUIを実現 ##プルリフレッシュ:[ISRefreshControl](https://github.com/ishkawa/ISRefreshControl) iOS6で実装されたプルリフレッシュをiOS5などでも使用出来るようにしたもの ##バックジェスチャー:[ISBackGesture](https://github.com/ishkawa/ISBackGesture) スワイプで戻るボタンの代わりになるようにするもの ##アニメーション:[BCGenieEffect](https://github.com/Ciechan/BCGenieEffect) グニュッとなるアニメーション ##プッシュ通知:[Parse](https://www.parse.com/) プッシュ通知をするためのBaaSサービス 100万APIまで無料というのがステキ iOS、Android、Windows Phoneなどマルチプラットフォームに対応しています。 ##クラッシュレポート:[Crashlytics](http://try.crashlytics.com/) コードに1行追加するだけで、クラッシュレポートを取得できる ###ちょっと別 ##iOSのUI系ライブラリまとめ:[cocoa CONTROLS](http://www.cocoacontrols.com/) iOSやOS Xの様々なUIがソース込で載っているサイト ##NSLogをカラーリング:[XcodeColors](https://github.com/robbiehanson/XcodeColors) XcodeのNSLogを好きな色に変更することができる |
|
| 426位 |
|
|||
|
21:22:17 |
(株式会社キュリオシティソフトウェア 所属) |
|
Androidアプリ開発時に使うEclipseなどとは違い、Xcode上のファイルグループ分けと実ファイルの物理構成はリンクしているわけではないので、実ファイルをどのように配置していくのかはプロジェクト初期にメンバーで都度決めていると思います。 自分がいつもやっているのは実ファイルをMVCに分類し、Classesディレクトリの下にMVCディレクトリを作りそこに配置していくことです。 例としてクラスAModel,BModel,AControllerとリソースであるstoryboardやxibがある場合は次のようにしています。 ``` SampleProject/Classes/Models/AModel.h SampleProject/Classes/Models/AModel.m SampleProject/Classes/Models/BModel.h SampleProject/Classes/Models/BModel.m SampleProject/Classes/Controllers/AController.h SampleProject/Classes/Controllers/AController.m SampleProject/Classes/Views/Main.storyboard SampleProject/Classes/Views/CustumCell.xib SampleProject/Classes/Views/CustumCell.h SampleProject/Classes/Views/CustumCell.m ``` ## なぜClassesディレクトリを使うのか 現在のXcode5系のテンプレートプロジェクトのまま作ってしまうとClassesディレクトリがなく、plistやpcxファイルと同じディレクトリに全てのコードが配置されるとプロジェクトの見通しが悪く感じてしまいます。また、Xcode3系のときはClassesディレクトリが自動で作成されていた事があり、そのClassesという名前をそのまま使っています。 ## なぜユースケースごとに分けないのか 複数のユースケースがある場合、どのユースケースのディレクトリに格納すべきか迷うのを避けるためです。webアプリであれば画面ごとにControllerを分けることも多いと思いますが、iOSアプリ開発ではViewControllerは画面に依存しているというよりもViewとModelに依存しており、さらにコンテナ機能によって使い回しがしやすいために画面ごとに分けるのは後々余計な頭を使ってしまいます。 ## なぜMVC以下は深く分けないのか さきほどの例ではAModel.hとBModel.hは実ファイルの構成上同じModels階層にいますが、この階層を深く分けたくなると思います。例を示すと次のようになります。 ``` SampleProject/Classes/Models/A/AModel.h SampleProject/Classes/Models/A/AModel.m SampleProject/Classes/Models/B/BModel.h SampleProject/Classes/Models/B/BModel.m ``` これで見通しの良さは向上するのですが、開発中、Xcode上でAModelのグループ名Aを変更したくなるときがあるでしょう。そのとき、グループ名を変えた上でディレクトリ名を同じように変えたくなりますよね。しかし、Xcode上で実ディレクトリ名を変えることはできないため、Xcode上での操作+GUI or CUIでのコマンド操作が必要になりどうしても開発のリズムを崩しリファクタリングが億劫になり良くありません(特にgit/svnを使っていた場合は面倒な感じを受けます)。 したがって、リファクタリングでMVC間をまたがるということはないとして、実ファイルはMVCには分けるがそれ以上は分類しないということで妥協します。Xcode上のグループは分かりやすくMVC以下も深く分けていくのが良いと思います。 ## MVCに分類できないのはどうするの? よくあるパターンとしてUtilitesとかCategoriesとかでしょうかね。ViewとControllerに分類できないならModelでいいとは思いますが、開発チーム内で話しあって決めたいところです。 ## 画像やサウンドなどリソースの配置はどうするの? [Xcode5からはAsset Catalog](http://qiita.com/yimajo/items/239eb68265e4ea772484)が導入され、画像のリソースの配置や命名についてはほとんど気にしなくなりましたが、Asset Catalogを使えない場合は次のような制約が適切です。 - リソースは一つのディレクトリに平たく置かず物理ディレクトリを分ける - Resourcesディレクトリを作りその下にimagesとsoundsとする - imagesディレクトリの下にbutton、iconsと構成していく - ディレクトリ構成とグループ構成を一致させる ソースコードの場合はリファクタリングすることが多いためディレクトリ構成を深くしないのですが、画像などのリソースファイルはディレクトリやグループ構成をリファクタリングしたくなることが少ないため、整理しやすさを重視してディレクトリを作成しグループ構成と合わせます。 自分の経験から次のような理由があります - デザイナから提供される素材が未整理で一つのディレクトリにまとめて送ってくるという事はないのでその構成を維持してあげる - 素材を貰う前の自作したダミー構成は捨てる - Xcodeを起動しなくてもディレクトリでわけてあれば他のプロジェクトの画像が把握しやすい - 再利用するときに他の開発メンバーやデザイナも画像を見つけやすい 画像をimagesディレクトリに置くこともメリットがあるので、開発チーム内での認識合わせによると思います。 ちなみにAsset Catalog上のAsset名は実ファイルの画像名と違ってもいいので、デザイナが送ってきたファイル名が規約に沿っていないなどであっても指摘したりファイル名を変更する必要がなくなり便利ですね。 # 参考 意図が同じかどうかは分かりませんが、はてなインターン2013の構成も自分と同じような構成だったので参考にリンクしておきます https://github.com/hatena/ios-Intern-Bookmark-2013/tree/master/InternBookmark/Classes # その他 - Xcode上でも物理ファイル構成の連携できてなかったっけ? - 他にもいい方法があれば教えて下さい |
|
| 427位 |
|
|||
|
11:05:50 |
|
|
ここまでは情報がかなりたくさんあるので、サクサク行けそう。 ####Socket.IO [リアルタイムWebを構築しやすくする「Socket.IO」とは ](http://codezine.jp/article/detail/7075)によると、「リアルタイムWeb技術の実装方式を隠蔽し、すべてのブラウザ・モバイルデバイスでリアルタイム通信を可能とすること」を目指して開発されている、node.js用サーバ側ライブラリとブラウザ用JavaScriptライブラリのセット、だそうです。 双方向通信で非同期にデータをやり取りする方法は色々あるが、それらをラップして統一したインターフェイスにする事で、保守性や習得しやすさを獲得している、って事でしょうか。独自解釈です。 ##環境 ゲスト: CentOS 6.2 ゲスト2: Windows 7 Home Premium SP1 64bit ホスト: MacBookAir MacOS 10.8 VM: Parallels Desktop 9 for mac ゲストを対象とし、IPアドレスは 10.211.55.2 とする。 #### ソフトウェア Nginx: v1.4.3 Node.js: v0.10.20 node-express: v3.4.4 node-forever: v0.10.9 #### サンプルアプリ [前回](http://qiita.com/ogwmtnr/items/03996d3798facbc600da)のものを流用。 一応作ったときの軌跡を以下に。 ```bash: $ cd $ express -e SampleApp $ cd SampleApp $ npm install ``` ##Socket.io インストール npm でインストールします。 ```bash:~/SampleApp $ npm install socket.io ``` ##サンプルアプリを Socket.io 対応 [【初心者向け】node.js(0.10x) + socket.io(0.9x)のサンプルプログラム](http://d.hatena.ne.jp/replication/20130609/1370708498)のサンプルプログラムを流用させていただきました。 クライアント側の`io.connect`で示すURLが、自分の環境では異なったため、そこだけ変更しました。 Nginx からプロキシできるなら、これで良いはず... ```js:~/SampleApp/app.js /** * Module dependencies. */ var express = require('express') , routes = require('./routes') , user = require('./routes/user') , http = require('http') , path = require('path'); var app = express(); app.configure(function(){ app.set('port', process.env.PORT || 3000); app.set('views', __dirname + '/views'); app.set('view engine', 'ejs'); app.use(express.favicon()); app.use(express.logger('dev')); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(path.join(__dirname, 'public'))); }); app.configure('development', function(){ app.use(express.errorHandler()); }); app.get('/', routes.index); app.get('/users', user.list); server = http.createServer(app); // add //http.createServer(app).listen(app.get('port'), function(){ // del server.listen(app.get('port'), function(){ //add console.log("Express server listening on port " + app.get('port')); }); // add start var socketIO = require('socket.io'); // クライアントの接続を待つ(IPアドレスとポート番号を結びつけます) var io = socketIO.listen(server); // クライアントが接続してきたときの処理 io.sockets.on('connection', function(socket) { console.log("connection"); // メッセージを受けたときの処理 socket.on('message', function(data) { // つながっているクライアント全員に送信 console.log("message"); io.sockets.emit('message', { value: data.value }); }); // クライアントが切断したときの処理 socket.on('disconnect', function(){ console.log("disconnect"); }); }); // add end ``` ```html:~/SampleApp/views/index.ejs <script src="/socket.io/socket.io.js"></script> <script type="text/javascript"> // var socket = io.connect('http://localhost:3000'); var socket = io.connect('http://10.211.55.2/'); socket.on('connect', function(msg) { console.log("connet"); document.getElementById("connectId").innerHTML = "あなたの接続ID::" + socket.socket.transport.sessid; document.getElementById("type").innerHTML = "接続方式::" + socket.socket.transport.name; }); // メッセージを受けたとき socket.on('message', function(msg) { // メッセージを画面に表示する document.getElementById("receiveMsg").innerHTML = msg.value; }); // メッセージを送る function SendMsg() { var msg = document.getElementById("message").value; // メッセージを発射する socket.emit('message', { value: msg }); } // 切断する function DisConnect() { var msg = socket.socket.transport.sessid + "は切断しました。"; // メッセージを発射する socket.emit('message', { value: msg }); // socketを切断する socket.disconnect(); } </script> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1>socket.ioのサンプルプログラム</h1> <div id="connectId"></div> <div id="type"></div> <br> <input type="text" id="message" value=""> <input type="button" value="メッセージを送る" onclick="SendMsg()"> <input type="button" value="切断する" onclick="DisConnect()"> <div id="receiveMsg"></div> </body> </html> ``` ##Nginx 設定ファイルを WebSocket 対応 プロキシ設定を探している時に、[Nginxの最新安定版がWebSocketのリバースプロキシに対応したそうなので試してみた](http://kitak.hatenablog.jp/entry/2013/04/27/180255)の貴重な情報を得たので、以下の設定を書き加えておく。 ```bash:/etc/nginx/conf.d/node-app.conf server { 〜略〜 # for WebSocket proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 〜略〜 } ``` ```bash: $ sudo service nginx reload ``` ##node で起動してコンソールを追いながら確認 forever でアプリを動かしている場合は、 ```bash:~/SampleApp $ forever stop app.js ``` で一度動作を止めて、 ```bash:~/SampleApp $ node app ``` で直接起動した方が、コンソール出力を目で追いながら動作を確認できるので、テスト中はこっちの方が良いのかもしれない。 `Command + c`で停止できます。 ##node で起動したら最初に Warn 出たんだけど… [最新Express.jsでのWarning解消](http://qiita.com/ksato9700/items/f7ea00d05b64ec031891)を参考に、`app.js`を一部修正しました。 ```js:~/SampleApp/app.js //〜略〜 app.use(express.bodyParser()); //〜略〜 //↓ //〜略〜 // app.use(express.bodyParser()); app.use(express.json()); app.use(express.urlencoded()); //〜略〜 ``` 改めて... ```bash:~/SampleApp $ node app ``` ##ブラウザで確認 ホスト、ゲスト2それぞれのブラウザで、 http://10.211.55.2/ にアクセスして、確認。 ##あなたの接続ID表示確認、メッセージ送受信と表示を確認 コングラッチュレーション…! ##感謝 参考にさせていただいたサイト管理人の皆様、誠にありがとうございました。 |
|
| 428位 |
|
|||
|
01:50:45 |
(INSANEWORKS,LLC 所属) |
|
[Gistに移動しました](https://gist.github.com/kotashiratsuka/eac44d7e569f3721be5a12b8c221cab6)
|
|
| 429位 |
|
|||
|
00:26:34 |
|
|
##Google Apps Scriptとは? 以下の記事見ながら触ってみるとなんとなくわかります。 - [無料でCron!Google Apps Scriptで時間を指定してURLをリクエスト。 | きじとら](http://kijtra.com/article/cron-by-google-apps-script) ##正規表現を使えばスクレイピングできる 以下の記事見て、うおーっと思ったけどなんか違った。 - [Google DocsでWebスクレイピング:Logic+Idea](http://kaniku.seesaa.net/article/212479636.html) できないのかなーと色々試してたらできた。 ```コード.gs function myFunction() { var response = UrlFetchApp.fetch("http://qiita.com"); var myRegexp = /<title>([\s\S]*?)<\/title>/i; var match = myRegexp.exec(response.getContentText()); var title = match[1]; title = title.replace(/(^\s+)|(\s+$)/g, ""); Logger.log(title); } ``` ログの出力 ``` [13-09-10 00:00:49:130 JST] Qiita [キータ] - プログラマの技術情報共有サービス ``` - [javascript - Google Apps Script Regex exec() returning null - Stack Overflow](http://stackoverflow.com/questions/16961953/google-apps-script-regex-exec-returning-null) - [googleのtitleを取得するプログラム(正規表現) - Hexaの日記](http://d.hatena.ne.jp/Hexa/20060610/1149954511) - [PHPでWEBページのタイトルを抜き出すサンプル [C!]](http://creazy.net/2008/05/php_get_page_title_sample.html) - [複数行にマッチさせる正規表現 | You Look Too Cool](http://stabucky.com/wp/archives/4334) - [Javascript で trim するには - Enjoi Blog](http://blog.enjoitech.jp/article/180) ##Googleの検索結果を出してみる ```コード.gs function myFunction() { var response = UrlFetchApp.fetch("http://www.google.co.jp/search?q=qiita"); var myRegexp = /<h3 class=\"r\">([\s\S]*?)<\/h3>/gi; var elems = response.getContentText().match(myRegexp); for(var i in elems) { var title = elems[i] title = title.replace(/(^\s+)|(\s+$)/g, ""); title = title.replace(/<\/?[^>]+>/gi, ""); Logger.log(title); } } ``` ログの出力 ``` [13-09-10 00:08:56:438 JST] Qiita [キータ] - プログラマの技術情報共有サービス [13-09-10 00:08:56:439 JST] Qiita APIドキュメント [13-09-10 00:08:56:439 JST] タグ一覧 [13-09-10 00:08:56:439 JST] The Official Qiita Blog [13-09-10 00:08:56:439 JST] Qiita:Career [13-09-10 00:08:56:440 JST] Qiitaについて [13-09-10 00:08:56:440 JST] Qiita:Team [13-09-10 00:08:56:440 JST] キータ (Qiita) on Twitter [13-09-10 00:08:56:440 JST] Qiita(キータ) | Facebook [13-09-10 00:08:56:441 JST] 「Qiita」とそのビジネスとは - SD Japan | (株)スタートアップ・デイティング [13-09-10 00:08:56:441 JST] ニュースリリース: プログラマのための技術情報共有サイト「Qiita(キータ ... [13-09-10 00:08:56:441 JST] Qiita 2-day Hackathon | PeaTiX [13-09-10 00:08:56:442 JST] Qiita AWS Tools Hackathon | PeaTiX ``` - [ダブルクォーテーションのreplaceの方法 - JavaScript - 教えて!goo](http://oshiete.goo.ne.jp/qa/342029.html) - [片鱗懐古のブログ: Rhinoのフィルタ演算は正規表現じゃなくても良かった](http://pieceofnostalgy.blogspot.jp/2011/03/rhino_13.html) - [HTMLタグを除去 - Tatsuya Wiki](https://sites.google.com/site/tatsuyafukata/pg/javascript/remove-html-tag) - [[JavaScript]String.matchとRegExp.execと後方参照 - chalcedonyの外部記憶装置・出張版](http://d.hatena.ne.jp/chalcedony_htn/20090315/1237121111) ##感想 これで定期的にサイトをチェックして特定のキーワードがあったらメールするとかのプログラムが簡単に書けそう。 |
|
| 430位 |
|
|||
|
12:45:57 |
(GMO Pepabo, Inc. 所属) |
|
## (1) homebrew をインストール
* http://brew.sh/ 公式のイントロの通り。rbenvなどをすでに brew で入れているかもしれない。 インストールの確認 ```bash brew -v #=> Homebrew 0.9.4 ``` パッケージのアップデート ```bash brew update ``` ## (2) zsh をインストール ```bash brew install zsh ``` `/usr/local/bin/zsh` を起動すると、新しい `zsh` のセッションに入る。何も設定されていないので表示が簡素。いったん `exit` でログアウトする。 ## (3) oh-my-zsh をインストール * https://github.com/robbyrussell/oh-my-zsh 公式の通り ```bash curl -L https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh | sh ``` これでインストールされる。 途中で、デフォルトのシェルをzshにする、と言う旨のメッセージが出るので、パスワードを入れて更新してもらう。 インストールしたら新しいタブを開く or ターミナルを再起動して、新しいセッションを立ち上げる。 ## (4) プラグインを使う `~/.zshrc` と言うファイルに設定を記述する。何かのエディタで開く。 このファイルの下の方にこういう行があるはずなので、 ```zsh plugins=(git) ``` ここを変更してプラグインを利用する。 ```zsh plugins=(git ruby osx bundler brew rails emoji-clock) ``` * `ruby` - ruby に関連する便利な機能を提供 * `bundler` - 単に `ruby` などとするだけで `bundle exec ruby` を実行したのと同じになる * `emoji-clock` 絵文字時計を表示するコマンドが使える :clock3: なお、以下のコマンドで設定の読み直しができる。 `source` コマンドは設定などを読み直す際のシェルの一般的なコマンドなので覚えておこう。 ```bash source ~/.zshrc ``` ## (5) テーマを適用する 先ほどの `~/.zshrc` の先頭の方に ```zsh ZSH_THEME="robbyrussell" ``` という記述がある。この、「robbyrussell」をテーマ名に変更して、設定を読み直せばテーマを変更できる。 * https://github.com/robbyrussell/oh-my-zsh/wiki/Themes ↑から気に入ったテーマを探すこともできる。 カスタムテーマをつくる場合は、まず、もとにしたいテーマのファイルをコピーしておく。 ```bash cp ~/.oh-my-zsh/themes/cloud.zsh-theme ~/.oh-my-zsh/themes/original.zsh-theme ``` そうして新しくつくったテーマの `~/.oh-my-zsh/themes/original.zsh-theme` を編集する。 `PROMPT` と言う変数をいじれば見た目などを変えられるが... こればっかりは慣れた人でも試行錯誤を繰り返すことになる。 テーマをつくったら、 `ZSH_THEME="original"` などと設定を変更してまた読み直してみる。 テーマの設定内容そのものはGitHubからでも参照できるので、本気でつくりたい場合は読んでみるといいかも... * https://github.com/robbyrussell/oh-my-zsh/tree/master/themes ### おまけ: Boxen この手のセットアップ作業を * コマンド一発でできる * どんなマシンでも再現できる * 設定内容を GitHub などに公開できる、プルリクエストなどを送ってもらえたりするかも と言う便利そうなツール、 [Boxen](http://boxen.github.com/) というものがあったりします。もちろん、 [zsh](https://github.com/boxen/puppet-zsh) のための設定内容もあります。 |
|
| 431位 |
|
|||
|
23:41:22 |
|
|
ついにRails 4がリリースされたので軽く触ってみたら、3.xから変わったところを見つけたので共有。まだ日本語の情報は見当たらなかった。
APIを試しに作ってみようと思い`curl`でPOSTリクエストを送ろうとしたら以下のようなエラーが。 ``` $ curl -X POST -d "name='hoge'" http://localhost:3000/bikes Can't verify CSRF token authenticity Completed 422 Unprocessable Entity in 1ms ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken): ... ``` Rails 3.xのときはWarningは出たものの、エラーにはならなかったような…。 `application_controller.rb`を見てみると、以下のようなコメントがありました。 ```rb:application_controller.rb class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception end ``` どうやらAPIを作りたい場合は、`:exception`ではなく`:null_session`を使うといいようです。 気になってactionpackのソースコードを読んでみました。Rails 3.xのときにソースコードを読んだときの記事を最後に載せたので参考にしてみてください。 ```rb:actionpack-4.0.0/lib/action_controller/metal/request_forgery_protection.rb def protect_from_forgery(options = {}) self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session) self.request_forgery_protection_token ||= :authenticity_token prepend_before_action :verify_authenticity_token, options end ``` 3.xのときと比べると32行目の`forgery_protection_strategy`というのが新しく追加されたようです。`with`オプションで指定したクラスをセットしているようなので詳しく見てみます。 ```rb:actionpack-4.0.0/lib/action_controller/metal/request_forgery_protection.rb def protection_method_class(name) ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify) rescue NameError raise ArgumentError, 'Invalid request forgery protection method, use :null_session, :exception, or :reset_session' end ``` デフォルトのように`:exception`が指定されてる場合は`Exception`クラスが、今回のようにAPI用に使う`:null_session`が指定された場合は`NullSession`クラスがどこかに定義されているようです。 ```rb:actionpack-4.0.0/lib/action_controller/metal/request_forgery_protection.rb class Exception def initialize(controller) @controller = controller end def handle_unverified_request raise ActionController::InvalidAuthenticityToken end end ``` あったあった。どこかのタイミングで`handle_unverified_request`が呼ばれて、冒頭のように例外が発生するわけですね。 ```rb:actionpack-4.0.0/lib/action_controller/metal/request_forgery_protection.rb class ResetSession def initialize(controller) @controller = controller end def handle_unverified_request @controller.reset_session end end ``` さらに`ResetSession`というクラスも見つかりました。これは例外を発生させる代わりにセッションをリセットするみたいです。これはRails 3.xのときと同じ挙動だったと思います。 ```rb:actionpack-4.0.0/lib/action_controller/metal/request_forgery_protection.rb class NullSession # ... # This is the method that defines the application behavior when a request is found to be unverified. def handle_unverified_request request = @controller.request request.session = NullSessionHash.new(request.env) request.env['action_dispatch.request.flash_hash'] = nil request.env['rack.session.options'] = { skip: true } request.env['action_dispatch.cookies'] = NullCookieJar.build(request) end # ... end ``` で、`NullSession`クラスを見てみると、`NullSessionHash`オブジェクトと`NullCookieJar`オブジェクトというのが出てきますが、こいつらはどうやら中身が空っぽのモックオブジェクトっぽいです。 --- ### 参考 Rails 3.xのときに`protect_from_forgery`の中身を追いかけた記録です。 [CSRFトークンの検証プロセス](http://qiita.com/naoty_k/items/ce037ea79bb5893f2b89) |
|
| 432位 |
|
|||
|
18:47:16 |
|
|
PythonによるCSVファイルの読み書きメモ.
[**3. pandasによる方法**](http://qiita.com/okadate/items/c36f4eb9506b358fb608#3-pandas%E3%82%92%E4%BD%BF%E3%81%86%E5%A0%B4%E5%90%88%E3%81%8A%E3%81%99%E3%81%99%E3%82%81) がおすすめです. - Change log ``` 2014/07/28 読み込み(Pandas)の追加. 2014/11/28 pandasを使う場合をまとめ. ``` ## **1. 読み込み** [Python documentation](http://docs.python.jp/2/library/csv.html) を参考に`with`文を使う. ```py import csv with open('some.csv', 'r') as f: reader = csv.reader(f) header = next(reader) # ヘッダーを読み飛ばしたい時 for row in reader: print row # 1行づつ取得できる ``` `with`文を使わなくても,以下のように読み込める. ```py import csv f = open('some.csv', 'r') reader = csv.reader(f) header = next(reader) for row in reader: print row f.close() ``` この場合は`close`文をつける. ## **2. 書き込み** 書き込みでも`with`文を使う. ```python import csv with open('some.csv', 'w') as f: writer = csv.writer(f, lineterminator='\n') # 改行コード(\n)を指定しておく writer.writerow(list) # list(1次元配列)の場合 writer.writerows(array2d) # 2次元配列も書き込める ``` 読み込みと同様に`with`なしでもOK. ```python import csv f = open('some.csv', 'w') writer = csv.writer(f, lineterminator='\n') writer.writerow(list) writer.writerows(array2d) f.close() ``` ## **3. pandasを使う方法**(おすすめ) pandasで読み込むとすっきりするし,便利な場合も多い. ```py import pandas as pd df = pd.read_csv('some.csv') print df # show all column print df['A'] # show 'A' column ``` 読み込んだDataFrameは書き込みも簡単. ```py df.to_csv('some2.csv') ``` 使い方は [**pandasでよく使う文法まとめ**@okadate - Qiita](http://qiita.com/okadate/items/7b9620a5e64b4e906c42) などを参考に. |
|
| 433位 |
|
|||
|
20:18:41 |
(JUBILEE WORKS, Inc. 所属) |
|
※以下は[objc.io, Issue #1, Lighter View Controllers](http://www.objc.io/issue-1/lighter-view-controllers.html)の日本語訳です。 # 軽量なView Controller [Issue #1 Lighter View Controllers](http://www.objc.io/issue-1/index.html), June 2013 By Chris Eidhof view controllerはiOSプロジェクトの中で一番大きいファイルになりがちで、必要以上に多くのコードを含んでいることが多い。ほぼ決まってView Controllerはコードの中で最も再利用性の低い部分だ。View Controllerをスリムにし、再利用可能にして、より適切な場所にコードを移すテクニックを見ていこう。 この記事の[サンプルプロジェクト](https://github.com/objcio/issue-1-lighter-view-controllers)がGitHubにあるので参照されたい。 ## データソースとその他のプロトコルを外に出す View Controllerスリム化の最も強力なテクニックのひとつが、`UITableViewDataSource`の部分を独立したクラスに移すことだ。これを2回以上やってみるとパターンが見えてきて、このための再利用可能なクラスを作ることになるだろう。 例えば我々のプロジェクトでは`PhotosViewController`というクラスがあり、次のようなメソッドを持つ。 ```objc # pragma mark Pragma - (Photo*)photoAtIndexPath:(NSIndexPath*)indexPath { return photos[(NSUInteger)indexPath.row]; } - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section { return photos.count; } - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { PhotoCell* cell = [tableView dequeueReusableCellWithIdentifier:PhotoCellIdentifier forIndexPath:indexPath]; Photo* photo = [self photoAtIndexPath:indexPath]; cell.label.text = photo.name; return cell; } ``` このコードの多くの部分が配列に関するもので、一部がview controllerの管理するphotosに固有のコードだ。そこで配列に関するコードを独自クラスに移してみよう。ここではcellの設定にblockを使うが、ユースケースや好みに応じてdelegateにしても構わない。 ```objc @implementation ArrayDataSource - (id)itemAtIndexPath:(NSIndexPath*)indexPath { return items[(NSUInteger)indexPath.row]; } - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section { return items.count; } - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { id cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath]; id item = [self itemAtIndexPath:indexPath]; configureCellBlock(cell,item); return cell; } @end ``` view controllerにあった3つのメソッドをなくし、代わりにこのオブジェクトのインスタンスを作ってtable viewのdata sourceとしてセットできる。 ```objc void (^configureCell)(PhotoCell*, Photo*) = ^(PhotoCell* cell, Photo* photo) { cell.label.text = photo.name; }; photosArrayDataSource = [[ArrayDataSource alloc] initWithItems:photos cellIdentifier:PhotoCellIdentifier configureCellBlock:configureCell]; self.tableView.dataSource = photosArrayDataSource; ``` これでindex pathを配列のインデックスにマッピングすることを考える必要がなくなり、table viewに配列を表示したい時はいつでもこのコードを再利用できる。さらに `tableView:commitEditingStyle:forRowAtIndexPath:`のようなメソッドを追加で実装して、このコードをすべてのtable view controllerで共有することも可能だ。 この方法の良いところは、このクラスを単体でテストでき、もう一度テストを書く必要がなくなることだ。もし配列ではないものを扱う場合であっても同じ原則が当てはまる。 今年我々が取り組んだアプリのひとつで、Core Dataをヘビーに使ったものがある。我々は同様のクラスを作ったが、配列を使うのではなく、fetched result controllerを使った。更新のアニメーションやsection header関連の処理、削除といったロジックをそこに実装した。このクラスのインスタンスを作ってfetch requestと、cellの設定のためのblockを与えれば、あとはこのクラスが面倒を見てくれる。 さらに、このアプローチは他のプロトコルにも応用できる。明らかな候補は`UICollectionViewDataSource`だ。これによって非常に高い柔軟性が得られる。例えば開発中のある時点で、`UITableView`の代わりに`UICollectionView`を使うことになった場合、view controllerのコードはほとんど何も修正する必要がないだろう。data sourceに両方のプロトコルをサポートすることさえ、やろうと思えば可能だ。 ## ドメインロジックはモデルに 次の例は(別のプロジェクトの)view controllerのもので、ユーザーのactive priorityのリストを得るためのコードだ。 ```objc - (void)loadPriorities { NSDate* now = [NSDate date]; NSString* formatString = @"startDate <= %@ AND endDate >= %@"; NSPredicate* predicate = [NSPredicate predicateWithFormat:formatString, now, now]; NSSet* priorities = [self.user.priorities filteredSetUsingPredicate:predicate]; self.priorities = [priorities allObjects]; } ``` これはしかし`User`クラスのカテゴリへ移した方がずっとすっきりする。そうすると`ViewController.m`はこうなる。 ```objc - (void)loadPriorities { self.priorities = [user currentPriorities]; } ``` そして`User+Extensions.m`は以下の通りだ。 ```objc - (NSArray*)currentPriorities { NSDate* now = [NSDate date]; NSString* formatString = @"startDate <= %@ AND endDate >= %@"; NSPredicate* predicate = [NSPredicate predicateWithFormat:formatString, now, now]; return [[self.priorities filteredSetUsingPredicate:predicate] allObjects]; } ``` コードによってはモデルオブジェクトに簡単に移動できないが、明らかにモデルのコードと密接に関連しているものがある。そんなときはストアクラスを利用することができる。 ## ストアクラスを作る 我々のサンプルアプリケーションの最初のバージョンにはファイルからデータを読み込んでパースするコードがあった。このコードは次のようにview controllerの中にあった。 ```objc - (void)readArchive { NSBundle* bundle = [NSBundle bundleForClass:[self class]]; NSURL *archiveURL = [bundle URLForResource:@"photodata" withExtension:@"bin"]; NSAssert(archiveURL != nil, @"Unable to find archive in bundle."); NSData *data = [NSData dataWithContentsOfURL:archiveURL options:0 error:NULL]; NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; _users = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"users"]; _photos = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"photos"]; [unarchiver finishDecoding]; } ``` view controllerはこれについて知る必要がない。我々はこの作業だけを行う *ストア* オブジェクトを作った。分離することによってそのコードが再利用可能になり、個別にテストできるようになり、view controllerを小さく保つことができる。ストアはデータの読み込み、キャッシュ、データベーススタックの設定などを担当することができる。ストアはしばしば *サービスレイヤー* または *レポジトリ* とも呼ばれる。 ## ウェブサービスロジックはモデルレイヤーに これは上記のトピックとよく似ている。ウェブサービスロジックをview controllerで処理してはならない。代わりにこれを別クラスにカプセル化しよう。view controllerはこのクラスのメソッドをコールバックハンドラ(例えばcompletionブロック)を使って呼ぶことができる。この方法の利点はキャッシングやエラーハンドリングもこのクラスの中でできることだ。 ## ビューのコードはビューレイヤーに 複雑なview階層の構築をview controllerの中で行うべきではない。Interface Builderを使うか独自の`UIView`サブクラス内にカプセル化しよう。例えば独自のdate picker controlを作る場合、全てをview controllerの中で構築するより`DatePickerView`の中に入れる方がよい。これもまた再利用性とシンプルさを高めるテクニックだ。 もしInterface Builderを好むなら、この作業はInterface Builderを使っても可能だ。view controllerにしか使えないと思われがちだが、カスタムビューを個別のnibファイルから読み込むこともできる。サンプルアプリではphoto cellのレイアウトを含む[`PhotoCell.xib`](https://github.com/objcio/issue-1-lighter-view-controllers/blob/master/PhotoData/PhotoCell.xib)を作っている。  上図のように、viewのプロパティを作って対応するsubviewにひもづけている(このxibではFile's Ownerは使用しない)。このテクニックは他のカスタムビューを作る際にも非常に便利だ。 ## コミュニケーション その他にview controllerの中でよく起こることのひとつは、他のview controllerやモデル、ビューとのコミュニケーションだ。これこそview controllerがやるべきことなのだが、可能な限り少ないコードで済ませたい部分でもある。 view controllerとモデルオブジェクト間のコミュニケーションのためのテクニックにはよく知られた方法(KVOやfetched result controllerなど)があるが、view controller間のコミュニケーションに関してはそれほど明確ではないことが多い。 view controllerのひとつがある状態を持っていて、他の複数のview controllerとの間でやりとりをするような場合によく問題になる。この状態を別のオブジェクトに移して、そのオブジェクトを複数のview controller間で受け渡すようにするとうまく行くことが多い。情報がただ一カ所にあるため、入れ子になったdelegateコールバックに悩まされることがないという利点がある。これについては複雑なテーマなので、今後連載の1回分を費やすかもしれない。 ## まとめ view controllerをより小さくするためのテクニックをいくつか見てきた。我々はこれらのテクニックを使えるところにはすべて使おうという努力はしない。我々のゴールはただひとつ、メンテ可能なコードを書くことだ。これまで見てきたパターンを知ることは、巨大で手に負えないview controllerをよりクリーンにする契機になるだろう。 ## 参考リンク * [View Controller Programming Guide for iOS](http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/BasicViewControllers/BasicViewControllers.html) * [Cocoa Core Competencies: Controller Object](http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/ControllerObject.html) * [Writing high quality view controllers](http://subjective-objective-c.blogspot.de/2011/08/writing-high-quality-view-controller.html) * [Stack Overflow: Model View Controller Store](http://programmers.stackexchange.com/questions/184396/mvcs-model-view-controller-store) * [Unburdened View Controllers](https://speakerdeck.com/trianglecocoa/unburdened-viewcontrollers-by-jay-thrash) * [Stack Overflow: How to avoid big and clumsy UITableViewControllers on iOS](http://programmers.stackexchange.com/questions/177668/how-to-avoid-big-and-clumsy-uitableviewcontroller-on-ios) |
|
| 434位 |
|
|||
|
09:29:10 |
(合同会社ユーキューブ 所属) |
|
よく入門書で
```bash $ gem install rails $ rails new project ``` みたいにしろと書いてありますが、これだとシステムのgemにインストールされます。システムのgemはbundlerいっこにして、必要なものは`vendor/bundle`に格納し、`bundle exec`で呼び出すのがクリーンでいいですよね。下記のようにします。 ```bash $ gem install bundler $ mkdir project $ cd project $ bundle init Writing new Gemfile to /path/to/project/Gemfile ``` のように、Gemfileだけ生まれるので、 ```bash $ vi Gemfile # A sample Gemfile source "https://rubygems.org" gem "rails" ``` のようにコメントアウトを外してやります。あとは ```bash $ bundle install --path vendor/bundle $ bundle exec rails new . ``` でOK。Gemfileが存在するので上書きするかどうか聞いてきますが上書きしちゃってください。 |
|
| 435位 |
|
|||
|
00:03:35 |
|
|
[@himara2](https://twitter.com/himara2) です。[iOS Advent Calendar](http://qiita.com/advent-calendar/2013/ios) の20日目を担当します。
iOS 7対応時に知っておきたい情報をまとめます。 # はじめに iOS 7が登場から3ヵ月が過ぎ、[普及率が75%を超えるほどiOS 7は浸透しています。](https://developer.apple.com/support/appstore/) 先日にはついにAppleが[「2014/2/1以降に申請するアプリはiOS 7に最適化されている必要がある」](https://developer.apple.com/news/index.php?id=12172013a)とアナウンスしました。 これからアプリのiOS 7対応は必須化していきます。 この記事ではこれからiOS 7対応をする方向けに、新APIやiOS 7対応時に助かるリンク集をまとめてみます。 # 1. 見た目関連のAPI ## Custom Transition * ViewController間の遷移が簡単にカスタマイズできるようになった * NavigationController, TabBarController, Modal などの遷移を自作できる * **UIViewControllerTransitioningDelegate** や **UIViewControllerAnimatedTransitioning** プロトコルを使う * [PinterestのiOS 7対応](http://blog-assets.pinterest.com/img/other/Transition-Small.gif)で紹介されている以下のようなTransitionが自作できる *  ### もっと詳しく * [Custom Transitions Using View Controllers | Qiita](http://qiita.com/335g/items/94762b06a66cfb3be6ad) ## TextKit * テキストの表現をより便利にしてくれるAPI * これまでCoreTextを使っていた表現を UILabel や UITextView で実装できる * アンカーテキストの装飾、文字の回り込みなどが指定できる *  ### もっと詳しく * [Text Kit Tutorial](http://www.raywenderlich.com/50151/text-kit-tutorial) * [NSAttributedString と TextKit](http://azu.github.io/slide/OCStudy/2013_November/nsattributedstring.html) ## Motion Effect * iOS 7らしい視差効果を表現する * 画面を傾けると奥行きが感じられるアレ * ホーム画面の他、Safari のタブ切り替えの画面などで使われている ### もっと詳しく * [魅せるUIの作り方](https://www.slideshare.net/techblogyahoo/5-ios7-workshopmakeupui) ## UIKit Dynamics * UIKitに物理演算の動きを与えられるAPI * 重力や張力、密度などを設定でき、バネやぷるぷるした動きを実装可能 * ゲーム用ではない(ゲーム用にはSpriteKitを使えとAppleは言っている) ### もっと詳しく * [UIKit Dynamics を使用してViewを滑らかに移動する方法](http://www.zero4racer.com/blog/1135) * [UICollectionView + UIKit Dynamics | objc.io](http://www.objc.io/issue-5/collection-views-and-uidynamics.html) * [魅せるUIの作り方](https://www.slideshare.net/techblogyahoo/5-ios7-workshopmakeupui) # 2. ロジック関連のAPI ## Background Fetch * OSが定期的にバックグラウンドでアプリを起こし、コンテンツ更新等の処理を行ってくれる * 発火の頻度はOSがユーザーのアプリ使用頻度から学習して適切なタイミングで実施 * SNSやニュースアプリなど、定期的にコンテンツが更新されるアプリで有効 ### もっと詳しく * [iOS 7対応アプリを作るなら知っておきたい「UXを向上させるBackground Fetchの使い方」](https://codeiq.jp/magazine/2013/12/3022/) * [SmartNewのiOS 7対応](http://developer.smartnews.be/blog/2013/11/11/smartnews-ios-7/) * [HBFav 2.6、バックグラウンドフェッチによるタイムラインの自動更新](http://d.hatena.ne.jp/naoya/20131209/1386562827) ## Silent Push Notification * Remote Push Notificationの拡張版 * pushの受け取り時にバックグラウンドでアプリを起こしてコンテンツ更新等の処理を行う * pushで流す値に `{content-availble: 1}` を指定すればSilent Pushとなる * メッセージングアプリなど不定期にコンテンツを受け取るアプリで有効 ### もっと詳しく * [iOS7で強化されたRemote Notifications](http://iti.hatenablog.jp/entry/2013/10/29/181607) * [アプリのバックグラウンド処理 | iOS 7エンジニア勉強会](http://www.slideshare.net/techblogyahoo/3-ios7-workshopmultitask-26997115) ## NSURLSession * NSURLConnectionにとってかわる通信周りを司るクラス * セッションの持ち方などが改善している * バックグラウンドでもファイルのダウンロード・アップロードが可能(ただしWi-Fi only) * ストレージサービスなど大容量ファイルを扱うアプリで有効 ### もっと詳しく * [iOS 7で一新された通信周り〜NSURLSessionってなに?〜](http://dev.classmethod.jp/references/ios-nsurlsession-1/) * [NSURLSession Tutorial](http://www.raywenderlich.com/51127/nsurlsession-tutorial) ## Dynamic Type * 画面に表示する文字サイズを動的に変更するAPI * 標準の設定アプリでテキストサイズを指定すると全アプリ共通でサイズが変更される * アプリ側では"Body"や"Headline"などのテキストスタイルを指定する ### もっと詳しく * [iOSアプリで動的にフォントサイズを変更する「Dynamic Type」新機能の実装法](https://codeiq.jp/magazine/2013/12/3376/) # 3. その他のAPI ## AVSpeech Synthesizer * iOS 7から追加されたテキストを音声読み上げしてくれるAPI * 日本語含む様々な言語で読み上げが可能 #### もっと詳しく * [iOS 7で追加された音声読み上げ機能(AVSpeech Synthesizer)でiPhoneにお喋りさせる](https://codeiq.jp/magazine/2013/12/3360/) * [AVSpeechSynthesizer による音声読み上げ](http://www.toyship.org/archives/1483) # その他 ## App Switcherについて * App Switcherとは、ホームボタンを2回クリックした時に現れる起動中アプリ一覧画面のこと * アプリの画面が大きく表示されるように変更され、UIが良いほどユーザは帰ってくるとAppleは言っている * 口座情報などの秘密のデータを扱う場合はモザイクをつけたりもできる * [アプリの画面を開いているアプリケーションのプレビュー画面から隠す](http://qiita.com/griffin_stewie/items/0b7c0f750a7eb62b182c) *  ## iOS 7対応で発生する問題に対するベストプラクティス * iOS 6の時と比べてステータスバーの20px領域分ずれてしまう * => [「既存のアプリをiOS 7に対応させる」3つのおすすめテクニック](https://codeiq.jp/magazine/2013/11/1580/) * ステータスバーの色を変えたり消したりしたい * => [iOS 7におけるUIStatusBarStyleのベストプラクティス](http://cockscomb.hatenablog.com/entry/2013/11/12/190905) * バックグラウンドで位置情報が取れない * iOS 7からは設定 > バックグラウンドの更新がONじゃないとLocation Services使えません * また、AppSwitcherで上スワイプでプロセス切られた場合も使えません ## iOS 7らしい"ぼかし効果"を得たい * 標準のAPIには実装されていない * iOS-blurというオープンソースを使う * [iOS-Blur](https://github.com/JagCesar/iOS-blur) * AppleのサンプルコードからUIImageのカテゴリを抜いてくる * [WWDC 2013 のサンプルコード](https://developer.apple.com/downloads/index.action?name=WWDC%202013) > iOS_UIImageEffects > UIImage+ImageEffects ## 既存のバージョンとiOS 7とを両立させる * 分岐させる方法 * マクロを使って分岐させる * [1行で iOS バージョン判定できる便利マクロ](http://qiita.com/shu223/items/dc1759fc9695cea27756) * respondsToSelector を使って分岐させる # iOS 7から追加されたAPI一覧 * その他の変更についてはこちらをご覧ください。 * [What's New in iOS | iOS Developer Library](https://developer.apple.com/library/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS7.html) |
|
| 436位 |
|
|||
|
08:07:54 |
(Fujitsu Computer Technologies Limited 所属) |
|
CentOSにntpサーバを入れて、日本標準時刻に自動的に合わせるためのメモです。
###NTPとは NTPとは、NetWork Time Protocolの略。 時刻を問い合わせる機能を持ち、正確な時間をコンピュータに反映することができる。 ####NTPのインストール % sudo yum -y install ntp ####時刻を他サーバに問い合わせる 日本標準時プロジェクトというものがあり、ここから日本の正確な時刻を配信している。 <a href="http://www2.nict.go.jp/aeri/sts/tsp/PubNtp/index.html" target="_blank"><img class="alignleft" align="left" border="0" src="http://capture.heartrails.com/150x130/shadow?http://www2.nict.go.jp/aeri/sts/tsp/PubNtp/index.html" alt="" width="150" height="130"></a> <a style="color:#0070C5;" href="http://www2.nict.go.jp/aeri/sts/tsp/PubNtp/index.html" target="_blank">日本標準時プロジェクト 公開NTP</a> <img border="0" src="http://b.hatena.ne.jp/entry/image/http://www2.nict.go.jp/aeri/sts/tsp/PubNtp/index.html" alt="" style=""> <br style="clear:both;"> ntp.nict.jpというサーバから正確な時刻を取得する。 % sudo ntpdate ntp.nict.jp 17 Jun 07:44:15 ntpdate[30214]: adjust time server 133.243.238.243 offset 0.005146 sec ###ntpdデーモンを起動して、自動的に時刻同期させる ####/etc/ntp.confの編集 デーモンを起動することで、時刻を自動同期できます。設定ファイルは、/etc/ntp.confです。 server 0.centos.pool.ntp.org server 1.centos.pool.ntp.org server 2.centos.pool.ntp.org となっている部分を、以下のように編集。 server -4 ntp.nict.jp server -4 ntp1.jst.mfeed.ad.jp server -4 ntp2.jst.mfeed.ad.jp server -4 ntp3.jst.mfeed.ad.jp ntp1.jst.mfeed.ad.jpはntp.nict.jpと同期している別サーバ。 ####NTPサービス起動 設定が完了したら、サービスを起動。 sudo service ntpd start ntpd を起動中: [ OK ] ntpq -pでステータスを確認することができます。 ntpq -p remote refid st t when poll reach delay offset jitter ============================================================================= ntp-b3.nict.go. .NICT. 1 u 45 64 1 51.180 36.062 0.004 ntp1.jst.mfeed. 172.29.3.50 2 u 45 64 1 46.038 35.957 0.004 ntp2.jst.mfeed. 172.29.2.50 2 u 44 64 1 37.927 31.670 0.004 ntp3.jst.mfeed. 172.29.3.50 2 u 43 64 1 41.949 30.128 0.004 各サーバの前に*とか+がつくと同期完了。 ####自動起動の設定 最後に、自動起動の設定をして終了 % sudo chkconfig ntpd on % chkconfig --list ntpd ntpd 0:off 1:off 2:on 3:on 4:on 5:on 6:off ####環境 + CentOS 6.4 ####参考 + <a href="http://sj6.org/ntp_install_for_centos/" target="_blank">CentOSにntpサーバで時刻同期設定 | 適当な日々</a> + <a href="http://sj6.org/ntp_install_for_centos/" target="_blank">CentOSにntpサーバで時刻同期設定 | 適当な日々</a> + <a href="http://www2.nict.go.jp/aeri/sts/tsp/PubNtp/index.html" target="_blank">日本標準時プロジェクト 公開NTP</a> 投稿元記事: http://futurismo.biz/archives/1487 |
|
| 437位 |
|
|||
|
20:56:27 |
|
|
jsoup は Java で HTML の解析・編集を行うためのライブラリ。
URL を指定すれば実際の Web ページを解析のインプットに指定でき、タグの検索には CSS セレクタが使えるので、 Web スクレイピングをしたい時にとても便利。 #導入 Maven のセントラルリポジトリに jar がある。 ```groovy:build.gradle dependencies { compile 'org.jsoup:jsoup:1.7.3' } ``` #解析する HTML の指定 ##URL を指定して実際の Web ページをインプットにする ```java: package sample.jsoup; import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; public class Main { public static void main(String[] args) throws IOException { Document document = Jsoup.connect("http://www.google.co.jp").get(); System.out.println(document.html()); } } ``` ```text:実行結果 <!DOCTYPE html> <html itemscope="" itemtype="http://schema.org/WebPage"> <head> <meta content="世界中のあらゆる情報を検索するためのツールを提供しています。さまざまな検索機能を活用して、お探しの情報を見つけてください。" name="description" /> <meta content="noodp" name="robots" /> <meta itemprop="image" content="/images/google_favicon_128.png" /> <title>Google</title> (以下略) ``` `Jsoup.connect("<URL>").get()` で、指定した URL に GET メソッドでアクセスし、結果をパースした `Document` オブジェクトを取得できる。 あとは、この `Document` オブジェクトから必要なタグを検索する。 ※HTTP のメソッドは GET と POST しか無いようなので、 RESTful API のクライアントとしては利用できないっぽい。([Connection.Method (jsoup 1.7.4-SNAPSHOT API)](http://jsoup.org/apidocs/org/jsoup/Connection.Method.html)) ###リクエストパラメータを設定する ```java: package sample.jsoup; import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; public class Main { public static void main(String[] args) throws IOException { Document document = Jsoup.connect("http://qiita.com/search") .data("q", "java") .get(); Elements elements = document.select(".brand, .page-title, .stats"); for (Element element : elements) { System.out.println(element.text()); } } } ``` ```text:実行結果 Qiita - プログラマの技術情報共有サービス javaの検索結果 856投稿 • 7163フォロワー ``` `data(String, String)` メソッドでリクエストパラメータを設定できる。 URL エンコードは内部でしてくれるので、全角文字もそのまま指定できる。 ##HTML 形式の文字列をインプットにする ```java: package sample.jsoup; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; public class Main { public static void main(String[] args) { String html = "<div><span>hoge</span><p>fuga</p></div>"; Document document = Jsoup.parse(html); System.out.println(document.html()); } } ``` ```text:実行結果 <html> <head></head> <body> <div> <span>hoge</span> <p>fuga</p> </div> </body> </html> ``` ##ローカルのファイルをインプットにする ```html:input.html <html> <head> <title>test page</title> </head> <body> <h1>テストページです。</h1> </body> </html> ``` ```java: package sample.jsoup; import java.io.File; import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; public class Main { public static void main(String[] args) throws IOException { Document document = Jsoup.parse(new File("input.html"), "UTF-8"); System.out.println(document.html()); } } ``` ```text:実行結果 <html> <head> <title>test page</title> </head> <body> <h1>テストページです。</h1> </body> </html> ``` #テキスト・ HTML を取得する ```html:input.html <div id="hoge"> <h1>Hoge</h1> <ul id="fuga"> <li>Fuga</li> <li id="piyo">Piyo</li> </ul> </div> ``` ```java: package sample.jsoup; import java.io.File; import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; public class Main { public static void main(String[] args) throws IOException { Document document = Jsoup.parse(new File("input.html"), "UTF-8"); Element hoge = document.getElementById("hoge"); System.out.println("[hoge.html()]\r\n" + hoge.html() + "\r\n"); Element fuga = document.getElementById("fuga"); System.out.println("[fuga.text()]\r\n" + fuga.text() + "\r\n"); Element piyo = document.getElementById("piyo"); System.out.println("[piyo.outerHtml()]\r\n" + piyo.outerHtml() + "\r\n"); } } ``` ```text:実行結果 [hoge.html()] <h1>Hoge</h1> <ul id="fuga"> <li>Fuga</li> <li id="piyo">Piyo</li> </ul> [fuga.text()] Fuga Piyo [piyo.outerHtml()] <li id="piyo">Piyo</li> ``` - `html()` メソッドで、そのタグの中身を HTML 形式の文字列で取得できる。 - `text()` メソッドで、そのタグの中身のうち、テキスト部分だけを抽出した文字列を取得できる。 - `outerHtml()` メソッドで、そのタグ自身を HTML 形式の文字列で取得できる。 #タグの検索 ##ID 指定で検索する ```html:input.html <h1 id="hoge">Hoge</h1> <h1 id="fuga">Fuga</h1> <h1 id="piyo">Piyo</h1> ``` ```java: package sample.jsoup; import java.io.File; import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; public class Main { public static void main(String[] args) throws IOException { Document document = Jsoup.parse(new File("input.html"), "UTF-8"); Element element = document.getElementById("hoge"); System.out.println(element.outerHtml()); } } ``` ```text:実行結果 <h1 id="hoge">Hoge</h1> ``` ###その他検索用メソッド ID 以外にも、 class や属性指定、兄弟タグ、親タグ、タグ内のテキスト、など様々な検索用メソッドが用意されている。 とりあえず、 Element クラスの API はざっと目を通しておいた方がよさげ。 [Element (jsoup 1.7.4-SNAPSHOT API)](http://jsoup.org/apidocs/org/jsoup/nodes/Element.html) ##CSS セレクタを使って検索する ```html:input.html <div id="hoge"> <ul> <li class="error">hoge</li> <li class="success">fuga</li> <li class="error">piyo</li> </ul> <span class="error">ERROR</span> </div> <span id="fuga">Fuga</span> ``` ```java: package sample.jsoup; import java.io.File; import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; public class Main { public static void main(String[] args) throws IOException { Document document = Jsoup.parse(new File("input.html"), "UTF-8"); Elements elements = document.select("#hoge ul .error"); for (Element element : elements) { System.out.println(element.outerHtml()); } } } ``` ```text:実行結果 <li class="error">hoge</li> <li class="error">piyo</li> ``` `select(String cssQuery)` メソッドで、 CSS セレクタを使用した検索が可能。 使用できる CSS セレクタについては、以下の Selector クラスの API ドキュメントで説明されている。 [Selector (jsoup 1.7.4-SNAPSHOT API)](http://jsoup.org/apidocs/org/jsoup/select/Selector.html) ほとんどの場合普通の CSS セレクタと同じように使えるが、 `E[foo="bar"]` タイプのセレクタを使用するときだけ、以下のような違いがある。 ```html:input.html <input id="hoge" name="HOGE" /> <input id="fuga" name="FUGA" /> ``` ```java: package sample.jsoup; import java.io.File; import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; public class Main { public static void main(String[] args) throws IOException { Document document = Jsoup.parse(new File("input.html"), "UTF-8"); Elements elements = document.select("[name=HOGE]"); System.out.println(elements.outerHtml()); } } ``` ```text:実行結果 <input id="hoge" name="HOGE" /> ``` 属性の値は __ダブルクォーテーション `"` で括ってはいけない__ 。 ( `"[name=\\"HOGE\\"]"` ではなく、 `"[name=HOGE]"` が正) おそらく、 Java のコード上ではダブルクォーテーションにエスケープが必要になるので、より簡潔に書けるようにこのような違いがあると思われる。 #プロキシ環境下で使う ##認証が不要な場合 ```java: package jsoup; import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; public class Main { public static void main(String[] args) throws IOException { System.setProperty("http.proxyHost", "プロキシホスト名"); System.setProperty("http.proxyPort", "プロキシポート"); Document doc = Jsoup.connect("http://www.google.co.jp").get(); } } ``` 認証が不要な場合は簡単で、システムプロパティにプロキシのホストとポートを設定してあげればいい。 ##認証が必要な場合 ```java: package jsoup; import java.io.IOException; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.net.Authenticator; import java.net.PasswordAuthentication; public class Main { public static void main(String[] args) throws IOException { // プロキシの設定(ホストとポート) System.setProperty("http.proxyHost", "プロキシホスト名"); System.setProperty("http.proxyPort", "プロキシポート"); // 認証情報の設定 Authenticator.setDefault(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("ユーザー名", "パスワード".toCharArray()); } }); // Jsoup 取得し Document doc = Jsoup.connect("http://www.google.co.jp").get(); } } ``` #参考 - [jsoup Java HTML Parser, with best of DOM, CSS, and jquery](http://jsoup.org/) - [Java内でjQueryを使う!jsoup ::: Serendipity 2 future lies'n sundome. (´・ω・)](http://www.h3.dion.ne.jp/~alpha-pz/misc2763.html) - [jsoupでHTMLをパースする - するめとめがね](http://tm8r.hateblo.jp/entry/2013/11/26/125937) - [ウェブスクレイピング - Wikipedia](http://ja.wikipedia.org/wiki/%E3%82%A6%E3%82%A7%E3%83%96%E3%82%B9%E3%82%AF%E3%83%AC%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0) - [Selectors Level 3 | W3C](http://www.w3.org/TR/css3-selectors/#selectors) - [java - How can I configure HTTPClient to authenticate against a SOCKS proxy? - Stack Overflow](http://stackoverflow.com/questions/1388822/how-can-i-configure-httpclient-to-authenticate-against-a-socks-proxy) - [Jakarta Commons によるHTTP処理(HttpClient) - Java入門](http://www.syboos.jp/java/doc/jakarta-commons-httpclient.html) - [java - How to add proxy support to Jsoup (HTML parser)? - Stack Overflow](http://stackoverflow.com/questions/7482748/how-to-add-proxy-support-to-jsoup-html-parser) - [Java Proxy Authentication - Stack Overflow](http://stackoverflow.com/questions/8669726/java-proxy-authentication) |
|
| 438位 |
|
|||
|
01:24:08 |
|
|
初めてSinatraでアプリを作ったので覚えたことまとめ。
事情によりローカルはApach + Passenger、リモートはNginx + Unicornなので、一応両方で動くようまとめ・・・たつもりなのですが。 ## 元記事 下記エントリの転載になります。 [【Ruby】Sinatraで、速攻でWebサイトを公開するための環境構築 - rokuroFire](http://www.rokurofire.info/2013/11/20/sinatra_builtenv/) ## 事前準備 * Ruby、bundlerのインストール * Apacheの場合、Passengerの設定をしておく * Nginxの場合、Unicornのgemをインストールしておく ## アプリのファイル構成 最小構成は下記。 ``` projectname/ -main.rb // Controller -config.ru // Rackの設定ファイル。最初に実行されるファイル -views/ -index.haml // ビューのテンプレート(haml)ファイル -public/ // webサーバのドキュメントルートはここを指定。画像もここ ``` 個人的に、作業ディレクトリは下記のようにしてみました ``` projectname/ -Gemfile -config.ru // Rackの設定ファイル。最初に実行されるファイル -main.rb // Controller -app/ // 何らかの処理 -module/ // モジュール類のファイル -views/ // ビューのテンプレート(haml)ファイル -index.haml -layout.haml -style/ // sass(scss)ファイル -baes.scss -db/ // データベースの設定ファイル(yml)を置く -log/ // ログ置き場 -public/ // webサーバのドキュメントルートはここを指定。画像もここ -tmp/ -vendor/ -bundle/ // gemの管理ディレクトリ -unicorn.rb // unicornの設定ファイル ``` ## Sinatraの設定 プロジェクト直下にconfig.ruを置きます。 また今回はControllerを別ファイル(main.rb)にしました。 ```config.ru require './main.rb' run MainApp ``` ```main.rb require 'rubygems' require 'sinatra/base' require 'logger' require 'unicorn' require 'haml' require 'sass' # config.ru ではこのクラスをrunしています class MainApp < Sinatra::Base # cssにアクセスした時の処理 get %r{^/(.*)\.css$} do scss :"style/#{params[:captures].first}" #scssテンプレート end # '/'にアクセスした時の処理 # views/index.haml get '/' do haml :index #views/index.haml(hamlのテンプレート) end end ``` %r{^/(.*)\.css$} の()の部分は params[:captures] という配列に格納されるようで、firstで0番目の要素を取得します。 つまりこの場合、/base.css にアクセスすると、views以下にある style/base.scss が呼び出されます。 ## bundlerでgemの管理 Railsではおなじみですが、Sinatraでもgemの管理にbundlerを使ってみます。 ``` # インストール $ gem install bundler $ cd projectname $ bundle init # Gemfileが作成される ``` ```Gemfile source "https://rubygems.org" gem 'sinatra' gem 'haml' gem 'mysql2' gem 'activerecord' gem 'unicorn' # Nginx + Unicornの場合 ・・・# などなど #【注意】 # gemは "activerecord" # .rb内でのrequireは "active_record" ``` ``` # gemを入れるディレクトリを作成 mkdir vendor mkdir vendor/bundle # パスを指定して bundle install # bundle exec をつけることで、Gemfileで指定されたgem環境でコマンドが実行される $ bundle install --path vendor/bundle # リモートのUnicornのバージョンを調べるなど $ bundle exec unicorn -v unicorn v4.7.0 ``` ##Webサーバ(バーチャルホスト)の設定 #####Apache x passengerの場合 静的なサイトと同様、サイト数分、VirtualHostブロックを追加するだけです。 私のUbuntuのApacheはバーチャルホストの設定ファイルがconfigとは別ファイルになっていました。環境によっては少し違うと思われます。 ``` $ cd /etc/apache2/sites-available/ $ sudo vim projectname-test # 既存のプロファイルをコピーでもおk ``` ```configファイル # プロジェクトディレクトリ以下の public を指定する # 今回は /var/www/hogehoge/projectname/ がプロジェクトディレクトリ <VirtualHost *:80> RailsEnv development ServerName local.projectname.com DocumentRoot /var/www/hogehoge/projectname/public <Directory /var/www/hogehoge/projectname/public> Options Indexes -MultiViews MultiViews Includes FollowSymLinks AllowOverride all </Directory> ErrorLog ${APACHE_LOG_DIR}/error.log LogLevel warn CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost> ``` ``` sudo a2ensite projectname-test // apacheは自動で再起動される ``` ##### Nginx x unicornの場合 Nginxをリバースプロキシとして設定します。 Nginx設定ファイルの中に、サイトの数分、upstreamブロックとlocationブロックを増やします。下記では2個のアプリに2個のホストを設定した場合を記述しています。 ```nginx.conf #下記を追記 worker_processes 1; events { worker_connections 1024; } http { #アプリ1 upstream app1 { server unix:/tmp/app1.sock; } #アプリ2 upstream app2 { server unix:/tmp/app2.sock; } #アプリ1 server { listen 80; server_name local.projectname.com; location / { root /var/www/hogehoge/projectname/public; proxy_pass http://app1; #unicornのupstreamを指定 proxy_set_header Host $host; } } #アプリ2 server { listen 80; server_name local.projectname2.com; location / { root /var/www/hogehoge/projectname2/public; proxy_pass http://app2; #unicornのupstreamを指定 proxy_set_header Host $host; } } } ``` unicornの設定ファイル unicorn.rbを編集します。 ```unicorn.rb # coding: utf-8 # プロジェクトディレクトリへのパス @path = "/var/www/hogehoge/projectname/" worker_processes 1 # CPUのコア数に揃える working_directory @path timeout 300 listen '/tmp/app1.sock' # Nginxのconfig内にあるupstreamで、このパスを指定 pid "#{@path}tmp/pids/unicorn.pid" # pidを保存するファイル # logを保存するファイル stderr_path "#{@path}log/unicorn.stderr.log" stdout_path "#{@path}log/unicorn.stdout.log" preload_app true ``` unicornの起動コマンド ``` $ bundle exec unicorn -E production -c unicorn.rb -D # unicorn.rbのあるアプリディレクトリで上記実行 # -E で環境を指定、-D でデーモン化 # bundlerを利用している場合には bundle exec # unicornを停止する場合は、pidを調べてkillする ``` ここまでで、とりあえずSinatraでWebサイトを公開することが出来ると思います。 ## 自動リロード 通常、アプリケーションファイルを変更してもWebサーバを再起動しないと反映されないため、開発環境では自動でリロードされるように設定。 ####Apache+Passengerの場合 ```tmp/always_restart.txt # tmp/以下に always_restart.txt を置くだけ。中身は空でOK ``` ####それ以外の場合 ``` gem install sinatra-contrib ``` ```main.rb require "sinatra/reloader" # if development? などの条件分岐をつけておく ``` Gitでデプロイする場合には、.gitignoreに上記txtファイルを追加 or dev環境の時のみ実行されるような分岐を書いておく。 ## Basic認証 ついでにベーシック認証についても調べたので書いておく。 ##### サイト全体にかける場合 ```main.rb # ルーティングファイルの最初に記述 use Rack::Auth::Basic do |username, password| username == "name" && password == "passwd" end ``` ##### 特定のパスにかける場合 ```main.rb helpers do def protected! unless authorized? response['WWW-Authenticate'] = %(Basic realm="Restricted Area") throw(:halt, [401, "Not authorized\n"]) end end def authorized? @auth ||= Rack::Auth::Basic::Request.new(request.env) @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == ['name', 'passwd'] end end get /protectedurl do protected! "ベーシック認証ページですよ" end ``` ## 環境の変更 ```cmd # sinatraの中で分岐する場合は以下の定数が使える ENV['RACK_ENV'] # "development" or "production" # Sinatraとは別にrubyの処理をコマンドラインで叩く場合、オプションを付ける # dev環境 $ RACK_ENV=development ruby app/dataset.rb # production環境 $ RACK_ENV=production ruby app/dataset.rb 同様の定数に環境名が入るので分岐に使える ENV['RACK_ENV'] # "development" or "production" ``` ##参考 * [SinatraとHamlとScssとCoffeeScriptでモダンなWeb制作環境を構築する #2](http://dev.classmethod.jp/server-side/language/modern-web-creating-environment-2/) * [Rubyの入門や書き捨てアプリを作る場合は sinatraがオススメ!](http://mukaer.com/archives/2013/06/06/ruby_sinatra/) * [apache-passengerで動かすsinatra環境のよさげな設定](http://shimada-k.hateblo.jp/entry/2012/12/08/140935) * [【初心者向け】RubyとSinatra、アンテナサイトの作り方](http://shgam.hatenadiary.jp/entry/2013/08/25/160937) * [sinatraでBasic認証](http://qiita.com/yaotti/items/11d6cea26d3c7111ea8c) * [Bundler再履修: bundle execって何? gemはどこに入るの?](http://memo.yomukaku.net/entries/IpCSQmo) |
|
| 439位 |
|
|||
|
12:42:10 |
|
|
# なんだこのプレゼンは?
と思ったプレゼンをネット上で見かけた。  [Build Warsへのリンク](http://markdalgleish.github.io/presentation-build-wars-gulp-vs-grunt/) JavaScriptのプロダクトをうまい具合にビルドするためのツール、 [Grunt](http://gruntjs.com/)と[glup](http://gulpjs.com/)を比較したプレゼンだ。 gifアニメが多用されてるし、オサレでカッコイイ。 何使ってこのプレゼン作ってるんだ!?と思って調べてみたら、 このプレゼンの作者が自分で作っていた。 [bespoke.js](https://github.com/markdalgleish/bespoke.js)というアプリだった。 # bespoke.js bespoke.jsというプレゼンアプリを早速使ってみたい。 セットアップや、プレゼンのスケルトン作成って めんどくさいのかなと思ったんだけど、yeomanを利用して テンプレを作成する形になってて、 プレゼン作成準備に必要な手間は少ししかなかった。 ## bespoke.jsを使うまで bespoke.jsをセットアップするのに必要なツールは以下の通り。 - [node.js](http://nodejs.org/) - [yeoman](http://yeoman.io/) Mac OS Xを使っているのであれば、 homebrewをインストールしてれば以下の流れで bespokeを使う準備は終わる。 ```bash > brew install node > npm install -g yo > npm install -g generator-bespoke > mkdir presentation-of-newbie > cd presentation-of-newbie > yo bespoke ``` 実行すると、プレゼンのタイトルなどを cliで問われるので、適当に答えていく。  # 出来上がったプレゼンを見てみよう ```grunt server``` を実行すれば、確認できる。  # デフォルトの内容を編集していこう 自動生成されたテンプレートにある src/index.jade ファイルを弄っていけば良い。 例えば、以下のようにする。  ```grunt server``` を実行したままにしていれば、 ホットリロード(!)が有効になってるので、 ブラウザ上でリロード動作が勝手に行われる。 素晴らしい。  # github pagesでの公開 github pagesへの公開にも対応している。 githubにリポジトリを登録して、 ```git init```して、```git add .```して、 ```git push origin master``` して、 その後に```grunt deploy``` を実行しよう。 gh-pagesリポジトリに 作成したプレゼンがpushされていく。 試しに作成したプレゼンはこちら。 http://substanz.github.io/presentation-of-newbie bespoke.jsヤバイ。 # 実際に使ってみた 先日行われた技術系イベントでLTするときに使ってみた。 http://futoase.github.io/switch-from-chef-to-docker/ # 以前からbespoke.jsを紹介していたエントリ - [プレゼン用のスライドショーを実装する、わずか1KBなのに多彩なエフェクトを備えタッチデバイスにも対応したスクリプト -Bespoke.js](http://coliss.com/articles/build-websites/operation/javascript/presentation-micro-framework-bespoke-js.html) |
|
| 440位 |
|
|||
|
10:11:02 |
(DTS corporation 所属) |
|
# はじめに
テキストボックスの変更を検知したくてイベントの挙動を調べたりしたのでメモ。 # change, keypress, keyupの挙動まとめ キー操作したときにどんなイベントが発生するのかは[日本語入力時に発生するキーイベントのテスト](http://freefielder.jp/lab/javascript/im.html)を使って確認させていただきました。  ## ポイント * changeはフォーカスが外れたときに発生する * keypressは全角やdelete,backspaceを入力しても発生しない * keyupはカーソル移動や全角・半角の切り替えなどすべてのキー操作を拾ってしまう ## change changeイベントを使うと変更を検知できます。 ```js $('#テキストボックスのid').change(function() { isChange = true; }); ``` changeイベントが発生するのはテキストボックスからフォーカスが外れた後です。 テキストボックスにフォーカスがあたったままブラウザ(タブ)の閉じるボタンを押された場合、変更を検知できません。 ## keypress keypressイベントはその名の通りキーが押されたときに発生します。 ```js $('#テキストボックスのid').keypress(function() { isChange = true; }); ``` keypressイベントが発生するのは印刷可能文字が押された時だけです。具体的には英数とEnterです。 印刷不可能文字(delete,backspace)や日本語(IMEがオンになった状態)ではkeypressイベントが発生しません。 日本語入力や、文字の削除は検知できません。 ## keyup keyupイベントはキーを全てのキー操作で発生します。 ```js $('#テキストボックスのid').keyup(function() { isChange = true; }); ``` カーソル移動や半角/全角キーの操作等、テキストボックスの中身が変わっていなくても発生してしまいます。 # テキストボックスの変更を検知する方法 change, keypress, keyupイベントの挙動を踏まえると、次の3つのやり方が考えられます。 ## changeイベントを使う フォーカスがあたっている間は変更を検知できなくていい場合はchangeで充分です。 ## 変更前と比較する 画面表示時に変更前の値を保持しておいて、keyupが発生するたびに比較します。 ```js $('#テキストボックスのID').keyup(checkChange(this)); function checkChange(e){ var old = v=$(e).find('#テキストボックスのID').val(); return function(){ v=$(e).find('#テキストボックスのID').val(); if(old != v){ old = v; isChange = true; delSuccessMSG(); } } } ``` フォーカスが当たったままでも変更が検知できます。 ## keypressとkeyupを組み合わせる 半角英数しか入力させないなら、keypressとkeyupを組み合わせて使うこともできます。 keypressで半角英数の入力を検知し、keyupでdeleteキーとbackspaceキーの入力を検知を検知します。 ```js // 半角英数の入力を検知 $('#テキストボックスのid').keypress(function() { isChange = true; delSuccessMSG(); }); // deleteキーとbackspaceキーの入力を検知 $('#テキストボックスのid').keyup(function(e) { if (e.keyCode == 46 || e.keyCode == 8){ isChange = true; delSuccessMSG(); } }); ``` フォーカスが当たったままでも変更が検知できます。 # ちなみに セレクトボックスやチェックボックスはchangeイベントを使って簡単に検知できます。 ```js $('#セレクトボックスのid').change(function() { isChange = true; }); ``` |
|
| 441位 |
|
|||
|
00:52:13 |
|
|
Google Guice の使い方メモ。
Wiki の User's Guide をざっと試してみた。 #特徴とか - 読みは「ジュース」 - Google が開発してる - DI コンテナ - ver 3.0 からは JSR330(Dependency Injection for Java)のリファレンス実装 - 設定は XML ではなく Java コード中に書く - アノテーションと型引数をフル活用 - 2013/10/31 現在の最新は 3.0(4.0 の Beta 版が公開されてる) #環境 ##Java 1.7.0_40 ##Google Guice 3.0 ```xml:pom.xml <dependency> <groupId>com.google.inject</groupId> <artifactId>guice</artifactId> <version>3.0</version> </dependency> ``` #使い方(基本) ```java:HelloWorld.java package google.guice; public class HelloWorld { public void hello() { System.out.println("Hello Google Guice!!"); } } ``` ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { @Inject private HelloWorld helloWorld; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() {} }); GuiceMain main = injector.getInstance(GuiceMain.class); main.helloWorld.hello(); } } ``` ```text:実行結果 Hello Google Guice!! ``` まず、 `Guice#createInjector(Module...)` メソッドで `Injector` のインスタンスを取得する。 引数の `Module` には `AbstractModule` を継承したクラスのインスタンスを渡す。 `configure()` メソッドで DI の設定を記述するが、今回の Hello World は単純なケースなので、特に設定は不要。 取得した `Injector` から `GuiceMain` クラスのインスタンスを取得すると、 `@Inject` アノテーションが付与された `helloWorld` フィールドに、勝手に `HelloWorld` クラスのインスタンスがインジェクションされるので、そのまま `hello()` メソッドを実行している。 #インターフェースの実装を指定する ```java:Speaker.java package google.guice; public interface Speaker { void thankYou(); } ``` ```java:JapaneseSpeaker.java package google.guice; public class JapaneseSpeaker implements Speaker { @Override public void thankYou() { System.out.println("ありがとう"); } } ``` ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { @Inject private Speaker speaker; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Speaker.class).to(JapaneseSpeaker.class); } }); GuiceMain main = injector.getInstance(GuiceMain.class); main.speaker.thankYou(); } } ``` ```text:実行結果 ありがとう ``` DI の設定は `configure()` メソッドの中で記述する。 `bind(Speaker.class).to(JapaneseSpeaker.class);` がその設定部分。 `Speaker` をインジェクションするときの具体的なクラスに `JapaneseSpeaker` を指定している。 ##@ImplementedBy アノテーションで実装クラスを指定する `Speaker` インターフェースに `@ImplementedBy` アノテーションを付与すると、上記と同じ設定ができる。 ```java:Speaker.java package google.guice; import com.google.inject.ImplementedBy; @ImplementedBy(JapaneseSpeaker.class) public interface Speaker { void thankYou(); } ``` `@ImplementedBy` と `configure()` メソッドの両方で設定がされていると、 `configure()` メソッドで設定した内容が優先される。 つまり、 `@ImplementedBy` はデフォルトの実装を定義するのに利用できる。 ただし、コンパイル時に具体的な実装に依存してしまうので、注意して使ったほうが良い。 #インジェクションできる場所について 以下の3つ。 - フィールド - メソッド - コンストラクタ ##フィールドインジェクション ```java: package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { @Inject private Speaker speaker; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Speaker.class).to(JapaneseSpeaker.class); } }); GuiceMain main = injector.getInstance(GuiceMain.class); main.speaker.thankYou(); } } ``` ```text:実行結果 ありがとう ``` フィールドに `@Inject` アノテーションを付与すれば、可視性が private でもインジェクションできる。 ##メソッドインジェクション ```java: package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { private Speaker speaker; @Inject public void setSpeaker(Speaker speaker) { System.out.println("setter injection"); this.speaker = speaker; } public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Speaker.class).to(JapaneseSpeaker.class); } }); GuiceMain main = injector.getInstance(GuiceMain.class); main.speaker.thankYou(); } } ``` ```text:実行結果 setter injection ありがとう ``` セッターメソッドに `@Inject` アノテーションを付与すると、セッターメソッド経由でインジェクションされる。 ##コンストラクタインジェクション ```java: package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { private Speaker speaker; @Inject public GuiceMain(Speaker speaker) { System.out.println("constructer injection"); this.speaker = speaker; } public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Speaker.class).to(JapaneseSpeaker.class); } }); GuiceMain main = injector.getInstance(GuiceMain.class); main.speaker.thankYou(); } } ``` ```text:実行結果 constructer injection ありがとう ``` コンストラクタに `@Inject` アノテーションを付与すると、そのコンストラクタ経由でインジェクションされる。 #アノテーションでインジェクションするクラスを指定する ```java:EnglishSpeaker.java package google.guice; public class EnglishSpeaker implements Speaker { @Override public void thankYou() { System.out.println("Thank you."); } } ``` ```java:Japanese.java package google.guice; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.google.inject.BindingAnnotation; @BindingAnnotation @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface Japanese { } ``` ```java:English.java package google.guice; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.google.inject.BindingAnnotation; @BindingAnnotation @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface English { } ``` ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { @Inject @Japanese private Speaker japaneseSpeaker; @Inject @English private Speaker englishSpeaker; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Speaker.class).annotatedWith(Japanese.class).to(JapaneseSpeaker.class); bind(Speaker.class).annotatedWith(English.class).to(EnglishSpeaker.class); } }); GuiceMain main = injector.getInstance(GuiceMain.class); main.japaneseSpeaker.thankYou(); main.englishSpeaker.thankYou(); } } ``` ```text:実行結果 ありがとう Thank you. ``` `@BindingAnnotation` アノテーションを設定した自作のアノテーション(`@Japanese`, `@English`)を作り、 `configure()` メソッド内で `annotatedWith(Class<?>)` を使えば、インジェクションする具体的なクラスをアノテーションごとに指定できる。 #インジェクションするインスタンスを指定する ```java:User.java package google.guice; public class User { private String name; public User(String name) { this.name = name; } public void introduce() { System.out.println("私の名前は" + this.name + "です。"); } } ``` ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { @Inject private User user; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(User.class).toInstance(new User("佐藤")); } }); GuiceMain main = injector.getInstance(GuiceMain.class); main.user.introduce(); } } ``` ```text:実行結果 私の名前は佐藤です。 ``` `toInstance()` メソッドにインスタンスを渡すと、そのインスタンスがインジェクションされる。 #Provides メソッドでインジェクションするインスタンスを定義する ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Provides; public class GuiceMain { @Inject private User user; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() {} @Provides private User provideUser() { return new User("鈴木"); } }); GuiceMain main = injector.getInstance(GuiceMain.class); main.user.introduce(); } } ``` ```text:実行結果 私の名前は鈴木です。 ``` Module クラスに Provides メソッドを定義すると、このメソッドの戻り値がインジェクションに利用される。 Provides メソッドの定義は `@Provides` アノテーションを付与すればいい。 #Provider クラスでインジェクションするインスタンスを定義する ```java:UserProvider.java package google.guice; import com.google.inject.Provider; public class UserProvider implements Provider<User> { @Override public User get() { return new User("田中"); } } ``` ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { @Inject private User user; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(User.class).toProvider(UserProvider.class); } }); GuiceMain main = injector.getInstance(GuiceMain.class); main.user.introduce(); } } ``` ```text:実行結果 私の名前は田中です。 ``` `com.google.inject.Provider` インターフェースを実装したクラスを作成して、 `configure()` メソッドの中で `toProvider(Class<Provider>)` に指定すると、 Provider クラスの `get()` メソッドが返した値がインジェクションに利用されるようになる。 ##@ProvidedByアノテーションで Provider クラスを指定する インターフェースに `@ProvidedBy` アノテーションを付与すると、上記と同じ設定ができる。 ```java:Speaker.java package google.guice; import com.google.inject.ProvidedBy; @ProvidedBy(SpeakerProvider.class) public interface Speaker { void thankYou(); } ``` `@ImplementedBy` と同じで、 `configure()` メソッドで Provider クラスが設定されている場合は、そちらが優先される。 #Provider クラス自体をインジェクションする ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Provider; public class GuiceMain { @Inject private Provider<User> speakerProvider; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(User.class).toProvider(UserProvider.class); } }); GuiceMain main = injector.getInstance(GuiceMain.class); User user = main.speakerProvider.get(); user.introduce(); } } ``` ```text:実行結果 私の名前は田中です。 ``` #Provider クラスの get() メソッドでチェック例外をスローする `Provider#get()` メソッドは例外をスローしない定義になっているので、そのままだと非チェック例外しかスローできない。 何らかの理由で `get()` メソッドでチェック例外をスローしたい場合は、 Throwing Providers という Guice の拡張機能を利用する。 Throwing Providers を使用する場合は、 `guice-throwingproviders-3.0.jar` をクラスパスに追加する。 pom.xml は以下。 ```xml:pom.xml <dependency> <groupId>com.google.inject.extensions</groupId> <artifactId>guice-throwingproviders</artifactId> <version>3.0</version> </dependency> ``` まず、 `CheckedProvider<T>` インターフェースを拡張した独自の Provider インターフェースを定義する。 ```java:CheckedSpeakerProvider.java package google.guice; import com.google.inject.throwingproviders.CheckedProvider; public interface CheckedSpeakerProvider extends CheckedProvider<Speaker> { @Override Speaker get() throws MyException; } ``` ```java:MyException.java package google.guice; public class MyException extends Exception { } ``` 次に、 `CheckedSpeakerProvider` インターフェースを実装した Provider クラスを作成する。 ```java:CheckedEnglishSpeakerProvider.java package google.guice; public class CheckedEnglishSpeakerProvider implements CheckedSpeakerProvider { @Override public Speaker get() throws MyException { return new EnglishSpeaker(); } } ``` 最後に、 `configure()` メソッドの中で Provider クラスを設定する。 ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.throwingproviders.ThrowingProviderBinder; public class GuiceMain { @Inject private CheckedSpeakerProvider provider; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { ThrowingProviderBinder.create(binder()) .bind(CheckedSpeakerProvider.class, Speaker.class) .to(CheckedEnglishSpeakerProvider.class); } }); GuiceMain main = injector.getInstance(GuiceMain.class); try { Speaker speaker = main.provider.get(); speaker.thankYou(); } catch (MyException e) { e.printStackTrace(); } } } ``` `ThrowingProviderBinder` クラスのメソッドを使って Provider クラスの設定をしている。 #インジェクションするインスタンスを生成するときのコンストラクタを指定する ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { @Inject private User user; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(String.class).toInstance("高橋"); try { bind(User.class).toConstructor(User.class.getConstructor(String.class)); } catch (NoSuchMethodException | SecurityException e) { addError(e); } } }); GuiceMain main = injector.getInstance(GuiceMain.class); main.user.introduce(); } } ``` ```text:実行結果 私の名前は高橋です。 ``` `toConstructor(Constructor)` メソッドで、インスタンスを生成するときのコンストラクタを指定できる。 #シングルトン Guice は、デフォルトだとインジェクションの度に新しいインスタンスを生成している。 シングルトンにしたい場合は、以下のいずれかの方法を用いる。 __クラスに `@Singleton` アノテーションを設定する__ ```java: package google.guice; import com.google.inject.Singleton; @Singleton public class SingletonScope { public int count = 1000; } ``` __`configure()` で `in(Singleton.class)` を指定する__ ```java: Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(SingletonScope.class).in(Singleton.class); } }); ``` __Provide メソッドに `@Singleton` アノテーションを設定する__ ```java: Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { } @Provides @Singleton private SingletonScope provideSingleton() { return new SingletonScope(); } }); ``` ##動作確認 ```java:SingletonScope.java package google.guice; import com.google.inject.Singleton; @Singleton public class SingletonScope { public int count = 1000; } ``` ```java:DefaultScope.java package google.guice; public class DefaultScope { public int count = 100; } ``` ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; public class GuiceMain { public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() {} }); SingletonScope singletonScope = injector.getInstance(SingletonScope.class); DefaultScope defaultScope = injector.getInstance(DefaultScope.class); System.out.printf("singleton.count = %d\n", singletonScope.count); System.out.printf("default.count = %d\n", defaultScope.count); singletonScope.count = 9999; defaultScope.count = 999; singletonScope = injector.getInstance(SingletonScope.class); defaultScope = injector.getInstance(DefaultScope.class); System.out.printf("singleton.count = %d\n", singletonScope.count); System.out.printf("default.count = %d\n", defaultScope.count); } } ``` ```text:実行結果 singleton.count = 1000 default.count = 100 singleton.count = 9999 default.count = 100 ``` ##起動時にシングルトンのインスタンスを生成させる ```java: package sample.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; public class Main { public Main() { System.out.println("Main Constructor."); } public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Main.class).asEagerSingleton(); } }); } } ``` 通常、シングルトンのオブジェクトは、初めてインジェクションされるときにインスタンスが生成される。 起動時にあらかじめ初期化をしておきたい場合は、 `asEagerSingleton()` を `configure()` の中で指定する。 #依存関係が解決できないときにエラーを無視する `@Inject` アノテーションの `optional` 属性に true を指定すると、依存関係が解決できないときにエラーを無視できる。 ```java: package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { @Inject(optional=true) @English private Speaker speaker; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { // 設定をコメントアウトしているので、 optional=false だとエラーになる // bind(Speaker.class).annotatedWith(English.class).to(EnglishSpeaker.class); } }); GuiceMain main = injector.getInstance(GuiceMain.class); System.out.println(main.speaker); } } ``` ```text:実行結果 null ``` インジェクションは実行されず、デフォルト値(この場合 null) になる。 #オンデマンドでインジェクションする ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { @Inject private Speaker speaker; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Speaker.class).to(EnglishSpeaker.class); } }); GuiceMain main = new GuiceMain(); System.out.println(main.speaker); injector.injectMembers(main); main.speaker.thankYou(); } } ``` ```text:実行結果 null Thank you. ``` `Injector#injectMembers(Object)` メソッドを使えば、好きなタイミングで、引数に渡したインスタンスに依存関係をインジェクションできる。 実行されるインジェクションはフィールドインジェクションかメソッドインジェクションで、コンストラクタインジェクションは無視される。 ```java:コンストラクタインジェクションは無視 package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; public class GuiceMain { private Speaker speaker; @Inject public GuiceMain(Speaker speaker) { this.speaker = speaker; } public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Speaker.class).to(EnglishSpeaker.class); } }); GuiceMain main = new GuiceMain(null); System.out.println(main.speaker); injector.injectMembers(main); System.out.println(main.speaker); } } ``` ```text:実行結果 null null ``` #static フィールドにインジェクションする ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; public class GuiceMain { @Inject private static Speaker speaker; public static void main(String[] args) { Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Speaker.class).to(EnglishSpeaker.class); requestStaticInjection(GuiceMain.class); } }); GuiceMain.speaker.thankYou(); } } ``` ```text:実行結果 Thank you. ``` `configure()` メソッドのなかで `requestStaticInjection(Class<?>)` を実行すると、 static フィールドに依存関係をインジェクションできる。 ただし、 Google Guice はこの使い方を推奨していない(テストしづらいし、グローバルな参照にほかならないとかとか)。 既存の実装が static な Factory を使用している場合に、 Guice による実装に移行するときに "繋ぎ" として使用することを想定しているらしい。 #インターセプター ```java:MyInterceptor.java package google.guice; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("before proceed"); Object result = invocation.proceed(); System.out.println("after proceed"); return result; } } ``` ```java:GuiceMain.java package google.guice; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.matcher.Matchers; public class GuiceMain { @Inject private Speaker speaker; public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Speaker.class).to(EnglishSpeaker.class); bindInterceptor(Matchers.any(), Matchers.any(), new MyInterceptor()); } }); GuiceMain main = injector.getInstance(GuiceMain.class); main.speaker.thankYou(); } } ``` ```text:実行結果 before proceed Thank you. after proceed ``` まず、 `MethodInterceptor` インターフェースを実装した、独自のインターセプターを実装する。 次に、 `configure()` メソッドでインターセプターの設定を記述する。 インターセプターの設定は、 `bindInterceptor(Matcher<? super Class<?>>, Matcher<? super Method>, MethodInterceptor...)` を使用する。 1つ目と2つ目の Matcher はインターセプターを適用するクラスを限定するための引数で、3つ目の引数に具体的なインターセプターのインスタンスを渡す。 `Matcher.any()` はなんでも OK を意味するので、前述の実装だと全てのクラスの全てのメソッドに対してインターセプターが適用される。 以下、 Matcher による絞り込みの実装例。 ##特定のパッケージ直下にあるクラスのみ対象 ```java: bindInterceptor(Matchers.inPackage(Hoge.class.getPackage()), Matchers.any(), new MyInterceptor()); ``` `Hoge` クラスのあるパッケージ直下にあるクラスが対象。 ##特定のパッケージ以下にあるクラスのみ対象(サブパッケージも含む) ```java: bindInterceptor(Matchers.inSubpackage("google.guice.aop.hoge"), Matchers.any(), new MyInterceptor()); ``` `google.guice.aop.hoge` 以下にあるクラス(サブパッケージも含む)が対象。 ##特定のパッケージ以外にあるクラスのみ対象 ```java: bindInterceptor(Matchers.not(Matchers.inPackage(Hoge.class.getPackage())), Matchers.any(), new MyInterceptor()); ``` `Matchers#not(Matcher)` メソッドで引数に渡した Matcher を否定した条件が指定できる。 上記の場合は、 `Hoge` クラスのあるパッケージ以外のパッケージに含まれるクラスが対象になる。 ##特定のアノテーションが付与されたクラス(メソッド)のみ対象 ```java:AopTarget.java package google.guice.aop; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface AopTarget { } ``` ```java: bindInterceptor(Matchers.annotatedWith(AopTarget.class), Matchers.any(), new MyInterceptor()); ``` `@AopTarget` アノテーションを付与したクラスのみが対象になる。 ```java: bindInterceptor(Matchers.any(), Matchers.annotatedWith(AopTarget.class), new MyInterceptor()); ``` `@AopTarget` アノテーションを付与したメソッドのみが対象。 ##特定のクラス、およびそのサブクラスのみ対象 ```java: bindInterceptor(Matchers.subclassesOf(HogeClass.class), Matchers.any(), new MyInterceptor()); ``` `HogeClass` と、それを継承したクラスのみが対象。 ##Matcher を自作する `AbstractMatcher` というクラスがあるので、それを継承すれば任意のクラス(メソッド)にマッチする Matcher を自作できる。 ```java:MyMatcher.java package google.guice.aop; import com.google.inject.matcher.AbstractMatcher; public class MyMatcher extends AbstractMatcher<Class<?>> { @Override public boolean matches(Class<?> clazz) { return clazz.getSimpleName().endsWith("Impl"); } } ``` ```java: bindInterceptor(new MyMatcher(), Matchers.any(), new MyInterceptor()); ``` 名前が `Impl` で終わるクラスが対象になる。 #参考 - [公式wiki](http://code.google.com/p/google-guice/wiki/Motivation?tm=6) - [Google Guice 3.0を利用したアノテーション駆動による依存性注入](http://www.infoq.com/jp/news/2011/04/guice_30) - [Google Guice - Wikipedia](http://ja.wikipedia.org/wiki/Google_Guice) - [Google Guice Javadoc](http://google-guice.googlecode.com/git/javadoc/packages.html) |
|
| 442位 |
|
|||
|
10:43:15 |
|
|
データベースを扱うのに CoreDataは便利ですが、大量データの更新や保存をする際にはメインスレッドを妨害しないように別のスレッドで処理する必要があります。 ここでは CoreDataで非同期処理を行うための Tipsを紹介します。 元ネタは [Multi-Context CoreData](http://www.cocoanetics.com/2012/07/multi-context-coredata/) です。より詳しい解説や図解はこちらをどうぞ。 ## NSManagedObjectContext とマルチスレッド NSManageObjectContext は CoreDataのデータオブジェクトを管理するクラスですが、このクラスはスレッドセーフではありません。このため、マルチスレッドで CoreDataのオブジェクトを扱えるようにするにはスレッドごとに NSManageObjectContextを用意する必要があります。 iOS 5以降では initWithConcurrencyType: に NSPrivateQueueConcurrencyType を指定して Contextオブジェクトを生成すると、Contextに対して専用のスレッドが割り当てられます。 ```objectivec: NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; ``` performBlock: で指定したブロックは Contextが管理するスレッド上で実行されます。以下の Userオブジェクトの新規作成処理はメインスレッドではなく、temporaryContext用のスレッドで実行されます。 ```objectivec: [temporaryContext performBlock:^{ NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:temporaryContext]; [managedObject setValue:@"hoge" forKey:@"name"]; }]; ``` ## NSManagedObject間の更新通知 temporaryContextで行われた更新処理が行われると、更新内容に応じてメインスレッド上で UIの描画処理やデータベースへの永続化処理をする必要があります。 iOS 5以降の NSManagedObejctContext は、Context間で親子関係を持つことができ、子で行った更新を親に対して伝搬させられます。 以下のようにメインスレッド用のコンテキストを用意しておきます。 ```objectivec: _mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; [_mainContext setPersistentStoreCoordinator:self.persistentStoreCoordinator]; ``` 更新用の Contextにはメインコンテキストを親としてセットしておきます。 ```objectivec: NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; temporaryContext.parentContext = _mainContext; ``` temporaryContextに対して行った更新内容は、save: メソッドで親コンテキストに対して通知されマージされます。逆に言えば、save: メソッドをコールしなければマージされないため、編集操作の破棄などキャンセル処理に利用することもできます。この例では編集内容をメインコンテキストにマージし、その後、メインコンテキストで永続化処理を行っています。メインコンテキストでは UIの更新処理も行う必要があるでしょう。 ```objectivec: [temporaryContext performBlock:^{ NSLog(@"データをバックグラウンドスレッドで更新する"); ・・・ NSError *error; NSLog(@"変更内容を main context にマージする"); if (![temporaryContext save:&error]) { // handle error } [_mainContext performBlock:^{ NSLog(@"データを永続化する"); NSError *error; if (![_mainContext save:&error]) { // handle error } }]; }]; ``` ## 永続化処理の非同期化 ここまでの例ではオブジェクトの更新処理はバックグラウンドスレッドで実行していますが、永続化処理はメインスレッドでの実行になるため、その間、UIの動作が止まってしまいます。 永続化処理をバックグラウンドで実行するには保存操作専用の NSManagedObjectContext を用意します。このコンテキストを書き込みコンテキストと呼びます。 コンテキスト間の親子関係は以下のようになります。 書き込みコンテキスト ↑ メインコンテキスト ↑ 更新コンテキスト ```objectivec: // 書き込みコンテキスト _writerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; [_writerContext setPersistentStoreCoordinator:self.persistentStoreCoordinator]; // メインコンテキスト _mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; _mainContext.parentContext = _writerContext; ``` 更新コンテキストで何か更新処理をすると変更内容がメインコンテキストに反映され メインスレッドで UIの描画処理を行います。さらにメインコンテキストにマージされた更新内容は、その親である書き込みコンテキストに伝搬してマージされ、バックグラウンドで永続化処理が行われます。 ```objectivec: NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; temporaryContext.parentContext = _mainContext; [temporaryContext performBlock:^{ NSLog(@"データをバックグラウンドスレッドで更新する"); ・・・ NSError *error; NSLog(@"変更内容を main context にマージする"); if (![temporaryContext save:&error]) { // handle error } [_mainContext performBlock:^{ NSLog(@"変更内容を writer context にマージする"); NSError *error; if (![_mainContext save:&error]) { // handle error } [_writerContext performBlock:^{ NSError *error; NSLog(@"データを永続化する"); if (![_writerContext save:&error]) { // handle error } }]; }]; }]; ``` 上記のようにすることで、メインスレッドから CoreDataの更新処理、永続化処理を分離でき、画面が固まるといった問題を回避できるようになります。また、更新処理を別のコンテキストに分離することで、更新処理を破棄する(save: しない)こともできるようになります。 以上。 |
|
| 443位 |
|
|||
|
20:34:18 |
|
|
元ネタ:
* [Three quick Rails console tips by Nick of 37signals](http://37signals.com/svn/posts/3176-three-quick-rails-console-tips) * [10 Rails console tricks](http://rors.org/2009/12/20/10-rails-console-tricks.html) ## urlヘルパーが生成するpath/urlを確認してみる。 `app`オブジェクトからURLヘルパーを呼び出すことができる。 ``` [1] pry(main)> app.api_hoge_path => "/api/hoge" [2] pry(main)> app.api_hoge_url => "http://www.example.com/api/hoge" ``` その他参考: [Rails でリンクパスを生成する方法色々・・とRails console で 生成される path を確認したい時](http://qiita.com/mm36/items/f266977e12df9d1dc548) ##リクエストを投げてコントローラを呼び出してみる。 `app`オブジェクトを使うと簡単にリクエストを投げてコントローラを呼び出すこともできる。 ``` [3] pry(main)> app.get "http://localhost:3000/api/hoge" => 200 [4] pry(main)> app.response.body => "{\"response\":\"hoge\"}" ``` ## view ヘルパーを呼び出す `helper`オブジェクトから View ヘルパーを呼び出すことができる。 ``` [8] pry(main)> helper.truncate("Testing", length: 6) => "Tes..." [9] pry(main)> helper.link_to "home", app.root_path => "<a href=\"/\">home</a>" ``` ## インスタンス変数に値を設定する 以下のように ヘルパーメソッドの中でインスタンス変数を参照している場合、`Object#instance_variable_set` を使ってインスタンス変数に値を設定することができる。 以下のようなヘルパーがあったとして ``` def say_hello "hello, #{@name}" end ``` @nameに値を設定するには以下のとおり ``` [99] pry(main)> helper.instance_variable_set :@name, "hoge" ``` これで 上記ヘルパーメソッドを呼び出すと ``` [99] pry(main)> helper.say_hello => "hello, hoge" ``` という結果が得られる。 ## params 変数に値をセットする 以下のようにヘルパーメソッドの中で params 変数を参照している場合、普通に`helper.say_goodbye`とメソッドを呼び出すと、params 変数の中身が空のため正しく動作しない。 ``` def say_goodbye "goobye, #{params[:name]}" end ``` そのような場合、OpenStruct クラスを使ってコントローラを偽装することで params 変数に値を設定することができる。 ``` [2] pry(main)> helper.controller = OpenStruct.new(:params => { :name => "hoge" }) => #<OpenStruct params={:name=>"hoge"}> [3] pry(main)> helper.say_goodbye => "goodbye, hoge" ``` ## sandboxモード exit すると DBへの変更がロールバックされる ``` $ rails c --sandbox ``` |
|
| 444位 |
|
|||
|
21:49:47 |
|
|
Mac歴1ヶ月、Windows歴5年とかなり長い間パソコンを触って来ましたが、全て独学で、周りにはパソコン関連の何かを教えてくれる人が全くいないので、ここに載っていない便利ツールや便利な設定を知っていたらぜひ教えてほしいです。タイトルには、MacBookAirと書いていますが、対象は何でもいいです。OSにも特にこだわりません。
ここでは、私がMacBookAirの環境を構築した際に便利だと思ったツールや設定を紹介していきたいと思います。 ##追記 <a href="http://qiita.com/syui/items/73a822970742b317aede" target="_blank">MacBookAirで使っている便利ツール - Qiita</a> <a href="http://qiita.com/syui/items/eb0de8ff93d5714d88aa" target="_blank">MacBookAirで使っている便利ツール vol.2 - Qiita</a> **更新しました。** ## MacBookAirを買ったら、すぐに確認しておきたい設定 01. トラックパッド 02. キーボード 03. 拡張機能 # トラックパッドの便利操作 Dockにある システム環境設定 > トラックパッド を選択します。そこで、有効にしたい項目にチェックを入れます。 ``` 2本指でタップすると、右クリック ``` # キーボードの便利操作 Dockにある システム環境設定 > キーボード を選択します。そこで、有効にしたい項目にチェックを入れます。 ``` Ctrl+F3を押すと、Dockにフォーカスする ``` # 拡張機能 Macには隠し機能というものがあります。今回は、それを有効にする方法を紹介します。 ### ユーザライブラリを表示する ``` chflags nohidden ~/Library/ ``` ### ネットワーク上にDS_storeを作らない ``` defaults write com.apple.desktopservices DSDontWriteNetworkStores true ``` ### Dockの待機時間ゼロ ``` defaults write com.apple.Dock autohide-delay -float 0 && killall Dock ``` ### Dashboardを無効にする ``` defaults write com.apple.dashboard mcx-disabled -boolean true ``` ## MacBookAirを買ったらすぐに入れたいアプリ 01. [BetterTouchTool](http://blog.boastr.net/) //トラックパッドを拡張するために役立つアプリ。 02. [Google Chrome](http://www.google.co.jp/chrome/intl/ja/landing_ch.html) //インターネットブラウザ 03. [Growl](http://growl.info/downloads) //通知を拡張するアプリ 04. [Kopypasta](http://itunes.apple.com/jp/app/kopypasta/id463243494?mt=12) //クリップボードをバックアップしてくれるアプリ 05. [Quicksilver](http://qsapp.com/) //アプリにショートカットキーを割り当てることができるアプリ 06. [WindowFlow](http://itunes.apple.com/jp/app/windowflow/id414445104?mt=12) //Windowsユーザーにお馴染みの[Tab+Alt]でウィンドウを切り替えを使えるようにするアプリ 07. [XtraFinder](http://www.trankynam.com/xtrafinder/) //Finderを拡張するアプリ 08. [Xcode](https://developer.apple.com/xcode/) //開発環境を提供するアプリ 09. [TinkerTool](http://www.bresink.com/osx/TinkerTool.html) //Macの隠し機能を簡単に有効にするアプリ 10. [VirtualBox](https://www.virtualbox.org/wiki/Downloads) //仮想環境を提供するアプリ 11. [Skitch](http://itunes.apple.com/jp/app/skitch/id425955336?mt=12) //スクーンショットの撮影と画像の編集を行うアプリ 12. [DashExpander](http://itunes.apple.com/jp/app/dashexpander/id458867049?mt=12) //無料の高機能エディタ 13. [Alfred](http://www.alfredapp.com/) //キーボード操作によるアプリの起動やWeb検索への連携を可能にするアプリ 14. [Google IME](http://www.google.co.jp/ime/index-mac.html) //Google 日本語入力 15. [Dropbox](https://www.dropbox.com/) //設定ファイルの共有やバックアップに使えるアプリ 16. [ShiftIt](http://code.google.com/p/shiftit/) //ウィンドウ操作を簡単にするアプリ 17. [Kobito](http://kobitoapp.com/) //Qiitaへの投稿を簡単にするアプリ 18. [GitHub for Mac](http://mac.github.com/) //GitHubへの投稿を簡単にするアプリ 19. [GIMP](http://www.gimp.org/downloads/) //言わずと知れた高機能の画像作成・編集アプリ 20. [Notational Velocity](http://notational.net/) //Simplenoteクライアント。メモを簡単に行うためのアプリ 21. [Evernote](http://www.evernote.com/about/intl/jp/) //高機能メモ帳Evernoteのクライアントアプリ。 22. [KeePassX](http://keepass.info/download.html) //パスワード管理アプリ。 23. [MacVim](http://code.google.com/p/macvim-kaoriya/) //Vimエディタの日本語拡張版 24. [iTerm2](http://www.iterm2.com/) //ターミナルアプリ。設定項目が豊富で安定しています。 25. [ClamXav](http://www.clamxav.com/download.php) //パソコンをスキャンし、コンピュータウィルスを見つけ出すためのアプリ 他にもたくさんのアプリを入れていますが、主要なアプリは以上の通りです。 # 各アプリのTips あまりに長くなりそうなので、思いついたものを適当に書き連ねていきます。 ### パスワードを共有する ` KeePassX `で管理したパスワードファイルをDropboxに入れます。iPhoneからもアクセスできるので非常に便利です。iPhoneのKeePassクライアントである` MiniKeePass `は非常に安定しており優秀なアプリです。 ### アプリにショートカットキーを割り当てる ` Quicksilver `を使って、アプリにショートカットキーを割り当てると便利です。私がショートカットキーを割り当てているアプリは、` Finder `と` iTerm2 `などです。 ### GIMPは、プラグインを入れるとさらに便利になる ` GIMP `は、有料の` Photoshop `と比べられることが多いのですが、プラグインを入れると、同等の仕事をこなしてくれるようになります。 ## GIMPのプラグイン一覧 [Separate+](http://cue.yellowmagic.info/softwares/separate.html) //CMYKファイルを扱うプラグイン [PSPI](http://tml.pp.fi/gimp/pspi.html) //Photoshop用のプラグインをGIMPでも使う [Liquid Rescale](http://liquidrescale.wikidot.com/en:start) //画像のリサイズに使えるプラグイン [Wavelet Denoise](http://registry.gimp.org/node/4235) //ノイズを除去するためのプラグイン [Gimp-reflection](http://registry.gimp.org/node/1025) //特殊効果としての反射を行うプラグイン ### URLをメモする ` Notational Velocity `は、URLをメモする際に非常に役に立ってくれます。URLをコピーし、ショートカットキーを押すと、それをノート化することができます。このアプリ自体もショートカットキーで起動するように設定ができます。 ` Simplenote `は、iPhone用にアプリが用意されているので、` Simplenote `を使った情報共有とメモが促進されます。記事内の文章も全てクリップされるので、非常にいい感じです。 ### Google Chromeでのブラウジングを快適にする [gleeBox](http://thegleebox.com/)と[Vichrome](https://chrome.google.com/webstore/detail/gghkfhpblkcmlkmpcpgaajbbiikbhpdi)または、[Shortcut Manager](https://chrome.google.com/webstore/detail/mgjjeipcdnnjhgodgjpfkffcejoljijf)などの拡張機能を入れると、` Google Chrome `の操作が快適になります。 例えば、` Shortcut Manager `にブックマークレット[ShareHtmlメーカー](http://dl.dropbox.com/u/2271551/javascript/bmltmk.html)を設定してみると、ショートカットキーで簡単にサイトや記事の紹介ができます。 ` gleeBox `の方は、特定のキーを押すと、コマンドモードが発動し、例えば、` !tweet `と入力すると、見ている記事をツイートしてくれるし、` !rss `と入力すると、RSS登録してくれます。 ## Terminal環境を整える Terminal = iTerm2 Shell = zsh Editor = Vim Virtual = tmux # iTerm2の設定を考える MacBookAirには、デフォルトでせっかく良い壁紙が設定されているので、ウィンドウを透明にしてみます。 これで、デスクトップにある壁紙がうっすらとではありますが、表示されるようになりました。 # zshの設定を考える まずは、` zsh `のインストールと、デフォルトシェルの変更を行います。ただ、Macには、最初から入っているみたいなので、Windows、Linuxユーザー向けの解説です。 ### Homebrewのインストール ` Homebrew `は、Unix向けに開発されたオープンソース・ソフトウェアを手軽にインストールすることができるMacOSX用のパッケージです。` Homebrew `を使うことで、アプリをコマンドから簡単にダウンロード、インストールできるようになります。 ``` /usr/bin/ruby -e "$(/usr/bin/curl -fksSL https://raw.github.com/mxcl/homebrew/master/Library/Contributions/install_homebrew.rb)" ``` ### zshのインストール ``` brew install zsh ``` ### zshをデフォルトシェルに設定する ``` sudo chsh -s /bin/zsh ``` ### Gitのインストール ` Git `とは、プログラムなどのソースコード管理を行う分散型バージョン管理システムのことです。このシステムを使うことで、独自の設定を簡単に共有、反映させることができます。 ``` brew install git ``` ### zshの補完をauto-fu.zshパッケージを使い簡単に強化する ``` mkdir ~/work/ ~/.zsh/ && cd ~/work/ git clone git://github.com/hchbaw/auto-fu.zsh.git cp ~/work/auto-fu.zsh/auto-fu.zsh ~/.zsh/auto-fu.zsh ``` ` .zshrc `に下記を追加して、` exec $SHELL `を実行し、設定を反映します。 ```~/.zshrc if [ -f ~/.zsh/auto-fu.zsh ]; then source ~/.zsh/auto-fu.zsh function zle-line-init () { auto-fu-init } zle -N zle-line-init zstyle ':completion:*' completer _oldlist _complete fi ``` ### zshのプロンプトを整えてみる ``` ~/.zshrc # プロンプト ## PROMPT内で変数展開・コマンド置換・算術演算を実行する。 setopt prompt_subst ## PROMPT内で「%」文字から始まる置換機能を有効にする。 setopt prompt_percent ## コピペしやすいようにコマンド実行後は右プロンプトを消す。 setopt transient_rprompt ## 256色生成用便利関数 ### red: 0-5 ### green: 0-5 ### blue: 0-5 color256() { local red=$1; shift local green=$2; shift local blue=$3; shift echo -n $[$red * 36 + $green * 6 + $blue + 16] } fg256() { echo -n $'\e[38;5;'$(color256 "$@")"m" } bg256() { echo -n $'\e[48;5;'$(color256 "$@")"m" } ## プロンプトの作成 # VCS settings zstyle ':vcs_info:*' max-exports 3 zstyle ':vcs_info:hg:*' get-revision true # hg で check-for-changes を有効にするには zstyle ':vcs_info:hg:*' use-simple true # この2つの設定が必要 autoload -Uz is-at-least zstyle ':vcs_info:git:*' check-for-changes true zstyle ':vcs_info:git:*' stagedstr "⚡" # 適当な文字列に変更する zstyle ':vcs_info:git:*' unstagedstr "±" # 適当の文字列に変更する zstyle ':vcs_info:git:*' formats '%{%F{white}%K{green}%} %s %{%k%f%}%{%F{white}%K{blue}%} %b %{%k%f%}%{%F{white}%K{red}%} %c%u %{%k%f%}' ## バージョン管理システムの情報も表示する autoload -Uz vcs_info ### プロンプトバーの左側 prompt_bar_left_self="%{%F{white}%K{blue}%} %n%{%k%f%}%{%F{white}%K{blue}%}@%{%k%f%}%{%F{white}%K{blue}%}%m %{%k%f%}%{%B%F{red}%K{blue}%}+ %{%b%f%k%}%{%B%F{white}%K{cyan}%} [%~] %{%k%f%b%}%{%k%f%}%(?.%F{white}%K{red}%} COMP %k%f.%B%K{red}%F{red}%} ERROR %b%k%f)%{%F{white}%K{green}%} -%h %{%k%f%}%{%B%F{white}%K{black}%} %D{%Y/%m/%d %H:%M} %{%b%f%k%}" prompt_bar_left="${prompt_bar_left_self} ${prompt_bar_left_status} ${prompt_bar_left_date}" ### プロンプトバーの右側 #prompt_bar_right="-[%{%B%K{magenta}%F{white}%}%d%{%f%k%b%}]-" ### 2行目左にでるプロンプト。 #prompt_left="-[%h]%(1j,(%j),)%{%B%}%#%{%b%} " prompt_left='%{%F{white}%K{yellow}%} $SHELL %{%k%f%}%{%B%F{white}%K{black}%} %# %{%b%k%f%} > ' ## プロンプトフォーマットを展開した後の文字数を返す。 ## 日本語未対応。 count_prompt_characters() { print -n -P -- "$1" | sed -e $'s/\e\[[0-9;]*m//g' | wc -m | sed -e 's/ //g' } ## プロンプトを更新する。 update_prompt() { local bar_left_length=$(count_prompt_characters "$prompt_bar_left") local bar_rest_length=$[COLUMNS - bar_left_length] local bar_left="$prompt_bar_left" # パスに展開される「%d」を削除。 local bar_right_without_path="${prompt_bar_right:s/%d//}" # 「%d」を抜いた文字数を計算する。 local bar_right_without_path_length=$(count_prompt_characters "$bar_right_without_path") # パスの最大長を計算する。 # $[...]: 「...」を算術演算した結果で展開する。 local max_path_length=$[bar_rest_length - bar_right_without_path_length] bar_right=${prompt_bar_right:s/%d/%(C,%${max_path_length}<...<%d%<<,)/} bar_right="%${bar_rest_length}<<${separator}${bar_right}%<<" # プロンプトバーと左プロンプトを設定 PROMPT="${bar_left}${bar_right}"$'\n'"${prompt_left}" # 右プロンプト RPROMPT="%{%F{white}%K{black}%} %l %{%k%f%}%{%F{black}%K{white}%} $LANG %{%k%f%}" case "$TERM_PROGRAM" in Apple_Terminal) # Mac OS Xのターミナルでは$COLUMNSに右余白が含まれていないので # 右プロンプトに「-」を追加して調整。 ## 2011-09-05 RPROMPT="${RPROMPT}" ;; esac # バージョン管理システムの情報を取得する。 LANG=C vcs_info >&/dev/null # バージョン管理システムの情報があったら右プロンプトに表示する。 if [ -n "$vcs_info_msg_0_" ]; then RPROMPT="${vcs_info_msg_0_}${RPROMPT}" fi } ## コマンド実行前に呼び出されるフック。 precmd_functions=($precmd_functions update_prompt) ``` # Vimの設定を考える ` Vim `の欠点といえば、挿入モードとコマンドモードの行き来がめんどくさいという点です。特に、日本語入力の切り替えも相まって、最初はかなり使いづらいエディタなのではないかと思います。それに、キーバインドも、シェルが通常採用している` Emacs `のものではないので、ターミナルを操作してきた人にとっても扱いづらいというものがあります。よって、ここでは、` Vim `のキーバインドを` Emacs `のようにする設定をおすすめしたいと思います。ただし、練習用に一部の操作は残しておきます。 まずは、` MacVim `へのパスを通します。コマンドを入力すると` MacVim `が起動するように` .zshrc `に以下を追記しておきましょう。 ```~/.zshrc export EDITOR=/Applications/MacVim.app/Contents/MacOS/Vim alias vi='env LANG=ja_JP.UTF-8 /Applications/MacVim.app/Contents/MacOS/Vim "$@"' alias vim='env LANG=ja_JP.UTF-8 /Applications/MacVim.app/Contents/MacOS/Vim "$@"' ``` ## Vim 挿入モードで使えるようになるキー ``` C-j //カーソルを上に移動する C-k //カーソルを下に移動する C-h //カーソルを左に移動する C-l //カーソルを右に移動する C-a //特定の記号で止まりながら、行頭へ移動する C-e //特定の記号で止まりながら、行末へ移動する C-S- //範囲指定 C-x //カーソル位置の文字を削除 C-w //カーソル前の単語を削除 C-t //カーソル後の単語を削除 C-u //やり直し C-z //最後に挿入した文字列を挿入 C-d-" //ダブルクオーテーションに囲まれた文字列を削除する S- //ウィンドウの調整 jj //ノーマルモードに移行する ``` ・Up = 上矢印 ・Down = 下矢印 ・Left = 左矢印 ・Right = 右矢印 ・C = Control ・S = Shift ・<Leader> = ¥ ` vim `を起動し、` .vimrc `を編集していきます。以下を追記します。 ```~/.vimrc "--------------------------------------------------------------------------- " [タイトル] " 基本操作 " [評価] " ☆☆☆☆☆ " [参考] " http://archiva.jp/web/tool/how_to_vim_2.html " [説明] " 基本操作に関わる設定です。ここを見ておけば、とりあえず操作できるだろうという設定項目。 "--------------------------------------------------------------------------- " 下に移動 inoremap <C-j> <Down> " 上に移動 inoremap <C-k> <Up> " 左に移動 inoremap <C-h> <Left> " 右に移動 inoremap <C-l> <Right> "行頭へ移動 inoremap <silent> <C-a> <C-r>=MyJumptoBol(' "。、.,/!?「」')<CR> "行末へ移動 inoremap <silent> <C-e> <C-r>=MyJumptoEol(' "。、.,/!?「」')<CR> "カーソル位置から前の単語を削除 inoremap <silent> <C-w> <C-g>u<C-r>=MyExecExCommand('normal! db')<CR> "カーソル位置から後の単語を削除 inoremap <silent> <C-t> <C-g>u<C-r>=MyDeleteWord()<CR> "カーソル後の1文字削除 inoremap <silent> <C-x> <C-g>u<Del> "カーソル以降削除 inoremap <silent> <C-K> <C-g>u<C-r>=MyExecExCommand('normal! D', 'onemore')<CR> "最後に挿入した文字列を挿入 inoremap <silent> <C-z> <C-g>u<C-a> "やり直し inoremap <silent> <C-u> <C-g>u<C-r>=MyExecExCommand('undo', 'onemore')<CR> "挿入モードで範囲選択(セレクトモード) set selectmode=key set keymodel=startsel,stopsel snoremap <C-S-Up> <S-Up><S-Up><S-Up><S-Up><S-Up> snoremap <C-S-Down> <S-Down><S-Down><S-Down><S-Down><S-Down> "カッコで囲まれた文字列を削除する imap <c-d> <esc>di "ウィンドウの調整 nnoremap <silent> <S-Left> :5wincmd <<CR> nnoremap <silent> <S-Right> :5wincmd ><CR> nnoremap <silent> <S-Up> :5wincmd -<CR> nnoremap <silent> <S-Down> :5wincmd +<CR> "ノーマルモードでも Enter で改行する noremap <CR> i<CR><ESC> " jj でノーマルモードに移動する inoremap jj <Esc> "クリップボードを共有する set clipboard=unnamed,autoselect "マウスを有効にする if has('mouse') set mouse=a endif "カーソルを括弧の中に移動 function! MyInsertBracket(lbrackets, rbracket) if index(a:lbrackets, getline('.')[col('.') - 2]) != -1 return a:rbracket . "\<Left>" else return a:rbracket endif endfunction inoremap <expr> ) MyInsertBracket(['('], ')') inoremap <expr> } MyInsertBracket(['{'], '}') inoremap <expr> ] MyInsertBracket(['['], ']') inoremap <expr> > MyInsertBracket(['<'], '>') inoremap <expr> " MyInsertBracket(['"'], '"') inoremap <expr> ' MyInsertBracket(['''', '`'], '''') "--------------------------------------------------------------------------- " [タイトル] " ファンクション関係 " [評価] " ☆☆☆ " [参考] " https://sites.google.com/site/fudist/Home/vim-nihongo-ban/tips/vim-key-emacs " [説明] " ファンクション関係です。あらゆる設定の互換性に関わります。 "--------------------------------------------------------------------------- " 行末でも停止する単語単位移動コマンド function! MyMoveWord_i(cmd) let isEol = 0 if col('$') == col('.') let isEol = 1 endif let prevline = line('.') silent exec 'normal! '.a:cmd if line('.') == prevline return '' endif if a:cmd == 'w' if isEol == 0 call cursor(prevline, 0) call cursor(line('.'), col('$')) endif if line('.') - prevline > 1 call cursor(prevline+1, 0) call cursor(line('.'), col('$')) endif elseif a:cmd == 'b' call cursor(line('.'), col('$')) if prevline - line('.') > 1 call cursor(prevline-1, 0) call cursor(line('.'), col('$')) endif endif return '' endfunction " カーソル以降の単語削除 function! MyDeleteWord() if col('.') == col('$') return '' endif let save_cursor = getpos('.') silent exec 'normal! wge' if save_cursor[1] != line('.') || (save_cursor[2] > col('.')) call setpos('.', save_cursor) return MyExecExCommand('normal! dw', 'onemore') endif silent exec 'normal! v' call setpos('.', save_cursor) return MyExecExCommand('normal! d') endfunction " IMEの状態とカーソル位置保存のため<C-r>を使用してコマンドを実行。 function! MyExecExCommand(cmd, ...) let saved_ve = &virtualedit let index = 1 while index <= a:0 if a:{index} == 'onemore' silent setlocal virtualedit+=onemore endif let index = index + 1 endwhile silent exec a:cmd if a:0 > 0 silent exec 'setlocal virtualedit='.saved_ve endif return '' endfunction " sepが空でなければ、sepをセパレータとしてジャンプ。 " 見つからなければ見かけの行頭へ。 " カーソル位置が見かけの行頭の場合は真の行頭へ。 function! MyJumptoBol(sep) if col('.') == 1 call cursor(line('.')-1, col('$')) call cursor(line('.'), col('$')) return '' endif if matchend(strpart(getline('.'), 0, col('.')), '[[:blank:]]\+') >= col('.')-1 silent exec 'normal! 0' return '' endif if a:sep != '' call search('[^'.a:sep.']\+', 'bW', line(".")) if col('.') == 1 silent exec 'normal! ^' endif return '' endif exec 'normal! ^' return '' endfunction " sepが空でなければ、sepをセパレータとしてジャンプ。 " 見つからなければ行末へ。 function! MyJumptoEol(sep) if col('.') == col('$') silent exec 'normal! w' return '' endif if a:sep != '' let prevcol = col('.') call search('['.a:sep.']\+[^'.a:sep.']', 'eW', line(".")) if col('.') != prevcol return '' endif endif call cursor(line('.'), col('$')) return '' endfunction ``` ## Vim Pluginを使う まずは、プラグイン管理のためのプラグインを導入します。 ``` git clone http://github.com/gmarik/vundle.git ~/.vim/bundle/vundle echo "source ~/.vim/bundle/vundle/test/minirc.vim" >> ~/.vimrc vim ~/.vim/bundle/vundle/test/minirc.vim ``` ` :BundleInstall `と入力すると、プラグインがインストールされます。設定ファイルを分けておくと便利かもしれません。以下に具体例を示します。 ```~/.vimrc source ~/dotfiles/vimrc source ~/dotfiles/vimrc.bundle ``` そして、おすすめなプラグインをインストールしていきます。簡単に説明すると、` .vimrc `という設定ファイルに` Bundle 'user_name/plugin_name' `と追記していき、` :BundleInstall `を実行すると、インストールされます。追記する場所は、` Bundle 'gmarik/vundle' `以下になります。 ```例 Bundle 'gmarik/vundle' "プラグインを管理するプラグインなので必須 " コメント処理を簡単にする Bundle 'scrooloose/nerdcommenter.git' " 自動閉じタグ Bundle 'yuroyoro/vim-autoclose' " visually indent guide Bundle 'nathanaelkane/vim-indent-guides' " XMLとかHTMLとかの編集機能を強化する Bundle 'xmledit' " Align : 高機能整形・桁揃えプラグイン Bundle 'Align' " フィルタリングと整形 Bundle 'godlygeek/tabular' " マルチバイト対応の整形 Bundle 'h1mesuke/vim-alignta' " ヤンクの履歴を管理 Bundle 'YankRing.vim' " undo履歴を追える Bundle 'Gundo' " テキストを括弧で囲む/削除する Bundle 'tpope/surround.vim' " 補完 autocomplpop.vim : insertmodeで自動で補完をpopup " Bundle 'AutoComplPop' " 究極のVim的補完環境 Bundle 'Shougo/neocomplcache' " neocomplcacheのsinpet補完 Bundle 'Shougo/neocomplcache-snippets-complete' " rubyでrequire先を補完する " Bundle 'ujihisa/neco-ruby' " A neocomplcache plugin for English, using look command " Bundle 'ujihisa/neco-look' " スクロールを快適にする Bundle 'Smooth-Scroll' " 単語移動がスマートな感じで Bundle 'smartword' " matchit.vim : 「%」による対応括弧へのカーソル移動機能を拡張 Bundle 'matchit.zip' " ソースコード上のメソッド宣言、変数宣言の一覧を表示 ">Bundle 'taglist.vim' " markdown Bundle 'tpope/vim-markdown' " ツリー型エクスプローラ Bundle 'The-NERD-tree' " ツリー状にファイルやディレクトリの一覧を表示 Bundle 'vtreeexplorer' " エンコーディング Bundle 'banyan/recognize_charcode.vim' " ファイラー Bundle 'Shougo/vimshell.git' " vim Interface to Web API Bundle 'mattn/webapi-vim' " cecutil.vim : 他のpluginのためのutillity1 Bundle 'cecutil' " urilib.vim : vim scriptからURLを扱うライブラリ Bundle 'tyru/urilib.vim' " utillity Bundle 'L9' " Buffer管理のLibrary Bundle 'thinca/vim-openbuf' " vim上のtwitter client ">Bundle 'TwitVim' " Lingrのclient Bundle 'tsukkee/lingr-vim' " vimからGit操作する Bundle 'tpope/vim-fugitive' " ステータスラインを拡張する Bundle 'Lokaltog/vim-powerline' " Unite 関連のプラグイン Bundle 'Shougo/unite.vim' Bundle 'tsukkee/unite-help' Bundle 'h1mesuke/unite-outline' Bundle 'Sixeight/unite-grep' Bundle 'basyura/unite-rails' Bundle 'thinca/vim-unite-history' Bundle 'tsukkee/unite-tag' Bundle 'choplin/unite-vim_hacks' " Twitter Client ~アウトライン表示で分かりやすいTwitterクライアント。 " http://d.hatena.ne.jp/basyura/20111230/p1 Bundle 'basyura/TweetVim' Bundle 'basyura/twibill.vim' Bundle 'basyura/bitly.vim' Bundle 'tyru/open-browser.vim' " Simplenote Client ~クラウドメモ帳のクライアント。ファイル操作が楽々。 " http://bugrammer.g.hatena.ne.jp/nisemono_san/20111107/1320677380 Bundle 'mrtazz/simplenote.vim' " Tagbar ~JavaScriptでも結構使える関数リストを表示する。 " http://extjs.sunvisor.net/325 Bundle 'majutsushi/tagbar' " w3m ~Vim上でブラウジングや調べものをする。 " :W3m google キーワードで検索 Bundle 'yuratomo/w3m.vim' ``` ### パッチを当てPowerLineを有効にする ` Vim `のステータスラインをかっこ良くするには、` PowerLine `というプラグインを使います。このとき、` iTerm2 `でフォントを設定していた場合は、うまく表示されないので、フォントにパッチを当てます。 ``` brew install fontforge brew install python-fontforge wget http://~MyFontFile.ttf && mv MyFontFile.ttf ~/bundle/vim-powerline/fontpatcher ./fontpatcher MyFontFile.ttf cp MyFontFile-Powerline.otf ~/.fonts ``` ココらへんは記憶が曖昧なので、自分の環境によってやり方を色々と変更してください。Macのフォント置き場は` ~/.fonts `だったかな。これはLinuxだったような気がしています。要は、` fontpatcher `スクリプトの場所にフォントを置いて、スクリプトを実行し、できたファイルをフォント置き場に移動するという手順。あとは、iTerm2にてフォントを変更すればいいのです。 # tmuxの設定を考える まずは、クリップボードを共有できるようにしましょう。 ``` mkdir ~/bin && echo "PATH=$HOME/bin:$PATH" >> ~/.zshrc git clone git://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard.git cd ~/tmux-MacOSX-pasteboard make reattach-to-user-namespace cp reattach-to-user-namespace ~/bin vim ~/.tmux.conf ``` ` ~/.tmux.conf `という設定ファイルに以下を追記します。 ```~/.tmux.conf set-option -g default-command "reattach-to-user-namespace -l zsh" ``` ### tmuxのプロンプトを設定する tmuxのプロンプトにいろいろな情報を表示してみます。以前エントリーを書いたので、そちらを引用します。 [tmux の Status-Line をカスタマイズする](http://qiita.com/items/daf224aa331982b8a910) ### Vimでの日本語入力切り替えを快適にする Google IME を使用している場合は、環境設定を行うことで、` Esc `を押すと、IME を off にできます。 |
|
| 445位 |
|
|||
|
14:41:26 |
(Increments inc. 所属) |
|

Facebookで人物を補完すると、その人物名の周りに枠が表示されて強調されますよね(gif画像参照)。 これのやり方を解説します。 # TL;DR textareaの強調表示は、textareaを透明にして後ろにいい感じの背景を設置してるだけ --- ## textareaの中にDOMを入れても表示されない パッと考えるとtextareaの中にDOMツリーを入れるとそれが表示されるんじゃないか、と思うかも知れません ```html:textareaの中に直接DOMを記述してみる <textarea><span>1</span></textarea> ``` が、ぜんぜんそんなことは無くて、そのまま文字列が表示されてしまいます。  ご存知のようにtextareaやinputは、他の要素のように子要素を表示するのではなく、自身のvalue属性の値を画面に表示する働きをします。value属性は文字列を格納するためのものなのでDOMを入れられないわけですね。 ## 強調用のDOMを重ねあわせる textareaにはDOMをそのまま入れられないので、仕方がなく周りに設置したDOMの位置を微調整して重ねあわせることでtextareaが強調されているかのように見せかけているわけです。 このとき問題になるのがtextareaと重ねあわせるDOMのz軸方向の位置関係です。textareaより手前にあるとtextareaを選択できなくなってしまします。一方でtextareaより奥にあると表示されません。 さてどうしたら良いのでしょう? ### background-color: transparentが肝 textareaの背景色を透明にして、後ろにあるDOMが表示されるようにすればよさそうですね。つまりtextareaが `background-color: transparent;` であればよいわけです。 ### いい感じの場所に背景色をつけるにはどうすればいいの? もう一つ考えなければならないのが、どこに背景を設置するかです。やり方はいろいろ考えられますが、後述するサンプル実装の場合は、textareaの文字列の背後に全く同じ透明な文字列を設置して、該当箇所を強調させています。全く同じ文字列を全く同じように背後に置いてあるので、位置がピタリと合う訳ですね。他にもやり方はありそうなので考えてみてください。 ## サンプル で、試しに作ってみました。 - https://github.com/yuku-t/jquery-overlay このプラグインは正規表現で指定された文字列に背景色を付けるシンプルなものです。 非常にシンプルなので、先日公開したtextareaに補完機能をつけるプラグインとも簡単に結合することが可能です。プラガブル万歳\(^o^)/ - http://yuku-t.com/jquery-textcomplete/#textarea3  ## textareaを使うと背景色を変えるくらいしか強調手段がない 上記のサンプルページを表示してみた人は気がついたかも知れませんが、背景色を付ける以外の強調方法を使えません。例えば文字の色を変えたり、イタリックにしたり、太さを変えたり、大きさを変えたりしたくてもできないのです。 理由はtextareaを使っていることです。あくまでも表示されている文字列はtextarea由来のものであり、こいつらの一部の色を変えたり、イタリックにしたりすることができないのは最初に見た通りです(てか、それができるならこんな回りくどいことをしなくてもいいわけで)。 ### contentEditableを使えばなんとかなる? contentEditableは使えないのか?と思う向きもあるかと思います。たしかにその通りでcontentEditableを使うこともできるかも知れません。こちらを使えば文字列の大きさも好き勝手に変えられるので表現力は上です。 が、いかんせん面倒なのでやってない+contentEditable内でのカーソル位置の制御が難しい(JavaScriptから調整不可?)なので、僕は諦めた次第です。 # まとめ textareaの強調表示は、textareaを透明にして後ろにいい感じの背景を設置してるだけ |
|
| 446位 |
|
|||
|
01:56:19 |
|
|
##挨拶
Mac Book Airが完全に使い物にならなくなったので、 Mac Book Pro Retinaに乗り換えると、同時に環境を構築し直さないといけなくなったので、 どうせなら、この記事として残しておこうと思った次第です。 もちろん、この記事はMacでの設定なので他OSユーザーの方は参考程度にしかならないかもです。 ご了承ください。 ##設定 まず、Sublime Text3をダウンロードし、インストールします。 http://www.sublimetext.com/3 起動しましたら、 大抵、最初にやることはPackage Controlを導入することです。 https://sublime.wbond.net/installation#Manual_Instructions こちらのSublime Text3の方をコピーして、Sublime Textに戻り、ターミナル(View->Show Console)を開き実行します。 (2013/10/03 時点) ```import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read()) ``` インストールが終わるとcmd+shift+PでCommand Palletを表示し、package controlが入っていたら成功。 これでパッケージをインストールする準備は出来ました。 ##日本語化 開発環境を日本語化する・しないは分かれますよね。 僕は英語がとても弱いのでほぼ日本語化してます。 ここで日本語化パッケージである、Japanizeをインストールします。 cmd+shift+PでCommand Palletを表示し、install packageを選択。 パッケージリストが出てくるので、japanizeと検索してjapanizeをインストール。 read.meに従って日本語化します。 2013/10/03時点ではWindows向けにread.meが書かれていましたので、Macの場合ですと、 ```\Users\ユーザー名\AppData\Roaming\Sublime Text 3\Packages\Japanize``` が、 ```/Users/ユーザー名/Library/Application Support/Sublime Text 3/Packages/Japanize``` となります。 ##プラグイン ここからはどうカスタマイズするか自由ですが、個人的におすすめのプラグインを紹介していきます。 command + shift + Pでパッケージコントロールが開くので、そこで様々なプラグインをいれていきます。 ###SFTP これで保存した時にrsyncと同様の動きが出来ます。 ・丸ごとダウンロード・アップロード ・ファイルを保存した瞬間に自動アップロード機能 ・ファイルを選んだ瞬間に自動ダウンロード機能 ランダムに出る購入販促アラートが出ますが、僕はあまり気にならないかも。 もし気になる場合は、16ドルで正規版になるので、気に入ったらご購入してみるといいかもです。 ###PHPcs PHPの補完機能。 PHPを使うのならばいれておいて損はないと思う。 ###SublimeCodeIntel 関数定義元にジャンプすることが出来る。 CTags?のが主流らしいが、特に理由なくこっちを利用しています。(あやふやですみません笑) Control + クリックで定義元に飛びます ###BracketHighlighter divとか{}とかの組み合わせを色付けして強調してくれます。 割と便利です。 ###SubLimeLinter コードのエラーチェック・コード規約などを指摘してくれる。 設定がややめんどくさいが、あるとないとじゃ完成した後のコードの奇麗さが段違い。 ##終わり これで最低限の設定は出来ました。 Japanizeのパッケージの様に、様々な便利なパッケージがあり、日々増え続けています。 SFTPやら、JSLintやら本当に便利なパッケージがたくさんです。 僕はSublime Textを使い、Node.jsとPHPのコーディングをしています。 その上で便利だったパッケージ等、次回ご紹介したいと思います。 わからない所、またこう説明したほうがいいんじゃない?という箇所がございましたらご指摘等下さい。 ありがとうございました。 |
|
| 447位 |
|
|||
|
18:46:47 |
|
|
先日、「[M7 と少しだけ戯れてみた](http://qiita.com/griffin_stewie/items/d5ddf41e5aa067f200b9)」というエントリでモーションアクティビティを試しに触ってました。その流れで自分の歩数を定期的に Tweet するようにしてみたらおもしろいかなぁと思って折角なので iOS 7 から導入された Background Fetch を使って見ようと思い、その時の内容をメモとして残してみようと思います。
ここに書く実験内容は Apple の審査を通った実績のあるものではない点をご了承ください。 ## Background Fetch とは * iOS 7 から追加された新しい Background Mode のひとつ * OS 側が不定期(OS の判断で適切だと思われるタイミング)で `- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler` メソッドを Background で呼び出してくれる * 実行される最短インターバルの指定が `setMinimumBackgroundFetchInterval:` メソッドで可能 * OS からの呼び出しタイミングはアプリの利用状況(パターン)を学習した上で決定される というもののようです。 [feedtailor さんの "そら気温" ](http://feedtailor.jp/wp/ "関西/大阪のiPhone・iPadアプリ開発 feedtailor Inc. 社長ブログ")でも使われています。 ## Background Fetch でできること Apple の紹介している例としては、 * SNS 系アプリのタイムラインの事前取得 * News 系アプリの新着情報事前取得 * 天気系アプリの情報取得(このあたりが [feedtailor さんの "そら気温" ](http://feedtailor.jp/wp/ "関西/大阪のiPhone・iPadアプリ開発 feedtailor Inc. 社長ブログ")がやっているところ) * 写真や動画の共有(別の技術 "Background Transfer" との併用) があげられています。 具体的には * Network 通信 * アプリケーションバッジの更新 * Local Notification の発行 * ローカルファイルの生成(UserDafaults の書き換えやおそらく DB 系操作も) などが可能です。 ということで、冒頭でも書いたように Background Fetch が発火したタイミングで現在の歩数累計を Background Fetch で Tweet してました。 ## 実装 必要なの作業は以下の通りです。 1. Bacground Modes を "Fetch" として Info.plist に記述 2. `- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions` で `setMinimumBackgroundFetchInterval:` を設定 3. `- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler` の中身を実装 ### 1. Bacground Modes を "Fetch" として Info.plist に記述 以下のスクリーンショットのように Xcode 5 上で設定  ### 2. `application:didFinishLaunchingWithOptions:` で `setMinimumBackgroundFetchInterval:` を設定 `application:didFinishLaunchingWithOptions:` 内で `setMinimumBackgroundFetchInterval:` を呼びます。ここでは定数の `UIApplicationBackgroundFetchIntervalMinimum` をセットしています。 ``` - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum]; return YES; } ``` ### 3. `application:performFetchWithCompletionHandler:` の中身を実装 ``` - (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { StepCountTweeter *tweeter = [[StepCountTweeter alloc] init]; [tweeter tweetStepCountWithUserName:nil tweetTextBlock:^NSString *(NSInteger numberOfSteps, NSDate *fromDate, NSDate *toDate, NSError *error) { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setLocale:[NSLocale systemLocale]]; [dateFormatter setDateFormat:@"MM/dd HH:mm:ss"]; NSString *text = [NSString stringWithFormat:@"%@ 〜 %@ の期間に iPhone 5s を持って %@ 歩 歩きました。 Tweet By Background Fetch" , [dateFormatter stringFromDate:fromDate] , [dateFormatter stringFromDate:toDate] , [@(numberOfSteps) stringValue]]; return text; } completionBlock:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) { if (error) { /// 失敗時には UIBackgroundFetchResultFailed を渡して completionHandler を呼ぶ completionHandler(UIBackgroundFetchResultFailed); } else { /// 成功時には UIBackgroundFetchResultNewData を渡して completionHandler を呼ぶ completionHandler(UIBackgroundFetchResultNewData); } }]; } ``` `application:performFetchWithCompletionHandler:` の中で具体的に Background Fetch として実現したいことを実装します。約 30 秒の猶予が与えられます。 StepCountTweeter クラスは今回歩数を Tweet するために僕が作ったクラスで SLRequest を使って Tweet するクラスです。Background Fetch でのポイントは `StepCountTweeter` クラスの `tweetStepCountWithUserName:tweetTextBlock:completionBlock:` メソッドの第3引数の completionBlock 内部で `- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler` の completionHandler を呼んでいるところです。この completionHandler は必ず呼ぶ必要があります。 ## Tips ### Background Fetch の有効/無効 アプリによっては、Background Fetch 自体は有効にしたいもの ユーザー設定として Background Fetch での動作を On/Off させたいとかそもそも Background Fetch で実行したい処理でログインが必須などの場合があるかと思います。アプリ側の状況で実際に Background Fetch の細かい On/Off が必要な場合は以下のように `setMinimumBackgroundFetchInterval:` に渡す値で有効 OR 無効の切り替えが行えます。 ``` // 無効にする [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalNever]; // 有効にする [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum]; // もしくは任意の NSTimeInterval ``` ### Background Fetch のデバッグ方法 Background Fetch のデバッグ方法は主に 2 つあります。 1つ目はスキームを分けてビルドのオプションでアプリ終了状態からの Background Fetch を試す方法です。  2つ目は Xcode の Debug メニューから "Simulate Background Fetch" を実行する方法です。この方法の場合にはアプリのプロセスが生きている間に Background Fetch が呼び出されるシチュエーションを試すことができます。  ### 時間のかかる通信処理 30秒の制限がありますが、"Background Transfer" を併用することで時間のかかる通信処理でも実装可能だと思います。 ## 実験 Background Fetch は OS によってスケジューリングされるということなのでどのくらいのスパンで実行されるのかが気になったので簡単に調べてみました。気になったのは * completionHandler に渡す結果によって Background Fetch の実行に影響がでるのか? * `setMinimumBackgroundFetchInterval:` に渡すインターバルの違いで起こる Background Fetch の実行への影響 です。 そこで以下のように値を調整しつつ試してみました。 1. 取得後の結果判定を Fail にしていた インターバルは UIApplicationBackgroundFetchIntervalMinimum 2. 取得後の結果判定を NewData に変更した インターバルは UIApplicationBackgroundFetchIntervalMinimum 3. インターバルを 2時間に広げる ## 実践してみての気付き 取得結果を UIBackgroundFetchResultFailed にしても、UIBackgroundFetchResultNewData しても特に Background Fetch の実行スパンが広がったり縮まったりしているように感じなかった。 UIApplicationBackgroundFetchIntervalMinimum を指定していると * 最短 10 分程度 * 最長 5 時間 くらいのインターバルで動いている感じだった。 インターバルを 2 時間に広げた場合には * 最短 2 時間前後( 2 時間切っている場合もあった) * 最長 22.5 時間 くらいのインターバルで動いている感じだった。 なんとなく、新たにビルド&インストールを行ったあとは Background Fetch が実行されるまで時間がかかるように感じた ## 感想 Apple の話によると Background Fetch はアプリの利用状況に応じて OS 側で呼び出してくれため、今回試してみたサンプルアプリの明示的な起動頻度等が影響しているかもしれないと思いました。アクティブに使われるようなアプリの場合は指定したインターバルに近い頻度で実行されるかもしれないですし、ほぼ起動しないようなアプリの場合は相当間が空くか最悪ほぼ実行されないとかがあるのかも知れないとも思いました。 ## 懸念 実行タイミングを明示的には指定できないもののある程度自由度が高いことができそうです。それ故にいたずらに重い通信処理を Wi-Fi 以外の通信手段の時に行われてしまうことやログ的な情報を送信されたり、広告系の何らかのデータ処理が行われそうで1ユーザーとしては嫌だなぁと思うことがあります。開発者はお行儀の良いユーザーに喜ばれるような実装を心がけて便利で有用なものにしてもらいたいと思います。 |
|
| 448位 |
|
|||
|
14:27:33 |
|
|
ctrlp.vimとは
--------------------------- [ctrlp.vim](https://github.com/ctrlpvim/ctrlp.vim)は多機能セレクタとかそんな感じのvimプラグインです。 環境変わるのが嫌で、カラースキーマやシンタックス関係意外あんまりvimにプラグインとか入れたくない派だったんですが、CtrlPは見た目よくてわかりやすく、結構気にいったので長く使ってます。 似たプラグインとしてはUniteがあります。比較するとUniteの方が高機能でいろいろ出来、他のプラグインと連携も出来ますが設定がやや複雑かなって感じです。 インストール ----------------------- pathogen: 適当なとこにgit clone > git clone https://github.com/ctrlpvim/ctrlp.vim ~/.vim/bundle NeoBundle: .vimrcに書くだけ > NeoBundle "ctrlpvim/ctrlp.vim" 使い方 ----------------------- Ctrl+pを押して適当に希望のファイル名っぽいのを入力すると勝手に絞り込んでくれます。便利。 例えば、railsかなんかを書いているとして、 > view とか打ち込むと、  って感じでview以下だけうまく絞り込めます。 さらにこっからpost以下だけ見たいってときは、 > view/pos とかすると、  と省略して入力してもいい感じに察して絞り込んでくれます。 また、git管理されてるディレクトリの中などで使うとルートフォルダっぽいとこを自動で認識して、そこ以下のファイルを再帰的に探してくれます。 検索にはいくつかのモードがあって、正規表現による絞り込みもできます。 使える正規表現の例とかは :help ctrlp-input-formats を読むのが一番いいです。 キーマッピング ------------------------ :help ctrlp-mappings するのが一番正確です。 デフォルトの設定でよくつかうやつだけ。 ###全般 |キー|動作| |:------|:-------| |Ctrl+p|CtrlP起動| |Ctrl+c,ESC|CtrlPを終了| |Ctrl+d|フルパス検索モードとファイル名のみの検索モードを切り替え| |Ctrl+r|正規表現検索モードと通常の検索モードを切り替え| |Ctrl+f,Ctrl+b|検索対象(バッファとか)を切り替え| ###コマンドラインでの操作 |キー|動作| |:------|:-------| |Ctrl+j|下を選択| |Ctrl+k|上を選択| |Ctrl+a|カーソルを先頭に| |Ctrl+e|カーソルを最後尾に| |Ctrl+u|コマンドラインをクリア| |Ctrl+n,Ctrl+p|コマンド入力履歴を辿る| |Ctrl+\|コマンドラインにいろいろコピペできるダイアログを開く (レジスタとか)| |Tab|ディレクトリ名を補完| ###ファイル操作 |キー|動作| |:------|:-------| |CR(Enter)|現在のウインドウで開く| |Ctrl+t|新しいタブで開く| |Ctrl+v|垂直分割して開く| |Ctrl+s,Ctrl+CR|水平分割して開く| |Ctrl+z|ファイルやディレクトリをマークする| |Ctrl+o|ファイルをダイアログつきで開く。マークしてあるとマークしたファイルを全部開く| |Ctrl+y|新しいファイルを現在のディレクトリに作成。マークしてあるとそのディレクトリに対して作成する| Extensions ------------------------------ これまでの内容にも出てきましたが、CtrlPにはファイルだけでなく、さまざまなものを同じCtrlPインターフェイスで扱うための拡張が標準で付属しています。 標準で付属しているものの一覧は :help ctrlp-extensions にあります。 個人的に便利なやつをいくつか紹介します。 *CtrlPBuffer* バッファセレクタとして使えます。 *CtrlPMRUFiles* これまで開いたファイル履歴から絞り込みます。 *CtrlPMixed* ファイル、バッファ、履歴を一度に絞り込みます。 ぶっちゃけこれで大体足りるといえば足りる。 *CtrlPQuickfix* vimのquickfixと連携出来ます。 :grepとかと組み合わせて使うのがメインかも。 *CtrlPTag* タグ一覧を表示、絞り込みできます。 *CtrlPLine* 現在のファイル内の各行を対象に絞り込みます。unite lineと大体一緒です。 簡易ファイル内grep的に使えます。 *CtrlPDir* ディレクトリを検索してカレントディレクトリを切り替えたりできます。 設定例 ----------------------------- :help ctrlp-options を見るのが(ry デフォルトのコマンドが結構長くてめんどくさいので、よく使うの中心に適当にキー割り当てするのがいいと思います。 自分はsをあんまり使わないのでPrefix扱いにしてます。Ctrl+pは切ってます。 ```vim:.vimrc " Prefix: s nnoremap s <Nop> nnoremap sa :<C-u>CtrlP<Space> nnoremap sb :<C-u>CtrlPBuffer<CR> nnoremap sd :<C-u>CtrlPDir<CR> nnoremap sf :<C-u>CtrlP<CR> nnoremap sl :<C-u>CtrlPLine<CR> nnoremap sm :<C-u>CtrlPMRUFiles<CR> nnoremap sq :<C-u>CtrlPQuickfix<CR> nnoremap ss :<C-u>CtrlPMixed<CR> nnoremap st :<C-u>CtrlPTag<CR> let g:ctrlp_map = '<Nop>' " Guess vcs root dir let g:ctrlp_working_path_mode = 'ra' " Open new file in current window let g:ctrlp_open_new_file = 'r' let g:ctrlp_extensions = ['tag', 'quickfix', 'dir', 'line', 'mixed'] let g:ctrlp_match_window = 'bottom,order:btt,min:1,max:18' ``` Extensionの作成 ------------------------------ 自分でctrlp-extensionを作ることもできます。それほど難しくはないです。 詳しくは[kaneshinさんによる記事](http://kaneshin.hateblo.jp/entry/vim-advent-calendar-2012)がわかりやすいです。 githubなどにいろんな方が作られたctrlp-extensionがあるので、探してみるのもいいと思います。 参考 ------------------------------ *:help ctrlp* [意外と知られていない便利なvimプラグイン「ctrlp.vim」](http://mattn.kaoriya.net/software/vim/20111228013428.htm) [ctrlp.vimを使う](http://qiita.com/ArcCosine@github/items/5ece3f39481f6aab9bc5) ### 2014/8/9追記 ctrlp.vimの開発が https://github.com/kien/ctrlp.vim から https://github.com/ctrlpvim/ctrlp.vim に移ったのでリンクなどを更新しました。 経緯は http://vim-jp.org/blog/2014/08/08/ctrlpvim-not-die.html を参照してください。 |
|
| 449位 |
|
|||
|
19:00:44 |
|
|
とある方の
__"エンターだけで ls と git status 表示するようにしてある"__ というつぶやきを見て便利そうだと思ったのでやってみました。  基本的なやり方は以下の記事が参考になりました。 - [zshでコマンドが空の状態でenter押したときに任意のコマンドを実行する方法 - kei_q](http://d.hatena.ne.jp/kei_q/20110406/1302091565) ただ、上記方法では私の環境の場合、プロンプトが1回余分に表示されてしまって気持ちが悪いので、その辺りも手を加えました。 #Installation 以下スクリプトを ``.zshrc`` に記述すれば、何も入力されていない状態で Enter を打つだけで ``ls`` と ``git status`` が表示されるようになります。 ```zsh:.zshrc function do_enter() { if [ -n "$BUFFER" ]; then zle accept-line return 0 fi echo ls # ↓おすすめ # ls_abbrev if [ "$(git rev-parse --is-inside-work-tree 2> /dev/null)" = 'true' ]; then echo echo -e "\e[0;33m--- git status ---\e[0m" git status -sb fi zle reset-prompt return 0 } zle -N do_enter bindkey '^m' do_enter ``` ``ls`` は以下記事の ``ls_abbrev`` を使用するのがお勧めです。 - [Zsh - chpwd内のlsでファイル数が多い場合に省略表示する - Qiita [キータ]](http://qiita.com/yuyuchu3333/items/b10542db482c3ac8b059) Enter はちょっとやり過ぎという方は別のキーに割り当てると良いでしょう。 #おわり ``ls`` や ``cd`` の入力をどれだけ減らせるかというのは、ターミナル使用者の力量が問われる部分ではないでしょうか。 (最近Twitterで cd後のls や mkdir後のls/cd の話が少し話題になってましたね) そんな私の ``zsh`` の設定は以下にあるので良ければどうぞ。 - [dotfiles/.zsh at master · yonchu/dotfiles](https://github.com/yonchu/dotfiles/tree/master/.zsh) また、良いアイディアやアドバイスなどありましたら教えてください。 |
|
| 450位 |
|
|||
|
18:53:52 |
(Backflip180, LLC. 所属) |
|
「Androidオープンソースライブラリ徹底活用」で紹介されているライブラリのご紹介です。
【書籍情報】 タイトル名:Androidオープンソースライブラリ徹底活用 著者名:八木俊広 出版社名:(株)秀和システム 書籍紹介ページは[こちら](http://www.shuwasystem.co.jp/products/7980html/4002.html) サンプルコードは[こちら](https://github.com/android-opensource-library-56/android-opensource-library-56) # UI関連ライブラリ + [android-support-v4](http://developer.android.com/tools/support-library/index.html) Fragmentなどの機能をAndroid 2.x系でも実現するための公式のサポートライブラリ + [ActionBarSherlock](https://github.com/JakeWharton/ActionBarSherlock) ActionBarをAndroid 2.x系でも利用するための定番ライブラリ + [Android-PullToRefresh](https://github.com/chrisbanes/Android-PullToRefresh) 引き下げて画面を更新。Pull To Refreshを実現するライブラリ + [SlidingMenu](https://github.com/jfeinstein10/SlidingMenu) 横から引き出すメニュー表示を実現するライブラリ + [SwipeListView](https://github.com/47deg/android-swipelistview) ListViewの要素をスワイプしてめくることができるライブラリ + [MultiChoiceAdapter](https://github.com/ManuelPeinado/MultiChoiceAdapter) ListViewで要素の複数選択を可能にするライブラリ + [StickyListHeaders](https://github.com/emilsjolander/StickyListHeaders) ListViewでセクション毎にヘッダを固定できるライブラリ + [android page curl](https://code.google.com/p/android-page-curl/) 本のページをめくるようなエフェクトが可能になるライブラリ + [ViewPagerIndicator](https://github.com/JakeWharton/Android-ViewPagerIndicator) ViewPagerのIndicatorを簡単にカスタマイズできるライブラリ + [NewQuickAction](https://github.com/lorensiuswlt/NewQuickAction) ポップアップメニューを実現するライブラリ + [Android ViewBadger](https://github.com/jgilfelt/android-viewbadger) iOS風のバッジを実現するためのライブラリ + [Android ProgressFragment](https://github.com/johnkil/Android-ProgressFragment) Fragmentでプログレス表示を簡単に実現するライブラリ + [HoloEverywhere](https://github.com/Prototik/HoloEverywhere) ICSのUIテーマであるHoloをAndroid 2.1以上で利用することができるライブラリ + [HoloColorPicker](https://github.com/LarsWerkman/HoloColorPicker) 色を選択するピッカーを実現するためのライブラリ + [aFileChooser](https://github.com/iPaulPro/aFileChooser) 端末内のファイルを選択するためのActivityを提供するライブラリ + [Android Validator](https://github.com/throrin19/Android-Validator) 入力内容のフォーマットチェックを行うライブラリ + [PhotoView](https://github.com/chrisbanes/PhotoView) 画像のズーム、スクロール操作を実現できるライブラリ + [ImageLayout](https://github.com/ManuelPeinado/ImageLayout) 画像の上にViewを配置できるレイアウト + [StyledDialogs](https://github.com/inmite/android-styled-dialogs) DialogFragmentベースのダイアログを簡単に利用できるライブラリ # 画像処理ライブラリ + [GPUImage for Android](https://github.com/CyberAgent/android-gpuimage) 画像にエフェクトをかけるためのライブラリ + [ZXing](https://code.google.com/p/zxing/) バーコードやQRコードを読み取るライブラリ + [svg-android](https://code.google.com/p/svg-android/) SVGを描画するためのライブラリ + [android gifview](https://code.google.com/p/android-gifview/) アニメーションGIFを再生するライブラリ # ネットワーク関連ライブラリ + [Asynchronous Http Client for Android](https://github.com/loopj/android-async-http) 非同期HTTP通信処理を簡単に実現できるライブラリ + [Volley](https://android.googlesource.com/platform/frameworks/volley/) Google公式のHTTP処理用ライブラリ + [Universal Image Loader for Android](https://github.com/nostra13/Android-Universal-Image-Loader) 大量の画像を取り扱う時に便利なライブラリ + [Scribe](https://github.com/fernandezpablo85/scribe-java) 色々なサービスのOAuth認証を手助けしてくれるライブラリ # データ処理ライブラリ + [JsonPullParser](https://github.com/vvakame/JsonPullParser) JSONを逐次解釈するためのライブラリ + [Gson](https://code.google.com/p/google-gson/) JavaオブジェクトとJSONを相互に変換するライブラリ + [dom4j](https://code.google.com/p/dom4j-android/) XML操作の定番ライブラリ + [jsoup](https://github.com/jhy/jsoup/) HTMLをパースするための定番ライブラリ # データベースライブラリ + [greenDAO](https://github.com/greenrobot/greenDAO) Androidに最適化されたDAOを生成するライブラリ + [ActiveAndroid](https://github.com/pardom/ActiveAndroid) アノテーションを使ったO/Rマッパーライブラリ + [SQLCipher for Android](https://github.com/sqlcipher/sqlcipher) SQLiteデータベースのデータを暗号化するライブラリ # 設定系ライブラリ + [UnifiedPreference](https://github.com/saik0/UnifiedPreference) 設定画面の作成をサポートするライブラリ + [DateTimePicker](https://github.com/flavienlaurent/datetimepicker) 使いやすい年月日のピッカー # 地図ライブラリ + [Polaris2](https://github.com/cyrilmottier/Polaris2) Google Maps Android API v2を拡張した地図ライブラリ # ログライブラリ + [android-logging-log4j](https://code.google.com/p/android-logging-log4j/) Androidでlog4jを使うためのライブラリ + [ACRA](https://github.com/ACRA/acra) クラッシュレポートを収集できるライブラリ # テストライブラリ + [Robotium](https://code.google.com/p/robotium/) ブラックボックステストを自動化するライブラリ + [FEST Android](https://github.com/square/fest-android) Android固有のクラスのアサーションを提供するライブラリ + [Mockito](https://code.google.com/p/mockito/) モックオブジェクトを作成できるライブラリ + [Robolectric](https://github.com/robolectric/robolectric) 実機やエミュレータなしでAndroidアプリケーションをテストするためのライブラリ # デバッグライブラリ + [smali](https://code.google.com/p/smali/) dexファイルをディスアセンブルして解析できるライブラリ + [dex2jar](https://code.google.com/p/dex2jar/) dexファイルをclassファイルに変換するツール # アニメーションライブラリ + [NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids) Android 3.0で追加された新しいアニメーションフレームワークをAndroid 2.xでも利用できるようにしたライブラリ + [ListViewAnimations](https://github.com/nhaarman/ListViewAnimations) ListViewの要素を表示したり操作したりする際のアニメーションを実現するライブラリ # グラフ描画ライブラリ + [HoloGraphLibrary](https://bitbucket.org/danielnadeau/holographlibrary) Holoテーマのようなグラフを描画することができるライブラリ # コード最適化ライブラリ + [AndroidAnnotations](https://github.com/excilys/androidannotations) コードの記述量を減らして、開発速度やメンテナンス性を向上させるライブラリ + [Android Query](https://code.google.com/p/android-query/) UI操作や通信処理のコードをシンプルに書けるライブラリ + [RoboGuice](https://github.com/roboguice/roboguice) Google GuiceベースのAndroid用のDIコンテナ + [Butter Knife](https://github.com/JakeWharton/butterknife) Viewのインジェクションに特化したライブラリ # 通知ライブラリ + [Crouton](https://github.com/keyboardsurfer/Crouton) シンプルなトースト表示ライブラリ # Web APIライブラリ + [Twitter4j](https://github.com/yusuke/twitter4j/) Twitter APIの定番ラッパーライブラリ + [Evernote SDK for Android](https://github.com/evernote/evernote-sdk-android) Evernote公式のAndroidライブラリ + [flickrj-android](https://code.google.com/p/flickrj-android/) Flickr APIにアクセスするためのライブラリ |
|
| 451位 |
|
|||
|
19:26:08 |
(XFLAG STUDIO, mixi, Inc. 所属) |
|
vagrant upしたときに
``` [default] The guest additions on this VM do not match the installed version of VirtualBox! In most cases this is fine, but in rare cases it can cause things such as shared folders to not work properly. If you see shared folder errors, please update the guest additions within the virtual machine and reload your VM. Guest Additions Version: 4.2.18 VirtualBox Version: 4.3 ``` のようなメッセージが表示された場合、ゲスト側にインストールされているGuest Additionsが古い。 これをバージョンアップする方法について簡単に解説する。 ## インストール まずは、Vagrantのプラグイン vagrant-vbguest [https://github.com/dotless-de/vagrant-vbguest](https://github.com/dotless-de/vagrant-vbguest ) をインストールする。 ``` $ vagrant plugin install vagrant-vbguest Installing the 'vagrant-vbguest' plugin. This can take a few minutes... Installed the plugin 'vagrant-vbguest (0.9.0)'! ``` こんな感じでインストールが完了する。 ## Guest Additionsのアップデート 以下のコマンドで、Guest Additionsをアップデートする。 ``` $ vagrant vbguest ``` しばらくするとアップデートが完了する。 ``` Installing the Window System drivers[FAILED] (Could not find the X.Org or XFree86 Window System.) An error occurred during installation of VirtualBox Guest Additions 4.3.2. Some functionality may not work as intended. ``` といったメッセージが表示されることがあるが、Xを使っていない場合は無視してよさそう。 ## インストールされたバージョンの確認 ``` $ vagrant vbguest --status GuestAdditions 4.3.2 running --- OK. ``` 以上で、vagrant upしたときにwarningが出るようなことはなくなる(はず)。 Guest Additionsの機能についてはこちらを参考にしてください。 Chapter 4. Guest Additions [http://www.virtualbox.org/manual/ch04.html](http://www.virtualbox.org/manual/ch04.html) ## Vagrantfileに記述する vbguestプラグインをインストールしてある状態で、Vagrantfileにvbguestプラグイン用の設定を記述することができます。 例 ``` Vagrant.configure("2") do |config| config.vbguest.auto_update = false config.vbguest.no_remote = true end ``` この場合は - `config.vbguest.auto_update` `vagrant up`時に自動的にGuest Additionsのアップデートを行わない(trueだと自動アップデートする) - `config.vbguest.no_remote` Guest Additionsのisoファイルをリモートからダウンロードしない。 という設定になります。 詳細は https://github.com/dotless-de/vagrant-vbguest/blob/master/Readme.md に記載されています。 |
|
| 452位 |
|
|||
|
19:58:17 |
(Freelancer 所属) |
|
Core Image や vImage 等、iOS SDK の標準フレームワークの機能が充実してきたとはいえ、コンピュータビジョンの分野においてできることの幅広さではやはり OpenCV に軍配が上がります。そんな OpenCV を iOS アプリに導入する手順です。
下記の公式ドキュメントを参考に、自分でやってみて補足を加えました。 [公式ドキュメント1](http://docs.opencv.org/doc/tutorials/introduction/ios_install/ios_install.html#ios-installation) / [公式ドキュメント2](http://docs.opencv.org/doc/tutorials/ios/hello/hello.html#opencvioshelloworld) / [公式ドキュメント3](http://docs.opencv.org/doc/tutorials/ios/image_manipulation/image_manipulation.html#opencviosimagemanipulation) ##準備編:フレームワークをビルドする ###1. Githubからソースコードを取得 githubからcloneします。[^1] cd ~/<my_working _directory> git clone https://github.com/opencv/opencv.git [^1]: 以前はItseezアカウントにあったのですが、いつの間にかOpenCVアカウントになってました ###2. ビルドの準備 ルートディレクトリに、Xcodeのバンドル内にあるDeveloperフォルダへのシンボリックリンクを作成します。(ビルドスクリプトが参照するため) cd / sudo ln -s /Applications/Xcode.app/Contents/Developer Developer ###3. ビルド ビルドスクリプトを実行します。 cd ~/<my_working_directory> python opencv/platforms/ios/build_framework.py ios ビルドが成功すると、最後に `** INSTALL SUCCEEDED **` と出て、 `~/<my_working_directory>/ios/opencv2.framework` にフレームワークができています。 - ※1: もしスクリプト実行時に `sh: cmake: command not found` というエラーが出る場合は、cmake が入ってないのでインストールが必要です。homebrew の場合は $ brew install cmake でインストールが始まります。 - ※2: pythonのエラーが出た場合はスクリプトを実行する**Pythonのバージョンが合ってない可能性**があります。私はOpenCV 3.1のビルドスクリプトをPython v3.5.1で実行しようとして以下のようなエラーが出ました。 > TypeError: cannot use a string pattern on a bytes-like object pyenvでPython 2.7に切り替えてこのエラーは解消されました。 - ※3: OpenCV 3.1のビルドスクリプトがxcodebuildでコケました。マスターの最新コミット(2016.12.2現在)でも同様。 ###バージョン確認方法 `opencv2.framework/Versions/A/Resources/Info.plist` に書かれているフレームワークバンドルのバージョン=OpenCVのバージョンとなります。 たとえば 3.0.0 の場合は Info.plist はこうなっています。 ``` <key>CFBundleVersion</key> <string>3.0.0</string> <key>CFBundleShortVersionString</key> <string>3.0.0</string> ``` ##導入編:Xcode プロジェクトにフレームワークを追加する ###1. フレームワークを追加 プロジェクトに opencv2.framework を追加します。 ###2. 実装ファイルの拡張子を.mmにする C++ のコードを書くため、実装ファイルの拡張子を .m から .mm に修正します。 ###3. ヘッダファイルをインポート #ifdef __cplusplus #import <opencv2/opencv.hpp> #endif ##実践編:簡単な画像処理を実行してみる ###1. UIImage 型の画像を cv::Mat 型に変換するメソッドを定義 UIImage 型の画像を cv::Mat 型に変換するメソッドを定義します。(cv::Mat は、画像のビットマップデータへのポインタと、幅,高さ,ビット深度などの様々なプロパティを保持するクラスです。) - (cv::Mat)cvMatFromUIImage:(UIImage *)image { CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage); CGFloat cols = image.size.width; CGFloat rows = image.size.height; cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to data cols, // Width of bitmap rows, // Height of bitmap 8, // Bits per component cvMat.step[0], // Bytes per row colorSpace, // Colorspace kCGImageAlphaNoneSkipLast | kCGBitmapByteOrderDefault); // Bitmap info flags CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage); CGContextRelease(contextRef); CGColorSpaceRelease(colorSpace); return cvMat; } ###2. cv::Mat 型の画像を UIImage 型に変換するメソッドを定義 cv::Mat を UIImage に変換するメソッドも定義しておきます。 - (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat { NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()]; CGColorSpaceRef colorSpace; if (cvMat.elemSize() == 1) { colorSpace = CGColorSpaceCreateDeviceGray(); } else { colorSpace = CGColorSpaceCreateDeviceRGB(); } CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); // Creating CGImage from cv::Mat CGImageRef imageRef = CGImageCreate(cvMat.cols, //width cvMat.rows, //height 8, //bits per component 8 * cvMat.elemSize(), //bits per pixel cvMat.step[0], //bytesPerRow colorSpace, //colorspace kCGImageAlphaNone|kCGBitmapByteOrderDefault,// bitmap info provider, //CGDataProviderRef NULL, //decode false, //should interpolate kCGRenderingIntentDefault //intent ); // Getting UIImage from CGImage UIImage *finalImage = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); CGDataProviderRelease(provider); CGColorSpaceRelease(colorSpace); return finalImage; } ###3. グレースケール画像に変換する 色空間変換を行う cvtColor() というメソッドと、上記で実装した UIImage と cv::Mat を相互変換するメソッドを用いて、下記のように UIImage をグレースケールに変換して UIImage として出力する処理を実装できます。 cv::Mat srcMat = [self cvMatFromUIImage:srcImage]; cv::Mat greyMat; cv::cvtColor(srcMat, greyMat, CV_BGR2GRAY); return [self UIImageFromCVMat:greyMat]; ##応用編:漫画カメラ風に写真を加工する 下記記事に、OpenCVを用いて**漫画カメラ風に加工する手順**を書いたので、よろしければご参照ください。 [たったの6ステップ!『漫画カメラ』風に写真を加工するiPhoneアプリの作り方](http://d.hatena.ne.jp/shu223/20121129/1354145960)  |
|
| 453位 |
|
|||
|
10:18:44 |
(Fringe81 所属) |
|
## 数値やbooleanは==で、文字列はequalsで同値比較してしまう
#### before 正しく同値比較できる。ただScalaだと同値比較は==を使ったほうが良いと考える。 ```scala b == true n == 2 x.equals(y) ``` #### after ```scala b == true n == 2 x == y ``` ==を使った方がいい理由は以下2点。 - 統一感 - equalsのレシーバがnullだった場合NullPointerExceptionが出るが、==ならレシーバがnullでもfalseという判定になり例外が出ない ## String#splitはいつもJava版のものを使っている ### before JavaのString#splitの引数はString型。 しかもこれは正規表現パターンとして扱われるので以下の様なケースではエスケープが必要になる。 ```scala val msg = "hello.world" val ary = msg.split("\\.") // Array("hello", "world") ``` ### after 引数がChar型なsplitがStringLike.scalaで用意されている。 今回のような場合はこちらを使うことでエスケープが不要になる。 ```scala val msg = "hello.world" val ary = msg.split('.') // Array("hello", "world") ``` ちなみにExtractorを利用することで、変数宣言と代入を一度に行うことができます。 ```scala val Array(v1, v2) = msg.split('.') ``` ## リスト要素の文字列変換でループ処理を書いてしまう #### before ```scala val sb = new StringBuilder() val l = List("one", "two", "three") for(i <- 0 until l.size) { sb.append(l(i)) if (i != l.size-1) { sb.append(", ") } } println(sb.toString) ``` #### after mkStringを使います。 ※Java8だとString#joinを使います。 ```scala val l = List("one", "two", "three") println(l.mkString(", ")) ``` ## 添字が欲しい時ついfor文を使ってしまう #### before 普段はforeach使ったりmapやfilterなどの関数をバリバリ使うようになってきたのに添字が欲しい時は昔ながらのfor文を書いてしまう。 ```scala for(i <- 0 until members.size) { println(i + " : " + members(i).name) } ``` #### after1 zipWithIndexを使うことで要素と添字のタプルのリストを得ることが出来ます。後続メソッドはforeachでもmapでも必要に応じて。 ```scala members.zipWithIndex.foreach { case (member, i) => println(i + " : " + member.name) } ``` #### after2 for文スタイルでも。 ```scala for((member, i) <- members.zipWithIndex) { println(i + " : " + member.name) } ``` ## mapメソッドをループ処理メソッドと勘違いしている beforeもafterも標準出力される結果は同じではあるのですが。 「map」という単語を辞書で引くと4番目くらいに「写像(数学)」と出てきます。写像処理、分かりやすく言えば変換処理といったところです。map関数は値だけでなく型も変えてよいことになっています。map関数は変換した戻り値を使って後続の処理を継続するというのが基本となります。 ※Java8でも同様のmapメソッドが追加 #### before1 mapメソッドを使っているのに戻り値を取っていないコードを見かけたらリファクタリングの合図です。 ```scala List(1,9,10,3).map { n => if ( n % 3 == 0 ) { println(n + "は3の倍数です。") } } ``` #### after このケースでは3の倍数の値をprintlnしたいだけなのでいわゆる副作用のある処理となります。こういう場合はforeach(戻り値はUnit型)を使います。 ```scala List(1,9,10,3).foreach { n => if ( n % 3 == 0 ) { println(n + "は3の倍数です。") } } ``` ## scala.collection.immutable.Listをjava.util.List相当と思っている Javaプログラミングにおいて、公開メソッドを以下のように宣言することはないと思います。 ```java:java public String foo(ArrayList<User> users) { ``` 通常、Listインタフェースを使って宣言し、利用側に具象クラスの(つまりアルゴリズムの)強制は行わないようにするでしょう。 Javaではこういうところを気をつけている人でもScalaになると次のように書く人が多いように感じます。 ```scala def foo(users: List[User]): String = { ``` 冒頭にあるようにscalaのListをjava.util.List相当と思っている、もしくは色んな本やblogでListがよく使われているからなんとなく使っている、のどちらかではないかと思いますが、scalaのListは、java.util.LinkedList相当です(厳密にはjava.util.LinkedListは双方向リストで、scalaのListは単方向リスト)。ゆえにhead/tail関数の実行はO(1)で速いのですが、ランダムアクセスには弱い構造となっています。 再帰で処理する際はhead/tailメソッドを多用するので関数型スタイルのプログラミングとは相性が良い構造といえます。 よって実際のリスト構造を生成する際、通常はListを使用するようにし、foo関数のような公開関数の型の宣言部にはSeqで定義しておくのが良いでしょう。 ```scala def foo(users: Seq[User]): String = { ``` ## rubyなどでお馴染みの式展開の存在を知らない Scalaでは文字列中の式展開がサポートされています。これをs補完子と呼びます。 ### before ```scala Logger.debug("No." + (i + 1) + " is " + user.name) ``` もしくは ```scala Logger.debug("No.%d is %s".format(i+1, user.name)) ``` ### after こっちの方がスッキリ書けることが多く知っていると重宝します。 s補完子内部ではStringBuilderが使用されています。 ```scala Logger.debug(s"No.${i + 1} is ${user.name}") ``` |
|
| 454位 |
|
|||
|
11:18:59 |
(シナプス株式会社 所属) |
|
# **前書き**
より一般化したものについては **[「ファイルアップロードの例外処理はこれぐらいしないと気が済まない」](http://qiita.com/mpyw/items/939964377766a54d4682)** を参照。ここではそれを元に画像ファイルに限定して、いくつかのパターンで例を構成してみる。また、フォームの送信と受信を同一ファイルで行うとする。 # **サンプル集** ## **1. [exif_imagetype](http://www.php.net/manual/ja/function.exif-imagetype.php) 関数を用いてチェックを行う** これは、画像に関するMIMEタイプを整数として返す関数である。finfoクラスが使えない環境においても統一的にこちらの関数は使えるはず。但し、画像の形式を判別するために必要なだけのバイト数を読み込めない場合にエラーを発生するので、 **[エラー制御演算子](http://www.php.net/manual/ja/language.operators.errorcontrol.php) `@`** を用いて抑制する必要がある。以前ここでは **[getimagesize](http://www.php.net/manual/ja/function.getimagesize.php)** 関数を用いる手法を掲載していたが、こちらもマニュアルには記載されていないだけでエラーを発生するリスクを抱えていたので、抑制がいずれ必要となるならば…ということで一番動作が速いこの関数を採用することにした。  なお、この定数から実際の文字列形式の **MIMEタイプ** や **拡張子** を得るための関数も用意されている。 - **[PHP Manual - image_type_to_mime_type](http://www.php.net/manual/ja/function.image-type-to-mime-type.php)** - **[PHP Manual - image_type_to_extension](http://www.php.net/manual/ja/function.image-type-to-extension.php)** ```html+php <?php if (isset($_FILES['upfile']['error']) && is_int($_FILES['upfile']['error'])) { try { // $_FILES['upfile']['error'] の値を確認 switch ($_FILES['upfile']['error']) { case UPLOAD_ERR_OK: // OK break; case UPLOAD_ERR_NO_FILE: // ファイル未選択 throw new RuntimeException('ファイルが選択されていません'); case UPLOAD_ERR_INI_SIZE: // php.ini定義の最大サイズ超過 case UPLOAD_ERR_FORM_SIZE: // フォーム定義の最大サイズ超過 throw new RuntimeException('ファイルサイズが大きすぎます'); default: throw new RuntimeException('その他のエラーが発生しました'); } // $_FILES['upfile']['mime']の値はブラウザ側で偽装可能なので、MIMEタイプを自前でチェックする $type = @exif_imagetype($_FILES['upfile']['tmp_name']); if (!in_array($type, [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG], true)) { throw new RuntimeException('画像形式が未対応です'); } // ファイルデータからSHA-1ハッシュを取ってファイル名を決定し、ファイルを保存する $path = sprintf('./uploads/%s%s', sha1_file($_FILES['upfile']['tmp_name']), image_type_to_extension($type)); if (!move_uploaded_file($_FILES['upfile']['tmp_name'], $path)) { throw new RuntimeException('ファイル保存時にエラーが発生しました'); } chmod($path, 0644); $msg = ['green', 'ファイルは正常にアップロードされました']; } catch (RuntimeException $e) { $msg = ['red', $e->getMessage()]; } } // XHTMLとしてブラウザに認識させる // (IE8以下はサポート対象外w) header('Content-Type: application/xhtml+xml; charset=utf-8'); ?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>画像アップロード</title> </head> <body> <?php if (isset($msg)): ?> <fieldset> <legend>結果</legend> <span style="color:<?=$msg[0]?>;"><?=$msg[1]?></span> </fieldset> <?php endif; ?> <form enctype="multipart/form-data" method="post" action=""> <fieldset> <legend>画像ファイルを選択(GIF, JPEG, PNGのみ対応)</legend> <input type="file" name="upfile" /><br /> <input type="submit" value="送信" /> </fieldset> </form> </body> </html> ``` もしファイルではなく `$data` に文字列として保持しているデータをチェックしたい場合、以下のようにする。 ```php @exif_imagetype( 'data://application/octet-stream;base64,' . base64_encode($data) ) ``` ## **2. [imagecreatefromstring](http://www.php.net/manual/ja/function.imagecreatefromstring.php) 関数から全てPNGとして保存する** 環境によって差があるが、PHPのGDライブラリは `GIF` `JPEG` `PNG` に加えて `BMP` `XPM` に対応している場合もある。ここではこれら全てを自動判別できる **[imagecreatefromstring](http://www.php.net/manual/ja/function.imagecreatefromstring.php)** 関数を利用する。この関数はリソース生成失敗時にエラーを発生するため、1番目の例と同様に **[エラー制御演算子](http://www.php.net/manual/ja/language.operators.errorcontrol.php) `@`** を用いて抑制する必要がある。 ```html+php <?php if (isset($_FILES['upfile']['error']) && is_int($_FILES['upfile']['error'])) { try { // $_FILES['upfile']['error'] の値を確認 switch ($_FILES['upfile']['error']) { case UPLOAD_ERR_OK: // OK break; case UPLOAD_ERR_NO_FILE: // ファイル未選択 throw new RuntimeException('ファイルが選択されていません'); case UPLOAD_ERR_INI_SIZE: // php.ini定義の最大サイズ超過 case UPLOAD_ERR_FORM_SIZE: // フォーム定義の最大サイズ超過 throw new RuntimeException('ファイルサイズが大きすぎます'); default: throw new RuntimeException('その他のエラーが発生しました'); } // GD画像リソースの生成を試みる if (!$img = @imagecreatefromstring(file_get_contents($_FILES['upfile']['tmp_name']))) { throw new RuntimeException('有効な画像ファイルを指定してください'); } // ファイルデータからSHA-1ハッシュを取ってファイル名を決定し、保存する if (!imagepng($img, sprintf('./uploads/%s.png', sha1_file($_FILES['upfile']['tmp_name'])))) { throw new RuntimeException('ファイル保存時にエラーが発生しました'); } $msg = ['green', 'ファイルは正常にアップロードされました']; } catch (RuntimeException $e) { $msg = ['red', $e->getMessage()]; } // リソースを解放 if (isset($img) && is_resource($img)) { imagedestroy($img); } } // XHTMLとしてブラウザに認識させる // (IE8以下はサポート対象外w) header('Content-Type: application/xhtml+xml; charset=utf-8'); ?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>画像アップロード</title> </head> <body> <?php if (isset($msg)): ?> <fieldset> <legend>結果</legend> <span style="color:<?=$msg[0]?>;"><?=$msg[1]?></span> </fieldset> <?php endif; ?> <form enctype="multipart/form-data" method="post" action=""> <fieldset> <legend>画像ファイルを選択</legend> <input type="file" name="upfile" /><br /> <input type="submit" value="送信" /> </fieldset> </form> </body> </html> ``` ## **3. 複数の画像ファイルを受け取ってそれぞれリサイズして保存する** ```html <input type="file" name="upfile[]"> <input type="file" name="upfile[]"> ``` のように受け取った時、例えば各項目の `error` は ```php $_FILES['upfile'][0]['error'] $_FILES['upfile'][1]['error'] ``` になると思われがちだが、実際には ```php $_FILES['upfile']['error'][0] $_FILES['upfile']['error'][1] ``` となることに要注意。なお、ここでは実際にサイズ取得が必要なので **[getimagesize](http://www.php.net/manual/ja/function.getimagesize.php)** 関数を利用することにする。 ```html+php <?php if (isset($_FILES['upfile']['error']) && is_array($_FILES['upfile']['error'])) { // 各ファイルをチェック foreach ($_FILES['upfile']['error'] as $k => $error) { try { // 更に配列がネストしていれば不正とする if (!is_int($error)) { throw new RuntimeException("[{$k}] パラメータが不正です"); } // $_FILES['upfile']['error'][$k] の値を確認 switch ($error) { case UPLOAD_ERR_OK: // OK break; case UPLOAD_ERR_NO_FILE: // ファイル未選択 continue 2; case UPLOAD_ERR_INI_SIZE: // php.ini定義の最大サイズ超過 case UPLOAD_ERR_FORM_SIZE: // フォーム定義の最大サイズ超過 throw new RuntimeException("[{$k}] ファイルサイズが大きすぎます"); default: throw new RuntimeException("[{$k}] その他のエラーが発生しました"); } // $_FILES['upfile']['mime']の値はブラウザ側で偽装可能なので // MIMEタイプを自前でチェックする if (!$info = @getimagesize($_FILES['upfile']['tmp_name'][$k])) { throw new RuntimeException("[{$k}] 有効な画像ファイルを指定してください"); } if (!in_array($info[2], [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG], true)) { throw new RuntimeException("[{$k}] 未対応の画像形式です"); } // 画像処理に使う関数名を決定する $create = str_replace('/', 'createfrom', $info['mime']); $output = str_replace('/', '', $info['mime']); // 縦横比を維持したまま 120 * 120 以下に収まるサイズを求める if ($info[0] >= $info[1]) { $dst_w = 120; $dst_h = ceil(120 * $info[1] / max($info[0], 1)); } else { $dst_w = ceil(120 * $info[0] / max($info[1], 1)); $dst_h = 120; } // 元画像リソースを生成する if (!$src = @$create($_FILES['upfile']['tmp_name'][$k])) { throw new RuntimeException("[{$k}] 画像リソースの生成に失敗しました"); } // リサンプリング先画像リソースを生成する $dst = imagecreatetruecolor($dst_w, $dst_h); // getimagesize関数で得られた情報も利用してリサンプリングを行う imagecopyresampled($dst, $src, 0, 0, 0, 0, $dst_w, $dst_h, $info[0], $info[1]); // ファイルデータからSHA-1ハッシュを取ってファイル名を決定し、保存する if (!$output( $dst, sprintf('./resized/%s%s', sha1_file($_FILES['upfile']['tmp_name'][$k]), image_type_to_extension($info[2]) ) )) { throw new RuntimeException("[{$k}] ファイル保存時にエラーが発生しました"); } $msgs[] = ['green', "[{$k}] リサイズして保存しました"]; } catch (RuntimeException $e) { $msgs[] = ['red', $e->getMessage()]; } // リソースを解放 if (isset($msg) && is_resource($img)) { imagedestroy($img); } if (isset($dst) && is_resource($dst)) { imagedestroy($dst); } } } // XHTMLとしてブラウザに認識させる // (IE8以下はサポート対象外w) header('Content-Type: application/xhtml+xml; charset=utf-8'); ?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>画像アップロード</title> </head> <body> <?php if (!empty($msgs)): ?> <ul> <?php foreach ($msgs as $msg): ?> <li style="color:<?=$msg[0]?>;"><?= htmlspecialchars($msg[1], ENT_QUOTES, 'UTF-8') ?></li> <?php endforeach; ?> </ul> <?php endif; ?> <form enctype="multipart/form-data" method="post" action=""> <fieldset> <legend>画像ファイルを選択(GIF, JPEG, PNGのみ対応)</legend> <ul> <?php for ($i = 0; $i < 10; $i++): ?> <li><input type="file" name="upfile[]" /></li> <?php endfor; ?> </ul> <input type="submit" value="送信" /> </fieldset> </form> </body> </html> ``` |
|
| 455位 |
|
|||
|
21:31:32 |
|
|
8/19担当、Qiita初投稿の[@inamiy](https://github.com/inamiy)です、こんにちは。
今回は、iOS/OSXアプリのUXの根幹である「Core Animation」について、 ハマりやすい点や、意外と知られていないtipsなどについて書きたいと思います。 ## アニメーション完了時のコールバック Delegateでif分岐しながら処理する方法もありますが、 `[CATransaction setCompletionBlock:]`を使う方が、より見通しの良いコードになります。 問題は、アニメーションが正常終了したかどうかの`finished`フラグがないという点ですが、 下記のようなコードを書くと、上手く判別できます。 ``` [CATransaction begin]; [CATransaction setCompletionBlock:^{ CAAnimation* animation = [layer animationForKey:@"myAnimation"]; if (animation) { // -animationDidStop:finished: の finished=YES に相当 [layer removeAnimationForKey:@"myAnimation"] // 後始末 } else { // -animationDidStop:finished: の finished=NO に相当 } }]; CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"position.x"]; animation.duration = 3; animation.byValue = [NSNumber numberWithDouble:100.0]; // completion処理用に、アニメーションが終了しても登録を残しておく animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; [layer addAnimation:animation forKey:@"myAnimation"]; [CATransaction commit]; ``` もし、`CATransaction`で毎回wrapするのが気に入らない場合は、 animationのdelegateオブジェクトを保持してblocksを実現する方法もあります。 [CAAnimationBlocks](https://github.com/xissburg/CAAnimationBlocks)というライブラリを使えば、`CAAnimation`毎に、 `-setCompletion:`でblockを登録することができます。 * https://github.com/xissburg/CAAnimationBlocks ## アニメーションを一時中断する [Technical Q&A QA1673: How to pause the animation of a layer tree](https://developer.apple.com/library/ios/#qa/qa2009/qa1673.html)が参考になります。 (下記例は CALayerのカテゴリで実装しています) ``` @implementation CALayer (TheWorld) - (void)pauseAnimations { CFTimeInterval pausedTime = [self convertTime:CACurrentMediaTime() fromLayer:nil]; self.speed = 0.0; // 時よ止まれ self.timeOffset = pausedTime; } - (void)resumeAnimations { CFTimeInterval pausedTime = [self timeOffset]; self.speed = 1.0; // そして時は動き出す self.timeOffset = 0.0; CFTimeInterval timeSincePause = [self convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; self.beginTime = timeSincePause; } ``` ここで、`CAAnimation`の`beginTime`は通常、 親の`CAAnimationGroup`の時間軸におけるオフセットとして各animationに対して設定しますが、 上記例のようにグループを使わない場合は、レイヤー自体が親になるので、 `CACurrentMediaTime()`を使ってレイヤー時間に変換して計算する必要があります。 ## fillModeについて 普段、何気なく使っている(かもしれない)下記のおまじない ``` おまじない // よく分からないけど、アニメーションが終了してもちゃんと残る animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; ``` ですが、これも`CAAnimationGroup`を使って考えると分かりやすいです。 例えば、 ``` CABasicAnimation* animation = [CABasicAnimation animationForKeyPath:@"position.x"]; animation.beginTime = 3; animation.duration = 5; animation.byValue = [NSNumber numberWithDouble:100.0]; CAAnimationGroup* group = [CAAnimationGroup animation]; group.animations = [NSArray arrayWithObject:animation]; group.duration = 10; [layer addAnimation:group forKey:@"myGroupAnimation"]; ``` を実行した場合、t=3〜8秒の間はアニメーションが実行されて表示されますが、 0〜3秒と8〜10秒は特に指定がなく、あいまいな状態になっています。 この場合、`animation.fillMode`の設定によって、 * kCAFillModeRemoved ・・・表示しない(デフォルト) * kCAFillModeForwards・・・8〜10秒が終状態として表示される * kCAFillModeBackwards・・・0〜3秒が始状態として表示される * kCAFillModeBoth・・・backwards+forwards * kCAFillModeFrozen・・・(OSX 10.5でDeprecated) となります。 なので、もしgroupを使わず、かつanimationの`beginTime`がデフォルトの0の (前節のように、がんばって変換して代入でもしない)場合、 fillModeにforwards以外を設定する意味は特にないので、 基本的に上記おまじないを「使う」か「使わない」かの2択で考えて良いでしょう。 (使う場合は、あとでしっかり後始末しましょう) ## 暗黙的アニメーションが内部でやっていること Appleのドキュメント[CALayer Class Reference](https://developer.apple.com/library/mac/#documentation/graphicsimaging/reference/CALayer_class/Introduction/Introduction.html)によると、 暗黙的アニメーション(implicit animation)を実行したとき、 内部では`CALayer`の`-actionForKey:`が呼ばれ、 下記の順番で`id <CAAction>`オブジェクト(実質`CAAnimation`)を探索します。 1. layer.delegateの`-actionForLayer:forKey:` 2. `layer.actions` 3. `layer.style` 4. `[CALayer defaultActionForKey:]` (途中で`[NSNull null]`が来ると探索をストップして`nil`扱いになる) もし、`id <CAAction>`オブジェクトが見つかった場合、`-runActionForKey:object:arguments:`が実行され、 これが ```objectivec: // selfは(id <CAAction>)オブジェクト、objectはlayer [object addAnimation:self forKey:actionKey]; ``` 相当のコードを実行します。 ここで、Core Animationの特殊な挙動として、 もし`CAPropertyAnimation` (`CAAnimation`のサブクラス)に `keyPath`が設定されないまま`-addAnimation:forKey:`が実行された場合、 そこで使われた**animationKeyがそのままkeyPathに代入される**ので、例えば、 ``` layer.opacity = 0.5 ``` という暗黙的アニメーションを実行したら、 `layer.animationKeys`に`opacity`キーが登録されつつ、opacity値を変化させるアニメーションが実行されます。 ## 暗黙的アニメーションをデフォルトでOFFにする 毎回、`[CATransaction setDisableActions:YES]`を書くのは面倒なので、 前節の手順のどこかで`[NSNull null]`を渡してあげると良いです。 その場で簡単にセットできるのは、2.の方法になります。 ```おまじない2 layer.actions = [NSDictionary dictionaryWithObjectsAndKeys: [NSNull null],@"onOrderIn", // visibleになったときのフェードイン [NSNull null],@"onOrderOut", // invisibleになったときのフェードアウト [NSNull null],@"sublayers", [NSNull null],@"contents", [NSNull null],@"position", [NSNull null],@"bounds", [NSNull null],@"transform", nil]; ``` なお、UIViewがwrapしているレイヤー(`view.layer`)が暗黙的アニメーションをしないのは、1.の方法によるもので、 `view.layer.delegate`(=`view`)が`-actionForLayer:forKey:`内で常に`NSNull`を返すためです。 ## カスタムプロパティをanimatableにする `CALayer`のサブクラスを作り、`+needsDisplayForKey:`、 `-actionForKey:`、`-drawInContext:`をオーバーライドして、 再描画を繰り返すと、animatableに見せることができます。 ``` @implementation MyLayer + (BOOL)needsDisplayForKey:(NSString*)key { if ([key isEqualToString:@"angle"]) { return YES; } return [super needsDisplayForKey:key]; } - (id <CAAction>)actionForKey:(NSString*)key { if ([key isEqualToString:@"angle"]) { CABasicAnimation* animation = [CABasicAnimation animation]; animation.duration = 1; // デフォルトを通常(0.25)より少し遅めにセット return animation; } return [super actionForKey:key]; } - (void)drawInContext:(CGContextRef)context { // angle値に応じて描画する } @end ``` ## おわりに(ライブラリ紹介) 以前、Core Animationを使って、起動画面(スプラッシュ)を好きなようにトランジションさせながら アプリを開始する簡単なクラスを書いたので、良かったら使ってみてください。 * https://github.com/inamiy/YISplashScreen その他にも、アニメーションまわりの良ライブラリを、いくつか紹介します。 * https://github.com/warrenm/AHEasing * https://github.com/dominikhofmann/PRTween * https://github.com/nicklockwood/iCarousel * https://github.com/mpospese/MPFoldTransition 下2つは特に有名ですね。ぜひ、お時間のあるときにソースを読んでみましょう。 では、良い月曜日を! |
|
| 456位 |
|
|||
|
21:40:37 |
(COOKPAD Inc. 所属) |
|
こんにちは、7日の予定が2日ほど遅延しております、すいません。
さて、私は OS X で ruby 1.9.3 の環境を構築するネタで書きたいと思います。 ## 今回使う物 * homebrew * readline * libyaml (2.0.0なら不要) * openssl * (trunk をビルドする場合) autoconf * rbenv * ruby-build * Command Line Tools (Xcode) 尚、環境は Xcode 4.5 + OS X Mountain Lion を想定しています。 ## 準備 最近の Xcode, 4.3 からは Xcode.app のなかに Developers ディレクトリが入ったためターミナルから gcc とか叩いても 見つからないと言われます。なので Command Line Tool をインストールしましょう。 Command Line Tool, 入れる方法は 2 通りあります: * Xcode の環境設定からダウンロードしてインストールする * [developer.apple.com の Downloads](https://developer.apple.com/downloads/index.action) (要 Apple ID でのログイン) から dmg を取ってくる 後者の方が Obj-C とか使って OS X, iOS 開発しない人には容量的にお得だと思います。私は Xcode も入れて dmg から CLI tools 入れてるけど。 C コンパイラ等コマンドラインのツール、Ubuntu / Debian でいう `build-essentials` パッケージみたいなのが欲しい場合は Xcode.app を入れずに CLI tools だけ直接DLしてインストールすれば容量の節約になります。 ### homebrew パッケージマネージャとして [homebrew](http://mxcl.github.com/homebrew/) をインストールします。 何故 homebrew が必要かというと、openssl を入れるためです。OS X に添付された openssl は何かがおかしいようで、例えば `net/https` 使おうとすると SEGV したりする事があるようです。 ``` text $ /usr/bin/ruby -e "$(/usr/bin/curl -fsSL https://raw.github.com/mxcl/homebrew/master/Library/Contributions/install_homebrew.rb)" ``` [mxcl/homebrew installation page](https://github.com/mxcl/homebrew/wiki/installation) にあるようにインストールします。スクリプトが不安なら URL 踏んで確認するといいと思います。 `/usr/local` 下にデフォルトではインストールされるみたいです。私は `~/brew` に入れるため `git clone https://github.com/mxcl/homebrew.git ~/brew` したりしています。 #### openssl を入れる OS X に標準添付されている openssl は何かがおかしいのか、SEGV を引き起こす事があるため homebrew で入れ直します。 ``` text $ brew install openssl ``` 終わり。 #### readline を入れる OS X 標準添付の readline は readline もどきで、実態は libedit とかいう別の物です。readline もあわせて入れてしまいましょう。 ``` text $ brew install readline ``` #### libyaml を入れる 標準添付ライブラリの yaml パーサーの psych に必要な物です。ruby-build が入れてくれた気がしますが brew で入れています。trunk とか ruby-build 使わずに build してるというのもあるけど。 ``` text $ brew install libyaml ``` ただし、2.0.0 には libyaml がバンドルされているので入れなくても大丈夫です。 #### autoconf を入れる 開発版, trunk (HEAD) をインストールする時に必要です。(使わないなら入れなくても問題ない) ``` text $ brew install autoconf ``` (autoconf も Xcode 4.3 で消えた…) ### rbenv + ruby-build を入れる ruby のバージョン管理システムとして [rbenv](https://github.com/sstephenson/rbenv) を使い、 自動で ruby をビルドするために [ruby-build](https://github.com/sstephenson/ruby-build) を使います。 rvm という選択肢もありますが、[rbenv の README](https://github.com/sstephenson/rbenv#readme) にも書いてあるように以下の点で rvm はトラブルの元になりやすいです。 * __rvm でインストールした ruby を使うためにはシェルに rvm のスクリプトを読み込んでおく必要があります。__ rbenv はスクリプトを読み込まずとも ~/.rbenv/shims 等に PATH を通せば使えます。cron 等で楽ですね。 * __cd 等のコマンドをオーバーライドしません。__ たぶん rvm の一番のトラブルメーカーでしょう。 * __設定ファイルがない!__ どの ruby を使いたいか以外、rbenv の設定がない。複雑じゃなくて良い。 * __ruby をインストールする機能がない。__ rvm とは違って自分で `./configure` して `make`, `make install` する事もできます。[ruby-build](https://github.com/sstephenson/ruby-build) で当然自動インストールもできます。 (自分でtrunkビルドしたり configure オプション弄ったりしてる人には重宝) * __gemset がないです。__ rvm の gemset は [bundler](http://gembundler.com/) が普及した今もはや必要無いと思う... これもトラブルメーカーかな * __黒魔術使ってないので特に考えずに使えます__ rvm は rvm への互換性のためになんか考慮する必要があるらしい? まあ、`$PATH` に入れるだけで使えるのは強み。 * __ディレクトリ推移した時にバージョンを切り替えても警告しません。__ cd とかオーバーライドされるより自分はマシと考えるけどどうなんだろう。 とりあえず rvm がゴミという事を理解していただけたとは思います。入れましょう。 homebrew でも入れられるらしいけど自分で git clone して持ってくるのが速いでしょう。 ``` text $ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv $ mkdir -p ~/.rbenv/plugins $ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build ``` の後、以下の二行をお手持ちのシェルの rc ファイルに書きます (zshrc とか bashrc とか。zsh と bash 以外で動くかは知らないです) ``` text export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)" ``` で、シェルを再起動するか `source ~/.zshrc` とかするか上の二行をシェルでも実行するかして反映させておきます。 ## Ruby を入れる 2.0.0 か 1.9.3 を入れます。1.8.7 は既に通常のバグフィックスリリースが終了していますし入れる理由はないんじゃないでしょうか。 というのは半分以上冗談じゃなくて、Xcode 4.3 から gcc が消え、llvm-gcc もしくは clang (どちらも LLVM 利用) の2つしかコンパイラが用意されていません。 ちなみにそれまではいつのまにか gcc が llvm-gcc へのシンボリックリンクになり、gcc は gcc-4.2 と指定しないと呼び出せなくなった。そして消えた。 で、残念ながら llvm-gcc, clang 両方で 1.8.7 のビルドは厳しいようで、入れるなら Xcode 4.2 持ってくるか osx-gcc-installer 使うか自分で gcc ビルドするかする必要があります。 (最適化を切ったりすれば行けるのかもしれんが、まぁ今更入れる理由なんて無いよね。素直に 1.9 を使おう!) 記憶してる限り 1.9.3 では clang, llvm-gcc 両方で普通にビルド可能で、1.9.2 だと llvm-gcc だとなんかおかしい、という感じです。clang 安定ですね。 尚、OS X Lion では未だに標準バンドルされている ruby が 1.8.7 です。残念ですね... Mountain Lion でどうなっているのか知りたい。 ### ruby-build を使う ``` text $ RUBY_CONFIGURE_OPTS="--enable-shared --with-readline-dir=$(brew --prefix readline) --with-openssl-dir=$(brew --prefix openssl) --with-libyaml-dir=$(brew --prefix libyaml)" rbenv install 1.9.3-p448 $ RUBY_CONFIGURE_OPTS="--enable-shared --with-readline-dir=$(brew --prefix readline) --with-openssl-dir=$(brew --prefix openssl)" rbenv install 2.0.0-p0 $ rbenv shell 1.9.3-p448 ``` ### 自分でビルドする prefix に `$HOME/.rbenv/versions/1.9.3-p448` のように指定してインストールします。versions の下に作るディレクトリ名は自分で決められて、その名前がバージョン切替時とかに渡す 名前になる。`$HOME/.rbenv/versions/foo` なら `rbenv shell foo` とかで切り替える事になる。 (trunk をインストールする例。rbenv でも 2.0.0-dev でインストールできるけど...) ``` text $ git clone https://github.com/ruby/ruby.git $ cd ruby $ autoconf $ mkdir build $ cd build $ ../configure --prefix=$HOME/.rbenv/versions/trunk --with-readline-dir=$(brew --prefix readline) --with-openssl-dir=$(brew --prefix openssl) $ make -j5 $ make install $ rbenv shell trunk ``` ## Tips ### だいたいのところで使えるようにしておく OS X なら `/etc/paths` に `/Users/<ユーザ名>/.rbenv/shims` を先頭に書いておけば だいたいの場面で自動で ruby やインストールしたコマンドへ `$PATH` が通るのでお勧めです。(`rbenv` コマンドへは通らないのでシェルの設定はあったほうが良い) ### rbenv rehash 注意として、`gem install` などでコマンドがインストールされても `rbenv rehash` しないといけません。(ただ、下手にラッピングされるよりマシ) ``` text $ gem i rails $ rails -v zsh: command not found: rails $ rbenv rehash $ rails -v Rails 3.2.3 ``` ## まとめ __rvm 捨てろ__ |
|
| 457位 |
|
|||
|
21:43:51 |
|
|
なにかのコマンドを実行した時に
``` Agreeing to the Xcode/iOS license requires admin privileges, please re-run as root via sudo. ``` というエラーが表示されることがあります。 こういうときは、まあメッセージにある通りなんですけど Xcode のライセンスに同意する必要があるので、Xcodeを起動して出てきたダイアログの "Agree" を押せばOKです。  コマンドライン上からやりたいときは `sudo xcrun cc` です。 ``` $ sudo xcrun cc WARNING: Improper use of the sudo command could lead to data loss or the deletion of important system files. Please double-check your typing when using sudo. Type "man sudo" for more information. To proceed, enter your password, or type Ctrl-C to abort. Password: You have not agreed to the Xcode license agreements. You must agree to both license agreements below in order to use Xcode. Hit the Enter key to view the license agreements at '/Applications/Xcode.app/Contents/Resources/English.lproj/License.rtf' ``` Enterを押すとライセンスが表示されて、最後に ``` By typing 'agree' you are agreeing to the terms of the software license agreements. Type 'print' to print them or anything else to cancel, [agree, print, cancel] ``` と聞かれるので指示通り ```agree``` と入力すればOKです。 |
|
| 458位 |
|
|||
|
15:34:48 |
|
|
# turbolinks について
Rails 4 から turbolinks という機能がデフォルトで bundle されるようになった。 http://kray.jp/blog/must-know-about-turbolinks/ ページ遷移をすべて ajax でやってくれるが、割りとおせっかい機能。 しかも jQuery と競合するから jquery-turbolinks を gem で入れることで回避するとかやめてほしい。 こういう機能はぜひともプラグインで提供していただきたい。 # turbolinks を切る さっそく turbolinks をオフにする。 ## Rails project 新規作成 turbolinks が bundler で最初に install されてしまうので、一旦 --skip-bundle をつけて skip ```sh:Zsh % rails new turbolinks_off --skip-bundle % cd turbolinks_off ``` ## Gemfile から turbolinks を外す Gemfile の turbolinks 行を comment out する。 ```ruby:Gemfile # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks # gem 'turbolinks' ``` ## application.html.erb から turbolinks を削除する ### 削除前 ```erb:application.html.erb <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> <%= javascript_include_tag "application", "data-turbolinks-track" => true %> ``` ### 削除後 ```erb:application.html.erb <%= stylesheet_link_tag "application", media: "all" %> <%= javascript_include_tag "application" %> ``` ## application.js から turbolinks を削除する application.js のみを script タグで読んだはずが、 asset pipeline で turbolinks が呼ばれるようになっているので削除する。 ```JavaScript:app/assets/javascripts/application.js //= require jquery //= require jquery_ujs //= require turbolinks <- 削除 //= require_tree . ``` ## bundle install 準備が整ったので bundle install ```sh:Zsh % bundle install ``` ## turbolinks 削除完了 これで無事 turbolinks を削除できた :) 既存のプロジェクトでも同じ場所を削除・編集して bundle install すれば削除できるはず。 |
|
| 459位 |
|
|||
|
19:59:09 |
(mixi 所属) |
|
## はじめに
 Rubyをあまりさわったことない初心者向けの内容です。 細かいところは置いておいて、とりあえずRSpecでテスト書く→コード実装のような、超シンプルなチュートリアルをまとめてみました。 - RSpecはポピュラーなテストフレームワーク - https://github.com/rspec/rspec - DSLとかでフォーマットされてて、自然言語(英語)っぽくテストが書ける - [David Chelimsky氏](https://github.com/dchelimsky)のブログでもチュートリアルがある(※2007年のエントリ) - [an introduction to RSpec - Part I](http://blog.davidchelimsky.net/blog/2007/05/14/an-introduction-to-rspec-part-i/) - 本なら[The RSpec Book](http://www.amazon.co.jp/dp/4798121932)もあるよ!!4,410円。 ## 下準備 ### install ``` $ gem install rspec ``` ### rspec -- init ``` $ rspec --init create spec/spec_helper.rb create .rspec ``` - [差分](https://github.com/luckypool/testing-with-rspec/commit/873c199a3d22a8d113bb488082ed9e46eea8c718) ## シンプルなチュートリアル 以下の差分はこちら: https://github.com/luckypool/testing-with-rspec ### (1) テスト対象を決める - 今回は `/lib/dog.rb` とかを想定する - まだコードは書かなくてOK - どんなclassかは決めておく。 ### (2) テストファイルをつくる - こんな感じ `spec/lib/dog_spec.rb` - \<name_of_spec\>_spec.rb というように `_spec.rb` というsuffixつける ### (3) 最初のテストコード - [差分](https://github.com/luckypool/testing-with-rspec/commit/c70d81959dcce678d39d9ef43582bd251d0a5244) ```ruby:spec/lib/dog_spec.rb require "spec_helper" describe "Dog" do it "is named 'Pochi'" end ``` - name が 'Pochi' であるインスタンスが生成されると期待を込めて。 - 実行結果は・・・ ```bash $ rspec spec/lib/dog_spec.rb * Pending: Dog is named 'Pochi' # Not yet implemented # ./spec/lib/dog_spec.rb:4 Finished in 0.00025 seconds 1 example, 0 failures, 1 pending ``` - 未実装でペンディングされてるよ!って結果になりました。 ### (4)対象モジュールを読み込んだ上でペンディングする - [差分](https://github.com/luckypool/testing-with-rspec/commit/d323a2e187bb05252f4e69b8397f3d15916b4cda) ```ruby:lib/dog.rb class Dog end ``` - まだ空っぽです ```ruby:spec/lib/dog_spec.rb require "spec_helper" require "dog" describe Dog do it "is named 'Pochi'" end ``` - 実際にモジュールを読み込んでみます。 - 実行結果は・・・ ``` $ rspec spec/lib/dog_spec.rb * Pending: Dog is named 'Pochi' # Not yet implemented # ./spec/lib/dog_spec.rb:5 Finished in 0.00035 seconds 1 example, 0 failures, 1 pending ``` - まだペンディング中! ### (5)期待通りに落ちるテストを書く - [差分](https://github.com/luckypool/testing-with-rspec/commit/5a4d879c2595c42a1757e3fd4c99cea658de9ed1) ```ruby:spec/lib/dog_spec.rb require "spec_helper" require "dog" describe Dog do it "is named 'Pochi'" do dog = Dog.new expect(dog.name).to eq 'Pochi' end end ``` - 英語っぽくテストがかけますね! - 追記。上記の例では expect を使っていますが、[コメントで頂いたような書き方](#comment-e03f791bd58e4a654e91)もできます! - [参考](#2-4)に追記したエントリや、まわりのRubyistに学んでシャレオツなコードを書きたいですね :) - 実行結果は・・・ ``` $ rspec spec/lib/dog_spec.rb F Failures: 1) Dog is named 'Pochi' Failure/Error: expect(dog.name).to eq 'Pochi' NoMethodError: undefined method `name' for #<Dog:0x007fea60987400> # ./spec/lib/dog_spec.rb:7:in `block (2 levels) in <top (required)>' Finished in 0.00031 seconds 1 example, 1 failure Failed examples: rspec ./spec/lib/dog_spec.rb:5 # Dog is name 'Pochi' ``` - ちゃんと落ちました。 ### (6)テストが通るように実装する - [差分](https://github.com/luckypool/testing-with-rspec/commit/f68636c6980b5a1dde060aa6430c3a898dfd1333) ```ruby:lib/dog.rb class Dog attr_accessor :name def initialize(name="Pochi") @name = name end end ``` - 実行結果はもちろん・・ ``` $ rspec spec/lib/dog_spec.rb . Finished in 0.00122 seconds 1 example, 0 failures ``` - パスしましたね! :) ### (7)テスト追加して実装を繰り返す - こんな感じでテストを追加してきましょう - [itじゃなくてxitとやってもpendingになります](https://github.com/luckypool/testing-with-rspec/commit/0fb2968747124c3680ff34c1c9cb48ad2b653c48) - [落ちるテストを追加して](https://github.com/luckypool/testing-with-rspec/commit/83c5cc71f8b82e2a11a81a3d63bae64fa1649495) - [テストが通るように実装](https://github.com/luckypool/testing-with-rspec/commit/1306817503fdcd7bf8bf7645608218d4b73d7df6) ```ruby:spec/lib/dog_spec.rb require "spec_helper" require "dog" describe Dog do it "is named 'Pochi'" do dog = Dog.new expect(dog.name).to eq 'Pochi' end it "has fangs" do dog = Dog.new expect(dog.fangs).to eq 2 end it "is alived" do dog = Dog.new expect(dog).to be_alived end end ``` ```ruby:lib/dog.rb class Dog attr_accessor :name, :fangs def initialize(name="Pochi") @name = name @fangs = 2 end def alived? true end end ``` - グリーン!! ``` $ spec spec/lib/dog_spec.rb ... Finished in 0.00249 seconds 3 examples, 0 failures ``` ## 参考 - [Better Specs](http://betterspecs.org/jp/) - [Rubyist Magazine - 改めて学ぶ RSpec](http://magazine.rubyist.net/?0035-RSpecInPractice) - [RSpec の入門とその一歩先へ](http://d.hatena.ne.jp/t-wada/20100228) ### Expectations テストコードは `expect(hogehoge).to be_true` なんて風に書けます。 他にも色々と表現できるのですが、詳しくは下記のREADMEに載っています。 - https://github.com/rspec/rspec-expectations あと、`should` という expectation もありますが、`expect`の方が新しいものだそうです。 - [RSpecのshouldはもう古い!新しい記法expectを使おう!](http://qiita.com/awakia/items/d880250adc8cdbe7a32f) - [should and should_not syntax](https://github.com/rspec/rspec-expectations/blob/master/Should.md) に説明があります。 - *By default, both `expect` and `should` syntaxes are available. In the future, the default may be changed to only enable the `expect` syntax.* - 新規テストには expect を使いたいですね :) ---- 以上! |
|
| 460位 |
|
|||
|
14:00:32 |
(株式会社ずんシステム 所属) |
|
http://yudoufu.hatenablog.jp/entry/2014/02/06/001440
↑この記事に触発されてスクリプト化してみた。 ```bash:grabtty.sh #!/bin/bash # 既存プロセスの標準出力と標準エラーを奪う http://bit.ly/1neWKth # ターゲットのPID pid=$1 [[ -d /proc/$1/fd ]] || exit 1 # 出力先はttyやファイルを指定 out="$2" # 出力先の指定がない場合は現在のttyを出力先にする if [[ -z $out ]]; then # プロセスに紐付いたttyを取得する http://bit.ly/1eDpRpu function search_tty() { local pid=${1:-$$} tty="" while [[ 1 -lt $pid ]]; do [[ -d /proc/$pid/fd ]] || break tty=$(readlink /proc/$pid/fd/1 2>/dev/null | awk '{print $1}') [[ -c $tty ]] && { echo "$tty"; return 0; } pid=$(perl -pe 's/\(.*\)/()/' /proc/$pid/stat | awk '{print $4}') done return 1 } out=$(search_tty) fi [[ -n $out ]] || exit 1 [[ -e $out ]] || touch "$out" [[ -e $out ]] || exit 1 # gdbスクリプトを作る tmpfile="/tmp/$(basename -- "$0").tmp.$$" ( echo "p close(1)" echo "p open(\"$out\", 1)" echo "p close(2)" echo "p open(\"$out\", 1)" echo "detach" echo "quit" ) > "$tmpfile" # ターゲットプロセスに対してgdbスクリプト実行 gdb -p "$pid" -x "$tmpfile" # 後始末 rm -f "$tmpfile" ``` 出力を奪いたいプロセスIDを指定して↓こんなふうに使える。 ```bash ./grabtty.sh 12345 ``` 出力先をファイルにしてもOK ```bash ./grabtty.sh 12345 /tmp/output ``` この場合は`tail -F`とかで見てると増えてくのが確認できます。 # 参考 - [コンソールから切れたプロセスを標準出力につなげなおす - 絶品ゆどうふのタレ](http://yudoufu.hatenablog.jp/entry/2014/02/06/001440) - [Linux - プロセスに紐付いたttyを取得する - Qiita [キータ]](http://qiita.com/kawaz/items/bd33fe1e29876939dddb) |
|
| 461位 |
|
|||
|
16:04:34 |
(Drivemode, inc. / DroidKaigi 所属) |
|
## Java の列挙型
Java の列挙型は、C 言語の enum と異なり、単なる int の定数列挙ではなく、オブジェクトの列挙になるので、メソッドを宣言したり、メンバ変数を宣言したりと、振る舞いを持たせることが出来る。 内部的には、列挙したオブジェクトは定数として扱われるので、列挙の数だけオブジェクトが予め生成されることになる。 詳細は Effective Java を読もう! ## 振る舞う enum 通常の enum の宣言は以下のようになる。 ```java public enum MyEnum { HOGE, FUGA; } ``` これにメンバ変数を追加すると、 ```java public enum MyEnum { HOGE(1), FUGA(2); private final int id; private MyEnum(final int id) { this.id = id; } } ``` コンストラクタは必ず private でなければならず、それ以外のアクセス修飾子とするとコンパイルエラーとなる。 コンストラクタの呼び出しは列挙の宣言のところで行う。 さらにメソッドを追加して、 ```java public enum MyEnum { HOGE(1), FUGA(2); private final int id; private MyEnum(final int id) { this.id = id; } public int getId() { return id; } } ``` これで、以下のようにメソッド呼び出しが行えるようになる。 ```java int hogeId = MyEnum.HOGE.getId(); int fugaId = MyEnum.FUGA.getId(); ``` メソッドの引数に列挙型オブジェクトを受けるようにしておけば ```java public void doSomething(MyEnum num) { num.getId(); } ``` とか出来るので、switch 文で分岐して振る舞いを変えるようなコードがスッキリする。 enum もクラスの一種なので、インタフェースを実装したり、抽象メソッドを宣言したりできる(別のクラスの継承と、宣言した enum を別のクラスで継承することはできない)。 ```java public enum MyEnum implements SomeInterface { HOGE(1), FUGA(2); private final int id; private MyEnum(final int id) { this.id = id; } public int getId() { return id; } } ``` 抽象メソッドを宣言した場合、定数オブジェクトの宣言のところで実装を書く。 ```java public enum MyEnum { HOGE(1) { @Override public void foo() { } }, FUGA(2) { @Override public void foo() { } }; private final int id; private MyEnum(final int id) { this.id = id; } public int getId() { return id; } public abstract void foo(); } ``` enum はもともと Comparable インタフェースと Serializable インタフェースを実装している。 ## 列挙型のメソッド enum 型には特別なメソッドが幾つか用意されている。 列挙した定数オブジェクトをすべて得るには、`Enum#values()`メソッドを使う。 ```java public enum MyEnum { HOGE(1), FUGA(2); private final int id; private MyEnum(final int id) { this.id = id; } // id から、定数オブジェクトを逆引きするメソッド public static MyEnum valueOf(int id) { // values() で、列挙したオブジェクトをすべて持つ配列が得られる for (MyEnum num : values()) { if (num.getId() == id) { // id が一致するものを探す return num; } } throw new IllegalArgumentException("no such enum object for the id: " + id); // Null-Object パターンにしたがって、列挙に UNKNOWN みたいなのを入れておくのも良い // return UNKNOWN; } public int getId() { return id; } } ``` String から enum への変換は予め用意されている。 ```java MyEnum hoge = MyEnum.valueOf("HOGE"); ``` enum から String への変換もある。 `Enum#toString()`はオーバライドが可能で、自分の好きな文字列にすることが出来る。 `Enum#name()`はオーバライドができないようになっている。宣言した通りの名前が得られる。 ```java String hoge1 = MyEnum.HOGE.toString(); String hoge2 = MyEnum.HOGE.name(); ``` `Enum#ordinal()`で、宣言された順番も得ることが出来る。 宣言された順番をコンパイラが勝手に検出してコードにしてくれるので、当然のことながら、順番を入れ替えるとかえってくる数字も変わってしまうので、かえってくる数字そのものを何処か別のところで比較したり使ったりすると、せっかくの型安全性も台無しになるので、あんまり使わないでね。 ```java int order = MyEnum.HOGE.ordinal(); ``` |
|
| 462位 |
|
|||
|
16:52:57 |
(コロプラ 所属) |
|
アニメーションさせるにはいくつか方法があるみたい。 * `beginAnimations:context:`メソッドを使う * `animateWithDuration:animations:`メソッドを使う * `CATransaction`を利用する方法 * `CABasicAnimation`を利用する方法 -------------------------- ##`animateWithDuration:animations:`を使う方法 `animateWithDuration:animations:`メソッドはクラスメソッドとして実行する。 (細かく指定する場合は`animateWithDuration:delay:options:animations:`メソッドを使う) blockでコールバックが実行できるので、こちらのほうが簡単。 ちなみにイメージとしては 1. `animateWithDuration:animations:`クラスメソッドの`animations:`に変更したい値をblock経由で渡す 2. 渡された情報を元に、最終的なアニメーション終了後の値が設定される 3. アニメーション実行 4. `completion:`で受け取ったblockを実行して終了 という流れ。 ###サンプルコード ```objc //UIViewを生成 UIView *animView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)]; [animView setBackgroundColor:[UIColor redColor]]; [UIView animateWithDuration:1.0f delay:0.5f options:UIViewAnimationOptionCurveEaseInOut animations:^ { //アニメーションで変化させたい値を設定する(最終的に変更したい値) animView.frame = CGRectMake(0, 0, 320, 100); } completion:^ { //完了時のコールバック }]; ``` ####optionsに指定できるオプション * UIViewAnimationOptionCurveEaseInOut * UIViewAnimationOptionCurveEaseIn * UIViewAnimationOptionCurveEaseOut * UIViewAnimationOptionCurveLinear -------------------------- ##`beginAnimations:context:`を使う方法 ※コメントで指摘いただきました。 `beginAnimations:context:`メソッドの`context`は主にユーザデータとして利用するもののようです。 最初は`CGContextRef`を渡していましたが、これは必要ありません。 こちらもUIViewのクラス・メソッドで実行する。 手順としては <del>1. `CGContextRef`型のコンテキストを取得</del> 1. UIViewの`beginAnimations:context:`メソッドでアニメーション実行の開始を宣言 2. `setAnimationDuration:`メソッドでアニメーション時間を設定 3. `setAnimationDelegate:`メソッドでアニメーションイベントのデリゲート先を設定 4. `setAnimationDidStopSelector:`メソッドで終了時のデリゲートメソッドを指定 5. <del>コンテキストに登録したい</del> viewの変更をそれぞれ設定 6. `commitAnimations`メソッドでアニメーションを実行 要はコンテキスト作って、その間に設定されたviewの変更はアニメーションされるようになる、ってことですね。 ###サンプルコード ```objc UIView *animView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)]; //アニメーション開始のコンテキストを設定 [UIView beginAnimations:nil context:nil]; //アニメーションの実行時間を設定(例では2秒) [UIView setAnimationDuration:2.0]; //アニメーションイベントを受け取るデリゲート先 [UIView setAnimationDelegate:self]; //アニメーション終了後に実行されるコールバック [UIView setAnimationDidStopSelector:@selector(endAnimation)]; //アニメーションの詳細 animView.frame = CGRectMake(0, 0, 320, 100); //...以下、アニメーションしたいものを列挙していく。 //アニメーション開始 [UIView commitAnimations]; ``` -------------------------- ##`CATransaction`を使う 上記ふたつはそれぞれUIViewに実装されたメソッドでした。 `CATransaction`は、CALayerに実装されたメソッドです。 ただ、基本的な概念は上記で説明したように、アニメーションの開始を宣言する部分と実際にどういう値に変化させたいかを指定することになります。 サンプルコードを見比べてもらえれば、UIViewに対する操作とCALayerに対する操作という違いがあるだけで、基本的な部分が一緒だということが分かるかと思います。 ```objc //CALayerを取得 CALayer *layer = animView.layer; //CoreAnimationのアニメーション開始宣言 [CATransaction begin]; [CATransaction setAnimationDuration:5.0]; //どう変化させたいのかの変化を指定 layer.frame = CGRectMake(0, 0, 200, 300); //CATransactionを入れ子にすることによって複数のアニメーションを実行可能 [CATransaction begin]; [CATransaction setAnimationDuration:1.0]; layer.opacity = 0.5; //CALayerのアニメーションを開始 [CATransaction commit]; [CATransaction commit]; ``` ###アニメーションを中止する 上記のアニメーションを実行中に、それを中止するには以下のメソッドを呼べば中止されます。 ```objc [layer removeAllAnimations]; ``` -------------------------- ##`CABasicAnimation`を使う CABasicAnimationクラスを使うことで、キーフレームアニメーションを実装できます。 CABasicAnimationを使う手順は、アニメーションさせたいプロパティ名をNSStringで与え、それに対してどうアニメーションさせるか、というのを指定します。 ###サンプルコード ```objc //なにがしかのUIViewからlayerを取得 CALayer *layer = sampleView.layer; //CABasicAnimationオブジェクトを生成 //サンプルでは"position"をアニメーションさせる例。 //他に、transformを変更したい場合は@"transform.translate.x"などと指定する CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; //アニメーション時間 animation.duration = 2.5; //アニメーションの繰り返し回数 animation.repeatCount = 1; //アニメーションの開始時間 animation.beginTime = CACurrentMediaTime() + 2; //2秒後から開始 //アニメーション終了時に、逆方向にもアニメーションさせるか animation.autoreverses = NO; //YESにした場合はアニメーションが往復する //アニメーションのイージングを制御 animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; //アニメーションの開始と終了の値を設定 animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)]; animation.toValue = [NSValue valueWithCGPoint:CGPointMake(320, 480)]; //アニメーション終了時、元の状態に戻すか否かの設定(サンプルではアニメーション後はそのまま) animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; //アニメーションキーを設定(任意の名前) [layer addAnimation:animation forKey:@"sample-animation"]; ``` ###複数アニメーションを組み合わせる アニメーションを組み合わせるには`CAAnimationGroup`クラスを利用します。 ####サンプルコード ```objc //ひとつめのアニメーション CABasicAnimation *animation1 = [CABasicAnimation animationWithKeyPath:@"position.x"]; //終点を設定 animation1.toValue = @50.0f; //ふたつめのアニメーション CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"position.y"]; animation2.fromValue = @0.0f; animation2.toValue = @55.0f; //アニメーショングループを作成 CAAnimationGroup *group = [CAAnimationGroup animation]; //アニメーショングループの設定 group.duration = 3.0; group.repeatCount = 1; group.removedOnCompletion = NO; group.fillMode = kCAFillModeForwards; //グループにアニメーションを追加 group.animations = @[animation1, animation2]; [layer addAnimation:group forKey:@"group-animation-test"]; ``` HTMLを使っている人には、CSSによるアニメーションをベースに考えるとイメージしやすいかもしれません。 -------------------------- ##UIImageViewを使ってスプライトアニメーション UIImageViewにはスプライトアニメーションを簡単に行える方法があります。 まず簡単にフローを書くと、 1. UIImageインスタンスをスプライトアニメーションで使用する分だけ生成(10コマなら10個) 2. 生成したUIImageをNSMutableArrayインスタンスの配列として格納 3. NSMutableArrayを、UIImageViewの`animationImages`プロパティにセット 4. UIImageViewの`animationDuration`、`animationRepeatCount`を設定する 5. UIImageViewの`startAnimating`メソッドでアニメーション開始 そもそもプロパティでスプライトアニメーション用のものが存在していて、それに設定していく、という流れです。 ###サンプルコード ```objc //スプライトアニメーションを表示するUIImageViewを生成 UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; //スプライトアニメーション用画像配列 NSMutableArray *imageList = [NSMutableArray array]; //コマ数分UIImageを生成 for (NSInteger i = 1; i < 10; i++) { NSString *imagePath = [NSString stringWithFormat:@"hoge_%d.png", i]; UIImage *img = [UIImage imageNamed:imagePath]; [imageList addObject:img]; } [self.view addSubview:imageView]; //スプライトアニメーション用画像をセット imageView.animationImages = imageList; //アニメーションする時間 imageView.animationDuration = 2.0; //アニメーションのリピート回数 imageView.animationRepeatCount = 5; //アニメーション開始 [imageView startAnimating]; ``` ##参考記事 * [CoreAnimation CATransaction](http://mm-workmode.blogspot.jp/2011/11/coreanimation-catransaction.html) * [CABasicAnimationの基本的な使い方(移動・回転・拡大・縮小)](http://www.objectivec-iphone.com/animation/CoreAnimation/CABasicAnimation.html) |
|
| 463位 |
|
|||
|
23:33:06 |
(IHANet 所属) |
|
皆さん、Rubyでコマンドライン引数のパース、どう書いてますか。
```rb:very_boring_and_waste_of_time_option_parsing.rb require 'optparse' params = {} opt = OptionParser.new opt.on('-a') {|v| params[:a] = v } opt.on('-b') {|v| params[:b] = v } opt.on('-c file_name') {|v| params[:c] = v } opt.parse!(ARGV) p params ``` ```shell-session % ./very_boring_and_waste_of_time_option_parsing.rb -a -c /tmp/file {:a=>true, :c=>"/tmp/file"} % ./very_boring_and_waste_of_time_option_parsing.rb -h Usage: very_boring_and_waste_of_time_option_parsing [options] -a -b -c file_name ``` だいたい、こんな感じですよね。 引数を3つ取るだけのこの処理、12行を要していますね。 「12行」のカウントには空行も入っているけど、こんな長い塊、空行を入れないと読めたもんじゃないから、やっぱり12行は必要です。 こういうの書いていると、こんな感じな気分になってきて、だんだん憂鬱になってきませんか。 * いちいちブロックを書く必要があるのが、もう、超うっとうしい * しかも、ブロックの中身はだいたい同じだし * オプションが増えるたびに、似たような行を増やさないといけないし * だいたい、「あ、変数名、paramsから変えようかな」とか思っただけでもう死にたくなるし * こんなの覚えてられないから、結局、毎回マニュアル見るはめになる。超だるい * かといって、この程度の処理にgemを使うのもだるいし * こんな単純作業みたいなコーディング強いられるの、Rubyっぽくないよね それにしても、もう少し、気軽に書けないものでしょうか。できれば標準ライブラリの機能だけで。 そんな素敵な方法、実は、もうあります。 では、`ARGV.getopts`の登場です。 ```rb:its_the_ruby_way.rb require 'optparse' params = ARGV.getopts('abc:') p params ``` ```shell-session % ./its_the_ruby_way.rb -a -c /tmp/file {"a"=>true, "b"=>false, "c"=>"/tmp/file"} % ./its_the_ruby_way.rb -h Usage: its_the_ruby_way [options] -a -b -c VAL ``` うおおおおおおおおおおおおおおおおおおおおおおおおお 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! 12行が3行に爆縮!! ヒャッハーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー ソースコード 行数 圧縮率 なんと 驚異の 75%!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ```rb:Ruby include EmotionInCodeMarkupDSL declaration do '世 界 よ' / こ れ が / %q<<<<<<<<<<<<<<<<<<<<< Rubyだッ!!!!!!!!!!!!!!!!!!!!!!! >>>>>>>>>>>>>>>>>>>>> end ``` # FAQ ## 長いオプションも使いたいんだけど 一応、できます。 ```rb:its_the_ruby_way.rb require 'optparse' params = ARGV.getopts('abc:', 'alpha', 'brabo', 'charlie:/tmp/default_file') p params ``` ```shell-session % ./its_the_ruby_way.rb --alpha -c /tmp/hoge {"a"=>false, "b"=>false, "c"=>"/tmp/hoge", "alpha"=>true, "brabo"=>false, "charlie"=>"/tmp/default_file"} ``` 長い版のオプション(‘--charlie’)には、デフォルト値も指定できます。 短い版のオプション(‘-a’とか)と、長い版のオプション(‘--alpha’とか)の同一視とか、気の利いた機能はないようですが、まぁ、些事と言えましょう。 ## ハッシュのキーは文字列? シンボルにできないの? はい。 あきらめましょう。 ## ヘルプ、もうちょっとなんとかならないの? はい。 あきらめましょう。 とはいえ、`--help`の結果をコピペして、ヘルプを出すメソッドをでっちあげておくなりすれば、たいてい間に合うでしょう。書き捨てのつもりのスクリプトとか、そういう場合は特に。 ## そんなの、マニュアルに書いてあったっけ? optparseのマニュアルをよく見ると、じつは書いてあります。下の方に。 * http://docs.ruby-lang.org/ja/2.1.0/library/optparse.html#argv |
|
| 464位 |
|
|||
|
12:28:19 |
(SI Agency 所属) |
|
意外と知られていない? 使われていない? xibの解説です。やっぱりUI関連はxib(Storyboard)がとても便利です。 コードから読み込み ---  これをこのように読み込みます。 ```objc - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. UINib *objectsNib =[UINib nibWithNibName:@"Objects" bundle:nil]; NSArray *objects = [objectsNib instantiateWithOwner:nil options:nil]; NSLog(@"%@", objects); // NSBundleから読み込むほうが簡単 NSLog(@"%@", [[NSBundle mainBundle] loadNibNamed:@"Objects" owner:nil options:nil]); return YES; } ``` --- 2017-06-20 追記 上記パフォーマンスの観点から `UINib` がいいと[コメントいただきました。](http://qiita.com/ryotapoi/items/3e3485baed79842c51f2#comment-8839c5f0e3313fe2c25d) ありがとうございます🙏 --- `instantiateWithOwner:options:`の戻り値はxibのObjectsの内容です。 xibを読み込むとObjectsが取得できます。(Objectsはクラスではなくインスタンス) 更に同時に以下の様なことも行われます。 * Attributes Inspectorで指定した値等の設定 実際には`setProperty:`されるのではなく`initWithCoder:`でオブジェクトが読み込まれているようです。xibはインスタンスをシリアライズしたものを保存しているんですねきっと。 * Action/Outletの接続 オブジェクトのシリアライズだけでは表現できない、他のオブジェクトとの関連も設定してくれます。 * UIViewの構造の生成 addSubview:やsetFrame:、それにAutoLayout関連の設定等も行ってくれます。 Files Owner --- Files Ownerとは、xibで生成されず、外部から与えられます。外部から与えられますので **Attributes Inspectorで設定することはできません**。 しかし **Action/Outletの接続はできます**  Classの設定は、Files Ownerとして与えられる予定のクラスが何でAction/Outletで何が接続できるかを判断するためのものとなります。  Attributesには何もでません。 コードで指定する時はこうなります。 ```objc - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. UIViewController *controller = [[UIViewController alloc] init]; UINib *objectsNib =[UINib nibWithNibName:@"Objects" bundle:nil]; NSArray *objects = [objectsNib instantiateWithOwner:controller options:nil]; NSLog(@"%@", objects); return YES; } ``` initWithNibName:bundle: --- UIViewControllerでxibを使う場合が多いと思いますが、`initWithNibName:bundle:`は **自身をOwnerとしてxibを読み込み初期化する** メソッドとなります。 xibの読み込みにUIViewControllerは不要 --- 通常xibを使うのはUIViewControllerの生成に関連する部分が多いため、UIViewControllerが必須と思っている方もいらっしゃるかもしれません。ですが上記のようにそれが必要なわけではありません。 `setFrame:`や`setBackgroundColor:`等のviewの組み立てコードを外に追い出し、`addTarget:action:forControlEvents:`のようなコードも追い出して、xibをロードするだけで全て済んだカスタムビューを取り出すといったことができます。 最近はnibをUITableViewに設定すると自動でそれを使ってくれるなど色々便利で、直接xibを読み込むことは少なくなったのですが…知っていれば役に立つ場面もまだあると思います。 その他 --- Storyboardはxibの集合と、xib同士の関連をひとまとめにしたものです。Storyboardを複数作って云々…という手もありますが、ちょっとした部品をメインのStoryboardとは別に作りたいならxibいいですよ。 |
|
| 465位 |
|
|||
|
15:59:10 |
(株式会社ずんシステム 所属) |
|
コマンドのパスを知りたいんじゃなく、コマンドの存在をチェックしたいだけなら`which`より`hash`を使ったほうが良いかもっていう話。→追記: `type`が最強っぽい。
# 比較してみる ## whichよりhashよりtypeが高速→typeの勝ち whichは実ファイルという実体があるプログラムです。hashとtypeはbashの組み込みコマンドです。なので当然ですがプログラムの起動コストがない分hashやtypeの方が速いです。 ```bash $ time bash -c 'for((i=0;i<10000;i++));do which perl; done >/dev/null' real 0m7.739s user 0m2.928s sys 0m5.923s $ time bash -c 'for((i=0;i<10000;i++));do hash perl; done' real 0m0.633s user 0m0.240s sys 0m0.391s $ time bash -c 'for((i=0;i<10000;i++));do type perl; done >/dev/null' real 0m0.603s user 0m0.168s sys 0m0.434s ``` 1万回回してますがhashやtypeはwhichの10倍以上速い。そして僅差ですがtypeの方がhashより速いっぽいです、これは何回やっても同じくらい速い。ってことでtypeの勝ち。 ## 探せるものの違い→typeの勝ち コマンドとして使えそうなものとしては、プログラム・組み込みコマンド・エイリアス・関数・あとはifなどの予約後があり、whichとhashとtypeでは探せるものに違いがあります。 | | 例 |which|hash|type| |:----------------|:----------------------------|:---:|:--:|:--:| |プログラム | /usr/bin/perl | ○ | ○ | ○ | |組み込みコマンド | cd, alias, read, hash, ... | | ○ | ○ | |エイリアス | alias ll='ls -l' | ○ | | ○ | |関数 | f(){ echo func; }, nvm | | ○ | ○ | |予約後 | if, for, ... | | | ○ | とりあえずtype最強のようです。 nvmとかのように関数として定義されるコマンドをチェックしたい場合は基本、whichでは探せません。 ### 補足(whichで関数を探す) 一応whichでも、非常にアクロバティックな書き方ですが関数定義があるかどうかのチェックができなくはないです。 ```text $ f() { echo func; } $ declare -f | command which --read-functions f f () { echo func } $ echo $? 0 ``` declare -f で関数定義をダンプします。その関数定義のダンプを標準入力から読み込んだ上で --read-functions オプションをつけると、その読み込んだ関数定義内に引数で渡された関数が存在するかのチェックができます。これはman whichに書いてありますが、さすがにこんなの普通は使わないと思うwww ##環境非依存度→hash,typeの勝ち Bashスクリプトを書こうとしている以上は組み込みコマンドである`hash`と`type`は常に存在を保証されています。それに対して`which`は外部プログラムであるがゆえにインストールされていないこともあり環境非依存度という点では劣ります。(まぁ、組み込みとかでない限り大抵のディストリビューションでは普通入ってますけどね) というわけで、hashとtypeの勝ち。 ## キャッシュの影響による正確さ→whichの勝ち? hashとtypeは組み込みコマンドだけあって毎回ゼロから起動されるwhichよりも賢いです。 実はbashは、初めてperlとかを実行した際にPATHを前から順にチェックして /usr/bin/perlがあることを発見したらperl=/usr/bin/perlということをメモリ内に学習して、2度目以降perlを実行する際にはPATHのチェックせずにいきなり/usr/bin/perlを実行します。そして`hash`と`type`はまずこのbash内部のハッシュテーブルをチェックして、エントリが無ければPATH環境変数を元にコマンドを探し始めます。組み込みコマンドな上にメモリキャッシュも活用してるからwhichより早いのは当然ですね。 ただしあらゆるキャッシュにつきもののトラブルですが、この学習が邪魔になることもあります。 ``` # 前準備 mkdir /tmp/test cp /bin/cat /tmp/test/copycat export PATH="/tmp/test:$PATH" # これは当然どっちも問題なし which copycat # 見つかる→正解 hash copycat # 見つかる→正解 type copycat # 見つかる→正解 # さっきまであったコマンドを消してみる rm -f /tmp/test/copycat which copycat # 見つからない→正解 hash copycat # 見つかる→不正解 type copycat # 見つかる→不正解 # type -aだとハッシュを見なくなる type -a copycat # 見つからない→正解 # ハッシュをクリアすると再度PATHをチェックする hash -r hash copycat # 見つからない→正解 type copycat # 見つからない→正解 ``` というわけでコマンドの存在チェックとしての正確さでは、毎回律儀にチェックする(むしろそれしか出来ない)`which`に軍配が上がりそう?それに対してhashとtypeはハッシュクリア対応可能、またtypeは追加オプションでも対応可能ってことで優劣はつけがたいかも。 ## 複数コマンドを纏めて探す→引き分け `which perl python ruby`や`hash perl python ruby`や`type perl python ruby`という感じで、複数コマンドのありかを纏めてチェックするのはwhichでもhashでも出来ます。どちらもこの場合、全てが見つかったら成功で一つでも見つからなかったら失敗という判定になります。 ## hashは標準出力が出ないのでちょっとだけ記述が楽→hashの勝ち そもそも別の用途のコマンドなんで仕様上当然なんですが以下の様な出力の違いがあります。 ```bash $ which perl; echo $? /usr/bin/perl 0 $ which aaa; echo $? /usr/bin/which: no aaa in (/usr/local/bin:/bin:/usr/bin) 1 $ hash perl; echo $? 0 $ hash aaa; echo $? -bash: hash: aaa: not found 1 $ type perl; echo $? 0 $ type aaa; echo $? -bash: type: aaa: not found 1 ``` なので、if文で使うときは邪魔な標準出力やエラー出力を隠すために↓こうなります。 ```bash if which python >/dev/null 2>&1; then # some code... fi if hash python 2>/dev/null; then # some code... fi if type python >/dev/null 2>&1; then # some code... fi ``` `2>&1` が不要になった分、`>/dev/null`が`2>/dev/null`になってますがトータルでhashの方が3文字少ないのでhashの勝ち。 # まとめ こんな感じに、思いついた争点を列挙してみたわけで結果は以下のとおり。 | |which |hash |type | |------------------|:------:|:------:|:------:| | 速度 |× |○ |◎ | | 探せるもの |△ |○ |◎ | | 環境非依存度 |△ |○ |○ | | 正確さ |○ |△ |△ | | タイプ数 |△ |○ |△ | | 複数同時チェック |引き分け|引き分け|引き分け| 異論あるかもしれんが、<del>総じて`hash`の勝ち</del>→異論あった結果`type`最強っぽいです! |
|
| 466位 |
|
|||
|
17:41:41 |
|
|
Rails 3.2.2 + RSpec 2.9.0 + Factory Gril 3.1.0。
##インストール ```sh gem install factory_girl_rails ``` ```ruby:GemFile group :development, :test do gem 'rspec' gem 'rspec-rails' gem 'guard-rails' gem 'factory_girl_rails' end ``` ```sh bundle install ``` ##基本的な使い方 ```ruby:spec/factories.rb FactoryGirl.define do # 使用する際は:team1を指定する # シンボルがそのままクラス名ならclassは省略できる # name、addressがモデルの属性 factory :team1, class: Team do name :team1.to_s address '住所' end end ``` ```ruby:spec/models/team_spec.rb require 'spec_helper' describe Team do it 'can save.' do # :team1はfactories.rbで指定したシンボル # buildだとDBに反映されない # createだと反映される team = FactoryGirl.build(:team1) team.save.should_not be_false end end ``` ##specファイルでFactoryGirlを省略できるよう設定 ```ruby:spec/spec_helper.rb config.include FactoryGirl::Syntax::Methods ``` ##他の属性に依存した値を作る ```ruby factory :hoge do first_name 'fuga' last_name 'hige' full_name '#{first_name}.#{last_name}' end ``` ##createした後に属性を操作 3.3で記法が変更。詳しくはコメント参照。 ```ruby factory :member1, class: Member do name :member1.to_s after_create do |m, evaluator| m.name.upcase! end end ``` ##連番付きのデータを生成 ```ruby:spec/factories.rb # 連番付きの名前を生成 sequence :member_names do |n| "name_#{n}" end factory :sequence_members, class: Member do # {}で囲むと遅延評価 # {}がないと連番付きのデータで更新されないので要注意 name { FactoryGirl.generate(:member_names) } end ``` ```ruby # 10件作る 10.times do FactoryGirl.create(:sequence_members) end ``` *** ## 属性の上書き ```ruby factory :member_base, class: Member do name :member_base.to_s end m = FactoryGirl.build(:member_base, { name: 'hogehogehoge' }) ``` ## 別名を付けられる ```ruby factory :member_base, aliases: [:player_base, :manager_base], class: Member do end ``` *** ## 一時的な属性 ```ruby factory :member_base, aliases: [:player_base, :manager_base], class: Member do # 一時的な属性を定義 # 以降で参照できる # また通常の属性と同様に外から上書きもできる ignore do upcased true end name :member_base.to_s after_create do |m, evaluator| # 上で定義した属性を参照 m.name.upcase! if evaluator.upcased end end end ``` ##traits ```ruby factory :member_base, class: Member do name :member_base.to_s # 値を設定済みの属性に別名を付ける感じ trait :type_player do member_type 0 end trait :type_manager do member_type 1 end # buildなどをする際は、 # build(:player_base)でできる # factoryが入れ子になっているけど指定する際には関係なさげ factory :player_base, traits: [:type_player] factory :manager_base, traits: [:type_manager] end end ``` ```ruby # どちらでも結果は同じ FactoryGirl.build(:player_base) FactoryGirl.build(:member_base, :player_base) ``` ## 同じデータを複数作る ```ruby FactoryGirl.build_list(:member_base, 100) FactoryGirl.create_list(:member_base, 100) ``` ## コールバック 3.3から変更あり。コメント参照。 - after_build - after_create - after_stub それぞれbuild、create、build_stubbedされた際に実行される。 ```ruby factory :member, class: Member do after_build do |m| end after_create do |m, evalator| end after_stub {} end ``` ## 継承みたいなの ```ruby factory :member, class: Member do name 'hoge' end # after_createとかの動きがよくわからない # 上書きできない? factory :member_ex, parent: :member do name 'hogehoge' end ``` ## 修正 ```ruby FactoryGirl.define do factory :member_base do name 'hoge' end end FactoryGirl.modify do factory :member_base do name 'hogehoge' end end ``` ## ハッシュで生成したデータを取得 ```ruby FactoryGirl.attributes_for(:member) ``` ## サンプル スポーツなんかのチームを管理することを想定。 チームの配下にメンバーが複数属する。 ```ruby # -*- coding: utf-8 -*- require 'faker/japanese' FactoryGirl.define do # メンバテーブルの基本データ factory :member_base, class: Member do name { Faker::Japanese::Name.name } trait :type_player do member_type 1 end trait :type_manager do member_type 2 end # 選手 factory :player, traits: [:type_player] # 指導者 factory :manager, traits: [:type_manager] end # チームとメンバーの中間テーブル factory :team_member do team_id 0 member_id 0 end # チームの基本データ factory :team_base, class: Team do ignore do member_count 0 end name {"チーム:#{Faker::Japanese::Name.name}"} after_create do |team, evaluator| # 指導者を作成 manager = FactoryGirl.create(:manager) FactoryGirl.create(:team_member, {team_id: team.id, member_id: manager.id}) # 指定された人数分の選手を作成 if evaluator.member_count > 0 members = FactoryGirl.create_list(:player, evaluator.member_count) members.each do |player| FactoryGirl.create(:team_member, {team_id: team.id, member_id: player.id}) end end end end end # 使い方 team = FactoryGirl.create(:team_base, member_count: 30) ``` |
|
| 467位 |
|
|||
|
13:57:04 |
|
|
最近のフレームワーク、っていうとどれもRailsと似たような感じで実装言語が違うだけでしょ?という感じですが、Liftはそんな中完全に逆を行ってます。
その思想は他の言語でも通用するはずなので、ぜひScalaなんぞ知らんという方も読んでいただければと思います。 # Liftの特徴 公式な特徴は[ホームページに記載](http://liftweb.net/)されていますが、個人的にプッシュしたいのは以下点です。これらは、[Templating](http://demo.liftweb.net/templating/)に詳しく書かれています([日本語訳](http://oss.infoscience.co.jp/scala/www.assembla.com/wiki/show/liftweb/View_First.html))。 1 デザイナフレンドリー 2 コントローラーがない 3 速い ## デザイナフレンドリー 自分の手元にあるerbやらjspやらasp(chtml/vbhtml)やらのファイルを見て、これをサーバーサイド側の言語を知らない人間に手渡せるか?と考えたときYesと言える人はどれくらいいるでしょうか。 それらのファイルにはそこそこコードが入り組んでいて、特にRailsの場合顕著ですがHelperが紛れ込んでいる場合その筋の人でもよく分からんということも多いのではと思います。 これは正直開発者の生産性を重要視し続けた結果でもあり、異なるスキルを持つ人との協業を難しくしています。 そこでLiftですが、LiftのViewはhtmlです。変な拡張子ではないですし中身にScalaのコードがしゃしゃり出てくることもありません。DreamWeaverなどのデザインツールにも取り込むことができます。 ただ、その反面Scala側にHTMLが多少混じります。サーバーサイドにHTMLタグが入るということにやっぱり嫌な感じはありますが、それはRubyやらJavaのコードがViewに紛れ込んでいるのと同じことです。 これは完全にトレードオフで、Liftは他のフレームワークとは逆の選択をしたということです。実際、サーバーからレンダリングしないといけないタグは正直大したことない量なので、それほど気になりません。 ## コントローラーがない 猫も杓子もMVCでControllerですが、そもそもなんでControllerなんぞないといけないんでしょうか?邪魔だと思ったことはないですか? 大体1Viewに1Controllerが相場(?)ですが、多くのリッチな画面は画面上で複数の処理が走っているのが普通です。Qiitaの投稿画面でも、投稿件数、記事の自動保存といった非同期処理から、記事の投稿、画像のアップロードといった同期処理まで多数の処理が動いています。 これらを1Controllerにまとめるのは正直現実的ではありません。何らかの形で分割はするのでしょうが、それが難しいからこそFat Controllerという問題が起きてきたりJavaScript側のフレームワークがないと手に負えなくなってきたりするのだと思います。 LiftにはControllerなんかありません。投稿件数の表示なら表示、投稿なら投稿、と画面上のアクションに応じて完全にコードを分離できます(これをsnippetと呼びます)。 なんだか怖い気もしますが、でもこれは当然な気もします。 「画面上にある機能を分割し、それに応じてコードが書ける」。多くのフレームワークがMVCにこだわって失った機能の分割という利点を(Controllerを経由しないといけないという性質上、MVCで機能分割が行いにくいのはある意味必然)、Liftは逆を行くことで非常に理にかなったモジュール化を行うことができます。 ## 速い Scalaという静的型言語でしかも非同期に強いということだけでイケてますが、上記のようにControllerを廃してViewの機能分割(snippet化)を可能にしたことで、並列実行による部分View描画が可能になっています。 [こちら](http://demo.liftweb.net/parallel)にサンプルがありますが、これだけ簡単に書けるフレームワークは他にないと思います。 そういう意味では、snippetの思想からしてWebアプリケーションを関数型で記述しているような感がLiftにはあります。 主流に逆らいさまざまなメリットを得たLiftフレームワーク、ぜひ一度お試しあれ。 ・・・といっても、情報がかなり少ないので(HelloWorld的なものしかない)、作り方についてまとめました。 [View First で始める Scala Liftフレームワーク](http://qiita.com/icoxfog417/items/f5136a2bae229f45f108) そして、作っているうちにやっぱりそんなバラ色というわけにもいかないこともわかってきたので、[こちら](http://qiita.com/icoxfog417/items/186f526288addcc2b12b)を追記しました。。。 |
|
| 468位 |
|
|||
|
04:36:07 |
|
|
[@hedjirog](https://twitter.com/hedjirog) です。[iBeacon Advent Calendar](http://qiita.com/advent-calendar/2013/ibeacon) の2日目を担当します。
iBeaconの技術情報はまだ少ないようですが、参考になりそうな記事やソースコードをまとめてみました。 ### * 宣伝 * iOS開発関連の記事をつぶやく [@ios_dev_bot](https://twitter.com/ios_dev_bot) を運用しています。 **Advent Calendarの投稿チェック** に良ければ利用してください。 # 目次 1. [技術記事](http://qiita.com/hedjirog/items/abd48a55387891cc8503#2-1) 2. [スライド](http://qiita.com/hedjirog/items/abd48a55387891cc8503#2-2) 3. [Apple公式ドキュメント](http://qiita.com/hedjirog/items/abd48a55387891cc8503#2-3) 4. [書籍](http://qiita.com/hedjirog/items/abd48a55387891cc8503#2-4) 5. [ソースコード](http://qiita.com/hedjirog/items/abd48a55387891cc8503#2-5) 6. [解説・関連記事](http://qiita.com/hedjirog/items/abd48a55387891cc8503#2-6) 7. [2013 iBeacon Advent Calendar](http://qiita.com/hedjirog/items/abd48a55387891cc8503#2-7) ※ お世話になったリンク先: [はてなブックマーク](http://b.hatena.ne.jp/search/tag?safe=on&sort=popular&q=iBeacon) / [Google検索](https://www.google.com/#q=iBeacon) / [SlideShare](http://www.slideshare.net/search/slideshow?q=iBeacon) / [Speaker Deck](https://speakerdeck.com/search?q=iBeacon) / [GitHub](https://github.com/search?o=desc&q=iBeacon&ref=searchresults&s=stars&type=Repositories) ## 1. 技術記事 * [iBeaconの解説 - Reinforce-Lab.’s Blog](http://reinforce-lab.github.io/blog/2013/10/21/ibeacon/) <a href="http://b.hatena.ne.jp/entry/http://reinforce-lab.github.io/blog/2013/10/21/ibeacon/"><img src="http://b.hatena.ne.jp/entry/image/http://reinforce-lab.github.io/blog/2013/10/21/ibeacon/"></a> > 最も分量の多い記事。iBeaconの説明やiOSアプリケーション開発時に知っておきたい知識など。 ---- * [[iOS 7] 新たな領域観測サービス iBeacon を使ってみる | Developers.IO](http://dev.classmethod.jp/references/ios7-ibeacon-api/) <a href="http://b.hatena.ne.jp/entry/http://dev.classmethod.jp/references/ios7-ibeacon-api/"><img src="http://b.hatena.ne.jp/entry/image/http://dev.classmethod.jp/references/ios7-ibeacon-api/"></a> > iBeaconやAPIの簡単な説明。加えて、サンプルアプリの作成と動作確認まで。 ---- * [iBeacon Tips: 正しいビーコン監視方法 | ブライテクノBlog](http://brightechno.com/blog/archives/220) <a href="http://b.hatena.ne.jp/entry/http://brightechno.com/blog/archives/220"><img src="http://b.hatena.ne.jp/entry/image/http://brightechno.com/blog/archives/220"></a> > ビーコン監視手順の解説。他記事で誤りが見受けられるとの指摘あり。 ---- * [[iOS 7 SDK] iBeacon (Core Location Framework エンハンスメント) について. | hirooka.pro](http://hirooka.pro/?p=3255) <a href="http://b.hatena.ne.jp/entry/http://hirooka.pro/?p=3255"><img src="http://b.hatena.ne.jp/entry/image/http://hirooka.pro/?p=3255"></a> > ビーコン送受信の動作確認。送受信側それぞれの実装。 ---- * [第 2 回・iOS7で iBeacon を使用してみよう! | ギャップロ](http://www.gaprot.jp/pickup/ios7/vol2/) <a href="http://b.hatena.ne.jp/entry/http://www.gaprot.jp/pickup/ios7/vol2/"><img src="http://b.hatena.ne.jp/entry/image/http://www.gaprot.jp/pickup/ios7/vol2/"></a> > iBeaconの利用の仕方について丁寧な解説。 ---- * [iBeacon Tips: Estimoteビーコンモジュールのモニタリング | ブライテクノBlog](http://brightechno.com/blog/archives/225) <a href="http://b.hatena.ne.jp/entry/http://brightechno.com/blog/archives/225"><img src="http://b.hatena.ne.jp/entry/image/http://brightechno.com/blog/archives/225"></a> > `Estimote Beacons` のUUIDについて。 ---- * [MacをiBeaconにする ステータスバー常駐アプリXBeaconを作ってみた - ReDo](http://greety.sakura.ne.jp/redo/2013/11/ibeacon-macxbeacon.html) <a href="http://b.hatena.ne.jp/entry/http://greety.sakura.ne.jp/redo/2013/11/ibeacon-macxbeacon.html"><img src="http://b.hatena.ne.jp/entry/image/http://greety.sakura.ne.jp/redo/2013/11/ibeacon-macxbeacon.html"></a> > MacをiBeaconの送信機に。 ---- * [Playing with iBeacon and Estimote in iOS 7 - Joris Kluivers](http://joris.kluivers.nl/blog/2013/09/27/playing-with-ibeacon/) <a href="http://b.hatena.ne.jp/entry/http://joris.kluivers.nl/blog/2013/09/27/playing-with-ibeacon/"><img src="http://b.hatena.ne.jp/entry/image/http://joris.kluivers.nl/blog/2013/09/27/playing-with-ibeacon/"></a> > `Estimote Beacons` と連携するソースコードあり。 ---- * [Mavericks as an iBeacon - Blended Cocoa](http://www.blendedcocoa.com/blog/2013/11/02/mavericks-as-an-ibeacon/) <a href="http://b.hatena.ne.jp/entry/http://www.blendedcocoa.com/blog/2013/11/02/mavericks-as-an-ibeacon/"><img src="http://b.hatena.ne.jp/entry/image/http://www.blendedcocoa.com/blog/2013/11/02/mavericks-as-an-ibeacon/"></a> > iOSとMavericksでのビーコン送信の比較。 ---- * [iOS 7 SDK: iBeacons Game](http://mobile.tutsplus.com/tutorials/iphone/ios-7-sdk-ibeacons-game/) <a href="http://b.hatena.ne.jp/entry/http://mobile.tutsplus.com/tutorials/iphone/ios-7-sdk-ibeacons-game/"><img src="http://b.hatena.ne.jp/entry/image/http://mobile.tutsplus.com/tutorials/iphone/ios-7-sdk-ibeacons-game/"></a> > サンプルソースコードとその解説。 ---- * [Can you Smell the iBeacon? | Cocoanetics](https://www.cocoanetics.com/2013/11/can-you-smell-the-ibeacon/) <a href="http://b.hatena.ne.jp/entry/https://www.cocoanetics.com/2013/11/can-you-smell-the-ibeacon/"><img src="http://b.hatena.ne.jp/entry/image/https://www.cocoanetics.com/2013/11/can-you-smell-the-ibeacon/"></a> > `Estimote Beacons` と連携する詳しいソースコードと解説あり。 ---- * [MacBeacon and Locate for iBeacon: An inexpensive way for developers to design and test iBeacons | TUAW](http://www.tuaw.com/2013/11/22/macbeacon-and-locate-for-ibeacon-an-inexpensive-way-for-develop/) <a href="http://b.hatena.ne.jp/entry/http://www.tuaw.com/2013/11/22/macbeacon-and-locate-for-ibeacon-an-inexpensive-way-for-develop/"><img src="http://b.hatena.ne.jp/entry/image/http://www.tuaw.com/2013/11/22/macbeacon-and-locate-for-ibeacon-an-inexpensive-way-for-develop/"></a> > `MacBeacon` と `Locate for iBeacon` の紹介。 ## 2. スライド * [5分でわかるiBeacon](http://www.slideshare.net/himaratsu/potatotips-ibeacon) <a href="http://b.hatena.ne.jp/entry/http://www.slideshare.net/himaratsu/potatotips-ibeacon"><img src="http://b.hatena.ne.jp/entry/image/http://www.slideshare.net/himaratsu/potatotips-ibeacon"></a> > 要点を絞った簡潔な説明。 ---- * [iOS7のiBeacon徹底解説、の資料](http://reinforce-lab.github.io/blog/2013/11/01/sumaben/) <a href="http://b.hatena.ne.jp/entry/http://reinforce-lab.github.io/blog/2013/11/01/sumaben/"><img src="http://b.hatena.ne.jp/entry/image/http://reinforce-lab.github.io/blog/2013/11/01/sumaben/"></a> * [iBeacon体験講座(iOS)](http://www.slideshare.net/reinforcelab/20131026-i-beacon) <a href="http://b.hatena.ne.jp/entry/http://www.slideshare.net/reinforcelab/20131026-i-beacon"><img src="http://b.hatena.ne.jp/entry/image/http://www.slideshare.net/reinforcelab/20131026-i-beacon"></a> > Passbook連携やビーコン送受信の流れなど。 ## 3. Apple公式ドキュメント ### リファレンス * [CLBeaconRegion Class Reference](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLBeaconRegion_class/Reference/Reference.html) ### プログラミングガイド * [Location and Maps Programming Guide](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/LocationAwarenessPG/LocationAwarenessPG.pdf) * [位置情報とマッププログラミングガイド](https://developer.apple.com/jp/devcenter/ios/library/documentation/LocationAwarenessPG.pdf) ### WWDC * [WWDC 2013 Session Videos - What’s New in Core Location](https://developer.apple.com/wwdc/videos/index.php?id=307) > WWDCのセッション動画と資料。Developer登録が必要。 ---- * [ASCIIwwdc - What’s New in Core Location](http://asciiwwdc.com/2013/sessions/307) <a href="http://b.hatena.ne.jp/entry/http://asciiwwdc.com/2013/sessions/307"><img src="http://b.hatena.ne.jp/entry/image/http://asciiwwdc.com/2013/sessions/307"></a> > Session 307 `What’s New in Core Location` の文字起こし。 ## 4. 書籍 * [iOS 7 by Tutorials | Ray Wenderlich](http://www.raywenderlich.com/store/ios-7-by-tutorials) > Chapter 24 が Core Location に関する内容。iBeaconを利用した2つのアプリのソースコードと解説あり。 ## 5. ソースコード ### iOS * [nicktoumpelis/HiBeacons](https://github.com/nicktoumpelis/HiBeacons) > iOS 7 でiBeaconを利用するデモアプリ。 ---- * [suwa-yuki/BeaconSample](https://github.com/suwa-yuki/BeaconSample) > 送信側、受信側それぞれのデモアプリ。 ---- * [Estimote/iOS-SDK](https://github.com/Estimote/iOS-SDK) > `Estimote iBeacons` のiOS向けSDK。サンプルプロジェクトあり。 ---- * [ohwutup/OWUProximityManager](https://github.com/ohwutup/OWUProximityManager) * [Estimote/beacons-demo](https://github.com/Estimote/beacons-demo) * [csmann/iBeaconDemo](https://github.com/csmann/iBeaconDemo) ### OS X * [lgaches/BeaconEmitter](https://github.com/lgaches/BeaconEmitter) > MacをiBeaconの送信機に。 ---- * [mttrb/BeaconOSX](https://github.com/mttrb/BeaconOSX) > Mavericksを送信機に。Bluetooth 4 で代用。 ### Android * [RadiusNetworks/android-ibeacon-service](https://github.com/RadiusNetworks/android-ibeacon-service) > iBeaconの受信をAndroidで。 ### Titanium * [jbeuckm/TiBeacons](https://github.com/jbeuckm/TiBeacons) > iBeaconの送受信を可能にするTitaniumモジュール。 ## 6. 解説・関連記事 * [iOS7の隠れキラーコンテンツとなる近距離無線通信「iBeacon」とは? - GIGAZINE](http://gigazine.net/news/20130911-ibeacon/) <a href="http://b.hatena.ne.jp/entry/http://gigazine.net/news/20130911-ibeacon/"><img src="http://b.hatena.ne.jp/entry/image/http://gigazine.net/news/20130911-ibeacon/"></a> > `Estimote Beacons`の紹介と、NFCとの比較。 ---- * [今注目のiBeaconのサービス事例や端末をまとめてご紹介 | スマートフォンECラボ](http://smartphone-ec.net/2987.html) <a href="http://b.hatena.ne.jp/entry/http://smartphone-ec.net/2987.html"><img src="http://b.hatena.ne.jp/entry/image/http://smartphone-ec.net/2987.html"></a> > 事例紹介。サービスと対応端末。 ---- * [[iOS 7] O2O業界の新たな風!? iBeaconとは | Developers.IO](http://dev.classmethod.jp/references/ios7-features-ibeacon/) <a href="http://b.hatena.ne.jp/entry/http://dev.classmethod.jp/references/ios7-features-ibeacon/"><img src="http://b.hatena.ne.jp/entry/image/http://dev.classmethod.jp/references/ios7-features-ibeacon/"></a> > 簡単な解説と、iBeaconの利点と欠点。 ---- * [iBeaconは結局何なのか - ReDo](http://greety.sakura.ne.jp/redo/2013/10/ibeacon.html) <a href="http://b.hatena.ne.jp/entry/http://greety.sakura.ne.jp/redo/2013/10/ibeacon.html"><img src="http://b.hatena.ne.jp/entry/image/http://greety.sakura.ne.jp/redo/2013/10/ibeacon.html"></a> > 図解あり。調査した際の内容。 ---- * [iOS「iBeacon」で出来そうなことを考えてみる - 鶴の眼で見たモノとコト](http://mizzan72.hatenablog.com/entry/2013/09/21/164352) <a href="http://b.hatena.ne.jp/entry/http://mizzan72.hatenablog.com/entry/2013/09/21/164352"><img src="http://b.hatena.ne.jp/entry/image/http://mizzan72.hatenablog.com/entry/2013/09/21/164352"></a> > 活用法の考察。 ## 7. 2013 iBeacon Advent Calendar * 1日目: [1行もコードを書かずにiBeaconで遊んでみる - 位置情報ブログ](http://geo.hatenablog.com/entry/2013/12/01/074148) <a href="http://b.hatena.ne.jp/entry/http://geo.hatenablog.com/entry/2013/12/01/074148"><img src="http://b.hatena.ne.jp/entry/image/http://geo.hatenablog.com/entry/2013/12/01/074148"></a> > iOSアプリの `Estimote Virtual Beacon` で iBeaconを体験。 ---- * 3日目:[iBeacon で忍者が密会する](http://qiita.com/usamik26/items/563e94301ca150c3d0c3) > iOSアプリ `Ninja Tryst` の紹介。実際に開発した経験に基づく注意点など。 ---- * 4日目:[iBeacon開発ハマリどころポイントまとめ](http://qiita.com/himara2/items/1d6c11a4d4839c3027d5) > バックグラウンド処理をする際や、デバッグ時などに注意すべき点について。 ---- * 5日目:[ビーコン的な演出をする for iBeacon](http://qiita.com/shu223/items/5018cf5e452c4fa0d002) > ビーコン発信をイメージさせるアニメーションの紹介。 ---- * 6日目:[iBeaconでクリスマスに備えよう!](http://qiita.com/youten_redo/items/7d0856bc521b19778c96) > iBeaconを利用したアプリの実装例。`AVSpeechSynthesizer` を利用した音声読み上げもあり。 ---- * 7日目:[AndroidでiBeacon信号を受信してみよう](http://qiita.com/KazuyukiEguchi/items/2f6c439ae8faeb06d23f) > `Bluetooth 4.0 LE` を利用したAndroidでのビーコン受信の方法。 |
|
| 469位 |
|
|||
|
10:22:00 |
(フリーランス 所属) |
|
## はじめに 間違ってパスワードを書いたファイルをGitHubにPushしてしまった場合、対象のファイルを削除しても commit 履歴には残ってしまっている。 これをコミットした履歴ごと削除するにはどうしたらいいか。 対応する方法を探していたら以下の公式のヘルプにまとまっていた。 [Remove sensitive data](https://help.github.com/articles/remove-sensitive-data) ## 手順 方法としては`git filter-branch`で歴史をばっさりと書き換えて`--force`オプションをつけてpushするようだ。 例えば Rakefile にパスワードを書いたまま誤ってGitHubにpushしてしまった場合は以下のようにする。 ``` bash $ git filter-branch --index-filter 'git rm --cached --ignore-unmatch Rakefile' \ --prune-empty --tag-name-filter cat -- --all $ git push origin master --force ``` ## おわりに [GitHubで多くのユーザーが秘密鍵を公開状態にしていたことが判明](http://opensource.slashdot.jp/story/13/01/26/0745251/GitHub%E3%81%A7%E5%A4%9A%E3%81%8F%E3%81%AE%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%8C%E7%A7%98%E5%AF%86%E9%8D%B5%E3%82%92%E5%85%AC%E9%96%8B%E7%8A%B6%E6%85%8B%E3%81%AB%E3%81%97%E3%81%A6%E3%81%84%E3%81%9F%E3%81%93%E3%81%A8%E3%81%8C%E5%88%A4%E6%98%8E) |
|
| 470位 |
|
|||
|
22:01:39 |
(Consensus Base Inc. 所属) |
|
以下に移行しました<br><a href="http://block-chain.jp/tech/ethereum-basics-for-engineer/" title="技術者向け Ethereumの基礎知識 (イーサリアム、エセリウム)">技術者向け Ethereumの基礎知識 (イーサリアム、エセリウム)</a>
|
|
| 471位 |
|
|||
|
18:17:59 |
|
|
この辺のはなし。
| 種類 | -child | -of-type | |-----------:|:------------|:------------| |最初|:first-child|:first-of-type| |最後|:last-child|:last-of-type| |n番目|:nth-child(n)|:nth-of-type(n)| |後ろからn番目|:nth-last-child(n)|:nth-last-of-type(n)| |1個だけ|:only-child|:only-of-type| #最初 ##:first-child 最初の要素(親要素から見て、子要素すべての最初) [デモ](http://jsrun.it/Ituki/n9Qs?type=0) ##:first-of-type ある要素の最初(親要素から見て、ある子要素の最初) [デモ](http://jsrun.it/Ituki/n9Qs?type=1) #最後 ##:last-child 最後の要素(親要素から見て、子要素すべての最後) [デモ](http://jsrun.it/Ituki/n9Qs?type=2) ##:last-of-type ある要素の最後(親要素から見て、ある子要素の最後) [デモ](http://jsrun.it/Ituki/n9Qs?type=3) #1個だけ ##:only-child 1個だけ子要素がある(親要素から見て、子要素が1個だけある) [デモ](http://jsrun.it/Ituki/n9Qs?type=4) ##:only-of-type 1個だけある子要素がある(親要素から見て、ある子要素が1個だけある) [デモ](http://jsrun.it/Ituki/n9Qs?type=5) #X番目 ##:nth-child(X) X番目 [デモ](http://jsrun.it/Ituki/n9Qs?type=6) ##:nth-of-type(X) ある要素のX番目 [デモ](http://jsrun.it/Ituki/n9Qs?type=7) ##:nth-child(Xn) Xの倍数 [デモ](http://jsrun.it/Ituki/mH7l) #最後からX番目 ##:nth-last-child(X) 最後からX番目 [デモ](http://jsrun.it/Ituki/n9Qs?type=8) ##:nth-last-of-type(X) ある要素の最後からX番目 [デモ](http://jsrun.it/Ituki/n9Qs?type=9) #X番目からYまで ##:nth-child(n+X) X番目以降(X番目から最後まで) [デモ](http://jsrun.it/Ituki/n9Qs?type=10) ##:nth-of-type(n+X) ある要素のX番目以降 [デモ](http://jsrun.it/Ituki/n9Qs?type=11) ##:nth-child(-n+X) X番目以前(最初からX番目まで) [デモ](http://jsrun.it/Ituki/n9Qs?type=12) ##:nth-of-type(-n+X) ある要素のX番目以前 [デモ](http://jsrun.it/Ituki/n9Qs?type=13) #最後からX個 ##:nth-last-child(-n+X) 最後からX個 [デモ](http://jsrun.it/Ituki/n9Qs?type=14) ##:nth-last-of-type(-n+X) ある要素の最後からX個 [デモ](http://jsrun.it/Ituki/n9Qs?type=15) #最後からX番目以前 ##:nth-last-child(n+X) 最後からX番目以前 [デモ](http://jsrun.it/Ituki/n9Qs?type=16) ##:nth-last-of-type(n+X) ある要素の最後からX番目以前 [デモ](http://jsrun.it/Ituki/n9Qs?type=17) #参考 - http://bl6.jp/web/css/nth-child-nth-last-child/ - http://flukeout.github.io/ |
|
| 472位 |
|
|||
|
20:15:48 |
(Nepula,Inc. 所属) |
|
[Typescript クイックガイド](http://phyzkit.net/typescript/) で、ソースを見たら Markdown で書かれてたのをみて「カッケー」と思ったのと、[利用規約やプライバシーポリシーは Github に置くといいよ](http://www.atmarkit.co.jp/ait/articles/1302/05/news091.html) というのを見て、なら「github に .markdown ファイルを置いてクライアントサイドでスタイリングできたら嬉しいじゃん?」と思いやってみた。 ## Javascript による markdown パーサいろいろ 上記 Typescript クイックガイド では [marked.js](https://github.com/chjj/marked) が使われているようだが、他にもいろいろある模様。下記にて比較してくださっている。 * [JavaScript - Markdownパーサーのshowdown.js、markdown-js、markedを簡単比較 - ぼっち勉強会](http://kannokanno.hatenablog.com/entry/2013/06/19/132042) もともと marked.js を使おうと思ってたし、上記サイトでも問題無さそうだったので、そのままいく。 ## 使い方 marked.js を読み込んだ後、 ```js: var formatted = marked(markdown_text) ``` で、HTML に整形された文字列が返されるというシンプルなもの。(整形オプションがいろいろあるようだが割愛) Typescript クイックガイドでは、HTML 内に Markdown テキストがベタ書きされていたが、せっかくなので管理がしやすいように外部 Markdown ファイルを読みこむようにしてみた。 ```html:md_renderer.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Markdown renderer</title> <meta name="description" content="Markdown renderer"> <!-- bootstrap --> <link rel="stylesheet" type="text/css" href="css/bootstrap.css" rel="stylesheet"> <link rel="stylesheet" type="text/css" href="css/bootstrap-responsive.css" rel="stylesheet"> <!-- js libraries --> <script type="text/javascript" src="js/jquery-2.0.3.js"></script> <script type="text/javascript" src="js/marked.js"></script> <script> $(document).ready(function(){ var target = $("#markdown_content"); $.ajax({ url: target[0].attributes["src"].value, }).success(function(data){ target.append(marked(data)); }).error(function(data){ target.append("This content failed to load."); }); }); </script> </head> <body> <!-- Content --> <div class="container"> <div id="markdown_content" src="./sample.markdown"> </div> </div> </body> </html> ``` 一番下の ```<div id="markdown_content" src="./sample.markdown">``` の src に読み込む Markdown ファイルを指定するようにした。 bootstrap で見栄えを良くしているが、必須ではない。 こちらが [デモ](http://blog.amay077.net/assets/images/posts/md_renderer/md_renderer.html) 。 さすがに描画に少し時間がかかるみたいです。 ``marked()`` は非同期版もあるようなので、そちらを使うべきだったかな。 これで、データ(Markdown)とプレゼンテーション部(HTML)を分離できて、なんか良さげな感じがする。 Octopress とか、HTMLを生成しちゃうんじゃなくて、クライアントサイドで 「Markdown を描画する」できると、投稿の手間が減って良いかもですね。 |
|
| 473位 |
|
|||
|
14:39:44 |
|
|
PhpStormで自分がよく使うショートカットです。[PhpStormでまず覚えるべきショートカット](http://qiita.com/items/bbdf850f0d90ad16415a) とかぶっているのは書いてないのでお先にどうぞ。 IntelliJ IDEA でもわりと共通しています。面倒なので表記がMac専用ですがWindowsでもたぶん同じ機能が使えますよ、きっと。 ## 無敵コマンド `⌘+⇧+A` 出てきたポップアップに、こんなのできるかなぁ、できたはずだけどな、と、やりたいことをなんとなく英語で書くと、機能をメニューで選んで直接実行できます。ショートカットが割り当ててあればそれも表記されるうえ、入れた単語が設定ダイアログで見つかれば、設定の該当箇所を開いてくれたり。 ## 定義と使用箇所を行ったり来たり 定義元へジャンプは、キャレット位置のワードに対して `⌘+B` する他にも、`⌘+ワードをクリック` があります。コマンドキーを押しながらマウスカーソルをホバーさせたら、辿れるワードだけアンダーラインが出てくれます。ツールチップにシグニチャが出たりもします。なんでマウスを好んでいるかというと、キャレットの位置がそのままなのが嬉しいからです。 定義元へジャンプの逆をやるのが `⌘+⌥+F7` です。変数やメソッドを使っている場所へジャンプ。コンテキストメニューの _Find by usage_ よりも素早くて、直前の検索ツールウィンドウの結果をクリアしてしまわないのが嬉しいです。 定義と使用箇所にジャンプする機能を使うなら、ぜひ憶えておいて欲しいショートカットが、 `⌘+⌥+←` です。Webブラウザの「戻る」と同じことができます。ほとんどのジャンプは、行き先のコードを参照するのみ、あってもせいぜい数行変更するだけで、だいたいはすぐに元のコードに戻ってきますよね。 「戻る」では戻れないぐらい探検しすぎたときは、最後に**編集した**場所に行く `⌘+⇧+⌫` が便利です。これ、戻る/進むのヒストリとは関係ないジャンプです。 ちなみにですが「進む」のは `⌘+⌥+→` です。戻りすぎたときにどうぞ。 ## 移動するまでもないコード参照 `F1` でキャレット位置のメソッドのDocコメントの内容が出せます。ドキュメントコメントは大事。 `⌘+P` ではメソッドのパラメータヒントが小さく出てきます。普通の補完とドキュメントがあればいいじゃないかと思いきや、キャレット位置のワードがどの引数に対応しているかが、太字になってわかるという機能です。憶えておいて損はないですよ。 ドキュメントがない場合は定義のコードを見るしかないですが、いちいち定義元へジャンプしなくても、`⌥+Space` で定義行がポップアップとして出てくれます。ちょっと参照したいだけの時はこの方が便利。 ## 行きたいところにすぐに行く `⌘+O` で適当に名前を打ってクラスを開きます。開いたら Structure ツールウィンドウ... でもいいんですが、キーボードから手を離したり、ウィンドウのフォーカスを移動したりしたくはないです。 `⌘+F12` で Structure ポップアップを開けます。そこで適当に何文字か打ってメソッド名を絞り込んでジャンプ。 文法的にクラス名とメソッド名を確実に指定できるので、grepで探してそこに行くより確実で処理も早いですね。 ## 補完 文法にしたがった補完は `^+Space` ですが、単語レベルの補完 `⌥+/` もあります。 `Map` や `array()` の要素名、SQLで取ってきた結果のカラム名などは文字列だったりすることも多いので、それだと文法上意味のある単語と認識されません。そういうのを補完するのに都合がいいです。押すたびに、同じファイルのより手前に出てきた単語を探してくれます。 (PHPだと、array地獄な某フレームワークとか、全クラスがマジックメソッドに感染している某フレームワークとか、`$repos->getEntity('Hoge')` ってクラス名なのに文字列じゃんって某ORMとか、とか… 単語補完ありがたいです) キャレット位置より後に向かって探すときは `⌥+⇧+/` です。 ## 選択やキャレット移動など `⌘+⇧+8` で矩形選択モードになります。個人的に矩形選択がないエディタは使わない主義です。 `⌥+↑` と `⌥+↓` が面白くて、「構造的にひとつ広く選択」「その逆」です。キャレット位置→変数名→式→行→構文ブロック→メソッド定義全体、というふうに選択が広がったり狭まったり。名前や式をコピーするのにマウスを使ったりカーソルキーを連打しなくてもいいやつですね。 他にも修飾キーとカーソルの組み合わせで、次の単語まで、とか、行末/行頭とかいけるので、ひととおりやってみたら、ある種の濃いエディタを使っている人も納得だと思いますよ。 ## リファクタリングやコード生成など `^+T` と `⌘+N` 最初はこれだけ憶えておけばOK。やりたいことをポップアップメニューで探して実行。どっちみち名前などを指定するためのダイアログが出てくるので、最初にメニューから選ぶのなんて大したことないですよ。 それでもカーソルキーとかマウスとか遠いよと言うような人は、メニュー項目に付いている番号を打てばいいと思います。 リファクタリング、オブジェクト指向バリバリに思えますが、経験的には、Rename と Extract と Inline をローカル変数やprivateに対してやるのが一番多い気がします。 ## バージョン管理 `⌘+9` はいこれだけ。 `git status` (あるいは他のバージョン管理システムの変更確認)した結果が常に見えている状態に。しかもエディタとリアルタイム同期。なにか操作して見えるんじゃなくて、常に見えているということが大事。 もしエディタが狭いと思ったらもういちど同じのを押せば隠せます。 ## 最後にこれだけ `ESC` あまり意識されてないと思うのがこれ。当然のことながら、ポップアップメニューやダイアログが出てきたときは、`ESC` キーで閉じることができますが、このキー、なんとウィンドウを閉じるキーというよりは、エディタに戻るためのキーと言ったほうが合っているのです。 つまり、ツールウィンドウにフォーカスが移ったとき、マウスに手を動かすことなくエディタにフォーカスを戻せるということ。バージョン管理を意識してコードを書いているときよくある操作は... 編集 → `⌘+9` → HEADとのdiffを見る(選んで`⌘+D`) → `ESC` → 編集再開 早いでしょ。 |
|
| 474位 |
|
|||
|
17:34:25 |
|
|
リポジトリのここのフォルダだけcloneしたいんだ、Subversionでいうところのcheckoutだ、という場合は`sparsecheckout`の機能を使用する。
特定のフォルダだけclone、という以外に絶対にcommitしたくないファイルを外したりできるので、覚えておくと便利(設定ファイルを個人ごとに書き換えなければならない場合など)。 1. `git clone xxx ` 2. `git config core.sparsecheckout true` 3. `echo I_want/this_folder/ > .git/info/sparse-checkout` 4. `git read-tree -m -u HEAD` 5. `echo I_forget/this_folder/ >> .git/info/sparse-checkout` まずは普通にcloneをしてきて、そのあとsparsecheckoutを有効にする。 次に、`.git/info/sparse-checkout`というファイルに残しておきたいフォルダ(`I_want/this_folder/`)を書き込む。 そして、`git read-tree -m -u HEAD`という呪文を唱えると先程記載しておいたフォルダ以外は姿を消すことになる。 上記5では、指定するのを忘れたフォルダを追記している。この場合、既存ファイルへの追記なので>>となる点に注意。もちろん普通にファイルを編集してもOK。 これで不要なファイルは除外できるが、フォルダ階層は調整できない(aaa/bbbをd/にcloneする、といったことができずd/aaa/bbbになる)。 ここはうまい方法はなさそうな気配。 <参考> http://blog.quilitz.de/2010/03/checkout-sub-directories-in-git-sparse-checkouts/ http://jasonkarns.com/blog/subdirectory-checkouts-with-git-sparse-checkout/ |
|
| 475位 |
|
|||
|
23:08:35 |
(えんかく.jp 所属) |
|
d3.js凄そうなんですけど、ちょっと応用しようとすると途端に表示されなくなったりとか、意図と違ったりとかで、挫折する気がします。
そんな超初心者向けのtipsです。 ## この記事で行うこと + d3.jsで丸を描く + d3.jsで線を引く + d3.jsで矢印を描く + svgの要素を知る ## 環境 Mac OS X(10.9.1) + Google Chrome(2014/2/26現在 最新) で確認しています。 ## 準備 コピペして試せるように準備しておきます。 htmlとjavascriptファイルを分けて、javascriptファイル側だけ編集すればいいようにしておきます。 何度もリロードすることになると思うので、d3.jsのファイルはローカルに保存しておいたほうがさくさく進めます。 ```arrownode.html <!DOCTYPE html> <head> <meta charset="utf-8"> <script src="d3.v3.min.js"></script> </head> <body> <div id='example'></div> <script src="arrownode.js"></script> </body> </html> ``` ```arrownode.js createsvg(); function createsvg () { // ここを編集していく } ``` ここでは、arrownode.html, arrownode.js, [d3.v3.min.js](http://d3js.org/d3.v3.min.js)は同じディレクトリに置きます。 ## 1.丸を描く(svgの基本を確認) まずはd3.jsで丸を1つ描いてみましょう。d3.jsの機能はほとんど使わずに、まずは静的に作ってみます。 ```arrownode.js createsvg(); function createsvg () { // id:exampleが指定されているタグ(ここではdivタグ)の下に、svgを追加します。 // widthとheightを指定します。 var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480, }); // svgの下にcircleを追加します。 // cx,cy:中心座標(x,y)、r:半径を指定します。 svg.append('circle') .attr({ 'cx': 100, 'cy': 90, 'r': 20, }); }; ``` index.htmlをWEBブラウザに読み込ませると、黒丸が表示されていると思います。  d3.jsで丸を描くというのは、実際どうなっているのか要素を検証してみましょう。 ``` <div id="example"> <svg width="640" height="480"> <circle cx="100" cy="90" r="20"></circle> </svg> </div> ``` 黒丸を描くというのは、svg配下のcircleという要素で描画されているということがわかります。 d3.selectで指定したタグ以下に、appendしたタグが加わり、attrで指定した要素がそのまま入っていることがわかります。記述した要素がすべて一対一で反映されているのでわかりやすいですね。 ## 2.丸を描く(d3.jsのdata機能を使う) d3.jsは、Data-Driven Documentsの名の通り、データを主として扱うライブラリですので、データ(ここでは円を描画するために必要なデータ)を中心にした記述をしてみます。 ```arrownode.js createsvg(); function createsvg () { var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480 }); // 座標(cx,cy)と半径(r)を指定 var c1 = [100, 90, 30]; // dataの挿入方法が独特なので注意が必要 // 詳しくは、[三つの小円](http://ja.d3js.node.ws/document/tutorial/circle.html)参照 var circle = svg.selectAll('circle').data([c1]).enter().append('circle') .attr({ // enterに入っているデータ一つ一つで下の処理を行う 'cx': function(d) { return d[0]; }, 'cy': function(d) { return d[1]; }, 'r': function(d) { return d[2]; }, }); }; ``` 半径の部分だけ、1の時と変えてみましたが、先ほどとほとんど同じ(ちょっと大きい)になりました。 複雑にしたのに何もメリットがないように見えますが、データが少ないからそう見えるのであって、データが増えても全く同じ方法が使えるところがポイントです。 データを増やすと、こうなります。 ```arrownode.js createsvg(); function createsvg () { var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480 }); // 指定を2つに var c1 = [100, 90, 30]; var c2 = [200, 120, 20]; // 指定した値を配列にする var carray = [c1, c2]; // dataに上で作成した配列を入れる var circle = svg.selectAll('circle').data(carray).enter().append('circle') .attr({ 'cx': function(d) { return d[0]; }, 'cy': function(d) { return d[1]; }, 'r': function(d) { return d[2]; }, }); }; ``` 丸が2つ出来ましたね。このように、carrayに値を追加するだけで、丸がどんどん増えていきます。  要素を確認すると、2つのcircle要素が並んでいることがわかります。 ``` <div id="example"> <svg width="640" height="480"> <circle cx="100" cy="90" r="30"></circle> <circle cx="200" cy="120" r="20"></circle> </svg> </div> ``` ## 3.丸を描く(色をつけよう) いつまでも丸が黒いと、気分が沈んでいきますので、色をつけましょう。 せっかくですので、d3.jsがデフォルトで定義している色ライブラリを使ってみます。 ```arrownode.js createsvg(); function createsvg () { var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480 }); var c1 = [100, 90, 30]; var c2 = [200, 120, 20]; var carray = [c1, c2]; // 10種類の色を返す関数を使う var color = d3.scale.category10(); var circle = svg.selectAll('circle').data(carray).enter().append('circle') .attr({ 'cx': function(d) { return d[0]; }, 'cy': function(d) { return d[1]; }, 'r': function(d) { return d[2]; }, // dはデータ要素そのもの、iはindex番号を返す // color(i)で、n番目の色データを返す 'fill': function(d,i) { return color(i); }, }); }; ``` これで、カラフルな円になります。色の値で悩まなくていいので、素敵ですね。  要素を見ると、fillに色情報が追加されているのがわかります。 ``` <div id="example"> <svg width="640" height="480"> <circle cx="100" cy="90" r="30" fill="#1f77b4"></circle> <circle cx="200" cy="120" r="20" fill="#ff7f0e"></circle> </svg> </div> ``` ## 4.丸に字をつけよう(グループ化もいっしょに) さて、丸の中に文字を入れたくなりました。svgにはテキストを表示する機能もあるのですが、ちょっと複雑になりますので、基本に立ち返って、まずはどういうsvgなら円とテキストが共存できるのか書いてみましょう。 ```arrownode.js createsvg(); function createsvg () { var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480, }); // circle要素とtext要素をgという要素でまとめる var g = svg.append('g') .attr({ // 座標はg側で設定する transform: "translate(100,90)", }); // circleは中心座標無しで指定する。 // 上にテキストを重ねるので色を追加している g.append("circle") .attr({ 'r': 20, 'fill': 'lightgreen', }); // テキストを指定する g.append("text") .text("1"); }; ``` 円とテキストをgという要素でまとめることで、同一グループであるということになります。座標指定もgのところでまとめられますので便利です。  要素を確認すると、circle要素とtext要素がgの下に並びます。 ``` <div id="example"> <svg width="640" height="480"> <g transform="translate(100,90)"> <circle r="20" fill="lightgreen"></circle> <text>1</text> </g> </svg> </div> ``` ちなみに、svgは上から下に要素を順番に描画していきますので、circle要素とtext要素の順番を逆にすると、テキスト描いてから円を描くので、テキストが下になってテキストが描画されていないように見えます。 ## 5.丸に字をつけよう(d3.jsのdata機能を使う) 「3.丸を描く」のスクリプトを拡張してテキストを追加します。ついでにテキストが円の真ん中にくるように調整します。 ```arrownode.js createsvg(); function createsvg () { var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480 }); var c1 = [100, 90, 30]; var c2 = [200, 120, 20]; var carray = [c1, c2]; var color = d3.scale.category10(); var g = svg.selectAll('g') .data(carray).enter().append('g') .attr({ // 座標設定を動的に行う transform: function(d) { return "translate(" + d[0] + "," + d[1] + ")"; }, }); // g.appendでデータ毎に要素を追加できる g.append('circle') .attr({ 'r': function(d) { return d[2]; }, 'fill': function(d,i) { return color(i); }, }); g.append('text') .attr({ // 真ん中若干下に配置されるように、文字色は白に。 'text-anchor': "middle", 'dy': ".35em", 'fill': "white", }) // iは0から始まるので、+1しておく .text(function(d,i) { return i+1; }); }; ``` テキストが真ん中にきてますね。  要素を確認すると、g要素が2つあることと、g要素の中にcircle要素とtext要素があることがわかります。 ``` <div id="example"> <svg width="640" height="480"> <g transform="translate(100,90)"> <circle r="30" fill="#1f77b4"></circle> <text text-anchor="middle" dy=".35em" fill="white">1</text> </g> <g transform="translate(200,120)"> <circle r="20" fill="#ff7f0e"></circle> <text text-anchor="middle" dy=".35em" fill="white">2</text> </g> </svg> </div> ``` その2 ## 6.線を引こう(pathの使い方) 円は一通り描いたので、次は線を引きましょう。 svgで線を引く簡単な方法として、pathという機能を使う方法があります。 まずはシンプルに引いてみましょう。 ```arrownode.js createsvg(); function createsvg () { var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480, }); var c1 = [100, 90, 30]; var c2 = [200, 120, 20]; var carray = [c1, c2]; // line関数を定義 (x,y)は配列の[0],[1]とする。 var line = d3.svg.line() .x(function(d) {return d[0];}) .y(function(d) {return d[1];}); // path要素を作成 var path = svg.append('path') .attr({ 'd': line(carray), 'stroke': 'lightgreen', 'stroke-width': 5, }); }; ``` シンプルにと言いましたが、svgでpathを表現する際の記法が独特なので、d3.svg.line関数に頼るのがよいです。 今回は、前回までのデータは変えずに、d3.svg.line関数を追加して、path要素をappendしました。 結果、このような線が引かれます。  要素を確認すると、このようにdのところがd3.svg.line関数の出力結果に置き換わっています。数字は座標通りですが、MとかLとかが追加されていますね。 ``` <div id="example"> <svg width="640" height="480"> <path d="M100,90L200,120" stroke="lightgreen" stroke-width="5"></path> </svg> </div> ``` ## 付録1.線を引こう(3つの点を滑らかに) 試しに配列を3つ渡すと、このようになります。 ```arrownode.js createsvg(); function createsvg () { var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480, }); var c1 = [100, 90, 30]; var c2 = [200, 120, 20]; // 3つ目を追加 var c3 = [300, 100, 20]; var carray = [c1, c2, c3]; var line = d3.svg.line() .x(function(d) {return d[0];}) .y(function(d) {return d[1];}); var path = svg.append('path') .attr({ 'd': line(carray), 'stroke': 'lightgreen', 'stroke-width': 5, }); }; ``` このように、3点で特に指定しないとpathの両端をつないで色を塗ってしまいます。'fill': 'none'と定義すると、内部の色がなくなり、線のみとなります。  'fill': 'none'を指定するついでに、3つの点を滑らかにつないでみましょう。 ```arrownode.js createsvg(); function createsvg () { var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480, }); var c1 = [100, 90, 30]; var c2 = [200, 120, 20]; var c3 = [300, 100, 20]; var carray = [c1, c2, c3]; var line = d3.svg.line() // interpolate指定で点のつなぎ方を指定する。 .interpolate('basis') .x(function(d) {return d[0];}) .y(function(d) {return d[1];}); var path = svg.append('path') .attr({ 'd': line(carray), 'stroke': 'lightgreen', 'stroke-width': 5, 'fill': 'none', }); }; ``` 点の座標は変えていないのに、指定一つ増やしただけで、超滑らかになっていますね!  要素を調べてみると、もう人の手では無理というpathの値になっています。このあたりまでくると、d3.jsの便利さが際立ってきます。 ``` <div id="example"> <svg width="640" height="480"> <path d="M100, 90L116.66666666666664, 95C133.33333333333331, 100,166.66666666666663, 110,199.99999999999997, 111.66666666666666C233.33333333333331, 113.33333333333333, 266.66666666666663, 106.66666666666666, 283.3333333333333, 103.33333333333331L300,100" stroke="lightgreen" stroke-width="5" fill="none"></path> </svg> </div> ``` ## 7.線に矢印をつけよう(markerの使い方) d3.jsを勉強していくときに、純粋に矢印を調べようとするとなかなか資料がなくて、svg側から調べるとすぐにわかったりします。 [svg要素の基本的な使い方まとめ/7.マーカーの定義と設定](http://www.h2.dion.ne.jp/~defghi/svgMemo/svgMemo_07.htm)を参考にしました。 svgでpathに矢印をつけるのは、wordやexcelのようには簡単ではなく、結構細かい指定が必要になります。 矢印を描くのはちょっと面倒なのですが、svg内でまず矢印の形を定義して、その後pathに対してその定義を割り当てるという方法を取ります。 その6で作った線の一番後ろに、矢印をつけてみます。 ```arrownode.js createsvg(); function createsvg () { var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480, }); var c1 = [100, 90, 30]; var c2 = [200, 120, 20]; var carray = [c1, c2]; // defs/markerという構造で、svgの下に矢印を定義します。 var marker = svg.append("defs").append("marker") .attr({ 'id': "arrowhead", 'refX': 0, 'refY': 2, 'markerWidth': 4, 'markerHeight': 4, 'orient': "auto" }); // 矢印の形をpathで定義します。 marker.append("path") .attr({ d: "M 0,0 V 4 L4,2 Z", fill: "steelblue" }); var line = d3.svg.line() .interpolate('basis') .x(function(d) {return d[0];}) .y(function(d) {return d[1];}); var path = svg.append('path') .attr({ 'd': line(carray), 'stroke': 'lightgreen', 'stroke-width': 5, 'fill': 'none', // pathの属性として、上で定義した矢印を指定します 'marker-end':"url(#arrowhead)", }); }; ``` 奇麗な矢印が出てきました!  要素を見ると、javascriptで要素を追加した通りにsvgの構造が出来ていますので、難易度が高いというより、svg知っているかどうかという話になってきそうです。 ``` <div id="example"> <svg width="640" height="480"> <defs> <marker id="arrowhead" refX="0" refY="2" markerWidth="4" markerHeight="4" orient="auto"> <path d="M 0,0 V 4 L4,2 Z" fill="steelblue"></path> </marker> </defs> <path d="M100,90L200,120" stroke="lightgreen" stroke-width="5" fill="none" marker-end="url(#arrowhead)"></path> </svg> </div> ``` ## 8.丸と線を矢印でつなげよう(全部まとめる) それではこれまでやってきたことをまとめてみましょう。 丸2つを矢印でつなげてみます。 ```arrownode.js createsvg(); function createsvg () { var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480, }); var c1 = [100, 90, 30]; var c2 = [200, 120, 20]; var carray = [c1, c2]; // 矢印定義 var marker = svg.append("defs").append("marker") .attr({ 'id': "arrowhead", 'refX': 0, 'refY': 2, 'markerWidth': 4, 'markerHeight': 4, 'orient': "auto" }); marker.append("path") .attr({ d: "M 0,0 V 4 L4,2 Z", fill: "steelblue" }); // 色定義 var color = d3.scale.category10(); // 丸と文字のグループ定義 var g = svg.selectAll('g') .data(carray).enter().append('g') .attr({ transform: function(d) { return "translate(" + d[0] + "," + d[1] + ")"; }, }); // 丸定義 g.append('circle') .attr({ 'r': function(d) { return d[2]; }, 'fill': function(d,i) { return color(i); }, }); // 文字定義 g.append('text') .attr({ 'text-anchor': "middle", 'dy': ".35em", 'fill': 'white', }) .text(function(d,i) { return i+1; }); // 線関数定義 var line = d3.svg.line() .interpolate('basis') .x(function(d) {return d[0];}) .y(function(d) {return d[1];}); // 線要素定義 var path = svg.append('path') .attr({ 'd': line(carray), 'stroke': 'lightgreen', 'stroke-width': 5, 'fill': 'none', 'marker-end':"url(#arrowhead)", }); }; ``` さて、どうでしょう! あれ?? コレじゃない感じがしますね。丸に重ならないように線が引きたいのに、丸の中心まで線がきてしまうのと、矢印が線の終わりからさらに伸びているので何を指しているのかわかりません。  ここから先は一工夫が必要になるようです。 ## 9.丸と線を矢印でつなげよう(pathの長さと矢印の位置をコントロールする) さて、この重なり具合を解消するためにできることは、pathの長さと矢印の位置をコントロールする必要があります。 pathの長さについては、stroke-dasharrayという破線を表示するための属性と、getTotalLengthというpathの長さを調べる関数を使うことで、コントロールできます。 矢印は、refX、refYという属性で相対位置を調整できるのでこちらを使います。 ```arrownode.js createsvg(); function createsvg () { var svg = d3.select("#example").append("svg") .attr({ width: 640, height: 480, }); // pathの計算で使うので、半径と矢印の微調整パラメータを別定義にしている。 var r1 = 30; var r2 = 20; var ref1 = 8; var c1 = [100, 90, r1]; var c2 = [200, 120, r2]; var carray = [c1, c2]; var marker = svg.append("defs").append("marker") .attr({ 'id': "arrowhead", // 矢印の位置を一番後ろから手前に少しずらす 'refX': ref1, 'refY': 2, 'markerWidth': 4, 'markerHeight': 4, 'orient': "auto" }); marker.append("path") .attr({ d: "M 0,0 V 4 L4,2 Z", fill: "steelblue" }); var color = d3.scale.category10(); var g = svg.selectAll('g') .data(carray).enter().append('g') .attr({ transform: function(d) { return "translate(" + d[0] + "," + d[1] + ")"; }, }); g.append('circle') .attr({ 'r': function(d) { return d[2]; }, 'fill': function(d,i) { return color(i); }, }); g.append('text') .attr({ 'text-anchor': "middle", 'dy': ".35em", 'fill': 'white', }) .text(function(d,i) { return i+1; }); var line = d3.svg.line() .interpolate('basis') .x(function(d) {return d[0];}) .y(function(d) {return d[1];}); var path = svg.append('path') .attr({ 'd': line(carray), 'stroke': 'lightgreen', 'stroke-width': 5, 'fill': 'none', 'marker-end':"url(#arrowhead)", }); // pathの長さを調べて、丸の半径2個分+矢印を後ろに下げる分の長さを引きます。 var totalLength = path.node().getTotalLength(); var t = totalLength - (r1+r2+ref1); path.attr({ // 破線の指定を行います。 'stroke-dasharray': "0 " + r1 + " " + t + " " + r2, // 破線の開始相対位置を指定します 'stroke-dashoffset': 0, }); }; ``` 破線の指定のところは、線を描く長さ、線を描かない長さ、線描く長さというように交互に指定します。 ここでは、最初0描いて(まったく描かないで)、丸1の半径分描かないで、その後計算した長さ分描いて、その後丸2の半径分描かないとしています。計算上ちょっと余るのですが、その先はまた0描いて、丸1の半径分描かないで…と指定を繰り返すので、実質問題なさそうです。 詳しくは、[Chained transitions between points on a path/line](http://bl.ocks.org/explunit/6082362)の実装が参考になります。 で、描画結果はこのようになりました。だいぶ思った通りになりました。  要素を見ると、かなり複雑になっていますが、ここまで一通り記事を眺めたら、なんとなくわかるのではないでしょうか。 ``` <div id="example"> <svg width="640" height="480"> <defs> <marker id="arrowhead" refX="8" refY="2" markerWidth="4" markerHeight="4" orient="auto"> <path d="M 0,0 V 4 L4,2 Z" fill="steelblue"></path> </marker> </defs> <g transform="translate(100,90)"> <circle r="30" fill="#1f77b4"></circle> <text text-anchor="middle" dy=".35em" fill="white">1</text> </g> <g transform="translate(200,120)"> <circle r="20" fill="#ff7f0e"></circle> <text text-anchor="middle" dy=".35em" fill="white">2</text> </g> <path d="M100,90L200,120" stroke="lightgreen" stroke-width="5" fill="none" marker-end="url(#arrowhead)" stroke-dasharray="0 30 46.40306854248047 20" stroke-dashoffset="0"></path> </svg> </div> ``` ## まとめ d3.jsを使って何かしようとする時に、どのような構造になるのかがわかっていると、学習もしやすくなりますし、理解もはやくなると思います。 d3.jsの華やかなサンプルやアニメーションに目が眩みがちになりますが、基礎を抑えておくことが勝利への近道です。 |
|
| 476位 |
|
|||
|
00:31:53 |
(フリーランス 所属) |
|
最近、vimの設定をやり直すスイッチが入って大分更新されたので改めて久々にvimrcを晒す。
ファイル自体は[joker1007/dotfiles](https://github.com/joker1007/dotfiles "joker1007/dotfiles")にある。 もし参考にされる方が居たら、丸々コピーすると色々問題あるかもしれないんで、適当に一部を抜粋するのが良いと思います。 特にキーマッピングは慣れがあるので、自分で決めた方が良いです。 実際には必要無いけど、環境に依って使うかもしれない設定とか混じってます。 文字コード周りは正直微妙。もうちょっと良い設定があったら知りたい。 macvim-kaoriya向けのlibrubyのローディングはファイル名調整しないと駄目かも。 最近は自分でコンパイルオプション弄ってrbenvで入れたrubyとリンクさせてるので、自分自身は使っていない設定。 vimrcの下にsnippet定義も書いてあります。 ## vimrc ```vim:vimrc set nocompatible " 文字コード, 改行コード {{{ set encoding=utf-8 set fileencodings=ucs_bom,utf8,ucs-2le,ucs-2 set fileformats=unix,dos,mac " from ずんWiki http://www.kawaz.jp/pukiwiki/?vim#content_1_7 " 文字コードの自動認識 if &encoding !=# 'utf-8' set encoding=japan set fileencoding=japan endif if has('iconv') let s:enc_euc = 'euc-jp' let s:enc_jis = 'iso-2022-jp' " iconvがeucJP-msに対応しているかをチェック if iconv("\x87\x64\x87\x6a", 'cp932', 'eucjp-ms') ==# "\xad\xc5\xad\xcb" let s:enc_euc = 'eucjp-ms' let s:enc_jis = 'iso-2022-jp-3' " iconvがJISX0213に対応しているかをチェック elseif iconv("\x87\x64\x87\x6a", 'cp932', 'euc-jisx0213') ==# "\xad\xc5\xad\xcb" let s:enc_euc = 'euc-jisx0213' let s:enc_jis = 'iso-2022-jp-3' endif " fileencodingsを構築 if &encoding ==# 'utf-8' let s:fileencodings_default = &fileencodings let &fileencodings = s:enc_jis .','. s:enc_euc .',cp932' let &fileencodings = s:fileencodings_default .','. &fileencodings unlet s:fileencodings_default else let &fileencodings = &fileencodings .','. s:enc_jis set fileencodings+=utf-8,ucs-2le,ucs-2 if &encoding =~# '^\(euc-jp\|euc-jisx0213\|eucjp-ms\)$' set fileencodings+=cp932 set fileencodings-=euc-jp set fileencodings-=euc-jisx0213 set fileencodings-=eucjp-ms let &encoding = s:enc_euc let &fileencoding = s:enc_euc else let &fileencodings = &fileencodings .','. s:enc_euc endif endif " 定数を処分 unlet s:enc_euc unlet s:enc_jis endif " }}} if has('vim_starting') set runtimepath+=~/.vim/bundle/neobundle.vim/ endif call neobundle#rc(expand('~/.vim/bundle/')) " NeoBundle {{{ " Let NeoBundle manage NeoBundle NeoBundleFetch 'Shougo/neobundle.vim' NeoBundle 'Shougo/vimproc', { \ 'build' : { \ 'windows' : 'make -f make_mingw32.mak', \ 'cygwin' : 'make -f make_cygwin.mak', \ 'mac' : 'make -f make_mac.mak', \ 'unix' : 'make -f make_unix.mak', \ }, \ } NeoBundle 'tyru/eskk.vim' NeoBundle 'tyru/skkdict.vim' " colorschemes NeoBundle 'altercation/vim-colors-solarized' NeoBundle 'baskerville/bubblegum' NeoBundle 'nanotech/jellybeans.vim' NeoBundle 'w0ng/vim-hybrid' NeoBundle 'vim-scripts/twilight' NeoBundle 'jonathanfilip/vim-lucius' NeoBundle 'jpo/vim-railscasts-theme' NeoBundle 'tpope/vim-rails' NeoBundle 'vim-ruby/vim-ruby' NeoBundle 'tpope/vim-cucumber' NeoBundle 'thinca/vim-quickrun' NeoBundle 'scrooloose/nerdcommenter' NeoBundle 'thinca/vim-ref' NeoBundle 'taka84u9/vim-ref-ri' NeoBundle 'ujihisa/ref-hoogle' NeoBundle 'vim-scripts/surround.vim' NeoBundle 'vim-scripts/L9' NeoBundle 'vim-scripts/YankRing.vim' NeoBundle 'vim-scripts/grep.vim' NeoBundle 'vim-scripts/sudo.vim' NeoBundle 'vim-scripts/errormarker.vim' NeoBundle 'vim-scripts/AnsiEsc.vim' NeoBundle 'kana/vim-smartchr' NeoBundle 'kana/vim-textobj-user' NeoBundle 'nelstrom/vim-textobj-rubyblock' NeoBundle 'motemen/hatena-vim' NeoBundle 'kchmck/vim-coffee-script' NeoBundle 'carlosvillu/coffeScript-VIM-Snippets' NeoBundle 'mattn/emmet-vim' NeoBundle 'claco/jasmine.vim' NeoBundle 'digitaltoad/vim-jade' NeoBundle 'tpope/vim-haml' NeoBundle 'nono/vim-handlebars' NeoBundle 'juvenn/mustache.vim' NeoBundle 'nathanaelkane/vim-indent-guides' NeoBundle 'kana/vim-submode' NeoBundle 'thinca/vim-poslist' NeoBundle 'thinca/vim-visualstar' NeoBundle 'Lokaltog/vim-easymotion' NeoBundle 'taku-o/vim-toggle' NeoBundle 'majutsushi/tagbar' NeoBundle 'thinca/vim-qfreplace' NeoBundle 'mattn/webapi-vim' NeoBundle 'eagletmt/ghcmod-vim' NeoBundle 'ujihisa/neco-ghc' NeoBundle 'dag/vim2hs' NeoBundle 'pbrisbin/html-template-syntax' NeoBundle 'tyru/open-browser.vim' NeoBundle 'kana/vim-textobj-indent' NeoBundle 'godlygeek/tabular' NeoBundle 'scrooloose/syntastic' NeoBundle 'rking/ag.vim' NeoBundle 'moro/vim-review' NeoBundle 'bling/vim-airline' NeoBundle 'basyura/bitly.vim' NeoBundle 'mattn/favstar-vim' NeoBundleLazy 'basyura/twibill.vim' NeoBundleLazy 'basyura/TweetVim', 'dev', { \ 'depends' : ['basyura/twibill.vim', 'tyru/open-browser.vim' ], \ 'autoload' : { \ 'commands' : [ "TweetVimHomeTimeline", "TweetVimSay", "TweetVimUserStream", "TweetVimUserTimeline" ] \ } \} NeoBundle 'tpope/vim-fugitive' NeoBundle 'tsukkee/unite-help' NeoBundle 'ujihisa/unite-gem' NeoBundle 'thinca/vim-unite-history' NeoBundle 'h1mesuke/unite-outline' NeoBundle 'eagletmt/unite-haddock' NeoBundle 'tsukkee/unite-tag' NeoBundle 'rhysd/unite-ruby-require.vim' NeoBundleLazy 'Shougo/unite.vim', { \ 'autoload' : { \ 'commands' : [ "Unite", "UniteWithBufferDir", "UniteWithCurrentDir" ] \ } \} NeoBundleLazy 'Shougo/neosnippet' NeoBundle 'Rip-Rip/clang_complete' if has('lua') NeoBundleLazy 'Shougo/neocomplete', { \ 'depends' : ['Shougo/neosnippet', 'Shougo/context_filetype.vim'], \ 'vim_version' : '7.3.885', \ 'autoload' : { \ 'insert' : 1, \ } \} else NeoBundleLazy 'Shougo/neocomplcache', { \ 'depends' : ["Shougo/neosnippet"], \ 'autoload' : { \ 'insert' : 1, \ } \} endif NeoBundleLazy 'Shougo/vimfiler', { \ 'depends' : ["Shougo/unite.vim"], \ 'autoload' : { \ 'commands' : [ "VimFilerTab", "VimFiler", "VimFilerExplorer", "VimFilerBufferDir" ], \ 'mappings' : ['<Plug>(vimfiler_switch)'], \ 'explorer' : 1, \ } \} NeoBundleLazy 'Shougo/vimshell', { \ 'depends' : 'Shougo/vimproc', \ 'autoload' : { \ 'commands' : [{ 'name' : 'VimShell', \ 'complete' : 'customlist,vimshell#complete'}, \ 'VimShellExecute', 'VimShellInteractive', \ 'VimShellTerminal', 'VimShellPop'], \ 'mappings' : ['<Plug>(vimshell_switch)'] \ }} NeoBundleLazy 'mattn/gist-vim', { \ 'autoload' : { \ 'commands' : [ "Gist" ] \ } \} NeoBundleLazy 'sjl/gundo.vim', { \ 'autoload' : { \ 'commands' : [ "GundoShow", "GundoToggle" ] \ } \} NeoBundleLazy 'kana/vim-altr', { \ 'autoload' : { \ 'mappings' : ['<Plug>(altr-forward)', '<Plug>(altr-back)'], \ } \} NeoBundle 'joker1007/vim-ruby-heredoc-syntax' NeoBundle 'joker1007/vim-markdown-quote-syntax' " }}} syntax enable filetype plugin indent on NeoBundleCheck if filereadable(expand('~/.vimrc.local')) execute 'source' expand('~/.vimrc.local') endif " augroup init (from tyru's vimrc) augroup vimrc autocmd! augroup END command! \ -bang -nargs=* \ MyAutocmd \ autocmd<bang> vimrc <args> " Basic Setting {{{ set bs=indent,eol,start " allow backspacing over everything in insert mode set ai " always set autoindenting on set nobackup set noswapfile " No Swap set viminfo=%,'100,<500,h set history=100 " keep 100 lines of command line history set ruler " show the cursor position all the time set nu " show line number set ambiwidth=double set display=uhex " 表示できない文字を16進数で表示 set scrolloff=5 " 常にカーソル位置から5行余裕を取る set virtualedit=block " 矩形選択でカーソル位置の制限を解除 set autoread " 他でファイルが編集された時に自動で読み込む set background=dark " Space prefix nnoremap [space] <Nop> nmap <Space> [space] xmap <Space> [space] " Edit vimrc nmap [space]v :edit $MYVIMRC<CR> nmap [space]g :edit $MYGVIMRC<CR> nnoremap <C-H> :<C-U>help<Space> " 編集中の行に下線を引く MyAutocmd InsertLeave * setlocal nocursorline MyAutocmd InsertEnter * setlocal cursorline MyAutocmd InsertLeave * highlight StatusLine ctermfg=145 guifg=#c2bfa5 guibg=#000000 MyAutocmd InsertEnter * highlight StatusLine ctermfg=12 guifg=#1E90FF " タブストップ設定 set tabstop=2 set shiftwidth=2 set softtabstop=0 set expandtab " 折り畳み設定 set foldmethod=marker nmap <silent> ,fc :<C-U>%foldclose<CR> nmap <silent> ,fo :<C-U>%foldopen<CR> " 検索設定 set incsearch set hlsearch set ignorecase set smartcase set wrapscan nohlsearch "reset highlight nnoremap <silent> [space]/ :noh<CR> map * <Plug>(visualstar-*)N map # <Plug>(visualstar-#)N " ステータスライン表示 set laststatus=2 set statusline=%<%f\ %m%r%h%w%{'['.(&fenc!=''?&fenc:&enc).']['.&ff.']'}%y%{tagbar#currenttag('[%s]','')}%{fugitive#statusline()}%{SyntasticStatuslineFlag()}%{exists('*SkkGetModeStr')?SkkGetModeStr():''}%=%l/%L,%c%V%8P\ set noshowmode set wildmenu set cmdheight=2 set wildmode=list:full set showcmd " tabline set showtabline=2 nnoremap <silent> <S-Right> :<C-U>tabnext<CR> nnoremap <silent> <S-Left> :<C-U>tabprevious<CR> nnoremap L :<C-U>tabnext<CR> nnoremap H :<C-U>tabprevious<CR> " completion set complete=.,w,b,u,t,i,d,k " クリップボード設定 set clipboard=unnamed " バッファ切り替え set hidden " Tab表示 set list set listchars=tab:>-,trail:< " タイトルを表示 set title " 対応括弧を表示 set showmatch " 自動折り返しを日本語に対応させるスクリプト用の設定 set formatoptions+=mM " matchitスクリプトの読み込み source $VIMRUNTIME/macros/matchit.vim " jkを直感的に nnoremap <silent> j gj nnoremap <silent> gj j nnoremap <silent> k gk nnoremap <silent> gk k nnoremap <silent> $ g$ nnoremap <silent> g$ $ vnoremap <silent> j gj vnoremap <silent> gj j vnoremap <silent> k gk vnoremap <silent> gk k vnoremap <silent> $ g$ vnoremap <silent> g$ $ " JとDで半ページ移動 nnoremap J <C-D> nnoremap K <C-U> " <Space>h or <Space>lで行頭か行末に移動する noremap [space]h ^ noremap [space]l $ " 編集中のファイルのディレクトリに移動 nnoremap ,d :execute ":lcd" . expand("%:p:h")<CR> " 最後に編集した場所にカーソルを移動する MyAutocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g`\"" | endif " colorscheme " 全角スペースをハイライト MyAutocmd ColorScheme * highlight ZenkakuSpace ctermbg=239 guibg=#405060 MyAutocmd VimEnter,WinEnter * call matchadd('ZenkakuSpace', ' ') if stridx($TERM, "xterm-256color") >= 0 let g:solarized_termcolors= 256 let g:solarized_contrast = "high" let g:solarized_termtrans = 1 colorscheme solarized else colorscheme solarized endif " 256色モード if stridx($TERM, "xterm-256color") >= 0 set t_Co=256 else set t_Co=16 endif " mark, register確認 " nnoremap ,m :<C-u>marks<CR> nnoremap ,r :<C-u>registers<CR> "---------------------------------------------------------}}} " vim-airline let g:airline_powerline_fonts = 1 let g:airline_theme = "wombat" " surround.vim {{{ nmap ,( csw( nmap ,) csw) nmap ,{ csw{ nmap ,} csw} nmap ,[ csw[ nmap ,] csw] nmap ,' csw' nmap ," csw" "}}} " Insert Mode Mapping {{{ inoremap <C-K> <ESC>"*pa imap <C-E> <END> imap <C-A> <HOME> " }}} " from http://vim-users.jp/2011/04/hack214/ {{{ vnoremap ( t( vnoremap ) t) vnoremap ] t] vnoremap [ t[ onoremap ( t( onoremap ) t) onoremap ] t] onoremap [ t[ " }}} " set paste nnoremap <silent> ,p :<C-U>set paste!<CR>:<C-U>echo("Toggle PasteMode => " . (&paste == 0 ? "Off" : "On"))<CR> " eskk {{{ let g:eskk#large_dictionary = { \ 'path': $HOME . "/.vim/dict/skk/SKK-JISYO.L", \ 'sorted': 1, \ 'encoding': 'euc-jp', \} " }}} " wq alias command! -nargs=0 Wq wq " UTF8、SJIS(CP932)、EUCJPで開き直す {{{ command! -bang -nargs=? Utf8 \ edit<bang> ++enc=utf-8 <args> command! -bang -nargs=? Sjis \ edit<bang> ++enc=cp932 <args> command! -bang -nargs=? Euc \ edit<bang> ++enc=eucjp <args> " }}} " YAMLファイル用タブストップ設定 au FileType yaml setlocal expandtab ts=2 sw=2 fenc=utf-8 " actionscript mxml用のファイルタイプ設定 MyAutocmd BufNewFile,BufRead *.as set filetype=actionscript MyAutocmd BufNewFile,BufRead *.mxml set filetype=mxml " バッファ切り替え {{{ nmap [space]n :<C-U>bnext<CR> nmap [space]p :<C-U>bprevious<CR> nnoremap <Leader>1 :e #1<CR> nnoremap <Leader>2 :e #2<CR> nnoremap <Leader>3 :e #3<CR> nnoremap <Leader>4 :e #4<CR> nnoremap <Leader>5 :e #5<CR> nnoremap <Leader>6 :e #6<CR> nnoremap <Leader>7 :e #7<CR> nnoremap <Leader>8 :e #8<CR> nnoremap <Leader>9 :e #9<CR> " バッファ一覧 nmap ,b :buffers<CR> " }}} " NERDCommenter let NERDSpaceDelims = 1 " smartchr {{{ function! s:EnableSmartchrBasic() inoremap <buffer><expr> + smartchr#one_of(' + ', '+', '++') inoremap <buffer><expr> & smartchr#one_of(' & ', ' && ', '&') inoremap <buffer><expr> , smartchr#one_of(', ', ',') inoremap <buffer><expr> <Bar> smartchr#one_of('<Bar>', ' <Bar><Bar> ', '<Bar><Bar>') inoremap <buffer><expr> = search('\(&\<bar><bar>\<bar>+\<bar>-\<bar>/\<bar>>\<bar><\) \%#', 'bcn')? '<bs>= ' : search('\(\*\<bar>!\)\%#')? '= ' : smartchr#one_of(' = ', ' == ', '=') endfunction function! s:EnableSmartchrRegExp() inoremap <buffer><expr> ~ search('\(!\<bar>=\) \%#', 'bcn')? '<bs>~ ' : '~' endfunction function! s:EnableSmartchrRubyHash() inoremap <buffer><expr> > smartchr#one_of('>', ' => ') endfunction function! s:EnableSmartchrHaml() call s:EnableSmartchrRubyHash() inoremap <buffer> [ []<Esc>i inoremap <buffer> { {}<Esc>i endfunction function! s:EnableSmartchrCoffeeFunction() inoremap <buffer><expr> > smartchr#one_of('>', ' ->') endfunction MyAutocmd FileType c,cpp,php,python,javascript,ruby,coffee,vim call s:EnableSmartchrBasic() MyAutocmd FileType python,ruby,coffee,vim call s:EnableSmartchrRegExp() MyAutocmd FileType ruby call s:EnableSmartchrRubyHash() MyAutocmd FileType ruby,eruby setlocal tags+=~/rtags MyAutocmd FileType haml call s:EnableSmartchrHaml() MyAutocmd FileType coffee call s:EnableSmartchrCoffeeFunction() " }}} " hatena.vim let g:hatena_user = 'joker1007' " shファイルの保存時にはファイルのパーミッションを755にする {{{ function! s:ChangeShellScriptPermission() if !has("win32") if &ft =~ "\\(z\\|c\\|ba\\)\\?sh$" && expand('%:t') !~ "\\(zshrc\\|zshenv\\)$" call system("chmod 755 " . shellescape(expand('%:p'))) echo "Set permission 755" endif endif endfunction MyAutocmd BufWritePost * call s:ChangeShellScriptPermission() " }}} " QFixHowm用設定======================================================{{{ set runtimepath+=~/qfixapp " ファイル拡張子をmkdにする let howm_filename = '%Y-%m-%d-%H%M%S.mkd' " ファイルタイプをmarkdownにする let QFixHowm_FileType = 'markdown' " 折り畳み正規表現 let QFixHowm_FoldingPattern = '^[=.*#]' " タイトル記号 let QFixHowm_Title = '#' "キーマップリーダー let QFixHowm_Key = 'g' "howm_dirはファイルを保存したいディレクトリを設定。 let howm_dir = '~/Dropbox/howm' let howm_fileencoding = 'utf-8' let howm_fileformat = 'unix' if has('win32') let mygrepprg = 'yagrep' elseif has('unix') let mygrepprg = 'grep' endif let QFixHowm_MruFileMax = 30 let QFixHowm_RecentMode = 2 " リネーム後のファイル名制限 let QFixHowm_FilenameLen = 80 "ブラウザの指定 if has('win32') let QFixHowm_OpenURIcmd = '!start "C:\firefox\firefox.exe" %s' elseif has('mac') let QFixHowm_OpenURIcmd = "call system('/usr/bin/open -a /Applications/Firefox.app/Contents/MacOS/firefox-bin %s')" elseif has('unix') let QFixHowm_OpenURIcmd = "call system('xdg-open %s')" endif " }}} " ポップアップメニューのカラーを設定 MyAutocmd Syntax * hi Pmenu ctermfg=15 ctermbg=18 guibg=#666666 MyAutocmd Syntax * hi PmenuSel ctermbg=39 ctermfg=0 guibg=#8cd0d3 guifg=#666666 MyAutocmd Syntax * hi PmenuSbar guibg=#333333 " TOhtml let g:html_number_lines = 0 let g:html_use_css = 1 let g:use_xhtml = 1 let g:html_use_encoding = 'utf-8' " grep.vim let Grep_Default_Options = '-i' nnoremap <C-G><C-G> :<C-u>GrepBuffer<Space> nnoremap <C-G><C-W> :<C-u>GrepBuffer<Space><C-r>= expand('<cword>')<CR> " quickrun{{{ " エスケープカラーを表示する。 MyAutocmd FileType quickrun AnsiEsc " ヤンクを取りやすいようにconcealcursorを無効にする。 MyAutocmd FileType quickrun setlocal concealcursor="" vnoremap <leader>q :QuickRun >>buffer -mode v<CR> let g:quickrun_config = {} let g:quickrun_config._ = {'runner' : 'vimproc'} let g:quickrun_config['rspec/bundle'] = { \ 'type': 'rspec/bundle', \ 'command': 'rspec', \ 'outputter/buffer/split': 'botright', \ 'exec': 'bundle exec %c %o --color --tty %s' \} let g:quickrun_config['rspec/normal'] = { \ 'type': 'rspec/normal', \ 'command': 'rspec', \ 'outputter/buffer/split': 'botright', \ 'exec': '%c %o --color --tty %s' \} let g:quickrun_config['rspec/zeus'] = { \ 'type': 'rspec/zeus', \ 'command': 'rspec', \ 'outputter/buffer/split': 'botright', \ 'exec': 'zeus test %o --color --tty %s' \} let g:quickrun_config['rspec/spring'] = { \ 'type': 'rspec/spring', \ 'command': 'rspec', \ 'outputter/buffer/split': 'botright', \ 'exec': 'spring rspec %o --color --tty %s' \} let g:quickrun_config['cucumber/bundle'] = { \ 'type': 'cucumber/zeus', \ 'command': 'cucumber', \ 'outputter/buffer/split': 'botright', \ 'exec': 'bundle exec %c %o --color %s' \} let g:quickrun_config['cucumber/zeus'] = { \ 'type': 'cucumber/zeus', \ 'command': 'cucumber', \ 'outputter/buffer/split': 'botright', \ 'exec': 'zeus cucumber %o --color %s' \} let g:quickrun_config['cucumber/spring'] = { \ 'type': 'cucumber/spring', \ 'command': 'cucumber', \ 'outputter/buffer/split': 'botright', \ 'exec': 'spring cucumber %o --color %s' \} function! s:RSpecQuickrun() if exists('g:use_spring_rspec') && g:use_spring_rspec == 1 let b:quickrun_config = {'type' : 'rspec/spring'} elseif exists('g:use_zeus_rspec') && g:use_zeus_rspec == 1 let b:quickrun_config = {'type' : 'rspec/zeus'} else let b:quickrun_config = {'type' : 'rspec/bundle'} endif nnoremap <expr><silent> <Leader>lr "<Esc>:QuickRun -cmdopt \"-l " . line(".") . "\"<CR>" endfunction MyAutocmd BufReadPost *_spec.rb call s:RSpecQuickrun() function! s:CucumberQuickrun() if exists('g:use_spring_cucumber') && g:use_spring_cucumber == 1 let b:quickrun_config = {'type' : 'cucumber/spring'} elseif exists('g:use_zeus_cucumber') && g:use_zeus_cucumber == 1 let b:quickrun_config = {'type' : 'cucumber/zeus'} else let b:quickrun_config = {'type' : 'cucumber/bundle'} endif nnoremap <expr><silent> <Leader>lr "<Esc>:QuickRun -cmdopt \"-l " . line(".") . "\"<CR>" endfunction MyAutocmd BufReadPost *.feature call s:CucumberQuickrun() function! s:SetUseSpring() let g:use_spring_rspec = 1 let g:use_zeus_rspec = 0 let g:use_spring_cucumber = 1 let g:use_zeus_cucumber = 0 endfunction function! s:SetUseZeus() let g:use_zeus_rspec = 1 let g:use_spring_rspec = 0 let g:use_zeus_cucumber = 1 let g:use_spring_cucumber = 0 endfunction function! s:SetUseBundle() let g:use_zeus_rspec = 0 let g:use_spring_rspec = 0 let g:use_zeus_cucumber = 0 let g:use_spring_cucumber = 0 endfunction command! -nargs=0 UseSpringRSpec let b:quickrun_config = {'type' : 'rspec/spring'} | call s:SetUseSpring() command! -nargs=0 UseZeusRSpec let b:quickrun_config = {'type' : 'rspec/zeus'} | call s:SetUseZeus() command! -nargs=0 UseBundleRSpec let b:quickrun_config = {'type' : 'rspec/bundle'} | call s:SetUseBundle() command! -nargs=0 UseSpringCucumber let b:quickrun_config = {'type' : 'cucumber/spring'} | call s:SetUseSpring() command! -nargs=0 UseZeusCucumber let b:quickrun_config = {'type' : 'cucumber/zeus'} | call s:SetUseZeus() command! -nargs=0 UseBundleCucumber let b:quickrun_config = {'type' : 'cucumber/bundle'} | call s:SetUseBundle() " }}} " libruby load if has('gui_macvim') && has('kaoriya') let s:ruby_libdir = system("ruby -rrbconfig -e 'print RbConfig::CONFIG[\"libdir\"]'") let s:ruby_libruby = s:ruby_libdir . '/libruby.dylib' if filereadable(s:ruby_libruby) let $RUBY_DLL = s:ruby_libruby endif endif " poslist nmap <C-O> <Plug>(poslist-prev-pos) nmap <C-I> <Plug>(poslist-next-pos) let g:poslist_histsize = 2000 " Unite.vim {{{ nnoremap [unite] <Nop> nmap ,u [unite] nnoremap <silent> [unite]ff :<C-u>Unite -buffer-name=files buffer file file/new<CR> nnoremap <silent> [unite]fr :<C-u>Unite -buffer-name=files file_mru<CR> nnoremap <silent> [unite]fa :<C-u>Unite -buffer-name=files file_rec/async<CR> nnoremap <silent> [unite]d :<C-u>Unite -buffer-name=files directory_mru<CR> nnoremap <silent> [unite]vff :<C-u>Unite -vertical -buffer-name=files buffer file file/new<CR> nnoremap <silent> [unite]vfr :<C-u>Unite -vertical -buffer-name=files file_mru <CR> nnoremap <silent> [unite]vp :<C-u>Unite -vertical -winwidth=45 -no-quit -buffer-name=files buffer file<CR> nnoremap <silent> [unite]F :<C-u>UniteWithBufferDir -buffer-name=files buffer file file/new<CR> nnoremap <silent> [unite]vF :<C-u>UniteWithBufferDir -vertical -winwidth=45 -buffer-name=files buffer file file/new<CR> nnoremap <silent> [unite]b :<C-u>Unite -buffer-name=buffers -prompt=Buffer>\ buffer<CR> nnoremap <silent> [unite]vb :<C-u>Unite -vertical -buffer-name=buffers -prompt=Buffer>\ buffer<CR> nnoremap <silent> [unite]vB :<C-u>Unite -vertical -buffer-name=buffers -prompt=Buffer>\ -winwidth=45 -no-quit buffer<CR> nnoremap <silent> [unite]o :<C-u>Unite -vertical -winwidth=45 -wrap -no-quit -toggle -buffer-name=outline outline<CR> nnoremap <silent> [unite]" :<C-u>Unite -buffer-name=register -prompt=">\ register<CR> nnoremap <silent> [unite]c :<C-u>Unite -buffer-name=commands history/command<CR> nnoremap <silent> [unite]C :<C-u>Unite -buffer-name=commands command<CR> nnoremap <silent> [unite]s :<C-u>Unite -buffer-name=snippets snippet<CR> nnoremap <silent> [unite]u :<C-u>Unite source<CR> nnoremap <silent> [unite]l :<C-u>Unite -buffer-name=lines line<CR> nnoremap <silent> [unite]m :<C-u>Unite -buffer-name=bookmark -prompt=bookmark> bookmark<CR> nnoremap <silent> [unite]rm :<C-u>Unite -buffer-name=ref -prompt=ref> ref/man<CR> nnoremap <silent> [unite]g :<C-u>Unite -buffer-name=grep grep<CR> nnoremap <silent> [unite]hd :<C-u>Unite haddock -start-insert<CR> let s:bundle = neobundle#get("unite.vim") function! s:bundle.hooks.on_source(bundle) let g:unite_enable_start_insert = 1 let g:unite_winheight = 15 let g:unite_winwidth = 45 let g:unite_source_grep_max_candidates = 500 " unite-ruby-require let g:unite_source_ruby_require_ruby_command = expand("~/.rbenv/shims/ruby") " ディレクトリに対するブックマークはvimfilerをデフォルトアクションにする call unite#custom_default_action('source/bookmark/directory', 'vimfiler') call unite#custom#source('buffer,file,file_mru', 'sorters', 'sorter_rank') call unite#custom#source('file_rec,file_rec/async', 'filters', \ ['converter_relative_word', 'matcher_default', \ 'sorter_rank', 'converter_relative_abbr', 'converter_file_directory']) call unite#custom#source( \ 'file_mru', 'converters', \ ['converter_file_directory']) function! s:unite_my_settings() " Overwrite settings. nmap <buffer> l <Plug>(unite_choose_action) nmap <buffer> <C-c> <Plug>(unite_choose_action) imap <buffer> <TAB> <Plug>(unite_select_next_line) nmap <buffer> <C-z> <Plug>(unite_toggle_transpose_window) imap <buffer> <C-z> <Plug>(unite_toggle_transpose_window) imap <buffer> <C-y> <Plug>(unite_narrowing_path) nmap <buffer> <C-y> <Plug>(unite_narrowing_path) nmap <buffer> <C-j> <Plug>(unite_toggle_auto_preview) nmap <silent><buffer><expr> f unite#do_action('vimfiler') " grep bufferの時はrをreplaceアクションにマップする let unite = unite#get_current_unite() if unite.buffer_name =~# '^grep' nnoremap <silent><buffer><expr> r unite#do_action('replace') else nnoremap <silent><buffer><expr> r unite#do_action('rename') endif nnoremap <buffer><expr> S unite#mappings#set_current_filters( \ empty(unite#mappings#get_current_filters()) ? ['sorter_reverse'] : []) endfunction MyAutocmd FileType unite call s:unite_my_settings() endfunction unlet s:bundle " }}} " Gist.vim {{{ nnoremap [gist] <Nop> nmap ,s [gist] nnoremap [gist]g :Gist<CR> nnoremap [gist]p :Gist -p<CR> nnoremap [gist]e :Gist -e<CR> nnoremap [gist]d :Gist -d<CR> nnoremap [gist]l :Gist -l<CR> let s:bundle = neobundle#get("gist-vim") function! s:bundle.hooks.on_source(bundle) if has("mac") let g:gist_clip_command = 'pbcopy' elseif has("unix") let g:gist_clip_command = 'xclip -selection clipboard' endif let g:gist_detect_filetype = 1 let g:gist_open_browser_after_post = 1 let g:gist_show_privates = 1 endfunction unlet s:bundle " }}} " Fugitive {{{ nnoremap [git] <Nop> nmap ,g [git] nnoremap [git]d :<C-u>Gdiff HEAD<Enter> nnoremap [git]s :<C-u>Gstatus<Enter> nnoremap [git]l :<C-u>Glog<Enter> nnoremap [git]a :<C-u>Gwrite<Enter> nnoremap [git]c :<C-u>Gcommit<Enter> nnoremap [git]C :<C-u>Git commit --amend<Enter> nnoremap [git]b :<C-u>Gblame<Enter> " ftdetect is often failed MyAutocmd BufEnter * if expand("%") =~ ".git/COMMIT_EDITMSG" | set ft=gitcommit | endif MyAutocmd BufEnter * if expand("%") =~ ".git/rebase-merge" | set ft=gitrebase | endif " }}} " project.vim let g:proj_window_width = 48 " vimfiler {{{ nnoremap <silent> ,vf :<C-U>VimFiler<CR> let s:bundle = neobundle#get('vimfiler') function! s:bundle.hooks.on_source(bundle) let g:vimfiler_as_default_explorer = 1 let g:vimfiler_max_directory_histories = 100 function s:ChangeVimfilerKeymap() nmap <buffer> a <Plug>(vimfiler_toggle_mark_all_lines) " j k 移動でループしないように nmap <buffer> j j nmap <buffer> k k nmap <buffer> s <Plug>(vimfiler_select_sort_type) nmap <End> <Plug>(vimfiler_clear_mark_all_lines) nmap <buffer> @ <Plug>(vimfiler_set_current_mask) nmap <buffer> V <Plug>(vimfiler_quick_look) endfunction MyAutocmd FileType vimfiler call s:ChangeVimfilerKeymap() if filereadable(expand('~/.vimfiler.local')) execute 'source' expand('~/.vimfiler.local') endif endfunction unlet s:bundle " }}} " vimshell {{{ nnoremap <silent> ,vs :<C-U>VimShell<CR> function! g:my_chpwd(args, context) call vimshell#execute('ls') endfunction let s:bundle = neobundle#get("vimshell") function! s:bundle.hooks.on_source(bundle) if has('win32') || has('win64') " Display user name on Windows. let g:vimshell_prompt = $USERNAME."% " else let g:vimshell_prompt = $USER . "@" . hostname() . "% " if has('mac') call vimshell#set_execute_file('html', 'gexe open -a /Applications/Firefox.app/Contents/MacOS/firefox') call vimshell#set_execute_file('avi,mp4,mpg,ogm,mkv,wmv,mov', 'gexe open -a /Applications/MPlayerX.app/Contents/MacOS/MPlayerX') endif endif "let g:vimshell_right_prompt = 'vimshell#vcs#info("(%s)-[%b] ", "(%s)-[%b|%a] ") . "[" . getcwd() . "]"' let g:vimshell_right_prompt = '"[" . getcwd() . "]"' let g:vimshell_max_command_history = 3000 MyAutocmd FileType vimshell \ call vimshell#altercmd#define('g', 'git') \| call vimshell#altercmd#define('l', 'll') \| call vimshell#altercmd#define('ll', 'ls -l') \| call vimshell#altercmd#define('be', 'bundle exec') \| call vimshell#altercmd#define('ra', 'rails') \| call vimshell#hook#add('chpwd', 'my_chpwd', 'g:my_chpwd') function! s:EarthquakeKeyMap() nnoremap <buffer><expr> o OpenBrowserLine() endfunction MyAutocmd FileType int-earthquake call s:EarthquakeKeyMap() endfunction unlet s:bundle " }}} " rubycomplete.vim {{{ MyAutocmd FileType ruby,eruby setlocal omnifunc=rubycomplete#Complete let g:rubycomplete_rails = 0 let g:rubycomplete_buffer_loading = 1 let g:rubycomplete_classes_in_global = 1 let g:rubycomplete_include_object = 1 let g:rubycomplete_include_object_space = 1 " let ruby_operators = 1 " }}} " For snippet_complete marker. if has('conceal') set conceallevel=2 concealcursor=i endif " neosnippet {{{ nnoremap <Space>se :<C-U>NeoSnippetEdit<CR> let s:bundle = neobundle#get('neosnippet') function! s:bundle.hooks.on_source(bundle) let g:neosnippet#snippets_directory = $HOME . '/.vim/snippets' " enable ruby & rails snippet only rails file function! s:RailsSnippet() if exists("b:rails_root") && (&filetype == "ruby") NeoSnippetSource ~/.vim/snippets/rails.snip endif endfunction function! s:RSpecSnippet() if (expand("%") =~ "_spec\.rb$") || (expand("%") =~ "^spec.*\.rb$") NeoSnippetSource ~/.vim/snippets/rspec.snip endif endfunction MyAutocmd BufEnter * call s:RailsSnippet() MyAutocmd BufEnter * call s:RSpecSnippet() endfunction unlet s:bundle " }}} " neocomplcache or neocomplete {{{ " Enable omni completion. MyAutocmd FileType css,scss setlocal omnifunc=csscomplete#CompleteCSS MyAutocmd FileType html,markdown setlocal omnifunc=htmlcomplete#CompleteTags MyAutocmd FileType javascript setlocal omnifunc=javascriptcomplete#CompleteJS MyAutocmd FileType python setlocal omnifunc=pythoncomplete#Complete MyAutocmd FileType xml setlocal omnifunc=xmlcomplete#CompleteTags MyAutocmd FileType sql setlocal omnifunc=sqlcomplete#Complete if has('lua') let s:bundle = neobundle#get('neocomplete') function! s:bundle.hooks.on_source(bundle) " Disable AutoComplPop. let g:acp_enableAtStartup = 0 " Use neocomplete. let g:neocomplete#enable_at_startup = 1 " Use smartcase. let g:neocomplete#enable_smart_case = 1 " Set minimum syntax keyword length. let g:neocomplete#auto_completion_start_length = 2 let g:neocomplete#manual_completion_start_length = 0 let g:neocomplete#sources#syntax#min_keyword_length = 3 let g:neocomplete#min_keyword_length = 2 let g:neocomplete#enable_prefetch = 1 " Define dictionary. let g:neocomplete#sources#dictionary#dictionaries = { \ 'default' : '', \ 'vimshell' : $HOME . '/.vimshell/command-history', \ } " キャッシュしないファイル名 let g:neocomplete#sources#buffer#disabled_pattern = '\.log\|\.log\.\|\.jax' " 自動補完を行わないバッファ名 let g:neocomplete#lock_buffer_name_pattern = '\.log\|\.log\.\|.*quickrun.*\|.jax' " Define keyword. if !exists('g:neocomplete#keyword_patterns') let g:neocomplete#keyword_patterns = {} endif let g:neocomplete#keyword_patterns['default'] = '\h\w*' " Plugin key-mappings. inoremap <expr><C-l> neocomplete#complete_common_string() " SuperTab like snippets behavior. imap <expr><TAB> neosnippet#expandable_or_jumpable() ? "\<Plug>(neosnippet_expand_or_jump)" : pumvisible() ? "\<C-n>" : "\<TAB>" smap <expr><TAB> neosnippet#expandable_or_jumpable() ? "\<Plug>(neosnippet_expand_or_jump)" : "\<TAB>" " Recommended key-mappings. " <CR>: close popup and save indent. inoremap <expr><CR> neocomplete#smart_close_popup() . "\<CR>" " <C-h>, <BS>: close popup and delete backword char. inoremap <expr><C-h> neocomplete#smart_close_popup()."\<C-h>" inoremap <expr><BS> neocomplete#smart_close_popup()."\<C-h>" inoremap <expr><C-y> neocomplete#close_popup() " AutoComplPop like behavior. "let g:neocomplete#enable_auto_select = 1 " Enable heavy omni completion. if !exists('g:neocomplete#sources#omni#input_patterns') let g:neocomplete#sources#omni#input_patterns = {} endif let g:neocomplete#sources#omni#input_patterns.ruby = '[^. *\t]\.\h\w*\|\h\w*::' let g:neocomplete#sources#omni#input_patterns.php = '[^. \t]->\h\w*\|\h\w*::' let g:neocomplete#sources#omni#input_patterns.c = '\%(\.\|->\)\h\w*' let g:neocomplete#sources#omni#input_patterns.cpp = '\h\w*\%(\.\|->\)\h\w*\|\h\w*::' " for TweetVim スクリーン名のキャッシュを利用して、neocomplcache で補完する if !exists('g:neocomplete#sources#dictionary#dictionaries') let g:neocomplete#sources#dictionary#dictionaries = {} endif let neco_dic = g:neocomplete#sources#dictionary#dictionaries let neco_dic.tweetvim_say = $HOME . '/.tweetvim/screen_name' " use clang_complete if !exists('g:neocomplete#force_omni_input_patterns') let g:neocomplete#force_omni_input_patterns = {} endif let g:neocomplete#force_overwrite_completefunc = 1 let g:neocomplete#force_omni_input_patterns.c = \ '[^.[:digit:] *\t]\%(\.\|->\)\w*' let g:neocomplete#force_omni_input_patterns.cpp = \ '[^.[:digit:] *\t]\%(\.\|->\)\w*\|\h\w*::\w*' let g:neocomplete#force_omni_input_patterns.objc = \ '[^.[:digit:] *\t]\%(\.\|->\)\w*' let g:neocomplete#force_omni_input_patterns.objcpp = \ '[^.[:digit:] *\t]\%(\.\|->\)\w*\|\h\w*::\w*' " clang_complete let g:clang_complete_auto = 0 let g:clang_auto_select = 0 "let g:clang_use_library = 1 endfunction unlet s:bundle else let s:bundle = neobundle#get('neocomplcache') function! s:bundle.hooks.on_source(bundle) " Disable AutoComplPop. let g:acp_enableAtStartup = 0 " Use neocomplcache. let g:neocomplcache_enable_at_startup = 1 " Use smartcase. let g:neocomplcache_enable_smart_case = 1 " Use camel case completion. let g:neocomplcache_enable_camel_case_completion = 1 " Use underbar completion. "let g:neocomplcache_enable_underbar_completion = 1 " Use fuzzy completion. " let g:neocomplcache_enable_fuzzy_completion = 1 " filename width let g:neocomplcache_max_menu_width = 40 " Set minimum syntax keyword length. let g:neocomplcache_auto_completion_start_length = 2 let g:neocomplcache_manual_completion_start_length = 0 let g:neocomplcache_min_syntax_length = 3 let g:neocomplcache_min_keyword_length = 2 let g:neocomplcache_plugin_completion_length = { \ 'snippets_complete' : 1, \ } " let g:neocomplcache_lock_buffer_name_pattern = '\*ku\*' let g:neocomplcache_enable_prefetch = 1 " Define dictionary. let g:neocomplcache_dictionary_filetype_lists = { \ 'default' : '', \ 'vimshell' : $HOME . '/.vimshell/command-history', \ } " キャッシュしないファイル名 let g:neocomplcache_disable_caching_file_path_pattern = '\.log\|\.log\.\|\.jax' " 自動補完を行わないバッファ名 let g:neocomplcache_lock_buffer_name_pattern = '\.log\|\.log\.\|.*quickrun.*\|.jax' " Define keyword. if !exists('g:neocomplcache_keyword_patterns') let g:neocomplcache_keyword_patterns = {} endif let g:neocomplcache_keyword_patterns['default'] = '\h\w*' " Plugin key-mappings. "imap <C-k> <Plug>(neosnippet_expand_or_jump) "smap <C-k> <Plug>(neosnippet_expand_or_jump) "inoremap <expr><C-g> neocomplcache#undo_completion() inoremap <expr><C-l> neocomplcache#complete_common_string() " SuperTab like snippets behavior. imap <expr><TAB> neosnippet#expandable_or_jumpable() ? "\<Plug>(neosnippet_expand_or_jump)" : pumvisible() ? "\<C-n>" : "\<TAB>" smap <expr><TAB> neosnippet#expandable_or_jumpable() ? "\<Plug>(neosnippet_expand_or_jump)" : "\<TAB>" " Recommended key-mappings. " <CR>: close popup and save indent. inoremap <expr><CR> neocomplcache#smart_close_popup() . "\<CR>" " <TAB>: completion. "inoremap <expr><TAB> pumvisible() ? "\<C-n>" : "\<TAB>" " <C-h>, <BS>: close popup and delete backword char. inoremap <expr><C-h> neocomplcache#smart_close_popup()."\<C-h>" inoremap <expr><BS> neocomplcache#smart_close_popup()."\<C-h>" inoremap <expr><C-y> neocomplcache#close_popup() "inoremap <expr><C-e> neocomplcache#cancel_popup() " AutoComplPop like behavior. "let g:neocomplcache_enable_auto_select = 1 " Shell like behavior(not recommended). "setlocal completeopt+=longest "let g:neocomplcache_enable_auto_select = 1 "let g:neocomplcache_disable_auto_complete = 1 "inoremap <expr><TAB> pumvisible() ? "\<Down>" : "\<TAB>" "inoremap <expr><CR> neocomplcache#smart_close_popup() . "\<CR>" " Enable heavy omni completion. if !exists('g:neocomplcache_omni_patterns') let g:neocomplcache_omni_patterns = {} endif let g:neocomplcache_omni_patterns.ruby = '[^. *\t]\.\h\w*\|\h\w*::' let g:neocomplcache_omni_patterns.php = '[^. \t]->\h\w*\|\h\w*::' let g:neocomplcache_omni_patterns.c = '\%(\.\|->\)\h\w*' let g:neocomplcache_omni_patterns.cpp = '\h\w*\%(\.\|->\)\h\w*\|\h\w*::' " for TweetVim スクリーン名のキャッシュを利用して、neocomplcache で補完する if !exists('g:neocomplcache_dictionary_filetype_lists') let g:neocomplcache_dictionary_filetype_lists = {} endif let neco_dic = g:neocomplcache_dictionary_filetype_lists let neco_dic.tweetvim_say = $HOME . '/.tweetvim/screen_name' endfunction unlet s:bundle endif " }}} " ref.vim let g:ref_open = 'vsplit' let g:ref_refe_cmd = "rurema" let g:ref_refe_version = 2 let g:ref_source_webdict_sites = { \ 'wikipedia:ja': 'http://ja.wikipedia.org/wiki/%s', \ 'weblio': 'http://ejje.weblio.jp/content/%s', \ } nmap ,rr :<C-U>Ref refe<Space> " indent-guides {{{ let g:indent_guides_enable_on_vim_startup = 1 let g:indent_guides_guide_size = 1 let g:indent_guides_auto_colors = 0 autocmd VimEnter,Colorscheme * :hi IndentGuidesOdd guibg=DarkGrey ctermbg=darkgrey autocmd VimEnter,Colorscheme * :hi IndentGuidesEven guibg=DarkCyan ctermbg=12 " }}} " submode.vim {{{ let g:submode_timeout = 0 call submode#enter_with('window/manip', 'n', '', '<Leader>w') call submode#enter_with('window/manip', 'n', '', '<C-W>-', '<C-W>-') call submode#enter_with('window/manip', 'n', '', '<C-W>+', '<C-W>+') call submode#enter_with('window/manip', 'n', '', '<C-W>>', '<C-W>>') call submode#enter_with('window/manip', 'n', '', '<C-W><', '<C-W><') call submode#leave_with('window/manip', 'n', '', '<Esc>') call submode#map('window/manip', 'n', '', '-', '<C-W>-') call submode#map('window/manip', 'n', '', '+', '<C-W>+') call submode#map('window/manip', 'n', '', '<', '<C-W><') call submode#map('window/manip', 'n', '', '>', '<C-W>>') call submode#map('window/manip', 'n', '', '=', '<C-W>=') call submode#map('window/manip', 'n', '', 'r', '<C-W>r') call submode#map('window/manip', 'n', '', 'R', '<C-W>R') call submode#map('window/manip', 'n', '', 'x', '<C-W>x') call submode#map('window/manip', 'n', '', 'j', '<C-W>j') call submode#map('window/manip', 'n', '', 'k', '<C-W>k') call submode#map('window/manip', 'n', '', 'l', '<C-W>l') call submode#map('window/manip', 'n', '', 'h', '<C-W>h') " }}} " vim-altr {{{ nmap <F3> <Plug>(altr-forward) nmap <F2> <Plug>(altr-back) let s:bundle = neobundle#get("vim-altr") function! s:bundle.hooks.on_source(bundle) " For ruby tdd call altr#define('%.rb', 'spec/%_spec.rb') " For ruby tdd call altr#define('lib/%.rb', 'spec/lib/%_spec.rb', 'spec/%_spec.rb') " For rails tdd call altr#define('app/models/%.rb', 'spec/models/%_spec.rb', 'spec/factories/%s.rb') call altr#define('app/controllers/%.rb', 'spec/controllers/%_spec.rb') call altr#define('app/helpers/%.rb', 'spec/helpers/%_spec.rb') endfunction unlet s:bundle " }}} " toggle.vim {{{ imap <silent><C-C> <Plug>ToggleI nmap <silent><C-C> <Plug>ToggleN vmap <silent><C-C> <Plug>ToggleV let g:toggle_pairs = { \'and':'or', \'or':'and', \'if':'unless', \'unless':'if', \'elsif':'else', \'else':'elsif', \'it':'specify', \'specify':'it', \'describe':"context", \'context':"describe", \'true':'false', \'false':'true', \'yes':'no', \'no':'yes', \'on':'off', \'off':'on', \'public':'protected', \'protected':'private', \'private':'public', \'&&':'||', \'||':'&&' \} " }}} " RSpec syntax {{{ function! RSpecSyntax() hi def link rubyRailsTestMethod Function syn keyword rubyRailsTestMethod describe context it its specify shared_examples_for shared_examples shared_context it_should_behave_like it_behaves_like before after around subject fixtures controller_name helper_name include_context include_examples syn match rubyRailsTestMethod '\<let\>!\=' syn keyword rubyRailsTestMethod violated pending expect double mock mock_model stub_model an_instance_of hash_including syn match rubyRailsTestMethod '\.\@<!\<stub\>!\@!' endfunction MyAutocmd Syntax ruby if (expand("%") =~ "_spec\.rb$") || (expand("%") =~ "^spec.*\.rb$") | call RSpecSyntax() | endif " }}} " Quickfix augroup quickfixopen autocmd! autocmd QuickfixCmdPost make cw augroup END nnoremap <silent> ,q :<C-U>copen<CR> nnoremap <silent> ]q :<C-U>cnext<CR> nnoremap <silent> [q :<C-U>cprev<CR> nnoremap <silent> ]Q :<C-U>clast<CR> nnoremap <silent> [Q :<C-U>cfirst<CR> " errormarker.vim let errormarker_disablemappings = 1 " Merge Setting if &diff nmap <buffer> <leader>1 :diffget LOCAL<CR> nmap <buffer> <leader>2 :diffget BASE<CR> nmap <buffer> <leader>3 :diffget REMOTE<CR> endif " TagBar nnoremap <silent> ,t :TagbarToggle<CR> let g:tagbar_left = 1 let g:tagbar_width = 30 let g:tagbar_updateonsave_maxlines = 10000 let g:tagbar_sort = 0 " Tabular nnoremap <Leader>a, :Tabularize /,<CR> vnoremap <Leader>a, :Tabularize /,<CR> nnoremap <Leader>a= :Tabularize /=<CR> vnoremap <Leader>a= :Tabularize /=<CR> nnoremap <Leader>a> :Tabularize /=><CR> vnoremap <Leader>a> :Tabularize /=><CR> nnoremap <Leader>a: :Tabularize /:\zs<CR> vnoremap <Leader>a: :Tabularize /:\zs<CR> nnoremap <Leader>a<Bar> :Tabularize /<Bar><CR> vnoremap <Leader>a<Bar> :Tabularize /<Bar><CR> " テーブルっぽく打つと自動的に位置調整を行う inoremap <silent> <Bar> <Bar><Esc>:call <SID>align()<CR>a function! s:align() let p = '^\s*|\s.*\s|\s*$' if exists(':Tabularize') && getline('.') =~# '^\s*|' && (getline(line('.')-1) =~# p || getline(line('.')+1) =~# p) let column = strlen(substitute(getline('.')[0:col('.')],'[^|]','','g')) let position = strlen(matchstr(getline('.')[0:col('.')],'.*|\s*\zs.*')) Tabularize/|/l1 normal! 0 call search(repeat('[^|]*|',column).'\s\{-\}'.repeat('.',position),'ce',line('.')) endif endfunction " gundo nnoremap U :<C-U>GundoToggle<CR> " open-browser let g:netrw_nogx = 1 " disable netrw's gx mapping. nnoremap gx <Plug>(openbrowser-smart-search) vnoremap gx <Plug>(openbrowser-smart-search) function! OpenBrowserLine() let matched = matchlist(getline("."), 'https\?://[0-9A-Za-z_#?~=\-+%\.\/:]\+') if len(matched) == 0 break endif execute "OpenBrowser " . matched[0] endfunction " syntastic let g:syntastic_auto_loc_list = 1 let g:syntastic_mode_map = { 'mode': 'active', \ 'active_filetypes': [], \ 'passive_filetypes': ['haskell'] } " ft hamstache MyAutocmd BufReadPost *.hamstache set filetype=haml " ag.vim let g:agprg="ag --nocolor --nogroup --column" " ruby buffer if has('ruby') nnoremap <silent> [space]r :set operatorfunc=Ruby<CR>g@ nnoremap <silent> [space]rr :call RubyLines()<CR> nmap [space]R [space]r$ nnoremap <silent> [space]rp :<C-u>call RubyPaste()<CR> xnoremap <silent> [space]r :<C-u>call Ruby(visualmode(), 1)<CR> xnoremap <silent> [space]R :<C-u>call Ruby('V', 1)<CR> command! -range Ruby :<line1>,<line2>call RubyLines() command! -range RubyPaste :<line1>,<line2>call RubyPaste() let s:ruby_buffer = "" ruby <<RUBY class VimRuby @@binding = binding def VimRuby.evaluate(code) result = eval(code, @@binding) if result.instance_of?(String) str = result.gsub(/"/, '\\"') VIM.command(%(let s:ruby_buffer = "#{str}" . "\n")) else str = result.inspect.gsub(/"/, '\\"') VIM.command(%(let s:ruby_buffer = '#{str}' . "\n")) end return result end end RUBY function! RubyEval(code) ruby p VimRuby.evaluate(VIM.evaluate("a:code")) endfunction function! Ruby(type, ...) let saved_sel = &selection let &selection = "inclusive" let saved_reg = @* if a:0 silent exe "normal! `<" . a:type . "`>y" elseif a:type == 'line' silent exe "normal! '[V']y" elseif a:type == 'block' silent exe "normal! `[\<C-v>`]y" else silent exe "normal! `[v`]y" endif call RubyEval(@*) let &selection = saved_sel let @* = saved_reg endfunction function! RubyLines() range let lines = getline(a:firstline, a:lastline) let code = join(lines, "\n") call RubyEval(code) endfunction function! RubyPaste() let saved_reg = @* let @* = s:ruby_buffer normal! p let @* = saved_reg endfunction endif " TweetVim {{{ nnoremap <silent> S :<C-u>TweetVimSay<CR> nnoremap <silent> [unite]t :<C-u>Unite tweetvim<CR> nnoremap <silent> [space]ts :<C-u>TweetVimUserStream<CR> nnoremap <silent> [space]tt :<C-u>TweetVimHomeTimeline<CR> let s:bundle = neobundle#get("TweetVim") function! s:bundle.hooks.on_source(bundle) let g:tweetvim_include_rts = 1 if has('mac') let g:tweetvim_display_icon = 0 else let g:tweetvim_display_icon = 1 end endfunction unlet s:bundle " }}} " code snippet highlight {{{ let g:markdown_quote_syntax_filetypes = { \ "coffee" : { \ "start" : "coffee", \}, \ "mustache" : { \ "start" : "mustache", \}, \ "haml" : { \ "start" : "haml", \}, \} " }}} " to 1.9 hash vnoremap <silent> <C-h> :s/:\([a-z0-9_]\+\)\s*=>/\1:/g<CR> if filereadable(expand('~/.vimrc.local.after')) execute 'source' expand('~/.vimrc.local.after') endif ``` ## rspec.snip ``` # describe snippet desc abbr describe end prev_word '^' describe "${1:name}" do ${2} end snippet desch abbr spec_helper describe prev_word '^' require 'spec_helper' describe ${1:`Snippet_RubySpecNameFromFilename()`} do ${2} end snippet desccon abbr describe for controller prev_word '^' describe "${1:GET|POST|PUT|DELETE} ${2:/some/path}${3}" do ${4} end snippet bef prev_word '^' before do ${1} end alias bef before snippet befa prev_word '^' before(:all) do ${1} end alias befa beforeall snippet aft prev_word '^' after do ${1} end alias aft after snippet afta prev_word '^' after(:all) do ${1} end alias afta afterall snippet con prev_word '^' context "${1}" do ${2} end alias con context snippet cona prev_word '^' abbr context after callback context "after #${1:save!}" do before do subject.$1 end ${2} end snippet subjc prev_word '^' subject { ${1:FactoryGirl.create(:${2:name\})} } alias subjc subject snippet subjb prev_word '^' subject { ${1:FactoryGirl.build(:${2:name\})} } # it snippet it prev_word '^' it "${1}" do ${2} end snippet itb prev_word '^' abbr it {} it { ${1} } snippet its prev_word '^' it "should ${1:work correctly}" do ${2} end snippet is prev_word '^' it { should ${1} } snippet isn prev_word '^' it { should_not ${1} } # should snippet sh should == ${1:value} ${2} snippet shn should_not == ${1:value} ${2} snippet shs should satisfy { |${1:obj}| ${2} } ${3} snippet shp should be_${1:predicate} alias shp shbe snippet shh should have(${1:num}).${2:things} ${3} snippet she should eq(${1:value}) ${3} snippet shne should_not eq(${1:value}) ${2} snippet shnredt response.should_not redirect_to(${1:url}) ${2} snippet shbw should be_within(${1:tolerance}).of(${2:result}) ${3} snippet shnbw should_not be_within(${1:tolerance}).of(${2:result}) ${4} snippet shhal should have_at_least(${1:num}).${2:things} ${3} snippet shhi should have(${1:n}).records ${2} snippet shns should_not satisfy { |${1:obj}| ${2} } ${3} snippet shbko should be_a_kind_of(${1:class}) ${2} snippet shnbko should_not be_a_kind_of(${1:klass}) ${2} snippet shnbe should_not be_${1:predicate} snippet shre should raise_error(${1:error}) ${2} snippet shnre should_not raise_error(${1:error}) ${2} alias shre shraise snippet shc expect { ${1} }.should change(${2:described_class}, :${3:count}).by(${4:1}) alias shc exshc snippet shnc expect { ${1} }.should_not change(${2:target}, :${3:method}) snippet shrt should respond_to(:${1:sym}) ${2} snippet shnrt should_not respond_to(:${1:sym}) ${2} snippet shr should_receive(:${1:message})${2} ${3} snippet shnr should_not_receive(:${1:message})${2} ${3} snippet wia with(${1:args}) ${2} snippet shm should match(/${1:regexp}/) ${2} snippet shnm should_not match(/${1:regexp}/) ${2} snippet shredt response.should redirect_to(${1:url}) ${2} snippet shbr response.should be_redirect ${1} snippet shnbr response.should_not be_redirect ${1} snippet shbs response.should be_success ${1} snippet shnbs response.should_not be_success ${1} snippet shtemp response.should render_template(:${1:template}) ${2} snippet shbio should be_instance_of(${1:class}) ${2} snippet shnbio should_not be_instance_of(${1:klass}) ${2} # shared_context snippet sc prev_word '^' abbr shared_context do end shared_context "${1:condition}" do ${2} end alias sc shared_context snippet scm prev_word '^' abbr shared_context with metadata shared_context "${1:condition}", :${2:key} => ${3:value} do ${4} end snippet inc prev_word '^' abbr include_context include_context "${1}" alias inc include_context # shared_example snippet se prev_word '^' abbr shared_examples do end shared_examples "${1:do something}" do ${2} end alias se shared_examples snippet sed prev_word '^' shared_examples "${1:}" do describe "as $1" do ${2} end end snippet ibl prev_word '^' abbr it_behaves_like it_behaves_like '${1}' alias ibl it_behaves snippet ine prev_word '^' abbr include_examples include_examples "${1}" alias ine include_examples # let snippet let prev_word '^' abbr let {} let(:${1}) { ${2} } snippet let! prev_word '^' abbr let! {} let!(:${1}) { ${2} } snippet letf prev_word '^' let(:${1:model}) do FactoryGirl.create(:${2:$1}) end # matcher snippet atl at_least(${1:n}).times snippet atm at_most(${1:n}).times snippet on once snippet tw twice snippet ber be_redirect snippet ex exactly(${1:n}).times alias ex exact snippet annot any_number_of_times snippet shham ${1:target}.should have_at_most(${2:num}).${3:things} ${4} snippet ant and_throw(${1:sym}) snippet any and_yield(${1:values}) snippet mat RSpec::Matchers.define :${1:matcher_name} do match do |model| # return Boolean ${2} end failure_message_for_should do # Failure message for should end failure_message_for_should_not do # Failure message for should end end alias mat matcher # stub snippet anr and_return(${1:value}) snippet anrb and_return { ${1} } snippet anra and_raise(${1:exception}) snippet st stub(:${1:method}).and_returns(${2:return}) alias st stub snippet sm stub_model(${1:model}, {${2}}) snippet mm mock_model(${1:model})${2} snippet moc ${1:var} = mock("${2:mock_name}"${3:, :null_object => true}) ${4} # FactoryGirl snippet facb FactoryGirl.build(:${1}) snippet fac FactoryGirl(:${1}, ${2}) snippet facc FactoryGirl.create(:${1}) snippet facs sequence(:${1}) {|n| "${2}#{n}"} snippet facn FactoryGirl.next(:${1:sequence-name}) snippet faca f.${1:model} {|a| a.association(:${2:$1})} snippet facd FactoryGirl.define do ${1} end snippet facf factory ${1:name} do ${2} end ``` ## rails.snip ``` snippet sc abbr scope :method, lambda prev_word '^' scope :${1}, lambda { ${2} } snippet dsc abbr default_scope lambda prev_word '^' default_scope lambda { ${1} } snippet bff abbr before_filter prev_word '^' before_filter :${1:method} delete hm snippet hm abbr has_many :objects prev_word '^' has_many :${1:objects} alias hm has_m snippet hmo abbr has_many with options prev_word '^' has_many :${1:objects}${2:, class_name: "${3\}", foreign_key: "${4:reference\}_id"} snippet hmt abbr has_many :objects, :through prev_word '^' has_many :${1:objects}, through: ${2:relation} delete ho snippet ho abbr has_one :object prev_word '^' has_one :${1:object} alias ho has_o delete bt snippet bt abbr belongs_to :object prev_word '^' belongs_to :${1:object} alias bt bel snippet bto abbr belongs_to with options prev_word '^' belongs_to :${1:object}${2:, class_name: "${3\}", foreign_key: "${4:reference\}_id"} delete logd snippet logd abbr logger.debug logger.debug(${1}) delete logi snippet logi abbr logger.info logger.info(${1}) delete logw snippet logw abbr logger.warn logger.warn(${1}) delete loge snippet loge abbr logger.error logger.error(${1}) delete logf snippet logf abbr logger.fatal logger.fatal(${1}) snippet fla abbr flash[...] prev_word '^' flash[:${1:notice}] = "${2:Successfully created...}" alias fla flash snippet rep abbr redirect_to (path) redirect_to ${1:model}_path${2:(${3:params\})} delete va snippet va abbr validates_associated validates_associated :${1:attr} delete vb snippet vb abbr validates_acceptance_of validates_acceptance_of :${1:attr} delete vc snippet vc abbr validates_confirmation_of validates_confirmation_of :${1:attr} delete ve snippet ve abbr validates_exclusion_of validates_exclusion_of :${1:attr}, in: ${2:%w( ${3:mov avi\} )} delete vf snippet vf abbr validates_format_of validates_format_of :${1:attr}, with: /${2:regexp}/ delete vi snippet vi abbr validates_inclusion_of validates_inclusion_of :${1:attr}, in: ${2:%w( ${3:mov avi\} )} delete vl snippet vl abbr validates_length_of validates_length_of :${1:attr}, within: ${2:3..20} delete vn snippet vn abbr validates_numericality_of validates_numericality_of :${1:attr} snippet vng abbr validates_numericality_of greater_than: n validates_numericality_of :${1:attr}, greater_than: ${2:num} snippet vnl abbr validates_numericality_of less_than: n validates_numericality_of :${1:attr}, less_than: ${2:num} delete vp snippet vp abbr validates_presence_of validates_presence_of :${1:attr} delete vu snippet vu abbr validates_uniqueness_of validates_uniqueness_of :${1:attr} snippet aln abbr allow_nil allow_nil: ${1:true} delete ra snippet ra abbr render :action render action: ${1} delete rc snippet rc abbr render :controller render controller: ${1} delete rf snippet rf abbr render :file render file: ${1} delete ri snippet ri abbr render :inline render inline: ${1} delete rj snippet rj abbr render :json render json: ${1} delete rl snippet rl abbr render :layout render layout: ${1} delete rp snippet rp abbr render :partial render partial: ${1} delete rt snippet rt abbr render :text render text: ${1} delete rx snippet rx abbr render :xml render xml: ${1} delete id delete object delete partial delete action snippet action abbr action: name action: ${1} snippet tzn abbr Time.zone.now Time.zone.now snippet res abbr resources prev_word '^' resources :${1:resources} alias res resources delete rst snippet rst abbr respond_to prev_word '^' respond_to do |format| format.html${1: { ${2\} \}} format.json { render json: ${3} } end snippet befs abbr before_save prev_word '^' before_save :${1:method} snippet befc abbr before_create prev_word '^' before_create :${1:method} snippet befu abbr before_update prev_word '^' before_update :${1:method} snippet befv abbr before_validation prev_word '^' before_validation :${1:method} snippet befvc abbr before_validation_on_create prev_word '^' before_validation_on_create :${1:method} snippet befvu abbr before_validation_on_update prev_word '^' before_validation_on_update :${1:method} snippet befd abbr before_destroy prev_word '^' before_destroy :${1:method} snippet afts abbr after_save prev_word '^' after_save :${1:method} snippet aftc abbr after_create prev_word '^' after_create :${1:method} snippet aftu abbr after_update prev_word '^' after_update :${1:method} snippet aftv abbr after_validation prev_word '^' after_validation :${1:method} snippet aftvc abbr after_validation_on_create prev_word '^' after_validation_on_create :${1:method} snippet aftvu abbr after_validation_on_update prev_word '^' after_validation_on_update :${1:method} snippet aftd abbr after_destroy prev_word '^' after_destroy :${1:method} snippet try abbr try(:method) try(:${1:method}) ``` ## coffee.snip ``` snippet fun abbr Function ${1:name} = (${2:args}) -> ${3:# body...} snippet funb abbr Function bind ${1:(${2:args\}) }=> ${3:# body...} snippet if if ${1:condition} ${2:# body...} snippet ife if ${1:condition} ${2:# body...} else ${3:# body...} snippet elif else if ${1:condition} ${0:# body...} snippet ifte abbr if...then...else if ${1:condition} then ${2:value} else ${3:other} snippet unl ${1:action} unless ${2:condition} snippet fora abbr Array Comprehension for ${1:name} in ${2:array} ${3:# body...} snippet foro abbr Object Comprehension for ${1:key}, ${2:value} of ${3:Object} ${4:# body...} snippet forr abbr Range Comprehension (inclusive) for ${1:name} in [${2:start}..${3:finish}]${4: by ${5:step}} ${6:# body...} snippet forrex Range Comprehension (exclusive) for ${1:name} in [${2:start}...${3:finish}]${4: by ${5:step}} ${6:# body...} snippet swi abbr Switch switch ${1:object} when ${2:value} ${3:# body...} alias swi switch snippet cla abbr Class class ${1:ClassName}${2: extends ${3:Ancestor}} ${4:constructor: (${5:args}) -> ${6:# body...}} $7 alias cla class snippet try abbr Try .. Catch try ${1} catch ${2:error} ${3} snippet req abbr Require ${3} = require(${1:'${2:sys\}'}) snippet log abbr Console.log console.log ${1:"${2:msg\}"} ``` |
|
| 477位 |
|
|||
|
00:56:50 |
|
|
iOSアプリのメモリ管理についてまとめました!
何かあったらコメントどぞー。 >6/24:コメントで間違いを指摘されたので、色々テキスト修正しました。 ##メモリ管理に気をつけないとどうなるの? ###動作がもっさりする 使い終わったオブジェクトを解放していないパターン。 解放されないのでどんどん使用メモリが増えていき、メモリ圧迫しちゃってもっさりする。 同じ端末で動かしている他のアプリも影響を受けてしまうので気をつけましょう!! ###アプリが落ちる よくあるのがEXC_BAD_ACCESSのパターン。 アクセスしようとしたデータが既に解放されちゃっているというエラー。 解放するタイミングにも気をつけましょう!! ##気をつけるポイント - <del>strongなプロパティは自分で解放する</del> (誤解してました…。自動で解放されるよう) - プロパティ属性を理解する - 解放するタイミング - メモリ不足時の挙動を理解する - Blocksの循環参照に気をつける ##プロパティ属性を理解する プロパティ属性とは、この括弧の中に書かれているやつです。 ```objective-c: @property (nonatomic, strong) NSString *message; ``` これをちゃんと理解して使い分けないと、いつの間にかデータが変わっていたり、循環参照が発生してメモリが解放されなくなったりするので注意が必要です。 これについては、ひじょーーに分かりやすくまとまっている記事があったので紹介します。 - [[iOS5] ARC (Automatic Reference Counting) : Overview - iOS 開発ブログ Natsu's note](http://blog.natsuapps.com/2011/11/ios5-arc-overview.html) - [[iOS5] ARC : プロパティ属性と使い方 - iOS 開発ブログ Natsu's note](http://blog.natsuapps.com/2011/11/ios5-arc-property.html) - [[iOS5] ARC : 循環参照 - iOS 開発ブログ Natsu's note](http://blog.natsuapps.com/2011/11/ios5-arc-strong-reference-cycle.html) あとこちらのガイドラインも参考になるのでおすすめです。 - [Objective-C のプロパティ属性のガイドライン #Objective-C #Mac #iOS #Xcode - Qiita [キータ]](http://qiita.com/items/80660f9aa20afaf671f3) ##解放するタイミング 次は解放するタイミングです。 基本、解放するタイミングは、`viewDidDisappear` や `dealloc` です。 最初、`viewDidUnload`に解放処理を入れると誤解していたのですが、iOS6からは全く呼ばれなくなっています。(iOS5でもメモリ不足時にしか呼ばれない) 表示する時だけ必要なものは、`viewDidAppear`で生成して`viewDidDisappear`で解放。生成コストが高かったり、表示していない時も必要なものは、`viewDidLoad`で生成して`dealloc`で解放。という感じでしょうか。 どういったデータを解放する必要があるのかというと、ARCのドキュメントに書いてありました。 引用:[ARCへの移行のリリースノート | Second Flush](http://secondflush2.blog.fc2.com/blog-entry-1087.html) > **・自分のオブジェクトのためにdeallocメソッドを記述する必要はまだありますか?** > > おそらく > > ARCはmalloc/free、Core Foundationオブジェクトの寿命の管理、ファイル記述子などを自動化しておらず、そのようなリソースは依然としてdeallocメソッドを記述することによって解放します。 > > インスタンス変数を解放する必要はありません(実際にはできません)が、ARCを使用してコンパイルしていない他のコードやシステムクラス上で[self setDelegate:nil]を呼び出す必要があるかもしれません。 > > ARCでのdeallocメソッドは[super dealloc];の呼び出しを必要と(または許可)せず、superへのチェーニングはランタイムによって適用と処理がなされます。 今の所これに当てはまるようなコードは書いた事がないので、それほど意識するケースは少ないのかもしれませんね。インスタンス変数も自動で解放してくれるようなので、解放というか例えば、NSNotificationCenterにaddObserverしたのをremoveするとか、そういう使い終わった事を示す処理を入れるケースが多そう。 あと、メモリ使用量が多いオブジェクトは、didReceiveMemoryWarning でも解放しときましょう。 ##メモリ不足時の挙動を理解する この辺をちゃんと理解しとかないと、メモリ不足時に EXC_BAD_ACCESS が発生して落ちるので要注意。 メモリ不足時には、以下のような処理が実行されます。 - 全ViewController で、didReceiveMemoryWarning が呼ばれる - iOS5の場合、最前面以外のViewControllerで viewDidUnload が呼ばれる iOS6の場合、メモリ不足時に viewDidUnload が呼ばれないので、メモリ使用量が多いオブジェクトは、didReceiveMemoryWarning で解放する必要があります。あとiOS5では viewDidUnload が呼ばれてviewが解放されるので、iOS6とは挙動が異なったりします。 参考:[iOS Viewライフサイクルまとめ、iOS6での変更点 | Zero4Racer PRO Developer's Blog](http://www.zero4racer.com/blog/929) ##Blocksの循環参照に気をつける あとは、Blocksを使う時に陥りがちな循環参照にも気をつけましょう。 お互いに参照しちゃってデータが解放されないパターンです。 これもわかりやすい記事があったので紹介します。 - [Objective-CのBlocksの循環参照に関する僕なりのベストプラクティス | 三度の飯とエレクトロン](http://blog.katty.in/2612) ##以上です 最低限知っておく事はこれくらいかな。。 メモリ管理について調べるとARC以前の記事がよく引っかかったりしますが、それに比べると大分楽になってますねー。 ただ、ARCであまり意識する事は無くなったとはいえ、ちゃんと開発しようとするとその辺の知識が必要になってきます。勉強しときたいと思う方は、とりあえずこの本が安いので読んでおく事をおすすめします。 [エキスパートObjective-Cプログラミング ― iOS/OS Xのメモリ管理とマルチスレッド - 達人出版会](http://tatsu-zine.com/books/objc) ##追記 非常に参考になる記事を発見したので追記。 [Objective-C - ARC のメモリ解放タイミングを調べた - Qiita [キータ]](http://qiita.com/amay077/items/95a4139e6f553d8a56a1) |
|
| 478位 |
|
|||
|
13:02:40 |
|
|
もうMySQL 5.5 GAが出てから一年が経ち、MySQL 5.6 GAもそろそろ出るころだし、新規で作るアプリケーションはutf8mb4でいきたいのでその方法。 まず、mysql2が0.3.11以前のバージョンではutf8mb4に対応してないので、それより新しいバージョンを使う必要があります。 ```Gemfile gem 'mysql2', '>= 0.3.12b4' ``` これで`encoding: utf8mb4`で接続できるようになります。 つぎにActiveRecord::Migrationでutf8mb4なデータベースを作成するようにコンフィグで`charset`と`collation`を指定します。 ```yaml:config/database.yml development: adapter: mysql2 encoding: utf8mb4 charset: utf8mb4 collation: utf8mb4_general_ci reconnect: false database: kamipo_development ``` そうすると、schema_migrationsがインデックス張れないエラーでこけます。 ``` Mysql2::Error: Index column size too large. The maximum column size is 767 bytes.: CREATE UNIQUE INDEX `unique_schema_migrations` ON `schema_migrations` (`version`) ``` MySQL側のinnodb_large_prefixオプションでキープレフィックスを3072バイトまで拡張できるのでmy.cnfで指定します。 ```my.cnf [mysqld] innodb_file_format = Barracuda innodb_file_per_table = 1 innodb_large_prefix ``` このオプションはROW_FORMATがDYNAMICかCOMPRESSEDのとき有効なので、create_tableメソッドで'ROW_FORMAT=DYNAMIC'がデフォルトで指定されるようにしておきます。 ```ruby:config/initializers/ar_innodb_row_format.rb ActiveSupport.on_load :active_record do module ActiveRecord::ConnectionAdapters class AbstractMysqlAdapter def create_table_with_innodb_row_format(table_name, options = {}) table_options = options.merge(:options => 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC') create_table_without_innodb_row_format(table_name, table_options) do |td| yield td if block_given? end end alias_method_chain :create_table, :innodb_row_format end end end ``` これでとりあえずutf8mb4で動きます! ### 参考 - [MySQL(InnoDB) で "Index column size too large. The maximum column size is 767 bytes." いわれるときの対策 - かみぽわーる](http://blog.kamipo.net/entry/2012/11/13/102024) |
|
| 479位 |
|
|||
|
16:57:39 |
|
|
前回は[dr6kaiz](http://qiita.com/users/d6rkaiz)さんの[pow + rbenvで手軽なRack環境構築](http://qiita.com/items/0f0b15b800fcd8a742f9)でした。
今回は[tmuxinator](https://github.com/aziz/tmuxinator/)を使って、コマンド一発で開発環境を起動する話をします。 ## tmuxinator とは tmuxinatorとは、tmuxで起動するセッションを予め定義しておき、コマンド一発でそのセッションを起動できるようにしたものです。ちなみに、screenで同じことをする[screeninator](https://github.com/jondruse/screeninator)というツールもあります。こちらが先に開発されたようです。 ## インストール ```sh $ cd $ gem install tmuxinator $ echo "[[ -s $HOME/.tmuxinator/scripts/tmuxinator ]] && source $HOME/.tmuxinator/scripts/tmuxinator" >> .zshrc $ source .zshrc $ echo $EDITOR /usr/bin/vi $ echo $SHELL /bin/zsh ``` - tmuxinatorはgemで提供されているので、`gem install`します。 - その後、補完スクリプトを`.zshrc`や`.bashrc`に追加して読み込みます。 - tmuxinatorは環境変数`$EDITOR`と`$SHELL`を使うそうなので、確認しておきます。 ## 設定 ```sh $ mux new cui-aboutme ``` - `tmuxinator`とそのaliasの`mux`というコマンドが用意されています。 - `mux new [project name]`でプロジェクトの設定ファイルを作成します。 - このコマンドを実行すると、`$EDITOR`で設定されたエディタで以下のような設定ファイルが開きます。 ```yml:~/.tmuxinator/cui-about.yml # ~/.tmuxinator/sample.yml # you can make as many tabs as you wish... project_name: Tmuxinator project_root: ~/code/rails_project socket_name: foo # Not needed. Remove to use default socket rvm: 1.9.2@rails_project pre: sudo /etc/rc.d/mysqld start tabs: - editor: layout: main-vertical panes: - vim - #empty, will just run plain bash - top - shell: git pull - database: rails db - server: rails s - logs: tail -f logs/development.log - console: rails c - capistrano: - server: ssh me@myhost ``` - `rvm`で利用するrubyのバージョンを指定したり、`pre`でセッション起動時に実行するコマンドを指定できたりします。 - `tabs`でセッション内で起動するウィンドウを定義します。この初期設定だと、8つウィンドウを起動します。 - `tabs`内では、`editor`や`shell`といったキーがウィンドウ名を表し、値がそのウィンドウが起動したときに実行されるコマンドとなります。この初期設定だと、セッション起動と同時に8つのウィンドウが起動して、自動的に`git pull`したり`rails s`したり`ssh`したりします。 - `layout`と`panes`で、そのウィンドウ内のペインとその配置を定義します。`editor`を例にとると、ウィンドウ内に`vim`のペインと何もしないペインと`top`のペインが`main-vertical`で表示されます。 ## 起動 ```sh $ mux cui-aboutme ``` - このコマンド一発で、上記で設定したセッションを起動します。 - 起動と同時に設定されたコマンドも自動で実行されます。 ## カスタマイズ例 ```yml:~/.tmuxinator/cui-aboutme.yml # ~/.tmuxinator/sample.yml # you can make as many tabs as you wish... project_name: cui-aboutme project_root: ~/workspace/rails/cui-aboutme tabs: - main: layout: tiled panes: - git fetch --prune && git status --short --branch - curl http://cui-about.me/users - tig - vim: vi - app: layout: even-horizontal panes: - rails c - powder log - test: guard ``` - 僕の場合は「tigを中心としたgitの作業をするウィンドウ」「エディタのウィンドウ」「コンソールやログをみるウィンドウ」「自動テストを行うウィンドウ」の4つを一度に起動できるようにしてます。 - あと、起動時に`git fetch`でリポジトリの更新を確認したり、`guard`で自動テストを開始したりしてます。 ## tips - 一度に同時に起動するウィンドウを一度にすべて閉じるための設定 ```.tmux.conf bind-keys C-b kill-session ``` - 複数のプロジェクトに共通する設定は`~/.tmuxinator/default.yml`で設定できます。 ## おまけ - テキストベースの自己紹介サービス[cui-about.me](http://cui-about.me)やってます。よかったらどうぞ。 |
|
| 480位 |
|
|||
|
14:41:01 |
|
|
他のバージョン管理システムと同様 git にも hook が色々存在しますが、役割を適用順に一覧にしたサイトが見当たらなかったので自分用にここに書いておこうと思います。
細かいことはは[公式ドキュメント](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%83%95%E3%83%83%E3%82%AF)を見てください。 また各々の hook script の書き方自体は、手元の環境の `.git/hooks/*.sample` を参考にしてください。 # commit関係 見ての通り、コマンドを実行してからの流れ順に書きます。 1. `git commit` 2. **pre-commit** commit前に起動しコードをチェックするなどで使う。 0以外を返すとcommit中止。`--no-verify`で無視。 3. **prepare-commit-msg** commit時のデフォルトメッセージ編集用。 4. `エディタが起動し commit msg の入力` 5. **commit-msg** commit msg が既定のフォーマットに沿っているかなど確認する為に使う。 0以外を返すとcommit中止。`--no-verify`で無視。 6. `実際にcommitが実行される` 7. **post-commit** commit後の通知や自動処理に使う # Eメールワークフロー関係 「Eメールワークフロー」=「gitを使ってpatchのやりとり」です。 私自身はこの機能を使ったことがないため、コマンドの使い方などは間違っている可能性があります。 1. `git am foobar.patch` 2. **applypatch-msg** patch中の commit msg のチェック用。0以外を返すと中止。 3. `一時的にpatchを適用(commitはまだ)` 4. **pre-applypatch** patchが適用された物が正常動作するかどうかなどの受け入れテスト用。 0以外を返すと適用中止。 5. `patchを正式に受け入れcommit` 6. **post-applypatch** apply 後の通知や自動処理に使う # [rebase](http://d.hatena.ne.jp/Seasons/20090329/1238351273) / merge関係 こちらはシンプルなので説明だけ - **pre-rebase** rebase する前にチェックして rebase を中断させるなどで使う。 0以外を返すとrebase中止。 - **post-rebase** 事後の告知とか自動実行とか。 - **post-merge** 同じく merge 版の事後の告知とか自動実行とか。こちらはpreはない。 # サーバサイドフック 外部から push されたときに動くスクリプトです 1. `git push your_server master` 2. **pre-receive** push を受けた際の最初に実行される。主に受け入れテスト用。 0以外を返すと受け入れ中止。 3. **update** push を受けた際ブランチ毎に実行される。主に受け入れテスト用。 0以外を返すと受け入れ中止。 4. `Received!` 5. **post-update** push を受けた際ブランチ毎に実行される。多分メッセージ表示とか用。 6. **post-receive** push を受けた際の最後に実行される。MLに告知を流したり色々処理したり。 |
|
| 481位 |
|
|||
|
15:42:21 |
(DTS corporation 所属) |
|
# はじめに
GSONは、Googleが提供するJSONデータとJavaオブジェクトを相互に変換するためのライブラリです。 google-gson - A Java library to convert JSON to Java objects and vice-versa - Google Project Hosting https://code.google.com/p/google-gson/ JavaでJSONを扱うためのライブラリは他に[Jackson](http://jackson.codehaus.org/)、[JSONIC](http://jsonic.sourceforge.jp/)等があります。 GSONのメインとなるのは `Gson` クラスです。 `GsonBuilder` クラスを使うとより詳細な条件を指定してJavaとJSONの変換を行うことができます。 ここではGsonクラスの使い方を紹介します。 # サンプルで使用するクラス シンプルなJavaのクラスです。 PostはフィールドにUserオブジェクトとCommentオブジェクトのListを持っています。 ```User.java public class User { public String email; public String fullname; public User(String email, String fullname) { this.email = email; this.fullname = fullname; } } ``` ```Post.java public class Post { public String title; public String content; public User author; public List<Comment> comments; public Post(User author, String title, String content) { this.comments = new ArrayList<Comment>(); this.author = author; this.title = title; this.content = content; } } ``` ```Comment.java public class Comment { public String author; public String content; public Comment(String author, String content) { this.author = author; this.content = content; } } ``` # シリアライズ - toJson() JavaからJSONへ変換する時は `toJson()` を使います。 デフォルトではnullのフィールドはJSONに含まれません。 ```ToJsonSample.java public class ToJsonSample { /* * JavaオブジェクトからJSONへの変換 */ public static void main(String[] args) { Gson gson = new Gson(); User user1 = new User("bob@jmail.com", null); //fullnameにnullをセット User user2 = new User("jeff@jmail.com", "Jeff"); List<User> userList = new ArrayList<>(); userList.add(user1); userList.add(user2); // JavaオブジェクトからJSONへの変換 System.out.println("ユーザー: " + gson.toJson(user1)); // JavaオブジェクトからJSONへの変換:List System.out.println("ユーザーリスト: " + gson.toJson(userList)); Post newPost = new Post(userList.get(0), "postTitle", "postContent"); Comment comment = new Comment("comment_author", "comment_comment"); newPost.comments.add(comment); newPost.comments.add(comment); // JavaオブジェクトからJSONへの変換:フィールドにListを含むオブジェクト System.out.println("コメント付き投稿: " + gson.toJson(newPost)); } } ``` ###実行結果 ``` ユーザー: {"email":"bob@jmail.com"} ユーザーリスト: [ { "email": "bob@jmail.com", "fullname": "Bob" }, { "email": "jeff@jmail.com", "fullname": "Jeff" } ] コメント付き投稿: { "title": "postTitle", "content": "postContent", "author": { "email": "bob@jmail.com", "fullname": "Bob" }, "comments": [ { "author": "comment_author", "content": "comment_comment" }, { "author": "comment_author", "content": "comment_comment" } ] } ``` # デシリアライズ - fromJson() JSONからJavaへ変換する時は `fromJson()` を使います。 ```FromJsonSample.java public class FromJsonSample { /* * JSONからJavaオブジェクトへの変換 */ public static void main(String[] args) { Gson gson = new Gson(); // JSONからStringへの変換 String str = gson.fromJson("\"hello\"", String.class); System.out.println("String: " + str); // JSONからJavaオブジェクトへの変換 User user = gson.fromJson("{\"email\":\"bob@jmail.com\",\"fullname\":\"Bob\"}", User.class); System.out.println("User: " + user.email + " / " + user.fullname); // JSONから配列への変換 int[] array = gson.fromJson("[1, 2, 3]", int[].class); System.out.println("int[]: " + array[0] + ", " + array[1] + ", " + array[2]); // JSONからListへの変換 List list = gson.fromJson("[\"hello\", \"hellohello\",\"hellohellohello\"]", List.class); System.out.println("List: " + list.get(0) + ", " + list.get(1) + ", " + list.get(2)); // JSONからフィールドにListを含むJavaオブジェクトへの変換 String jsonStr = "{\"title\":\"投稿タイトル\",\"content\":\"本文本文本文\"," + "\"author\":{\"email\":\"bob@jmail.com\",\"fullname\":\"Bob\"}," + "\"comments\":[{\"author\":\"Tom\",\"content\":\"コメント本文\"}]" + "}"; Post post = gson.fromJson(jsonStr, Post.class); System.out.println("Post: タイトル=" + post.title + ", 著者=" + post.author.fullname + ", コメント件数=" + post.comments.size()); } } ``` ###実行結果 ``` String: hello User: bob@jmail.com / Bob int[]: 1, 2, 3 List: hello, hellohello, hellohellohello Post: タイトル=投稿タイトル, 著者=Bob, コメント件数=1 ``` |
|
| 482位 |
|
|||
|
01:12:24 |
(株式会社キュリオシティソフトウェア 所属) |
|
自分がやっているiOSアプリの受託開発をするうえでの開発フェーズとフローのまとめです。クライアントから案件の依頼があり、見積→契約成立→要件定義→設計→実装→テスト→検収作業という流れです。 | フェーズ |カテゴリ|内容| |:-----------|:------------:|:------------| |見積|Q&A|企画書や画面イメージ、参考になる他アプリを提示してもらえるか| |見積|Q&A|細かな仕様の要件がある場合はRFP(Request For Proposal:提案依頼書)があるか| |見積|Q&A| iTunes Connectを利用してアプリの更新を行うAppStoreのアカウントについて整理| |見積|明記すること|機能一覧| |見積|明記すること|見積有効期限| |見積|明記すること|動作確認するiOSのバージョンや端末を前提条件に| |見積|明記すること|納品完了(検収)条件を見積書前提条件に| |見積|明記すること|リジェクト時対応| |見積|金額|| |要件定義|要求まとめ|リリースするiOSアプリの目的、何をもって目的達成(ダウンロード数や会員数増加)とするか| |要件定義|要求まとめ|機能一覧に優先度を付ける| |要件定義|方式検討|技術的な課題がある部分のプロトタイプの作成| |要件定義|UI|画面イメージやワイヤーフレームの検討| |要件定義|UI|デザインを発注| |設計|内部設計|SQLiteのDB設計またはCoreDataのエンティティを設計しておく| |設計|開発指針決め|NSNotificationやdelegate, blocksなどを利用する場合はあらかじめ方針を決める| |設計|開発指針決め|ライブラリの選定| |実装|環境|接続するサーバー環境を切り分ける場合はDEBUG, RELEASEマクロ定義などで分けられるように| |実装|CI|Jenkinsを使って楽を出来るようにする| |実装|実装中の提案|仕様の曖昧な部分に対して提案しながら確認| |テスト|異常系|通信速度制限| |検収|受け入れ|発注側に問題ないかを確認してもらう| |リリース|リリース準備|iTunes Connectに登録するアプリ情報やスクリーンショットを決める| それぞれのフェーズの詳細を書いておきます # 見積フェーズ ## 見積り用のQ&A 見積フェーズでは発注側に企画書や画面イメージ、参考になる他アプリを提示してもらい見積金額を提示します。見積用の資料としてRFPがあれば曖昧な仕様にならずに見積もりを行うことも出来るかもしれませんが、発注側がRFPを用意してくれることは稀です。もし発注側が大手で契約を頻繁に行なっているようであればRFPを作成して頂くほうがスムーズに進みますとダメ元で言ってみるのも有効です。 そもそも、発注側はまずは規模感を知りたい、他社と比較したい事が多く、技術的に難しい場合があっても大きな工数や金額を出してしまうと話が進まなくなってしまうので、見積書には技術的に課題があるポイントや開発する上でのリスクを明示し、それを説明し工数・金額の数字の妥当性を理解してもらいましょう。またこの段階で仕様認識の齟齬がある場合も、上記のやりとりで早めに齟齬をなくすようにするのが良いでしょう。 早めにクライアントの予算も確認したいところですね 参考: [【はてな匿名ダイアリー】やり手クライアント VS 初心者SEの場合](http://anond.hatelabo.jp/20130724170059) - 対応させたいiOSのバージョンはiOS6からかiOS7からか- iPadも専用に対応させたいか(もしくはiPhoneと同じ見た目で良いか)- 企画書や画面イメージ、参考になる他アプリを提示してもらえるか- サーバーと連携する必要があるか - 細かな仕様の要件がある場合はRFP(Request For Proposal:提案依頼書)があるか- アプリの申請を行うのはどちらか- 納品完了(検収)条件は何か(開発期間、アプリの検収、ソースコードの納品、AppStoreでのリリースのいずれか) 参考に2014年1月末現在でiOSの利用率はiOS7が80%、2013年12月1日はiOS7が74%だった旨も伝え判断基準にしてもらうと良いかもしれません。 ## 見積書に明記してはっきりさせておく事 ### 見積有効期限 提出することになる概算見積もりはあくまで概算です。詳細見積のための打ち合わせを重ねているうちに明らかになっていなかった仕様が出てきたりもしますので、見積書には見積有効期限を明示することで、提出した概算見積もりから再度詳細な見積もりを出すことを念押しすることができます。 ### 動作検証 また、動作確認するiOSのバージョンや端末を見積書に明記しておくことで、あらかじめやるべきことに線引をすることができます。当然、新しいiOSや端末が出た場合は別途見積をしその動作確認をすればよいのであって、そのiOSのバージョンや端末だけで動作すればいいようなプログラムは作るべきではありません。 ### 納品条件 1. AppStoreでリリース 2. AppStoreへの申請。発注側にソースコードを納品(発注側のsvnもしくはgitへのコミット) 3. 稼働日での換算 4. レベニューシェア 1はAppleの審査によってリジェクトされると納品できない状態になるため、もし金額を多めに交渉できるとしてもまったくオススメ出来ません。Appleの審査に通ると思える仕様変更を手探りで行う場合もあり、審査方針の規約変更になど心理的に負担が大きくなります。 2は申請できる状態になった=発注側の受け入れで問題がなかったという状態なので審査に依存しないため1よりはオススメです。また、ソースコードの納品形態として発注側のsvn/gitにコミットして納品完了とするパターンもあります。 3は受託というよりも業務委託のパターンです。このパターンでは仕様変更や追加などについて稼働日が変わらなければ再見積ができない、つまり開発者の努力によって貰える金額が増えないためオススメしません。 4はやったことがないので分かりませんが、開発側で判断する材料として企画が優れていてリリース後に回収できる見込みがどのくらいかという目安を計算する必要があるでしょう。発注側に有利なので、開発側はその他の条件を交渉しやすい気もします...。 ### リジェクト時対応 リジェクトされた場合に、作業に再見積が発生するのかなど想定しておくのもよいでしょう。開発側の不適際なのか、発注者の要求などで仕方がなかったのか等で切り分けることになりますが、あらかじめリジェクト事例を別途提示すると親切です。リジェクト事例を持ち合わせていない場合は、[最低予算1万ポイントで。iPhoneアプリの審査でリジェクトを食らった事例をお教えください](http://q.hatena.ne.jp/1231517350)などのリジェクト集を発注側にも情報共有しておくのもいいかもしれません。 ## 金額 見積で算出する数値は、機能ごとの工数とし、その工数を金額に変換したものを提示しますが、見積する工数については見積ポーカーと2点見積を複合したやり方で見積を行います。それらのメリットを詳しく知りたい場合は[【PDF】ゲーム開発 プロジェクトマネジメント講座 - SQUARE ENIX](http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&cad=rja&ved=0CEoQFjAD&url=http%3A%2F%2Fwww.jp.square-enix.com%2Finfo%2Flibrary%2F2011%2Fdldata%2FPM%2FPM.pdf&ei=wszaUZSpIaTQiAfBl4H4Aw&usg=AFQjCNFe-5lFowviD1vIuZMztplIhfcHWQ&sig2=vf_Eu0SuCEnbhCy9rSx1-A)を参照してください。 AppStoreで公開するためのアカウントについても、あらかじめ整理しておく必要があります。発注側にソースコードを渡せば全てやってくれる場合もありますが、こちらで代理として申請を行うのか、こちらのアカウントを使って全ておまかせで公開するかなど細かいことですが見積に関わってきますので確認しておきます。 # 要件定義フェーズ ## 要求まとめ 発注側とコミュニケーションをしながらリリースしようとするiOSアプリの目的やそのための要求を詰めていくフェーズです。要件定義として打ち合わせを行い、要求を優先度順にまとめるなどドキュメント作成作業の間、技術的に課題がある部分を洗い出すためのプロトタイプ作成を進めておくことも重要です。 ## 方式検討 また、複数の方法で設計と実装が出来る場合、発注者に方式検討のメリットデメリットやそれらの方法を選んだ場合の制限を提示しましょう。方式検討はメリットデメリットをわかりやすくした表にすることが有効です。トラブル時などに、なぜこの方法を選んだのか、もうひとつの方法を選ばなかったのかということをすぐに振り返ることもできます。 ## UI ### 画面の要件決め 発注側と開発側で画面イメージやワイヤーフレームをさらに詳細にしていく際には、必ずAppleの[【PDF】iOS ヒューマンインターフェースガイドライン](iOS ヒューマンインターフェイス ガイドライン)を参照するようにして下さい。これは使いやすいアプリのためのドキュメントでもありますし、ヒューマンインターフェースガイドラインに沿っていないことでリジェクトされるのを防ぐためです。 ### デザイン デザインの発注が必要な場合、慣れているデザイン業者に頼むのが一番楽なのですが、発注者がiOSアプリのデザイン経験がないデザイン業者に発注する場合など、どのように納品物定義をしていいかわからないこともあるので、その場合は[【Qiita】iOSアプリ開発でアプリデザイン経験のない人にデザインの納品物をお願いするときのリスト](http://qiita.com/yimajo/items/11be7daeee0a7cd093c6 )を参考にしてもらうようにしましょう。 # 設計フェーズ ## 内部設計 SQLiteを使うのであれば、テーブル定義書を残しましょう。他にはNSUserDefaulstsを素直に使わない場合や、ファイル保存が必要になったが、圧縮したり分割したりする場合など、コードを追えば分かるものの、なぜそのような設計をしなければいけないのかという理由を明記されていると嬉しいものです。 ## 開発指針 設計フェーズではざっくり言うとiOSアプリをどのように作っていくかを決めますので、外部ライブラリの選定や、開発指針(NSNotification, blocks, delegateをどのような場合に使うかなど)を固めておくのも良いと思います。開発指針に関しては開発メンバーの力量を加味したり、優先度をあらかじめ決めておくと楽でしょう。 次のトピックが参考になります。 [【Qiita】変数/クラス名の命名規則に使われる記法の分類](http://qiita.com/yimajo/items/a3fac0026c07ec538fc2) [【Qiita】nilとNSNullの違いとNSNullをnilのように振る舞うようにする](http://qiita.com/yimajo/items/c9338a715016e7a812b1) また、要件定義フェーズで決まりきらなかった内容や後回しになった事を設計と並行して決めることになります。異常系の動作仕様などは、優先順位が低く要件定義フェーズに時間的余裕もない場合も多いでしょうから設計時に決めても良いでしょう。実装フェーズに入ってからでも問題ない場合が多いのではないかと思います。 # 実装フェーズ 実装フェーズでは前のフローで決めきれなかった異常系の処理や、曖昧な仕様のままの正常系の処理などを決めなければいけなくなるはずです。そのような部分は大抵イレギュラーな動作だったりクライアントの判断がつけづらい事であったりするため、都度都度クライアントにどのような仕様を望んでいるかを確認していては話が進まないことも多いでしょう。ここでも開発側であらかじめ提案できるドキュメントまたは実装を進めてから問題があるかの確認をするほうが良いでしょう。 ## 実装中の提案 UIの改善に関しては、開発者のほうが最近のアプリを日常的にも使っていて同じ事が簡単に出来るよと提案が出来ることも多いと思います。[【Qiita】UIWebViewの上下フリックでツールバーを表示したりしなかったり](http://qiita.com/yimajo/items/34d6ba63e4eadfc8f421)のようなあっさりと出来るテクニックはさくっと実装してクライアントに確認すると喜ばれますのでお勧めです。 # テストフェーズ ネットワークが関係するアプリの場合、テストには必ず通信速度を制限して行いましょう。[【Qiita】iPhone実機(iOS6.x)で通信速度を制限する](http://qiita.com/yimajo/items/efd3a033ac42afd93714)が参考になるはずです。 # 検収フェーズおよびリリース準備 審査用にiTunes Connectにあらかじめ各種情報を登録することができます。最終的にアプリをXcodeを使ってアップロードしますがあらかじめ登録をしておくほうが良いでしょう。 # おわりに 他にもこんな事をやったほうが良いとかその順序でやるのはセンスがないわー、とかアジャイルだともっとこうなるわーとかあればコメントに書いていただけると楽しいかなと思います |
|
| 483位 |
|
|||
|
00:47:51 |
|
|
androidで厄介なレイアウト回り、onMeasureとonLayoutを理解し、オーバーライドするとより理想のレイアウトに近づけます。
結構挙動が難しいので、長くなってしまいますがこれらの概要です。 (※おおざっぱな流れで細かい部分は違いますが、イメージはつかめると思います) そもそもこの2つは? ------------------------------------ AndroidではView生成時や、Viewの内容が更新される(TextView#setText()等)で、 View自体の再レイアウトが必要なときに2つが呼び出されます。 以下のようなイメージです。 1. 親ViewのonMeasureが呼び出される 1. 親Viewが子Viewのmeasureを呼び、幅高さを計測させる 1. (2)の情報をもとに、親Viewが自分自身の幅高さを設定する 1. 親ViewのonLayoutが呼び出される 1. 親Viewが子Viewのlayoutを呼び、子Viewの場所を確定させる このようなフローで各Viewのサイズ、場所が決まるわけです。 ※ここでは単一のViewGroupで書きましたが、実際はこの流れが再帰的に発生します。 簡単にいうと、**onMeasureは自分自身の幅高さを確定させるもの、onLayoutは子Viewの位置を決めるもの** です。 詳細を見ていきましょう。 onMeasure -------------------------------------------------- onMeasureですべきことは以下の2つです - (ViewGroupの場合)子Viewのサイズを measureメソッドを用い、適切なサイズに設定する - 引数や子Viewから、自分自身のサイズを setMeasuredDimension で確定させる ただしonMeasureの引数はちょっと面倒で、サイズ情報とモード情報の2つが入ってます。 ```java: @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); //高さも同様 //サイズ確定のためがこの後に続く } ``` sizeはわかると思います。ではこのModeとは何なのか? Modeには以下の3種類が定義されています * MeasureSpec.EXACTLY : sizeで指定した値に幅(高さ)を設定すること。子Viewもこの範囲に収まるようにサイズを決める。 * MeasureSpec.UNSPECIFIED : 特に指定なし。自由で構いません * MeasureSpec.AT_MOST : sizeで指定した値以下にする。子Viewもそれ前提でサイズを決める これらが幅と高さで独立して渡されます。 つまり 「幅は240で高さは自由で」「幅は自由だけど高さ100以内」 という指定がされるわけです。 これらはよく複数回連続して呼び出されます。 仮に次のようなXMLを考えてみましょう(記述を端折ってます) ```xml:layout.xml <LinearLayout width="fill" height="wrap" orientation="horizontal"> <TextVIew width="0dip" height="wrap" weight="1"/> <Button width="wrap" height="wrap"/> </LinearLayout> ``` よくあるパターンですね。この場合はおおざっぱに以下のようになります。 (幅480、高さ800 とした場合) 1. LinearLayout の onMeasure が ```W=480|EXACTRY, H=0|UNSPECIFIED``` で呼び出される 1. LinearLayout が```W=0|EXACTLY, H=0|UNSPECIFIED``` で TextView#measure を呼び出す 1. LinearLayout が```W=0|UNSPECIFIED, H=0|UNSPECIFIED``` で Button#measure を呼び出す 1. 子Viewの仮の幅の計算完了。余白を計算できるので、TextViewとButtonの幅が確定する 1. TextViewとButton の measure を ```W=確定幅|EXACTLY, H=0|UNSPECIFIELD``` で呼び出す。 1. これで子Viewのサイズ確定処理が完了。LinearLayout 自身のサイズを決める 1. 幅は引数がEXACTLYなので480 / 高さはTextViewとButtonの高さの大きいほうを使う 1. setMeasuredDimensionを呼び出して幅高さを確定させる という流れになります。(※実際はもっと複雑です) 子Viewのmeasureに渡す引数には、```MeasureSpec.makeMeasureSpec(int size, int mode)```を用います。 また、子Viewの確定サイズを知るには、 ```getMeasuredWidth(), getMeasuredHeight()``` を用います。 onLayout ---------------------------------------- これでようやくonMeasureが終わりました。 あとは簡単です。それをもとにonLayoutをオーバーライドしてレイアウトするだけです。 このときの注意点は以下の通りです。 - 子Viewのサイズは必ずgetMeasuredWidth(),getMeasuredHeight() で返ってくる値を使ってください - 特に初回レイアウト時、getWidth()/getHeight()は0で値がないです。 - getMeasuredWidth(),Height()と違う値を指定しないでください。どうなるかわかりません。 - 自分自身の左上に配置したいときは(0, 0)に配置します。引数のleff, topは無視でOKです。 もちろん、ちゃんと作るには各ViewのLayoutParamからマージン等を計算する必要ありますし Gravity等も考慮必要です。 そこでよく使うのが、親Viewに配置させて、ちょっとだけ場所をずらすパターンです。 ```java:CustomizedLinearLayout.java @Override public void onLayout(int left, int top, int right, int bottom, boolean changed) { super.onLayout(left, top, right, bottom, changed); View view = findViewById(R.id.view_id); int vl = view.getLeft(); //super.onLayoutでViewのleft,top等には値が入っている int vt = view.getTop(); int vr = view.getRight(); int vb = view.getBottom(); //ちょっと左にずらしますよ... vl -= 100; vr -= 100; view.layout(vl, vt, vr, vb); //このViewだけ位置をずらす } ``` FrameLayoutやLinearLayoutの基本のパターンからちょっとずらしたいときに非常に有効です。 逆に親Viewに頼らない自作Viewの場合、特定画面でのみ使うことが多いので、GravityやMargin属性を全く考慮せず(XML側でも指定せずに)レイアウトすることが多いです。 まとめ ------------------- - onMeasureはサイズを確定させる - onMeasureの引数にはモードとサイズが2つ含まれているので注意 - onMeasureでは必ずsetMeasuredDimensionを呼び出す - onLayoutでの子Viewの位置を決める - 両方とも、まずsuper.onXXXを呼び出した後にカスタムすると楽できる うまく使いこなせばかなりレイアウトの自由度があがります。 慣れるまでは難しいと思いますが積極的にトライしてみてください。 ※おまけ ちゃんとやろうとすると、onMeasureは相当面倒です。 LinearLayoutの内部処理がいかにすごいことをしているか.... 逆にいうと、**それだけ重い処理になります** 。 しかもView階層が深いとその重い処理がView階層分だけ再帰的に処理が走りますし。 深すぎるView階層には注意です。階層を浅くするため、自作ViewGroupやRelativeLayoutをうまく使いましょう |
|
| 484位 |
|
|||
|
13:42:23 |
(pixiv Inc. 所属) |
|
Elasticsearchはデフォルトで文字列を要素解析して保存する。これによって部分一致検索ができるようになるのだが、完全一致検索が難しくなる。
## 便利なTermsがうまくいかない KibanaにはTermsという値の上位10件とかを表示してくれるパネルがある。次のようなデータを入れているとする。 ```example.json { "@timestamp": "2013-12-17T10:12:40+09:00", "remote_addr": "203.0.113.10", "country_code": "JP", "request_uri": "/index.php", "user_agent": "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)" } ``` すると、いまアクセスされている国の比率とかを見たければ、`country_code`を指定してあげれば次の様なグラフが書けて非常に便利だ。  しかし、`user_agent`を指定するとつぎのようになったりして、うまくいかないことがある。  これはKibanaが空気を読んで要素解析してしまったせいである。できれば完全一致で見たい。 ## Mapping Templateを使う Elasticsearchはスキーマレスでインデックスが増えたら勝手にデータ型とかを判別してくれるが、フィールドのデータ型や、要素解析などを指定することもできる。そのためにはMapping Templateを使う。 ```test_template.json { "template": "*", "mappings": { "_default_": { "_source": { "compress": true }, "properties" : { "request_uri" : { "type" : "string", "index" : "not_analyzed" } } } } } ``` `template`フィールドで、どのインデックスにマッチするかを指定する。ここでは`"*"`としているのですべてのインデックスにマッチする。例えば`"access-*"`とすれば、`access-`から始まるインデックスだけに適用することもできる。この例では`request_uri`フィールドの型を`string`型、インデックスの要素解析を`not_analyzed`で無効にしている。これによって、`request_uri`の完全一致ができるようになる。 書けたらテンプレートをElasticsearchに登録する。 ``` $ curl -XPUT elasticsearch.local:9200/test_template/ -d "`cat test_template.json `" ``` ## Multi Fieldを使う 前の例では、`request_uri`の要素解析を`not_analyzed`にできたが、逆に部分一致検索をしようとしたらできなくなってしまった。これは困るので部分一致検索も完全一致検索も両方実現したい。そこで、1つのフィールドからデータ型や要素解析の方法が異なる複数のフィールドを生成する`Multi Field`を使う。 ```multi_field_template.json { "template": "*", "mappings": { "_default_": { "_source": { "compress": true }, "properties" : { "request_uri" : { "type": "multi_field", "fields": { "request_uri": { "type": "string", "index" : "analyzed" }, "full": { "type": "string", "index" : "not_analyzed" } } } } } } } ``` この例では、Multi Fieldではフィールド名を指定すると、`request_uri.full`の用に入れ子になったフィールドができる。また、Multi Fieldのフィールド名を元のフィールド名と同じにしておくと、`request_uri`で指定できるようになる。これで部分一致検索には元の`request_uri`フィールドを使い、完全一致検索は`request_uri.full`フィールドを使って実現できるようになった。 ## Dynamic Templateを使う Mapping Templateには特定のフィールドを指定しなくても、フィールド名の部分一致や型一致でテンプレートを適用してくれる。これをDynamic Templateという。このDynamic Templateを使って、すべての文字列型フィールドに先ほどのMulti Fieldを適用する。 ```template_all.json { "template": "*", "mappings": { "_default_": { "_source": { "compress": true }, "dynamic_templates": [ { "string_template" : { "match" : "*", "mapping": { "type": "multi_field", "fields": { "{name}": { "type": "string", "index" : "analyzed" }, "full": { "type": "string", "index" : "not_analyzed" } } }, "match_mapping_type" : "string" } } ], "properties" : { "@timestamp" : { "type" : "date", "index" : "not_analyzed" } } } } } ``` ## Termsがうまくいくようになった こんなかんじでできるようになって便利だ。  ## Mapping Templateを追加したが適用されないとき Mapping Templateは新しくインデックスができた時に適用されるので、インデックスを作り直すか、新しいインデックスを作るかしないと適用されない。fluent-plugin-elasitcsearchからログを入れているのであれば、日本時間9:00 AMになれば新しくインデックスができて適用されるはずだ。 ## まとめ Mapping Templateを使うと特定フィールドのデータ型を指定したりできてとても便利だ。しかし、自分はあまりこの機能を使わないようにしている。なぜならElasticsearchが持つスキーマレスなデータ型というのが失われるからで、できるだけデータを挿入するときにデータ型には気を遣うべきだと思っている。 とはいえ文字列の完全一致は使いたい。なので、文字列は`not_analyzed`にしてしまうか、Multi Fieldをうまく使ってよくするのが良いと思う。 |
|
| 485位 |
|
|||
|
02:26:33 |
(シナプス株式会社 所属) |
|
# 前書きとか
## ターゲット層は? 最低限 **HTML** についてある程度理解をされている方が対象です。 **XHTML** や **CSS** についての理解もあった方が望ましいと思います。 **C言語** などの基本的なプログラミング言語の経験もあれば幾分か理解がラクになるでしょう。なお、本講座では **XHTML5** の書き方で統一することにします。以下に有用な参考サイトを紹介します。 **XHTML5 移行メモ** http://kanow.jp/web/xhtml5-memo.xhtml **HTMLクイックリファレンス** http://www.htmq.com/ **W3G** https://w3g.jp/ ## 他に入門サイトとか既にあるんじゃないの? 入門向けといえども、[スパゲティコード](http://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%91%E3%82%B2%E3%83%86%E3%82%A3%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0)を量産するようなものでは納得がいかないので、今回この講座の作成にあたることになりました。なお、私は普段から **例外処理** (想定外のリクエストが来たときの対応) をガチガチに固めるコーディングを心がけています。 「そこまでしなくてもいいだろ」と思う箇所もあるかもしれませんが、この講座では徹底していきたいと思います。エラーを全く出させないコーディングをしましょう。<ins>熟練者が分かっていて手抜きをするのと、初心者が手抜きと知らずに手抜きしてしまうのでは全く事情が異なります。</ins> ## 何故にQiita上で? だって書くのめっちゃラクじゃんwwwwwwwwwwww # 目次 ## 1. PHP概論 1. [PHPを使うための準備](http://qiita.com/items/4508dc677b11e487effc) 1. [PHPで出来ることと処理の流れ](http://qiita.com/mpyw/items/176fb1f6fb75da74339f) 1. [変数と定数](http://qiita.com/mpyw/items/95e205056e25b7a59dfa) ## 2. Webフォームでの実践(入門編) 1. [XSS攻撃への対策](http://qiita.com/mpyw/items/565b3670dd0c7f9162fa) 1. [ユーザーに挨拶してみよう](http://qiita.com/mpyw/items/55369c245b0d22755151) 1. [想定外の入力への対応](http://qiita.com/mpyw/items/f298828002bbc488fe89) 1. [ちょっとひといき](http://qiita.com/mpyw/items/85f930cd893ddb27ff12) ## 3. 型と演算子 1. [デバッグ用関数](http://qiita.com/mpyw/items/96ed939a208e3cb95472) 1. [文字列](http://qiita.com/mpyw/items/d61b50d90e84e289e2be) 1. [数値・論理値・リソース](http://qiita.com/mpyw/items/346c535ade9dcf6e1b12) 1. [配列](http://qiita.com/mpyw/items/5a30250ba2d622352959) 1. [NULLと未定義の違い](http://qiita.com/mpyw/items/0a4ea0bc9a695da33f0c) 1. [型変換](http://qiita.com/mpyw/items/3aac811629622f7f4d8b) 1. [ビット演算](http://qiita.com/mpyw/items/ce626976ec4dc07dfec2) 1. [演算子](http://qiita.com/mpyw/items/1ea553fea092f597169a) 1. 緩やかな比較・厳密な比較 ## 4. 制御構造と関数 1. 条件分岐とループ処理 1. 関数とクロージャ 1. ジェネレータ 1. 関数に似た言語構造 1. 変数初期化の必要性 1. 配列操作関数とその応用的な使い方 1. マジック定数 ## 5. 参照 1. PHPにおけるプリミティブ型 2. シンボルテーブル 3. 値渡しと参照渡し 4. 参照カウント法とガベージコレクション ## 6. スーパーグローバル変数 1. GET・POST・COOKIE・REQUEST 1. FILES・SERVER・ENV 1. SESSION ## 7. エラーと例外 1. エラーの種類 1. 例外処理の基本 1. エラーと例外の違い 1. 例外スタックを用いた応用 ## 8. Webフォームでの実践(応用編) 1. ユーザー入力をフィルタリングする汎用関数の作成 1. カレンダー表示スクリプトの作成 1. メールフォームの作成(3ファイルに分割) 1. メールフォームの作成(1ファイルに梱包) ## 9. クラスとオブジェクト(前半) 1. クラス・オブジェクトとアクセス権 1. 動的メソッドと動的プロパティ 1. 静的メソッドと静的プロパティ・クラス定数 1. 静的クラスとシングルトンの実現 ## 10. 画像アップロード機能付きの掲示板作成 1. 基本的な機能の実装 1. ファイルロック処理の実装 1. 画像ファイルアップロード処理の実装 1. CSRF攻撃への対策 ## 11. クラスとオブジェクト(後半) 1. マジックメソッド 1. オブジェクトと参照渡し 1. クラスの継承と遅延静的束縛 1. 抽象クラス・インターフェース・トレイト 1. 名前空間 ## 12. 正規表現 1. 正規表現を使うかどうかの検討 1. PCRE正規表現構文の基本 1. エスケープ処理 1. 各PCRE正規表現関数とサブパターンの扱い 1. 最短マッチと最長マッチ 1. 独占的最長マッチと再試行無しサブパターン 1. 後方参照と先読み・後読み 1. コメントと再帰パターン 1. 条件付きサブパターン ## 13. データベース 1. リレーショナルデータベースとは 1. phpMyAdminの基本的な使い方 1. PDOの基本 1. PDOの応用 ## 14. Twitter風SNS作成 1. データベースと設定ファイルの作成 1. 汎用関数モジュールの作成 1. 汎用的な変数構造フィルタリング関数の作成 1. 致命的なエラー発生時のためのページを準備 1. ユーザー・ログイン機能の実装 1. セッション固定攻撃への対策 1. ツイートの実装 1. ユーザーページの実装 1. フォロー機能の実装 1. タイムラインの実装 1. 返信と非公式リツイートの実装 1. お気に入りの実装 1. 公式リツイートの実装 1. ログアウトの実装 1. ツイートダウンロード機能の実装 # 後書きとか ## お前これホントに全部つくんの???www さぁどうだろうねぇ(すっとぼけ) |
|
| 486位 |
|
|||
|
00:30:38 |
(IPROS 所属) |
|
## ageが20かつnameが太郎(SQLのAND) ```shell-session age:20 AND name:"太郎" ``` ## ageが20かnameが太郎(SQLのOR) ```shell-session age:20 OR name:"太郎" ``` ## 正規表現 ```shell-session name:/joh?n(ath[oa]n)/ ``` ## ageが1か2(SQLのIN) ```shell-session age:(1 2) ``` ## ageが20ではない(SQLの!=) ```shell-session -age:20 ``` ### または ```shell-session NOT(age:20) ``` ## ageが20から30の間(SQLのBETWEEN) ```shell-session age:(20 TO 30) ``` ## ageが10以上20未満 ```shell-session age:(>=10 AND < 20) ``` ## dateが2012/01/01以前 ```shell-session date:[* TO 2012-01-01] ``` ## dateが2012/01/01 ~ 2012/12/31の間(型がdateである必要あり) ```shell-session date:[2012-01-01 TO 2012-12-31] ``` ## 時刻まで指定 ```shell-session date:[2014-01-11T17:04:39+09:00 TO *] ``` |
|
| 487位 |
|
|||
|
01:27:25 |
(株式会社メルカリ 所属) |
|
[列挙型 - Wikipedia](http://ja.wikipedia.org/wiki/%E5%88%97%E6%8C%99%E5%9E%8B)
まず列挙型の定義は〜となるんだけど、ここでは「あらかじめ定義した値のいずれかしか取らない特殊な型」という感じを想定してます。 要は[SplEnum](http://jp2.php.net/manual/ja/class.splenum.php)みたいなのですが、拡張モジュールの力を借りなくても、PHPだけで作れます。リフレクションを使うだけ。 ```php <?php abstract class Enum { private $scalar; public function __construct($value) { $ref = new ReflectionObject($this); $consts = $ref->getConstants(); if (! in_array($value, $consts, true)) { throw new InvalidArgumentException; } $this->scalar = $value; } final public static function __callStatic($label, $args) { $class = get_called_class(); $const = constant("$class::$label"); return new $class($const); } //元の値を取り出すメソッド。 //メソッド名は好みのものに変更どうぞ final public function valueOf() { return $this->scalar; } final public function __toString() { return (string)$this->scalar; } } ``` ```php:使い方 <?php // トランプのスート型を定義する。4種類しか値を取らない。 // Enumをextendして、定数をセット final class Suit extends Enum { const SPADE = 'spade'; const HEART = 'heart'; const CLUB = 'club'; const DIAMOND = 'diamond'; } //インスタンス化 $suit = new Suit(Suit::SPADE); echo $suit; //toString実装済みなので文字列キャスト可能 echo $suit->valueOf(); //生の値を取り出す。intやfloat等の場合に。 // 適当な値を突っ込もうとすると、InvalidArgumentExceptionが発生して停止 //$suit = new Suit('uso800'); //__callStaticを定義してあるのでnewを使わずこんな感じでも書ける(PHP5.3以降) $suit = Suit::SPADE(); ``` PHPには組み込みのEnum型はないので、無理にEnumを作った所で速度的な旨味はありません。ただ、型チェックだけで「この定義済みの値のいずれかである」ことが保証できます。 ユーザーの入力なんかを予め検証して、適当な型にキャストしておけば、以降は型チェックだけで安心してデータを扱えるようになります。べんり。 ```php <?php function doSomething(Suit $suit) { //Suitというタイプヒントによって、 //$suitは必ず4種類のうちのどれかだと保証されている } ``` 元の値を取り出すメソッド名に困ったので、JavaScript風にvalueOf()にしてますが、まあ別に何でもOKです。 |
|
| 488位 |
|
|||
|
00:26:28 |
|
|
# はじめに
最近 jQuery はじめたけど、セレクタ自由に使えないと捗らないのでまとめておく。 # 注意点 * 以下に出てくるセレクタ特殊文字('.', ',', ':', '#', '[', ']')はエスケープが必要 # 基本セレクタ タグ名 or クラス名 or ID による指定ができる。 ```js $("form"); // タグ名="form" $(".className"); // クラス名="className" $("#formId"); // ID="formId" ``` # 複合条件の指定 区切りなしでAND、カンマ区切りでOR。 ## AND 指定 ```js $("ul.listClass1"); // タグ名="ul" and クラス名="listClass1" $("ul#listId1"); // タグ名="ul" and ID="listId1" ``` ## OR 指定 ```js $("div,.listClass1"); // タグ名="div" or クラス名=listClass1 $("ul,#divId1"); // タグ名="ul" or ID="divId1" ``` # 階層関係の指定 ## 親-子関係 ```js $("#root > .node"); // 「ID="root"」を親に持つ「クラス名="node"」 $(".node").parent("#root"); // 「クラス名="node"」の親のうち「ID="root"」の要素 ``` ## 先祖-子孫関係 ```js $("#root .node"); // 「ID="root"」を先祖に持つ「クラス名="node"」 $(".node").parents("#root"); // 「クラス名="node"」の先祖のうち「ID="root"」の要素 ``` ## 兄-弟関係 ```js $(".node").siblings(); // 「クラス名="node"」の兄弟(自分を除く) ``` # フィルタ ## 属性 ```js $('input[name]'); // タグ名="input" and name属性を持つ $('input[name="quiz1"]'); // タグ名="input" and name属性が"quiz1"と一致 $('input[name!="quiz1"]'); // タグ名="input" and name属性が"quiz1"と不一致 $('input[name^="quiz1"]'); // タグ名="input" and name属性が"quiz1"と先頭一致 $('input[name$="quiz1"]'); // タグ名="input" and name属性が"quiz1"と末尾一致 $('input[name*="quiz1"]'); // タグ名="input" and name属性が"quiz1"と部分一致 ``` ## 使用可否 ```js $('input[type="text"]:enabled'); // 入力可能テキストボックス $('input[type="text"]:disabled'); // 入力禁止テキストボックス ``` ## 選択状態 ```js $('input[type="checkbox"]:checked'); // 選択済チェックボックス $('input[type="radio"]:checked'); // 選択済ラジオボタン $('select > option:selected'); // リストボックス・コンボボックス内の選択済要素 ``` ## 可視性 ```js $('div:hidden'); // 不可視状態になっているdiv要素(display:noneも含む) $('input:hidden'); // 不可視状態になっているinput要素≒$('input[type="hidden"]'); $('div:visible'); // 可視状態になっているdiv要素 ``` なお、単純な":hidden"だとhead,title,scriptタグ等にもマッチする可能性がある。 # おことわり * ここで挙げたのは、個人的によく使いそうなもののみである。 * 動作未確認のため、表記ミス・勘違いがあるかもしれない。 * 指摘があれば多分修正する。 |
|
| 489位 |
|
|||
|
19:14:15 |
|
|
Google IO 2013(2013/05/15)で発表された新しいgoogle playの機能です。
# 概要 β版アプリを配布する機能です。 アプリ管理画面に公開版、β版、α版のapk登録場所があって、βとαはユーザー限定して配布できます。 本番公開前にユーザーテストを行うことができます。 公開後に、最新バージョンを一部のユーザーにのみ提供してテストすることもできます。 # メリット 一部ユーザー限定にアプリを配布して試してもらうことができます。 アプリに対して評価の★をつけたりすることができません。 なので一般公開前にひどい評価になるようなことがありません。 また、公開版なしにβ版のみの状態では、アプリの検索にひっかかって見つかることもありません。 一般公開せずに、いつまでも限定ユーザーのみに提供ということも可能です。 特定のgoogleグループに入ってる人限定とか。 ベータ版利用人数に制限はないと思われます。 # 利用フロー 1.テスター募集(android限定・googleアカウントを教えてもらう) 2.抽選・選別 3.ユーザー限定リリース 4.テストサイクル回す 5.一般公開に移行 # 既にある開発者向けサービスとの違い testflightで問題になるUUIDの収集の手間。 deploygateで必要な開発者オプション(提供元不明をインストール許可)設定 これらのユーザーへのデメリット・リスクなしに提供できます。 ### deploygateのメリット deploygateには誰がどの端末にどのバージョンをインストール、アンインストールしたというログが残ります。 あと強制終了などのデバッグログ記録など。 # 配布先の指定 2パターン。 1.googleグループを指定 2.google+のサークルを指定する方法 通常は1になると思います。 # 配布するユーザーの登録手順 後にスクリーンショット付で書きますが、 1.非公開のgoogleグループを作成する 2.ユーザーを招待する 3.このグループ向けに配布する つまり、招待するためのgmailのアドレスを教えてもらう必要があります。 # 実際の手順 ## まずはアプリの登録 新しいアプリを追加から  アプリ名いれてAPKをアップロードを選択  ## ベータ版アップロード ベータ版テストを選んで  アップロード完了。 ベータ版から製品版に移行することもできます。(製品版にプロモートボタン) ## アプリ情報を編集 通常と同じ。 公開されないけどマーケットの説明文とかスクリーンショットなど必要です。 有料にしたらベータ版はどうなるのか未確認。おそらく無料でしょう  ## google グループを作成 まずは普通に作る。 このメールアドレスが登録するものになります。  テスト版URLを書いておきましょう。 トピック立てておくのもわかりやすい  招待する前にプライバシー設定を。 メンバー間のメールアドレスを表示しないようにする トピックをたてる・投稿をできなくするなど。  ## 招待する 招待する相手を登録  ここでgoogle groupのメールアドレスを登録。 下のリンクがテスターに伝えるものになります。  ## 招待を受ける 招待されるとメールがくる(Gmail) URLを開いてブラウザでログイン。  ## テスター登録 グループに登録されているURLを開くと、この画面になる。 テスターになるを選んでもらう  これでダウンロードできるように。  |
|
| 490位 |
|
|||
|
16:31:29 |
|
|
# 作成
```pl hoge# ln -s /hoge/fuga/watshi ./watashi もしくは hoge# ln -s /hoge/fuga/watshi watashi また、もし同じ名前でいいのなら、 hoge# ln -s /hoge/fuga/watshi ``` すると ```pl lrwxrwxrwx 1 hoge hoge 24 Feb 5 16:20 watashi -> /hoge/fuga/watshi ``` *パスの最後にスラッシュはいりません リンクの方にスラッシュをつけると、エラーになります。 ```pl hoge# ln -s /hoge/fuga/watshi/ ./watashi/ ln: target `./watashi/' is not a directory: No such file or directory ``` また、URIの方にスラッシュを付けると見辛い事になります。 ```pl hoge# ln -s /hoge/fuga/watshi/ ./watashi hoge# ll lrwxrwxrwx 1 hoge hoge 24 Feb 5 16:20 watashi -> /hoge/fuga/watshi// ``` # 削除 ```pl hog# unlink ./watashi ``` rmを使ってやることもできますが、、、 ```pl hoge# rm ./watashi rm: remove symbolic link `watashi'? y ``` でも rm だと、↓みたいにもしスラッシュをつけちゃうとだと実体を削除しちゃうから注意 ```pl hoge# rm watashi/ ``` |
|
| 491位 |
|
|||
|
23:36:39 |
|
|
## angular雑感 2014-04-30 シングルページのWebアプリケーションにはとても相性が良いです。サーバー側でAPIだけ定義しておけば、`$resource`で簡単にCRUDするモデルを扱うことができ、`$routeProvider`を使えばルーティングができます。 これまでjQueryでごりごり書いてきた人も多いと思います。ただ残念ながらこれまでのjQuery資産の9割は使えないと思います。うまくプラグイン化されていれば使えなくはないです。どうしても使いたい場合は、`$apply`を呼び出したりして、angularの世界に合わせる必要があります。 学習コストは最初は低いですが、ある程度複雑なことをやろうとすると、一気に高くなります。フレームワークというのは往々にしてそういうものですね。特にDI、スコープ、ディレクティブが肝であり鬼門だと思います。 利点の1つとして、JSファイルの読み込み順を気にしなくて良いというのがあります。DIの依存性解決のおかげです。gruntでディレクトリ以下のJSファイルをそのままconcatしても、簡単に動作させることができます。 IE8以下必須の場合には使わない方が良さそうです。1.3ではサポートしないそうです。https://docs.angularjs.org/guide/ie 現時点、使えそうで使えない標準モジュールも結構あります。touchとかcookieとか。。。(ngTouchは1.2で使えるようになってるかもしれないです。自分が試した1.1では微妙な動作だった記憶があります)cookieはexpires指定ができません。 # プロバイダーに関して (factory, service, provider) ## constant, value, service, factory, providerの使い分け - constant: プロバイダーに注入したい時や、configフェーズで使いたい場合。絶対不変な値。decoratorできない - value: factory, serviceに注入したい時や、runフェーズで使いたい場合。値が変わる可能性がある場合。 - service: CoffeeScriptのclassで定義したようなものを使いたい場合 - factory: runフェーズで注入したい場合 - provider: インスタンス化する前にconfigフェーズで設定する必要がある場合 `value`も`factory`も`service`も、結局は`provider`の機能制限版のようなものなので、よくわからない場合は`factory`だけ使っていれば済むと思います。 [Developer Guide/Providers](https://docs.angularjs.org/guide/providers) ## config, run angularの実行フェーズは2段階あります。config と run です。それぞれ注入できるサービスが違います。[この表](https://docs.angularjs.org/guide/providers#conclusion)が分かりやすいです。 ## provider http://jsfiddle.net/akkunchoi/Dcpq2/ ```javascript: var app = angular.module('sample', []); app.provider('api', function(){ return { $get: function(){ return 'api'; }, connect: function(){ // 処理... } }; }); app.provider('person', function(apiProvider){ apiProvider.connect(); return { init: function(){ // 設定とか... }, $get: function(){ return 'person'; } }; }); // config: ここでは主にproviderの設定を行う // 例えば $routeProvider app.config(function(personProvider){ personProvider.init(); console.log(personProvider); }); // run: $get()した結果を取得 // 例えば $route app.run(function(person){ console.log(person); }); ``` `app.config()`には`app.provider()`か`app.constant()`で登録したものしか注入できません。プロバイダーを config で注入する場合には`Provider`を付ける必要があります。 `app.run()`には`app.constant()`や、プロバイダーが`$get()`した戻り値であるインスタンスを注入できます。 ## factory http://jsfiddle.net/akkunchoi/QR7FZ/ ```javascript: var sample = angular.module('sample', []); // core factoryを定義 sample.factory('person', function(){ return { name: 'akkunchoi' }; }); // 依存性注入により、上で定義したオブジェクトが入る sample.run(function(person){ console.log(person.name); // => 'akkunchoi' }); ``` ## service http://jsfiddle.net/akkunchoi/37LGj/ ```javascript: var sample = angular.module('sample', []); var MyUserService = (function(){ var number = 0; MyUserService = function(){ this.number = ++number; }; MyUserService.prototype.getNumber = function(){ return this.number; }; return MyUserService; })(); sample.service('person', MyUserService); // 依存性注入により、上で定義したオブジェクトが入る sample.run(function(person){ console.log(person.getNumber()); // => 1 }); sample.run(function(person){ console.log(person.getNumber()); // => 1 }); ``` MyUserServiceクラスをnewした結果が、注入するオブジェクトになります。そのため、functionやプリミティブな値を注入することはできません。 # ディレクティブ ## 簡単な例 ```html: <div sample-code>Content</div> ``` ```javascript: app.directive('sampleCode', function(){ return function(scope, element, attrs){ console.log(scope, element, attrs); }; }); ``` - scopeは[ng.$rootScope.Scope](http://docs.angularjs.org/api/ng.$rootScope.Scope) - elementはjQuery(またはjQuery Lite) - attrsは [ng.$compile.directive.Attributes](http://docs.angularjs.org/api/ng.$compile.directive.Attributes) 属性名はコロン、アンダーバー、ハイフンで単語を区切りますが、 **directive定義する時はキャメルケースになることに注意!** ## ディレクティブのオプション http://jsfiddle.net/akkunchoi/dGLUT/ - restrict: デフォルトは'A'。'AEC'を組み合わせて指定する。restrict指定せず要素名をディレクティブとして書いても無反応なのが気づきにくい。 - `scope: {}`を指定すると isolated scope を作成し、ディレクティブ内での名称 => 属性名のマッピングを行う。 '=' は 属性名同じで値を解釈する '&' は 関数として実行可能 '@' は 常に文字列として解釈 - `scope: {}`のスコープ(isolate)は、どこが隔離されているかというと、templateの内部とlink()で受け取るscope。DOM要素の子は隔離されていない(この辺りの仕組みはよく理解できいない) http://jsfiddle.net/LqzxP/2/ - `scope: true`のスコープはプロトタイプ継承を行う。プリミティブな値や親で未定義の値を変更しても、親には反映されない - template: HTMLを記述する - templateUrl: URLを指定する - link: function link(scope, element, attrs) { ... } - transclude: trueにするとng-transcludeが使えるようになる。ディレクティブのDOM内部のデータが、テンプレート内で"ng-transclude"した場所に埋め込まれるようになる。 - controller: 実行したいコントローラーを指定する。 - controllerAs: コントローラの省略名 - require: linkの第四引数に受け取るコントローラを指定。ディレクティブ名(ng-contoller用に登録したコントローラは指定できない??)。先頭に`^`をつけると親からも探索する。`?`をつけると存在しない場合でもエラーにならない。 - replace: テンプレートで内容を置き換える(非推奨になるらしい??) - priority: 大きいほど先に実行 - terminal: trueならそこで終わり ## その他 - 同じ名前のディレクティブでも何度も定義できる。その場合上書きにはならず、優先度順に実行していく。 - 未定義のディレクティブをテンプレートに記述してもエラーにはならない。 - decoratorする場合は "〜Directive" - `element.on('$destroy', ...)`または`scope.$on('$destroy', ...) `でクリーンアップするコードを書く必要がある(??) 当然ですが、ng-controllerもdirectiveのひとつです。内部でcontrollerオプションを指定しているだけです。 # スコープ スコープ階層はJavaScriptのプロトタイプ継承そのものです。 別の記事にしました。 - [スコープに関して](http://qiita.com/akkun_choi/items/f9db1e920069a2909602) - [`$apply`と`$digest`について](http://qiita.com/akkun_choi/items/22048f31f9add7fda2c5) ## $on が受け取る特殊なイベント - $viewContentLoaded: ng-viewによって中身の読み込みが完了した時に呼ばれます。jQueryMobileの`pageinit`的なもの。 ```javascript: $scope.$on('$viewContentLoaded', function() { //call it here }); ``` - $includeContentLoaded: ng-includeによって中身の読み込みが完了した時。 その他のイベント - $locationChangeStart - $locationChangeSuccess - $routeUpdate - $routeChangeStart - $routeChangeSuccess - $routeChangeError - $destroy ## $emit vs $broadcast - $emit: 上位のスコープにイベントの発火を伝えます - $broadcast: 下位のスコープにイベントの発火を伝えます ## $watch 値が変更した時に何か実行したい時使います。オブジェクトや配列の中身が変わった時を知りたい場合は $watchCollection というのもあります。 # ビュー周り ## ng-classである条件の時だけクラスをつけたい http://jsfiddle.net/akkunchoi/95VJR/ `flag`にbooleanが入るようして、`selected`というCSSクラスを付けたい場合 ```html: <span ng-class="{selected: flag]">項目</span> ``` こういう書き方もできる ```html: <span ng-class="{true: 'js-selected'}[flag]">項目</span> ``` ## デフォルト値 ```html: {{gallery.date || 'Various'}} ``` ## ng-attr-* IE11でtextareaにplaceholderを使うと「Error: 引数が無効です」というエラーが出ました。起きない場合もあり、動的にtextareaを生成をしているのが条件かもしれないですが、よくわかってません。 https://github.com/angular/angular.js/issues/5025 http://stackoverflow.com/questions/20647572/angularjs-v1-2-5-script-error-with-textarea-and-placeholder-attribute-using-ie11/20649762#20649762 調べた所 `ng-attr-placeholder` にすると解決できました。 はて、ng-attr-ってなんだろ?APIリファレンスにはありません。[ディレクティブの ngAttr attribute bindings](https://docs.angularjs.org/guide/directive)に書いてありました。 ブラウザが属性値を参照して何か特別な動作をする場合には`ng-attr-`というプレフィックスを付けるとangularが解釈した後の値に変えてくれます。例えばSVGの属性を指定する時などにもこれが必要になるようです。 ## select の ng-options select要素の生成にng-optionsがどうも思ったように動作しない。具体的に言うとselectのvalue属性が指定できない、変な空白要素が出てくる、など。 改めてAPIリファレンスを見ると[このようなNote](https://docs.angularjs.org/api/ng/directive/select)が書かれていました。 > ngModelは値ではなく参照で比較します。これはオブジェクトの配列をバインドする際に重要なポイントです。[このjsfiddleを参照下さい](http://jsfiddle.net/qWzTb/) つまり、このようにして使います。 ``` javascript: $scope.genres = [ {label: '日本', group: 'アジア'}, {label: '中国', group: 'アジア'}, {label: 'アメリカ', group: '欧州'} ]; // genreに label の名称を入れるのは間違い。参照を設定する。 $scope.genre = $scope.genres[0]; ``` ```html: <select ng-options="genre.label group by genre.group for genre in genres" ng-model="genre"> ``` # その他 ## angular.module 登録したモジュールは `ng-app` で指定すると実行されます。 ```html: <html ng-app="sample"> ``` `angular.module`の第二引数では依存モジュールを指定できます。 循環した場合でも起点(ng-app)があるのでエラーは出ないです。 依存モジュールを指定した場合、注入できるのは先に読んだモジュールで定義されたproviderだけのようです。 http://jsfiddle.net/akkunchoi/2DLKc/ ### 実行順 モジュールの実行順は、基本は依存モジュール(第二引数)の指定順ですが、常に必要とされているモジュールから実行されます。以下の例ではまず `plugin1` が実行され、`plugin2`が必要としている `plugin3` が実行され、`plugin2` が実行されます。 ```javascript: angular.module('sample', ['plugin1', 'plugin2', 'plugin3']); angular.module('plugin1', []); angular.module('plugin2', ['plugin3']); angular.module('plugin3', []); // plugin1 // plugin3 // plugin2 // sample ``` http://jsfiddle.net/akkunchoi/H9Jst/ ## setTimeout, window, location, document setTimeout, window, location, documentは使わないようにします。特にsetTimeoutは **内部の関数でスコープ内の値を変更する場合は必ず$timeoutにする必要があります**。$timeout等にすることで、angularは内部でスコープのどの値が変更されたかを確認して、変更されていればビューに通知するなどの処理を行います(いわゆるdigestループ)。ここがangularを使う上での重要なポイントです。 - setTimeout => $timeout - window => $window - location => $location - document => $document ## 難読化する前に ng-annotate (ngmin) Angularのコードを難読化(uglify,minify)すると動かないことがあります。一番シンプルなインジェクションの方法では、引数名でどのサービスを注入するか判断します。しかし難読化すると引数名が変わってしまうのでインジェクションできません。 ```javascript: // 難読化すると動かない例 angular.module('sample') .controller('SampleCtrl', function($scope){ $scope.foo = 'bar'; }); // 難読化するとこのように変数名が短くなる。よく"a"が見つかりません的なエラーになるのはこのため。 angular.module('sample') .controller('SampleCtrl', function(a){ a.foo = 'bar'; }); ``` $injectや配列記法にすると面倒です。そういう時に [ng-annotate](https://github.com/olov/ng-annotate) を使います。`.controller()` 等はもちろん、functionの前に `/* @ngInject */` をつけると任意の関数を配列記法にしてくれます。 [ngmin](https://github.com/btford/ngmin)というのもありますが、以下の場合に変換してくれません。 - angular.moduleして後で、別の変数に変更した場合 - `$routeProvider`の`resolve` - `$provide.decorator`の`$delegate`注入時 ## Provider一覧 $injectorを直接調べました。(1.2) ``` $anchorScrollProvider: Constructor $animateProvider: Constructor $browserProvider: Constructor $cacheFactoryProvider: Constructor $compileProvider: Constructor $controllerProvider: Constructor $documentProvider: Constructor $exceptionHandlerProvider: Constructor $filterProvider: Constructor $httpBackendProvider: Constructor $httpProvider: Constructor $injector: Object $interpolateProvider: Constructor $intervalProvider: Constructor $localeProvider: Constructor $locationProvider: Constructor $logProvider: Constructor $parseProvider: Constructor $provide: Object $qProvider: Constructor $rootElementProvider: Object $rootScopeProvider: Constructor $sceDelegateProvider: Constructor $sceProvider: Constructor $snifferProvider: Constructor $templateCacheProvider: Constructor $timeoutProvider: Constructor $windowProvider: Constructor currencyFilterProvider: Object dateFilterProvider: Object filterFilterProvider: Object formDirectiveProvider: Object inputDirectiveProvider: Object jsonFilterProvider: Object limitToFilterProvider: Object lowercaseFilterProvider: Object ngBindDirectiveProvider: Object ngBindHtmlDirectiveProvider: Object ngBindTemplateDirectiveProvider: Object ngBlurDirectiveProvider: Object ngChangeDirectiveProvider: Object ngCheckedDirectiveProvider: Object ngClassDirectiveProvider: Object ngClassEvenDirectiveProvider: Object ngClassOddDirectiveProvider: Object ngClickDirectiveProvider: Object ngCloakDirectiveProvider: Object ngControllerDirectiveProvider: Object ngCopyDirectiveProvider: Object ngCutDirectiveProvider: Object ngDblclickDirectiveProvider: Object ngDisabledDirectiveProvider: Object ngFocusDirectiveProvider: Object ngFormDirectiveProvider: Object ngHideDirectiveProvider: Object ngHrefDirectiveProvider: Object ngIfDirectiveProvider: Object ngIncludeDirectiveProvider: Object ngInitDirectiveProvider: Object ngKeydownDirectiveProvider: Object ngKeypressDirectiveProvider: Object ngKeyupDirectiveProvider: Object ngListDirectiveProvider: Object ngModelDirectiveProvider: Object ngMousedownDirectiveProvider: Object ngMouseenterDirectiveProvider: Object ngMouseleaveDirectiveProvider: Object ngMousemoveDirectiveProvider: Object ngMouseoutDirectiveProvider: Object ngMouseoverDirectiveProvider: Object ngMouseupDirectiveProvider: Object ngNonBindableDirectiveProvider: Object ngOpenDirectiveProvider: Object ngOptionsDirectiveProvider: Object ngPasteDirectiveProvider: Object ngPluralizeDirectiveProvider: Object ngReadonlyDirectiveProvider: Object ngRepeatDirectiveProvider: Object ngRequiredDirectiveProvider: Object ngSelectedDirectiveProvider: Object ngShowDirectiveProvider: Object ngSrcDirectiveProvider: Object ngSrcsetDirectiveProvider: Object ngStyleDirectiveProvider: Object ngSubmitDirectiveProvider: Object ngSwitchDefaultDirectiveProvider: Object ngSwitchDirectiveProvider: Object ngSwitchWhenDirectiveProvider: Object ngTranscludeDirectiveProvider: Object ngValueDirectiveProvider: Object numberFilterProvider: Object optionDirectiveProvider: Object orderByFilterProvider: Object requiredDirectiveProvider: Object scriptDirectiveProvider: Object selectDirectiveProvider: Object styleDirectiveProvider: Object textareaDirectiveProvider: Object uppercaseFilterProvider: Object ``` |
|
| 492位 |
|
|||
|
05:44:37 |
|
|
きっかけはこの記事です。
http://qiita.com/masuidrive/items/21a282c7bf54fd6a4985 ---- >ここ数年、位置情報を使ったアプリ・サービスが増えましたが、GPSから取得出来る緯度経度だけではデータとして使いにくい事があります。 > >GoogleのGeocodingサービスなどで、緯度経度から住所への変換ができますが、件数や速度の問題があります。 > >そこで、国土交通省のデータを元に、緯度経度から住所への変換を行ってみましょう。 今回は、総務省のデータを元に、緯度経度から住所への変換を行ってみましょう。 ## 政府統計の総合窓口 - http://www.e-stat.go.jp/SG1/estat/eStatTopPortal.do ダウンロード先の道順を説明すべきなのですが、実は道順を忘れました・・・。 私は以下をブックマークしています。 http://e-stat.go.jp/SG2/eStatGIS/page/download.html #Step1:統計調査(集計)を選択 平成22年度国勢調査(小地域)を選択しましょう。 #Step2:統計表を選択(複数選択可能) 右側に何やら項目がいっぱい出てきたと思います。 適当に男女別人口総数及び世帯総数とかを選んでください。 ※欲しいデータは小地域のポリゴン情報なので、そのエリアの統計情報はなんでもいいのです。 選んだら「統計表各種データダウンロードへ」のボタンをポチっとしてください。 統計表各種データダウンロードというページが表示されると思います。 #Step3:地域選択 取得したい地域を選びます。 リストボックスですが複数選択が可能です。 仮に北海道札幌市中央区を選択してみましょう。 #Step4:データダウンロード ダウンロード可能なデータのリンクが表示されます。 統計データとして、選択した札幌市中央区の「男女別人口総数及び世帯総数」がダウンロード可能です。 でも、今回欲しいのは境界データとなります。 どの形式で欲しいかは皆様のお好みですが、今回は「世界測地系緯度経度・G-XML形式」のものをダウンロードしてみましょう。 クリックすると「A002005212010DDXWC01101.zip」というデータがダウンロード出来るはずです。 zipを解凍すると中に「h22ka01101.xml」というデータが入っているはずです。 ※ファイル名はダウンロードする形式、地域で変わります。 ちなみに、うまくダウンロードできないっていう方は、ブラウザでJavaScriptが利用可能になっているかを確認してください。それでもうまくいかないときはブラウザを変えてみたりしてみましょう。 ※一時期、Chromeでうまくダウンロードできないことがありました。 時々、ダウンロードまでちょっと待たされる時もあります。 あせらず待ちましょう。 ## MongoDBのセットアップ さて、ダウンロードしたデータを何に格納するかという事なんですが、 今回はMongoDBを選択します。自分が使っているからという理由だけです。 セットアップ方法は、環境によっても違うので割愛します。 探せばいっぱい出てくるので許してください・・・。 ここで重要なのは、v2.4以降を使う事です。 なぜv2.4以降なのか? それは、$geoIntersectsという新しいオペレーターが追加されているからです。 MongoDBは早い時期から地理空間インデックスをサポートしていますが、 近い点を探したり、矩形内の点を探したりといったことが中心でした。 $geoIntersectsを使うと、点と交差しているオブジェクトを検索可能となります。 要するに、座標を指定すると、その座標を含んでいる矩形を検索する事が可能になります。 つまり、ある緯度経度から住所を検索する事が出来るという事です。 今回は2.4.4を使用しました。 ## データの取り込み 今回はPHPを使ってみます。MongoDBが使えるPHP環境を各自ご用意ください。 MongoDBドライバーあたりでつまづく事があるかもしれません。 でも大丈夫。世界中の人たちが一度は通った道です。(要するに検索してください。事例はいっぱいあります。) 個人的にCakePHPを勉強中なので、コンソールから呼び出せるシェルを作ってみました。 今回はCakePHPバージョン2.3.6を使用しました。 コードは以下に置いてあります。 https://github.com/hamichamp/cakephp-mongodb-areashell-register こんな感じで使います。 ``` cd /path/to/cakephp/app Console/cake area register h22ka01101.xml ``` 実行するとこんなデータがMongoDBに登録されます。 ``` db.areas.findOne(); { "_id": ObjectId("51ccfb4daeff866427000000"), "KEN_NAME": "北海道", "GST_NAME": "札幌市", "CSS_NAME": "中央区", "MOJI": "北二十二条西", "geo": { "type": "Polygon", "coordinates": [ [ [ 141.32808442756, 43.086489717686 ], [ 141.32849826202, 43.086189931106 ], [ 141.32888347461, 43.085910941125 ], [ 141.32928102957, 43.085634088697 ], [ 141.3299137151, 43.085193570141 ], [ 141.32991340185, 43.085188855547 ], [ 141.32990537189, 43.085045811125 ], [ 141.32988194884, 43.084629834813 ], [ 141.32913822969, 43.084553030273 ], [ 141.32864072166, 43.084501904683 ], [ 141.32812962799, 43.084454605932 ], [ 141.3276909357, 43.084397339069 ], [ 141.32715673633, 43.084336135481 ], [ 141.32710470274, 43.08479820257 ], [ 141.32708042035, 43.085111816898 ], [ 141.32708857833, 43.085169201229 ], [ 141.32714414276, 43.08527092383 ], [ 141.32741124595, 43.085605243866 ], [ 141.32742722034, 43.085627737437 ], [ 141.32782427229, 43.086145890449 ], [ 141.32788514741, 43.086226345645 ], [ 141.32808442756, 43.086489717686 ] ] ] }, "modified": ISODate("2013-06-28T02:56:13.0Z"), "created": ISODate("2013-06-28T02:56:13.0Z") } ``` ##地理空間インデックスを作成する ある地点を指定するとそれを含むエリアを返してくれるように、地理空間インデックスを作成しましょう。 インデックスの作成には以下のコマンドを実行します。 ``` db.areas.ensureIndex({ geo: "2dsphere" } ) ``` ##検索してみよう 正しくインデックスが作成されていれば、緯度経度からエリアを検索する事が可能なはずです。 早速試してみましょう。 今回登録したエリアは北海道札幌市中央区です。 試しに**札幌時計台ビル**の位置を指定して、どんなエリアが返ってくるのかを確認します。 ちなみに、札幌時計台ビルの住所は**北海道札幌市中央区北1条西2丁目**です。 まず、GoogleMapで位置を確認します。 https://maps.google.co.jp/maps?q=43.06311+141.35348 緯度経度としては北緯43.06311、東経141.35348の位置っぽいですね。 では、MongoDBでその位置を指定して検索してみましょう。 検索コマンドはこんな感じです。 ``` db.areas.find({geo:{ "$geoIntersects": { "$geometry": { "type": "Point", "coordinates": [ 141.35347, 43.06311 ]}} }} ); ``` 結果はこうなりました。 ``` { "_id" : ObjectId("51ccfb66aeff866427000113"), "KEN_NAME" : "北海道", "GST_NAME" : "札幌市", "CSS_NAME" : "中央区", "MOJI" : "北一条西2丁目", "geo" : { "type" : "Polygon", "coordinates" : [ [ [ 141.352972957864, 43.0634023736146 ], [ 141.353744729415, 43.0635133528866 ], [ 141.354578860156, 43.0636236896466 ], [ 141.354826623582, 43.0624630766765 ], [ 141.354993733154, 43.0618615167524 ], [ 141.353423531985, 43.0616286708429 ], [ 141.353254226241, 43.0622482860281 ], [ 141.353130068628, 43.0628367470057 ], [ 141.352972957864, 43.0634023736146 ] ] ] }, "modified" : ISODate("2013-06-28T02:56:38Z"), "created" : ISODate("2013-06-28T02:56:38Z") } ``` ちゃんと**北海道札幌市中央区北1条西2丁目**を返してくれましたね! いかがだったでしょうか。 意外と簡単にできますので、よかったら試してみてくださいね。 実は、データをダウンロードするのが、一番手間がかかります・・・。 |
|
| 493位 |
|
|||
|
00:00:05 |
|
|
delegateやprotocolの勉強をしたいという人がいるので、なるべくわかりやすくここに書いておこう。propertyとか、delegateっていうのはObjective-Cで楽しいところでもある。
でも、delegateだけじゃなくて、他にもいろいろ非同期的な処理をやる方法あるんで、それも、まとめて説明する。 適当に思いつくだけ書くと、非同期的な処理をするために、Objective-Cでは以下のようなやり方がある。他にもあるかもしれないが、だいたいこれだけある。そして、どれを使ってもいい。 ### Objective-C、C/C++でも可能 ・関数のアドレスを保持しておいてコールバックする。 ・pthread条件変数を使う。(デッドロックやスレッド管理) ・非ブロッキングI/Oする。(データの検査コスト大、結局カーネル空間からユーザ空間に必要なデータをコピーするときにブロックされる) ・シグナルハンドラを設定し、シグナルを飛ばす。シグナル駆動I/O。(カーネルからユーザにコピーするときにブロック、シグナル管理むずい) ・poll(2)やselect(2)を使って多重化I/Oする。(カーネルからユーザに(略) ・aio.hのPosix非同期I/O関数を使って非同期I/Oする。(ブロック一切ないが実装むずい) ・libevのepollを使って非同期I/Oする。(現実的な解法) ### Objective-Cだけが可能 ・delegateコールバックを呼び出す(Cより安全なコールバック) ・KVOを使う(Cより安全かつ簡単なコールバック) ・NSNotificationを使う(ブロードキャスト通知、なんでもありになるので逃げ) ・Blocksを使う(クロージャ) ・GCDを使う(マルチスレッド処理、dispatch_sourceを使って非同期I/Oするのも可) *これを読んだだれかこれの他言語版を書いてほしいと思う。* 余談だが、DMA(Direct memory Access)はドライバを開発していると使うことが多い。マイコンプログラム書く人も知っているだろう。DMAC(Direct Memory Access Controller)というチップが基板に実装されていて、2つのメモリの一方の特定の番地に書き込みをすると、もう一方のメモリにDMACがデータ転送する仕組みである。これをやることで、CPUがデータ転送するために待たなくていい。 あと、メモリの転送というのは非常に遅い。memcpy(3)の時間を測ってみた事がある人ならわかると思う。カーネルからユーザへの転送はさらに特別な考慮が必要で、よりコスト高なので、パフォーマンスを考えるときは、非ブロッキングといえども非常に遅い転送になることも考慮しないといけない。こういうことをふまえたときに、Objective-CのGCDを使った非同期I/Oって簡単だし素晴らしいよねーという気持ちになれると思う。 ## delegate #### (使い道1)コールバックのためのdelegate で、delegateというのはコールバックです。しかも、そのコールバックを受けるためにdelegateが実装されている必要があります。Javaのimplementと一緒。 Cのコールバック関数とやることは同じだが、関数ポインタを管理しなくていいし、通知先、渡されるオブジェクトに制限がある(@protocolで定義したものが制限になる)。1デリゲートは一本の矢印で書ける。基本的には、delegateで全て事足りるようになっていないといけないと思う。 delegateの通知を受ける先のハンドラが遠くてコード追いづらいというデメリット、あとdelegateのセットと、dealloc時のnil代入を忘れがちなので、そういうところ注意しないとダメだが。 そこらじゅうで説明のサイトあると思うけど、実装の例。 例えば、ファイルI/Oが完了したらdelegateが飛んでくるようなViewを実装する。 ```objc:CustomView.h @protocol FileReadWriteDelegate //delegateの定義 @required //requiredが指定されてると必ず実装する必要がある - (void)onDidOpenFile:(id)sender; - (void)onDidCloseFile:(id)sender; @optional //optionalは実装しなくてもいい - (void)onDidReadFile:(id)sender; @end @interface CustomView:UIView <FileReadWriteDelegate> // delegate通知を受け取るために必要 @end ``` ```objc:FileManage.h @interface FileManage:NSObject @property id<FIleReadWriteDelegate> delegate; //コールバック先。FileReadWriteDelegateのデリゲートだけを指定できる @end ``` ```objc:CustomView.m @interface CustomVIew() @property (retain) FileManage *fileManage; @end @implement CustomView - (id)initWithFrame:(CGFrame)frame{ if(self = [super:initWithFrame:frame]){ _fileManage = [[FileManage alloc] init]; [_fileManage setDelegate:self]; //デリゲートがこちらを向くように指定する(dealloc時nil代入を忘れるな) } } -(void)load:(NSString*)filePath{ [self.fileManage readFile:filePath]; } - (void)dealloc{ [_fileManage setDelegate:nil]; //これを忘れると、CustomViewがdeallocされているのに、_fileManageがdelegateを飛ばそうとして落ちる。 [_fileManage release]; } - (void)onDidOpenFile:(id)sender{ //delegateのハンドラ、だいたいはここでViewを操作する } - (void)onDidCloseFile:(id)sender{ //delegateのハンドラ、だいたいはここでViewを操作する } - (void)onDidReadFile:(id)sender{ //delegateのハンドラ、だいたいはここでViewを操作する } @end ``` ```objc:FileManage.m @implement FileManage - (void)readFile:(NSString*)filePath{ //ファイル開く [self open:filePath]; [self.delegate onDidOpenFile:self]; //デリゲートで通知 //巨大なファイルを読む処理が入る.何秒もかかる。 [self read]; //ファイルが読み終わった if([self.delegate respondsToSelector:@selector(onDidReadFile:)){//optionalなので実装されてないかもしれないのでチェックする [self.delegate onDidReadFile:self]; //デリゲートで通知 } //ファイル閉じる [self close]; [self.delegate onDidCloseFile:self]; //デリゲートで通知 } @end ``` イメージつかめてもらえただろうか? ###### delegateよりBlocks使えじゃなかったの? 以前にdelegateよりBlocksをどんどん使っていこうみたいなことを書いた。Blocksのだいたいの使い方は次の記事をみてもらったらいいと思う。 [Blocksとdelegateの基本](http://qiita.com/items/bda0356495a6480dc4c5) 実際、Blocksで早く楽に書けるんだけど、それはそれで設計が自由になりすぎるというか、やりかた間違えると複雑になってしまう。皆でやるプログラミング向きではないっていうか・・ たとえば、MVCで、MにVを更新するBlockを持たせたりとかできるし、それ俺やってた。それも楽だけど、結局Vの処理をするためだけなのにMのコードも追わないとダメになる部分が増えたり、Blockの解放どこでやるかみたいなところを結局考えることになるし。Block解放するのいつ?とか。Vが解放されたタイミングか?Mが解放されたタイミングか?Blockはスタックかヒープかどこにあるのか?BlockとBlockを持つインスタンスが循環参照になっててdealloc走らないんじゃないか?このBLockの処理はMVCのどれ?みたいなのとか。 delegateは単なる矢印一本の関係なので設計はBlocksほど自由ではないから、delegate使っている限りは、設計もMVCとか、オブジェクト指向らしい感じを自然と保てる。やっぱり善し悪しかなぁと思う。 でも設計を崩さない範囲でBlocks使うけどね。 #### (使い道2)抽象化のためのdelegate @requiredがあるdelegateを実装したクラスは、指定されたメソッドを必ず実装しなければいけないという制約がある。その場合は、命名を*Delegateではなく、*Protocolとするのが慣例だと思う。 ## KVO Macの時代からずっとプログラミングしてる人には常識なんですが、iPhoneプログラマーは案外知ってないことの一つですね。なんでもかんでもdelegateする必要ないんです。 KeyValueObserverといって、プロパティの値が変わったら、そのプロパティを監視しているオブジェクトにKVO通知がいくんですね。これを使ったらdelegateよりも簡単に非同期処理ができてしまう罠。まぁdelegateより実際簡単ですね。 注意点としては、addObserverしたら、必ずremoveObserverしましょうということと、init,deallocでKVO使うのやめようということ、また、KVO意識しようということです。 さっきのdelegateのコールバックは、こんな感じに書き換えられる。 ```objc:CustomView.h @interface CustomView:UIView @end ``` ```objc:FileManage.h @interface FileManage:NSObject @property (readonly , getter=isLoaded) BOOL loaded //監視する変数 @end ``` ```objc:CustomView.m @interface CustomVIew() @property (retain) FileManage *fileManage; @end @implement CustomView - (id)initWithFrame:(CGFrame)frame{ if(self = [super:initWithFrame:frame]){ _fileManage = [[FileManage alloc] init]; } } - (void)load:(NSString*)filePath{ [self.fileManage addObserver:self forKeyPath:@"loaded" options:nil context:nil]; [self.fileManage readFile:filePath]; } - (void)dealloc{ [self.fileManage removeObserver:self forKeyPath:@"loaded"]; [_fileManage release]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ //KVOのハンドラ FileManage *fileManage = object;//オブジェクトごと渡ってくる //Viewを更新したりするといいと思う。 } ``` ```objc:FileManage.m @implement FileManage - (void)readFile:(NSString*)filePath{ //ファイル開く [self open:filePath]; //巨大なファイルを読む処理が入る.何秒もかかる。 [self read]; //ファイルが読み終わった self.loaded = YES; // KVO通知発生する。 //ファイル閉じる [self close]; } @end ``` ## NSNotification NSNotificationは、1対多の通知機構。アプリ内であれば、notification nameさえ指定して、notification centerに登録していればどれだけオブジェクトが関係なくても受け取れる。逃げである。 これはもうだるいので適当に調べてほしいと思う。 ## Blocks クロージャ。以前のブログで書いた。 NSArrayにたくさんaddObjectしておいて、あとから全部実行するなんてこともできる。 BlocksKitというライブラリが便利らしいので、それ使ったら、UI関連のものがだいたいBlocksで実装できるんですって。 ## GCD kqueueのラッパー。以前のブログで書いた。 コンカレントキューと、シングルキューというものがある。 キューにBlockに書いたタスクを順番に入れていく。 同期か、非同期処理か選べる。 シングルキューは排他制御に使われる。mutexよりも非常に高速。 NSNetworkOperationはGCDをラッパーしたもので、HTTPの処理が書ける。 ちょっと後半ばてて失速してしまったが、また気が向いたら続きを書く。 |
|
| 494位 |
|
|||
|
20:09:39 |
(株式会社インフィニットループ 所属) |
|
Marvericksをクリーンインストールしたついでに、python環境を刷新してみた。 従来はvirtualenv+virtualenvwrapperくらいで間に合ってたくらい。 ディレクトリ単位でpython環境切り替え可能っていうからきっと便利。 そして既に先輩たちにより、いくつか同じ情報書かれてたけど、自分用ってことで割り切り。 # 環境 * OSX 10.9.1 * [Homebrew](http://brew.sh/index_ja.html) # pyenv ## インストール ### pyenvだけ ```zsh $ brew install pyenv ``` なんとこれで終わり。 ### pyenvにプラグインとしてvirtualenvも使うなら ``` $ brew install pyenv-virtualenv ``` 最初からこれするだけで、pyenvも一緒に入ってくれる。 ## 設定 インストール時に注意点として下記を促してくれるので適宜。 ``` To enable shims and autocompletion add to your profile: if which pyenv > /dev/null; then eval "$(pyenv init -)"; fi To use Homebrew's directories rather than ~/.pyenv add to your profile: export PYENV_ROOT=/usr/local/opt/pyenv ``` ## 使い方 ### 環境をつくる ``` $ pyenv install 2.7.6 ``` 指定バージョンのpythonがインストールされる。 もちろんpipも共に。 なお、インストール可能なバージョンの一覧を確認するには ``` $ pyenv install -l ``` インストール済みの環境一覧を確認するなら ``` $ pyenv versions ``` ### global: 環境を共通利用 これは個人的にはしてもしなくても、な気がする。しなかったらsystem側のpythonを見に行くだけ。 ``` $ python --version;which python Python 2.7.5 /usr/bin/python $ pyenv global 2.7.6 $ exec $SHELL -l $ python --version;which python Python 2.7.6 /usr/local/opt/pyenv/shims/python ``` ### local: 任意ディレクトリで任意環境を適用する バージョン3.3.3環境を導入。 ``` $ pyenv install 3.3.3 ``` 任意ディレクトリに移動して環境指定する。 ``` $ cd ~/Projects/sandbox/ $ pyenv local 3.3.3 $ pyenv version 3.3.3 (set by /Users/xxx/Projects/sandbox/.python-version) ``` 指定ディレクトリ以外はglobalのほうをみてることを確認。 ``` $ cd $ pyenv version 2.7.6 (set by /usr/local/opt/pyenv/version) ``` ### virtualenv `pyenv install`時に指定したバージョンごとの環境を元に、新たに環境を複製しくれるのがvirtualenv。 同じバージョンで違うパッケージ構成にしたい時とか便利。 ``` $ pyenv virtualenv 2.7.6 sandbox276 ``` ここでつくられた環境も`pyenv versions`にのってくる。 `pyenv local sandbox276`とかすれば任意ディレクトリでの環境利用も同じく使える。 ## 環境の削除 バージョンごと環境もvirtualenv環境一緒。 ``` $ pyenv uninstall 3.3.3 $ pyenv uninstall sandbox276 $ pyenv versions system * 2.7.6 (set by /usr/local/opt/pyenv/version) ``` ただしlocal指定で、任意ディレクトリに環境つくった場合、`.python-version`というファイルが出来上がってそこに環境名が書かれているけど、それは消えないのでご注意を。 # 感想 バージョンで縦にフレキシブル! virtualenvで横にフレキシブル! きっと便利! # 参考 * [pyenv](https://github.com/yyuu/pyenv) * [pyenv-virtualenv](https://github.com/yyuu/pyenv-virtualenv) |
|
| 495位 |
|
|||
|
17:03:32 |
|
|
**2014/4/10追記**
brewdlerの機能はhomebrewに包含されました。 この記事は古いのでみんなhomebrew使いましょう! 経緯: https://github.com/Homebrew/homebrew/pull/24107 Brewfileを書いて、homebrewをインストールした後に以下のコマンドを実行すれば問題無いです。 ``` brew bundle ``` # 開発環境の構築の構築 開発環境の構築、Vagrantを使って構築するようになったのだけど、 開発環境の構築の構築、つまるところホストとなるべくMac OS Xマシン側の 環境構築について、なんか楽したいな〜と思って、 homebrewをBundlerのごとくBrewfileとか作って管理できたらいいのに って思って検索したらあった。よかったよかった。:beer: [brewdler](https://github.com/andrew/brewdler) # 使い方 ## brewdlerをインストール ``` > gem install brewdler ``` ## Brewfileを編集 以下のように```Brewfile``` を書く ```rb:Berwfile brew 'go' ``` ## 書きだしたformulaをインストール ``` brewdle install ``` 簡単。 |
|
| 496位 |
|
|||
|
18:33:53 |
|
|
Homebrew での開発環境構築も少し慣れてきたので、Apache, PHP, MySQL, Composer をインストールして、Yii Framework を動かすところまで書きたいと思います。
# 実行環境 * OS X: 10.10 * Homebrew: 0.9.5 # Homebrew のインストール **ターミナルを起動して、以下のコマンドを実行** ~~~sh ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" ~~~ * Mac 本体のパスワードを聞かれるので入力 * 「... コマンドラインデベロッパーツールをインストールしますか?」みたいなことを聞かれるのでインストールをクリック * インストール後、Enter で Homebrew のインストールが開始されます コマンドラインデベロッパーツールについてのメッセージが出なかった場合は以下を入力。 ~~~sh xcode-select --install ~~~ **パスを通す ( bash なら .bash_profile に追加)** ~~~sh:.bash_profile export PATH=/usr/local/bin:$PATH export PATH=/usr/local/sbin:$PATH ~~~ **Java for OS X 20xx-xxx をダウンロード** 以下からダウンロードして、ダブルクリック。 http://support.apple.com/kb/DL1572 **Git のインストール** Mac OS X に元々入っているけど、最新のものを。 ~~~sh brew install git ~~~ **Homebrew 本体のチェック** とりあえずここで Homebrew がおかしくないかチェック。 「Your system is ready to brew.」と出れば OK。警告などが出たらその都度メッセージを読み解きながら対処。 ~~~sh brew doctor ~~~ OS X Yosemite でかつ Xcode がインストール済みの場合 6.1 以上でないと、エラーか警告が出るかもしれません。Xcode 6.1 以上をインストールしましょう。 # MySQL のインストールと設定 ~~~sh brew install mysql ~~~ **MySQL の起動** ~~~sh mysql.server start ~~~ **root パスワードの変更** ~~~sh mysqladmin -u root password # 新しい root パスワードを入力する ~~~ **MySQL のセキュリティ設定** ( ルートパスワード項目以外はすべてそのままで OK ) ~~~sh mysql_secure_installation ~~~ **MySQL の停止** ~~~sh mysql.server stop ~~~ # Apache のインストールと設定 **リポジトリを追加** ~~~sh brew tap homebrew/dupes brew tap homebrew/apache ~~~ **外部 formula がインストール可になっているか確認** ~~~sh brew search httpd24 ~~~ **Apache のインストール** ~~~sh brew install httpd24 ~~~ OS X 標準で入ってる Apache ではなく、 Homebrew でインストールしたものか確認してみたり。 ( 多分 Yosemite では /usr/sbin/ に httpd, apachectl などが置いてある ) ~~~sh which apachectl # /usr/local/bin/apachectl と出れば OK。 # /usr/sbin/apachectl と出る場合はターミナルを再起動して再度確認してみる apachectl -v ~~~ **http.conf の設定** ( 場所は /usr/local/etc/apache2/2.4/ 、DocumentRoot のパスは任意 ) ~~~httpd.conf # php5 のモジュールの追加 LoadModule php5_module /usr/local/opt/php54/libexec/apache2/libphp5.so # DocumentRoot の変更 DocumentRoot "/Users/jamband/Dropbox/htdocs" # .htaccessでURLのoverwriteを許可 ( Directory "/Users/jamband/Dropbox/htdocs" 内 ) AllowOverride All # .php で php を実行できるように MIME タイプの追加 ( IfModule mime_module 内 ) AddType application/x-httpd-php .php # indexファイルに index.php を使えるように修正 ( IfModule dir_module 内 ) DirectoryIndex index.php index.html # Virtual hosts のコメントアウトを解除 Include /usr/local/etc/apache2/2.4/extra/httpd-vhosts.conf # httpd-vhosts.conf には dummy があるので削除し、適当に任意のホスト追加し # /etc/hosts も書き換えておく ~~~ **変更後は Apache の再起動** ~~~sh apachectl stop apachectl start ~~~ access_log, error_log の場所は /usr/local/var/log/apache2/ です # PHP のインストールと設定 **リポジトリを追加** ( homebrew/dupes は Apache をインストール時に追加しましたが PHP 単体でインストールする場合は追加します ) ~~~sh brew tap homebrew/dupes brew tap homebrew/versions brew tap homebrew/homebrew-php ~~~ **外部 formula がインストール可になっているか確認** ~~~sh brew search php ~~~ **PHP54 のインストール** APC も入れたかったので今回は php54 にしました ~~~sh brew install php54 ~~~ インストール後は Caveats を見ながら、またいろいろ修正、設定していきます **php.ini の設定** 場所は /usr/local/etc/php/5.4/ ~~~php.ini date.timezone = Asia/Tokyo default_charset = "UTF-8" mbstring.language = Japanese mbstring.internal_encoding = UTF-8 mbstring.detect_order = UTF-8,SJIS,EUC-JP,JIS,ASCII ~~~ **APC, Xdebug のインストール** ~~~sh brew install php54-apc brew install php54-xdebug ~~~ 設定ファイルは /usr/local/etc/php/5.4/conf.d/ に置かれます # Composer のインストールと設定 **外部 formula がインストール可になっているか確認** ~~~sh brew search composer ~~~ **Composer のインストール** ~~~sh brew install composer ~~~ # Yii Framework でユニットテスト及び機能テストを実行 これは読み手が限定されるので、必要な方だけどうぞ。 **プロジェクトのルートディレクトに移動して composer.json を作成** ~~~composer.json { "require": { "yiisoft/yii": "~1.1" }, "require-dev": { "phpunit/phpunit": "~3.7", "phpunit/phpunit-selenium": "*", "phpunit/phpunit-story": "*", "phpunit/dbunit": "*", "phpunit/php-invoker": "*" }, "config": { "vendor-dir": "protected/vendor" } } ~~~ **ライブラリのインストール** protected/vendor/ が作成され、そこにいろいろ置かれる。 ~~~sh composer install ~~~ **ユニットテストの実行** ~~~sh cd ./protected/tests ../vendor/bin/phpunit unit ~~~ **Selenium RC のインストール** ~~~sh brew install selenium-server-standalone ~~~ **selenium-server-standalone を起動** ~~~sh selenium-server -p 4444 ~~~ **機能テストを実行** ~~~sh ../vendor/bin/phpunit functional ~~~ # 各パッケージの削除方法 **MySQL** ~~~sh brew uninstall mysql rm -rf /usr/local/var/mysql ~~~ **Apache** ~~~sh brew uninstall httpd24 rm -rf /usr/local/etc/apache2/2.4 rm -rf /usr/local/var/log/apache2 ~~~ **PHP** ~~~sh brew uninstall php54 brew uninstall php54-apc brew uninstall php54-xdebug rm -rf /usr/local/etc/php rm -rf /usr/local/opt/php54/ ~~~ **Homebrew本体** インストールしたパッケージなどがすべて削除されるので注意して行なってください。 また、.bash_profile などに追加したパスは手動で削除します。 [How do I uninstall Homebrew?](https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/FAQ.md#how-do-i-uninstall-homebrew) |
|
| 497位 |
|
|||
|
11:12:55 |
(Mercari, Inc. 所属) |
|
追記: コメント欄にて、若干変なパスが含まれている場合の対処方法について、補足いただきました。
ご指摘ありがとございますm(_ _)m bash / zshなどでシェルスクリプトを書いていると、環境変数の設定を外部ファイルにまとめてsource して使う、というのはよくある。 そのような場合にsourceされたファイル内での絶対パスがイマイチうまく取れない、という問題に突き当たる事がある。 ## bashの話: うまくいかない方法と理由 通常、シェルスクリプト内で自分自身の絶対パスをとるときによくやるのが下記の手法だとおもう。 script_dir=$(cd $(dirname $0); pwd) が、単体で呼び出した場合にはちゃんと動くのだが、bashからsourceで呼び出した場合にはうまく動かない。 なぜかというと、sourceされたファイルの実行が、呼び出し元のシェルプロセス内になるから。 bashでは`$0`は実行コンテキストに依らず、実行ファイル名が入っている。 そのため、source されたファイル内では$0が大本のファイルの情報となっているため、直感と異なる動きとなってしまう。 ## zshの話 ちなみに、zshだとちゃんと動く。zshは、`$0`をコンテキストに合わせて置き換える。 関数内であれば関数名になるし、sourceすればsourceされたファイルパスが入る。 そのため、zshなら`$0`を使っていれば問題ない。 ## bashの話: 解決の糸口 一般に言われるのは、`$BASH_SOURCE`を使う方法。 これには、呼び出されたファイル自身の絶対パスが入っている。そのため、bash限定であればこれを利用して script_dir=$(cd $(dirname $BASH_SOURCE); pwd) を使えば、解決できる。 ## これ使うと何が辛いか bash限定なんですよね。zshかbashかを判定して、場合分けするような処理をいちいち書きたくない。 ## じゃあどうすんの こうすればいい script_dir=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd) `$BASH_SOURCE`が定義されていればそれを使い、無いなら`$0`を使う。 これでさくっと解決できる。 最後のほう書くの面倒になって説明が雑になった。。。わけじゃないよ!>< |
|
| 498位 |
|
|||
|
00:40:56 |
|
|
社内でBerkshelfがウワサになっていたので試してみた。
# Berkshelfとは? Berkshelfとは、「Chefのcookbookとその依存関係を管理するツール」らしい。 http://berkshelf.com/ 要するに、定義ファイルに使うcookbookを定義しておけば、リポジトリから勝手に依存関係を解消した上で 落としてきてくれるらしい。RubyのBundlerみたいな感じ。 同じようなので、Librarian-Chefというのもあるが、Berkshelfの方が現時点で主流っぽい。 # Berkshelfの使い方 Berkshelfはgemで配布されているので、gem installでインストールできる。 ```` $ gem install berkshelf ```` 使用したいcookbookを定義するBerksfileを作成する。 Berkshelfの公式サイトにも書いてあるが、opscodeからmysqlとnginxのcookbookを使う場合はこんな感じ。 cookbookのmetadata.rbに定義された依存パッケージもあわせて落としてくれる。 ```` $ cat Berksfile site :opscode cookbook 'mysql' cookbook 'nginx', '~> 0.101.5' ```` こんな感じでgithubから持ってくる事も出来る。 ```` cookbook "zabbix2.0", git: "https://github.com/uchiunyo/chef-zabbix2.0.git" ```` cookbookはberksコマンドで落としてくる。Berksfileにzabbixのcookbookを定義して実行した。 ```` $ berks --path=vendor/cookbooks Installing zabbix2.0 (0.1.0) from git: 'https://github.com/uchiunyo/chef-zabbix2.0.git' with branch: 'master' at ref: 'c201bba54ef606b040dcdb057397faafca870913' Using yum (2.2.2) ```` ちゃんと依存パッケージであるyumもopscodeから落としてきてくれていた。 ```` $ ls vendor/cookbooks yum zabbix2.0 ```` 後はいつもどおり.jsonにcookbookを定義してchefを実行すればOK!! # Berkshelf + knife-soloを使ったchef-repoを考えてみる じゃあBerkshelfを使ってどんな感じにchef-repo構成すればやりやすいかなーと考えてみた。 ちなみにchefの実行はknife-soloを使っているので、その辺りも考慮したい。 とりあえずこんな感じかな?というのをgithubに置いてみた。 https://github.com/uchiunyo/chef-repo 使い方はこんな感じを想定している。 まずは事前準備。構築する環境はvagrantのCentOS6.4(vmcent1)にZabbix2.0 Serverを構築する。 githubからchef-repo持ってきて、bundle installでchef-soloとかknife-solo,Berkshelfを用意する。 ```` $ git clone https://github.com/uchiunyo/chef-repo.git $ cd chef-repo $ bundle install --path=vendor/bundle ```` そしてberksを実行してgithubよりcookbookと、依存cookbookを落としてくる。 ```` $ bundle exec berks --path=vendor/cookbooks ```` ではvmcent1にZabbix2.0 Serverを構築する。 まずはknife-solo prepareでchefをインストールする。 ```` $ bundle exec knife solo prepare vmcent1 ```` .chefから、構築したい構成の.jsonを選択し、nodesの中にコピーする。 今回はZabbix-ServerのDBにローカルのMySQLを使うので、zabbix_db_local.jsonを使う。 ```` $ cp .chef/zabbix_db_local.json nodes/vmcent1.json ```` vmcent1でchefを実行する。 ```` $ bundle exec knife solo cook vmcent1 ```` Zabbix Serverの出来上がり!!って感じです。 こんな感じで使いたいcookbookはガシガシBerksfileに書いて、.jsonファイルに雛形を用意して使い分ける事によって うまいこと管理できないかなーと思ってみた。 助言異論反論コメントなどあれば、頂けると嬉しいです。 |
|
| 499位 |
|
|||
|
11:34:09 |
(Zalando SE 所属) |
|
まず、前提としてレポジトリに 入れるのは LF のみが良いようです。 `core.autocrlf` という git の global オプションで改行コードの自動変換を設定できます。 - `true`: コミット時に CRLF -> LF。チェックアウト時に LF -> CRLF。 - `input`: コミット時にのみ CRLF -> LF に変換。[Windows の場合のみ `true` と同じ。](http://git-scm.com/book/ja/ch7-1.html) - `false`: 変換しない。 では、自動変換をするべきか? ## する派 Windows, Mac, Linux で以下の設定。 ```bash:設定を追加 git config --global core.autocrlf input git config --global core.safecrlf true ``` `core.safecrlf` は、改行コードが混在している場合は変換しないというオプション。 ## しない派 ツールの設定をすれば、自動変換は必要ない。バイナリ等、いろいろな問題の原因になるため。 ## ファイルの種類ごと派 .gitattributes で設定する。共同開発者の global 設定に左右されない。 ## その他 たまに改行コードが `^M`(CR のみ)になり、diff で全てのコードが一行になってしまうことがあります。これは自動変換してくれないので、手動で直しましょう。 ## 参考 - [git-config(1) Manual Page](http://www.kernel.org/pub/software/scm/git/docs/git-config.html) - [Dealing with line endings · github:help](https://help.github.com/articles/dealing-with-line-endings) |
|
| 500位 |
|
|||
|
10:23:17 |
(Google Inc 所属) |
|
# はじめに [ようへいさんのポスト](http://inokara.hateblo.jp/entry/2013/12/29/060324)を参考にしてdocker上でnginxとfluentdとnorikraを動かせたので、[docker indexにイメージを掲載した](https://index.docker.io/u/kazunori279/)。dockerのある環境ならあっという間に動くはず。 # 準備 * [dockerが動く](http://knowledge.sakura.ad.jp/tech/1811/?utm_source=dlvr.it&utm_medium=twitter)こと * ホスト側で[fluentd用のファイルディスクリプタ設定](http://qiita.com/kazunori279/items/5aedb6ed548225545a3c)を済ませていること # 手順 ## norikraを動かす まずはログの集約先となるnorikraを動かす。 ``` $ sudo docker run -p 26578:26578 -p 26571:26571 -p 24224:24224 -p 24224:24224/udp -d kazunori279/fluentd-norikra-server ``` 初回はイメージのダウンロードにちょっと時間がかかる。終わったら、norikraを載せたコンテナがちゃんと動いているか、`docker ps`で確認。 ``` $ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7874efdd2e06 kazunori279/fluentd-norikra-server:latest /bin/sh -c /etc/init 16 minutes ago Up 16 minutes 0.0.0.0:24224->24224/tcp, 0.0.0.0:24224->24224/udp, 0.0.0.0:26571->26571/tcp, 0.0.0.0:26578->26578/tcp grave_turing ``` 加えて、ブラウザからnorikraのWeb UIを開いてみる。URLは`http://<dockerホストのIP>:26578/#`となる。  動いてる動いてる。ちなみに、この時点ですでにfluentd(td-agent)とfluent-plugin-norikraも利用可能な状態なので、norikraの準備はこれで終わり。docker簡単すぎてすごい。 ## nginxを動かす norikraにログを流すnginxを用意しよう。別マシン上で動かしてもいいし、同じマシン上で動かしてもいい。 ``` sudo docker run -d -e NORIKRA_IP=<norikraが動いているマシンのIP> -p 80:80 kazunori279/fluentd-nginx ``` これも初回はイメージのダウンロードが行われる。なお、このnginxとnorikraの間ではfluentdによるUDP通信が必要なので、例えばGoogle Compute Engineを使う場合は`NORIKRA_IP`としてInternal IPを指定する必要がある。 では、nginxのコンテナを確認してみよう。 ``` $ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9d694d33b77d kazunori279/fluentd-nginx:latest /bin/sh -c sed -e "s 13 minutes ago Up 13 minutes 0.0.0.0:80->80/tcp thirsty_torvalds ``` いい感じ。このイメージにはnginxとfluentdが含まれていて、上記コマンドの`NORIKRA_IP`で指定したマシン上のnorikraにすべてのログが転送される設定になっている。 curlでnginxを何回か叩いてみる。 ``` $ curl http://localhost/ <html> <head> <title>Welcome to nginx!</title> </head> <body bgcolor="white" text="black"> <center><h1>Welcome to nginx!</h1></center> </body> </html> ``` ここで、ブラウザからnorikraのWeb UIをリロードして、ログが届いてるか見てみよう。  きてるきてる。`Targets`の`show fields`をクリックすると、`nginx_access`というターゲットが自動作成されてフィールドも正しく認識されているのがわかる。  ## SQLを試す せっかくなので、SQLを試してみよう。Web UIから`Queries`の`editor`をクリックして、以下の様なSQLを入力。 ``` select * from nginx_access where path like '%foo%' ``` これはURI上に文字列`foo`が含まれるログのみを集める、という意味。`Add Query`ボタンをクリックしたら、`curl http://localhost/?foo=bar`のように`foo`をURLに含めて再度アクセスしてみる。  うまいこと集計されてるね! 再起動しなくても実行中にクエリをいろいろ追加・修正できるし、すべてがオンメモリだから速そう。norikraいいね! dockerと組み合わせるととても快適だ。これからnorikra用のダッシュボードUIをどうしようか考え中。 # References * [norikra を試す](http://inokara.hateblo.jp/entry/2013/12/29/060324) * kazunori279/fluentd-norikra-serverの[Dockerfile](https://github.com/kazunori279/dockerfiles/blob/master/fluentd-norikra-server/Dockerfile) * kazunori279/fluentd-nginxの[Dockerfile](https://github.com/kazunori279/dockerfiles/blob/master/fluentd-nginx/Dockerfile) |
|
| 501位 |
|
|||
|
13:36:49 |
|
|
ちょっと検索すると<code>rewrite</code>するやり方が結構出てくるのだけど色々見てたら<code>return</code>を使う方法がシンプルだったので採用してみた。正規表現とか<code>if</code>とか出てこないのでわかりやすい。
```nginx server { listen 80; server_name example.com; return 301 https://$host$request_uri; } server { listen 443; ssl on; # ... } ``` ## 参考文献 - http://qiita.com/shiftky@github/items/6bda0ba6a9e697361e7f - http://stackoverflow.com/questions/18523180/redirecting-to-ssl-using-nginx - http://serverfault.com/questions/250476/how-to-force-or-redirect-to-ssl-in-nginx |
|
| 502位 |
|
|||
|
18:28:04 |
|
|
# はじめに
コマンドラインで利用できるgitブラウザとして人気の[tig](http://jonas.nitro.dk/tig/)ですが、 使っていくうちにデフォルトの挙動では物足りなく感じ、.tigrcを利用したカスタマイズをしたくなってきます。 .tigrcをいじると色々なカスタマイズができますが、一度に多くの設定を書いてしまうと、 何が変わったかわかりづらかったり、変更の利点が曖昧になる時もあると思います。 そこで本記事では、.tigrcに関して筆者がオススメする4つの表示制御設定を紹介します。 ただ設定を紹介すると設定4行とコメントだけで終わるので、 スクリーンショットでbefore/afterを比較したり、オススメ理由を書いていきます。 なお、表示されているリポジトリは、本稿執筆時点で筆者が個人的に気になっているプロダクトである [favico.js](https://github.com/ejci/favico.js) のmasterブランチです。 「やけに丁寧だね!」と読者からコメントをいただけるような内容になっていれば幸いです。 # コミットIDを出そう tigのデフォルト表示では、コミットのIDは表示されていません。  [拡大表示](http://cl.ly/image/0z0q2K1Q3011) これを表示するには次の設定を記述します。 ```bash:~/.tigrc # main viewの左端にコミットIDを表示する set main-view = id date author commit-title:graph=yes,refs=yes # デフォルト # set main-view = date author commit-title:graph=yes,refs=yes ``` すると、ログの左端にコミットIDが表示されるようになります。  [拡大表示](http://cl.ly/image/3213231b111f) なお、表示するIDの文字数を変えることもできます。 たとえば12文字にするなら ```bash:~/.tigrc set main-view = id:width=12 date author commit-title:graph=yes,refs=yes ``` とします。 # 画面の垂直分割を試してみよう tig 1.2まではmain viewで任意のコミットにカーソルを(j/kで上下移動して)動かしてenterすると、画面を水平分割して当該コミットのdiffが表示されました。  [拡大表示](http://cl.ly/image/213S213y1z31) 2.0からは画面サイズに応じて自動的に縦分割と横分割が選択されるようになりました。常に縦分割するように強制したい場合は ```bash:~/.tigrc # 画面を垂直方向に分割する set vertical-split = yes # 横分割に強制する # set vertical-split = no # デフォルト値 # set vertical-split = auto ``` すると、diff表示が常に画面の右側に表示されるようになります。  [拡大表示](http://cl.ly/image/0A1f1r2R1b2I) ファイルやコミットログで1行あたりの文字長を制限しているリポジトリ(※1行80文字など)では、 特に使いやすい設定かと思います。逆に1行がひたすら横に長い環境では使いづらいでしょう。 # 水平分割の上下比率を変えよう 先に紹介した垂直分割では使いづらい環境では、 水平分割の上下比率を調整することで使い勝手が向上するかもしれません。 tigのデフォルト表示では、水平分割した際の画面比率は、下画面が66%になります。  [拡大表示](http://cl.ly/image/213S213y1z31) 下画面をさらに広げてみましょう。80%にしてみます。 ```bash:~/.tigrc # 水平分割したウィンドウの下画面サイズを % で指定(行数指定も可) set split-view-height = 80% ``` すると次のようになります。  [拡大表示](http://cl.ly/image/0F3E1V3S170Y) 自分の環境で使いやすい比率を探してみましょう。 # 差分の文脈をカスタマイズしよう diff表示において、差分の前後の文脈(diff-context)がわかりづらい場合があります。 tigのデフォルト表示では、前後3行が見える状態になっています。  [拡大表示](http://cl.ly/image/213S213y1z31) 3行では不足する場合、これを増やすことができます。試しに6行にしてみます。 ```bash:~/.tigrc # 差分の前後の表示行数(diff-context)を指定 set diff-context = 6 ``` すると、次のように6行分が表示されるようになりました。  [拡大表示](http://cl.ly/image/2K2D063a2Q1g) あまり長すぎても差分の確認時に不要な行が出てしまってノイズになりますので、 環境に合わせて加減を調整するとよいと思います。 # おわりに 本稿では.tigrcで可能な4つの設定に絞ってtigの画面表示カスタマイズ設定を紹介しました。 紹介した設定の詳細や、その他の.tigrcで設定可能な項目は下記の本家マニュアルを参照してください。 [tigrc(5) Manual Page](http://jonas.nitro.dk/tig/tigrc.5.html) 機会があればキーバインド編も紹介したいと思います。 → [書きました](http://qiita.com/yoshikazusawa/items/3eaa6db78fa348d38bfe) |
|
| 503位 |
|
|||
|
00:02:50 |
|
|
AngularJSはテストを重視しているフレームワークだと言われています。
それは、DIが標準搭載されているのでサーバーとの通信などのテストしにくい部分を簡単にモックに差し替えることが出来たり、ユニットテストやEnd to End(E2E)テストのためのフレームワークを持っていたりするからでしょう。 そして、現在標準で含まれているE2Eテストのための機能は、今後ProtractorというSelenium WebDriverJSベースのフレームワークに移行すると発表されています。([AngularJS 1.2 & Beyond](https://docs.google.com/presentation/d/1WHCcp3G3HxoE7b_ut_ERKJF4zQK_P4qFlESjE2E9AUQ/preview?sle=true#slide=id.p)) これまでのE2Eテストフレームワークを捨てて新しいものに乗り替えるのには理由があります。 それは、Seleniumをベースにすることで次のような恩恵を受けられるからです。 * ブラウザを操作するためのAPIが充実 * 複数ブラウザでの実行が可能 * リモートページのテストが可能 # インストール Protractorはnpmでインストールすることができます。 ~~~ npm install protractor ~~~ npmでインストールされたディレクトリにwebdriver-managerという実行ファイルがあるので、それを使ってSelenium ServerやWebDriverをローカル環境にインストールします。 ~~~ node_modules/protractor/bin/webdriver-manager update ~~~ # Grunt GruntでProtractorを扱うために、次のプラグインを使います。 これらのプラグインは必須というわけではないので、自分のお気に入りのものを使うといいと思います。 ([grunt-start-webdriver](https://npmjs.org/package/grunt-start-webdriver)というプラグインもあります) * grunt-protractor-runner * protractorのテストを実行するプラグイン。 * grunt-contrib-connect * Grunt内で起動するWebサーバープラグイン。protractorでテストするにはWebサーバが必要なので。 * grunt-shell/grunt-shell-spawn * シェルを実行するためのプラグイン。Selenium Serverのインストールやバックグラウンド起動のために利用する。 Gruntfileはこんな感じに記述します。 ~~~js:Gruntfile.js module.exports = function (grunt) { grunt.initConfig({ // dist配下のファイルをhttp://localhost:9000/で公開する connect: { options: { port: 9000, hostname: 'localhost' }, my_target: { options: { base: 'dist' } } }, // test/conf.jsの設定に従ってテストを実行 protractor: { options: { keepAlive: true, noColor: false }, my_target: { options: { configFile: "test/conf.js" } } }, shell: { options: { stdout: true }, // Selenium Serverをバックグラウンドで実行 selenium: { command: "node_modules/protractor/bin/webdriver-manager start", options: { stdout: false, async: true } }, // Selenium Serverのインストールおよび更新 protractor_install: { command: "node_modules/protractor/bin/webdriver-manager update" } } }); require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); }; ~~~ # conf.js 続いてProtractorの設定ファイルを作成します。 node_modules/protractor/referenceConf.js にベースになるファイルがあるので参考にしましょう。 ~~~js:conf.js exports.config = { // Seleniumサーバーのアドレス seleniumAddress: "http://localhost:4444/wd/hub", // テストで利用するブラウザなどの条件を設定することができます。 // 詳細は https://code.google.com/p/selenium/wiki/DesiredCapabilities capabilities: { browserName: "chrome" }, // テスト対象のspecファイルのパス specs: ["spec.js"], // テスト対象のアプリケーションのベースURL baseUrl: 'http://localhost:9000/', // 利用するテストフレームワーク framework: "jasmine", // jasmine用の設定 // 詳細は https://github.com/juliemr/minijasminenode jasmineNodeOpts: { showColors: true } } ~~~ なお、テストフレームワークはデフォルトでjasmineを使うようになっていますが、mochaを使うことも可能です。 * https://github.com/angular/protractor/blob/master/docs/using-mocha.md # API Protractorでは、WebDriverJSのAPIに加えてAnuglarJSアプリのテストがしやすくなるようなAPIが用意されています。 以降では、Protractor特有のAPIを中心に解説します。 詳細はAPIリファレンスを参照してください。 * https://github.com/angular/protractor/blob/master/docs/api.md ## グローバル変数 用意されてるAPIのうち、よく利用するものはグローバル変数に登録されています。 * `by`, `By` * protractor.Byのエイリアス。 * `element` * protractor.elementのエイリアス。 * protractor.findElementなどを駆使してうまい具合に探してくれる。 * 内部でbrowser.waitForAngularを呼んでいるので、レンダリングのタイミングが早すぎて要素が取れないということがない。 * `browser` * protractor.getInstance()と同じ。WebDriverのインスタンスをラップしたもの。 * `$` * protractor.findElement(protractor.By.css("・・・"))のエイリアス。 * `$$` * protractor.findElements(protractor.By.css("・・・"))のエイリアス。 ## ブラウザ操作 browserというインスタンスを使って、様々なブラウザ操作を行うことができます。 browser.get()でページを開いたり、browser.actions()でマウスイベントを発行したり、browser.wait()で処理が完了するまで待ったり、などなど。 WebDriverJSの基本的なAPIに、Protractorでは以下の機能が追加されています。 * waitForAngular * AngularJSのレンダリングと`$http`の呼び出しが完了するまで待つ。 * addMockModule/clearMockModules * モックの登録と解除を行う。 * debugger * アプリケーションの動作を止めてデバッガを起動する。 ## ロケーター HTMLの要素を指定するためのDSLです。 WebDriverJSでは、idやタグ名やcssセレクタで要素を指定するAPIが用意されていますが、Protractorでは以下のようなAPIが追加されています。 * by.binding * `by.binding('status')`で`<span>{{status}}</span>`が取得できます。適当なタグで囲ってあげないと外側の要素が取れます。 * by.repeater * `by.repeater('item in items')`とすると`<div ng-repeat="item in items">`の要素が取得できます。指定した行や列の要素だけを取る手段も用意されています。 * by.model * `ng-model`の名前で指定して、selectタグやinputタグの要素を取得できます。 ## 要素の操作 element(by.binding("xxxx"))などで取得した要素に対して行う操作です。 getText()やgetAttribute()に加えて、Protractorでは以下のようなAPIが追加されています。 * all * ng-repeatの全要素を取得できる * $ * protractor.findElement(by.css(・・・))のエイリアス * $$ * protractor.findElements(by.css(・・・))のエイリアス * evaluate * 現在の要素のscopeに対してスクリプトを実行することができます。 * element(by.id("something")).evaluate("value")ってやると、`id='something'`要素の`$scope.value`の値がとれたりします。 結果は全てPromiseで返ってくるようになっているので、値を使いたい場合はthenでつなげて書きます。 ~~~ // inputタグの値を取得 var input = element(by.model("status")); input.getAttribute('value').then(function(attr) { ・・・ }); // バインドされた値の取得 var output = element(by.binding("output")); output.getText().then(function (text) { ・・・ }); // ng-repeatの全値を取得 var collection = element.all(by.repeater("item in items")); collection.then(function (rows) { ・・・ }); // ng-disabledの値を取得。有効なときはnullになる。 var button = element(by.id("myButton")); button.getAttribute("disabled").then(function(disabled) { ・・・ }); ~~~ ## テストのためのAPI ProtractorではJasmineに改良を加えており、expectの引数にPromiseを渡し、普通の値と同じように結果を比較することが可能になっています。 例えば、上記のgetText()やgetAttribute()などは全部Promiseを返すようになっていますが、`expect(element(by.binding("xxx")).getText()).toEqual("yyy")`のように書くことができます。 # テストの例 テスト対象として以下のようなHTMLを考えてみます。 inputTextに値を入れてボタンを押すと、入力した文字が大文字に変換されてdisplayTextに表示されるものと思ってください。 ~~~html:sample.html <body> <div ng-controller="SampleCtrl"> <input type="text" ng-model="inputText"> <br/> <div>{{displayText}}</div> <br/> <button ng-click="toUpper()">push</button> </div> </body> ~~~ テストは次のように書きます。 ~~~js:spec.js describe('basics', function () { var input; var display; var button; beforeEach(function () { // http://localhost:9000/sample.html を開く browser.get('/sample.html'); // 各要素を取得 input = element(by.model("inputText")); display = element(by.binding("displayText")); button = element(by.tagName("button")); }); it('should be to upper value', function () { // input要素に文字列を入力 input.sendKeys('test'); // ボタンをクリック button.click(); // displayTextに"TEST"と表示されていることを確認 expect(display.getText()).toEqual("TEST"); }); }); ~~~ # テストの実行 Selenium Serverを起動します。テストの実行前に必ず立ち上げておく必要があります。 ~~~ grunt shell:selenium ~~~ アプリケーションを起動します。Gruntでテスト実行と同時に立ち上げるようにしておくのがおすすめです。 ~~~ grunt connect:my_target:keepalive ~~~ そしてテストを実行します。 ~~~ grunt protractor:my_target ~~~ ブラウザが起動してテストが実行され、次のように結果が表示されます。 ~~~ Running "protractor:my_target" (protractor) task Using the selenium server at http://localhost:4444/wd/hub . Finished in 7.281 seconds 1 tests, 1 assertions, 0 failures Done, without errors. ~~~ ## デバッグモード テストが思ったとおりに動かないと思ったらデバッグモードを使いましょう。 Gruntfileのprotractorのoptions.debugをtrueにします。 (protractorの実行ファイルを直接実行している場合は、オプションに`debug`を指定します) ~~~js:Gruntfile.js protractor: { options: { keepAlive: true, noColor: false, debug: true }, my_target: { options: { configFile: "test/conf.js" } } }, ~~~ `grunt protractor:my_target`を実行するとコンソールに下記のように出てきます。 `c`や`r`を入力するとテストの実行が開始されます。 ~~~ < debugger listening on port 5858 connecting... ok break in node_modules/protractor/lib/cli.js:8 6 */ 7 8 var util = require('util'); 9 var path = require('path') 10 var fs = require('fs'); debug> ~~~ テストコード中に`browser.debugger();`と書いておくと、テストを実行した時にそこで止まってくれます。 replを起動すると、JavaScriptのコードを書いて変数の中身を確認できたり、とても便利です。 その他ステップ実行などいくつかのコマンドが用意されています。デバッガの中で`help`と打ってみましょう。 # おわりに 個人的には、小さいプロダクトを書く時はユニットテストはあまり効果を感じられなくて、書くのも面倒に感じてしまいます。 でもE2Eテストなら手間もそんなにかからないし、ドキュメント代わりにもなるのでおすすめです。 |
|
| 504位 |
|
|||
|
21:09:46 |
|
|
#はじめに
2017/01/20現在、挙動のテストが追いついていないので、このページを訪れる人に役立つと思われるリンクを置いておきます。 https://speakerdeck.com/tacamy/modanri-ben-yu-huontozhi-ding https://rxon.now.sh/crossPlatformYu.md --- 以下は2013年11月の情報なので参考にしないでください(その間にChoromeがDirectWriteに対応してnameテーブルの参照メタデータが変わったりしていて、設定すべきfont-family値が変わっていることが大きな要因です)。 --- # 序文 Windows8.1とOS X Mavericksのリリースが開始されました。 Windows8.1ではRC版にインストールされていた游ゴシックに加えて、游明朝もインストールされています。 OS X Mavericksには既に字游工房がデザインした大日本スクリーン製造のヒラギノファミリーがインストールされていますが、それに加えて字游工房の游ゴシック体・游明朝体がインストールされました。これはiBooksがOS Xでもスタートするのに当たっての対応と思われます。 そしてこれらのフォントはOSバンドルフォントですから、当然ブラウザからも利用することが可能です。 せっかく一番人気の書体 ( http://twitpic.com/3nkgh8 ) なのですから使わない手はありません。 まずはWindows8.1、OS X Mavericksで搭載されている游書体ファミリーの構成を見ていきましょう。 # 各OSにバンドルされた游書体ファミリーの構成 ##Windows 8.1 * 游ゴシック Light * 游ゴシック Regular * 游ゴシック Bold * 游明朝 Light * 游明朝 Regular * 游明朝 Demibold ##OS X 10.9 * 游ゴシック体 ミディアム * 游ゴシック体 ボールド * 游明朝体 ミディアム * 游明朝体 デミボールド ##異なるポイント ###搭載ウェイトが違う CSSだと、何気なくBoldにしたり、Androidだとtext-shadowで太くしたりするかも知れませんが、フォントというのはウェイト毎にデザインも大きく変わります。 以下はヒラギノ角ゴシック体ファミリーの一番細いウェイトから一番太いウェイトまでのアウトラインを重ねたものですが、微妙にバランスを整えながら、一文字一文字丁寧にて作業で調整していることがわかります。  機械的に単純に太らせたり細らせたりしているわけではないのです。 紙面に印字したとき(画面に表示させたとき)に、版面が黒々しく見えないか、文字によって同じウェイトなのに細く見えたり太く見えたりしないか、色々なことを気にしながら調整を行います。 使う場面・文字の大きさ・文字の量によって、最適なフォントウェイトがあるために、フォントベンダーは様々なウェイトを用意し、色々な用途に文字を使えるようにしているのです。 ###名前が違う フォントファイルにはname tableと呼ばれるメタデータが含まれており、これを元にフォントを指定することが可能です。 name tableはMicrosoft用とMacintosh用でそれぞれ値が定義されているため、同じように指定して同じフォントをインストールして同じブラウザで見ても、OSが異なるためにフォントが適用されないこともあります。 そしてこのOSバンドル游書体もやっかいなことに、Windows搭載游書体の値と、OS Xに搭載游書体の値が異なり、font-familyで1つだけ指定して完結することはありません。 また、市販の游書体や、厳密に言えばWindowsPhone搭載の"Yu Gothic"とも異なる値です。 ####フォント構造について それでは、OTMというURW++社のフォント内部を閲覧できるソフトウェアで、Windows8.1搭載游ゴシックRegularのname tableの値を見てみましょう。  ※ライセンス番号は一時的に隠してあります name tableのレコードで定義されているJapan Font Family nameをfont-familyの値に使うことで、ChromeとFirefoxは、使用したいフォントを指定することが可能となります。 IEに関してはPreferred Familyの値"游ゴシック"を使います。 ここで他のウェイトの游ゴシックを見てみましょう。 #####游ゴシック Boldのname table  #####游ゴシック Lightのname table  Japan Font Family nameがLightだけ異なることがわかると思います。これが原因で、"游ゴシック"をfont-familyの値に使った場合、FirefoxとChromeでは游ゴシック Lightを使うことはできません。 もしFirefoxとChromeで游ゴシック Lightを使いたい場合は、font-familyには"游ゴシック Light"とする必要があります。 #####Preferred Familyについて 一方、IEはPreferred Familyの値を文字通り優先的に使うので、font-familyに"游ゴシック"、font-weightにlighterまたは300(200、100でも可)と指定した場合、游ゴシック Lightが使用されます。また、Preferred Familyで一致するものがなければ、Font Family nameを使います(ココ重要)。 これは、Font Family nameの値には予め定められた4種類のバリエーションしか指定できない仕様に起因します(regular, italic, bold, bold italic。どのバリエーションかはFont Subfamily nameで定義されます)。 Preferred Familyについては、同一のPreferred Family内のPreferred Subfamilyにて4種類以上のバリエーションを持たせることが可能で、基本的に以下の9種類を定義します。 |name table Preferred Subfamily|OS/2 table usWeightClass| |:-----------|:------------| |Thin|100| |Extra-light (Ultra-light)|200| |Light|300| |Normal (Regular)|400| |Medium|500| |Semi-bold (Demi-bold)|600| |Bold|700| |Extra-bold (Ultra-bold)|800| |Black (Heavy)|900| (字游工房デザイン・大日本スクリーン製造制作販売で、Mac OS XにはW3とW6が乗っており、iOS6からProとProNではなく、ProNだけになったことで有名な)ヒラギノ角ゴシック体・ヒラギノ明朝体で言えば、W1が100でThin、W3が300でLight、W6が600でDemibold、W9が900でHeavy、という感じです(ヒラギノはLightと言いつつも結構太い方ですが)。 各ウェイトの値はOS/2 tableのusWeightClassで定義されます。 余談はさておき以上のことをまとめると、Windows8.1に限定すると以下の通りとなります。 ##Windows8.1で游ゴシックのRegularとBoldを使う場合※お手軽バージョン ```css selector-for-YuGothicRegular{ font-family:"游ゴシック"; font-weight:normal; } selector-for-YuGothicBold{ font-family:"游ゴシック"; font-weight:bold; } ``` ##Windows8.1で游ゴシックのRegularとBoldとLightを使う場合 ```css selector-for-YuGothicLight{ font-family:"游ゴシック Light"; font-weight:300; } selector-for-YuGothicRegular{ font-family:"游ゴシック"; font-weight:400; } selector-for-YuGothicBold{ font-family:"游ゴシック"; font-weight:700; } ``` ##Windows8.1で游明朝のRegularとDemiboldを使う場合※お手軽バージョン ```css selector-for-YuMinchoRegular{ font-family:"游明朝"; font-weight:normal; } selector-for-YuMinchoBold{ font-family:"游明朝"; font-weight:bold; } ``` ##Windows8.1で游明朝のRegularとDemiboldとLightを使う場合 ```css selector-for-YuMinchoLight{ font-family:"游明朝 Light"; font-weight:300; } selector-for-YuMinchoRegular{ font-family:"游明朝"; font-weight:400; } selector-for-YuMinchoDemibold{ font-family:"游明朝"; font-weight:600; } ``` #OS X Mavericksの場合 上記の通り、Mavericksでは以下のようなバンドル構成です。 * 游ゴシック体 ミディアム * 游ゴシック体 ボールド * 游明朝体 ミディアム * 游明朝体 デミボールド ではWindows8.1バンドルと同様にname tableを見ていきましょう。  どうでしょうか。絶望的ですね。Englishで指定しようにも半角スペースがないし、Japaneseでも"体"が入ってるし。マジで訳がわかりません。ウェイトは3種類ではなくそれぞれ2種類。そして標準ウェイトになるウェイトがWindows8.1がRegularに対して、MavericksはMediumです。ホントどうかしてます。 まぁiOS版のiBooksで搭載されていた方が早かったと思いますので、責めるべきはAppleではなくMicrosoftだ、という向きもあるでしょうが、まさかOS Xに游書体が乗ると思わなかったという反論もでき、当の字游工房さんはきっと守秘義務契約でお互いの内容は漏らせないんだと思うので、帰結すべきは資本主義が生んだ害と言うところでしょうか(何 さて、では先ほどのWindows8.1用CSSにこれを追加してみましょう。 ##Windows8.1とMavericksで游ゴシックのRegular(MacはMedium)とBoldを使う場合※お手軽バージョン ```css selector-for-YuGothicRegular{ font-family:"游ゴシック","YuGothic"; font-weight:normal; } selector-for-YuGothicBold{ font-family:"游ゴシック","YuGothic"; font-weight:bold; } ``` ##Windows8.1とMavericksで游ゴシックのRegular(MacはMedium)とBoldとLight(MacはMedium)を使う場合 ```css selector-for-YuGothicLight{ font-family:"游ゴシック Light","YuGothic"; font-weight:300; } selector-for-YuGothicRegular{ font-family:"游ゴシック","YuGothic"; font-weight:400; } selector-for-YuGothicBold{ font-family:"游ゴシック","YuGothic"; font-weight:700; } ``` ##Windows8.1とMavericksで游明朝のRegular(MacはMedium)とDemiboldを使う場合※お手軽バージョン ```css selector-for-YuMinchoRegular{ font-family:"游明朝","YuMincho"; font-weight:normal; } selector-for-YuMinchoBold{ font-family:"游明朝","YuMincho"; font-weight:bold; } ``` ##Windows8.1とMavericksで游明朝のRegular(MacはMedium)とDemiboldとLight(MacはMedium)を使う場合 ```css selector-for-YuMinchoLight{ font-family:"游明朝 Light","YuMincho"; font-weight:300; } selector-for-YuMinchoRegular{ font-family:"游明朝","YuMincho"; font-weight:400; } selector-for-YuMinchoDemibold{ font-family:"游明朝","YuMincho"; font-weight:600; } ``` 上記で指定すると、Windows8.1(IE11)では  Mavericks(Chrome)では  のように見えます。 Windows8.1 IE11のレンダリングはDirectWrite対応で綺麗です。それに比べてMacのApple Type Services for Unicode Imagingによるレンダリングは本来のパス情報を太らせてしまうので、同じウェイト同士で比べると差が顕著ですね。特に明朝体が圧倒的に異なります。 #マルチクラス設計における利便性の高いfont-family定義 実際はシングルクラスでいちいちfont-familyを指定せず、マルチクラスとしてfont-weight:normal;をベースに、font-weight:bold;を利用するケースが多いと思います。 その際問題になるのはBoldです。 Webフォントを使う場合でも大きな問題になりますが、指定したfont-familyより太いウェイトがないフォントファミリーの場合、OSが勝手に太く見せる処理をかけてしまい、フォント本来の持つ綺麗さが大きく損なわれてしまう場合があります。  通常は游ゴシック Regularを使うべきポイントであるにも関わらず、 font-family:"游ゴシック Light"; font-weight:bold(または600以上); と指定してしまうと、このように視認性の悪く、質の悪い見栄えの文字になってしまいます。 これを避けつつ、font-weightの上書きでboldを(IE以外でも)使うには、name tableのFont Family nameが同じで、OS/2 tableのusWeightClassが異なるフォントファミリー2つを使う必要が出てきます。 そのため、Windows8.1にバンドルされている游ゴシック Lightを(IE以外で)使うことはできません(IEはFont Family nameではなく、Preferred Familyを使うのでLightをfont-weightで指定することが可能です)。 Windows8.1に搭載の游書体Lightを諦め、Regular(MacはMedium)、Bold(明朝はDemibold)同士でセットとし、font-weightでフォントを切り替える方法を考えてみましょう。 ##Windows8.1とMavericksで游書体をマルチクラスで指定する場合 ```css selector-for-YuGothic{ font-family:"游ゴシック","YuGothic"; } selector-for-YuMincho{ font-family:"游明朝","YuMincho"; } selector-for-Normal{ font-weight:normal; } selector-for-Bold{ font-weight:bold; } ``` *** #ヒラギノと最低限のフォールバック できるだけクオリティの高い書体のみを指定しつつ、IE8対応もしつつフォールバックする場合は以下のようになると思います(XP-IE8でも検証済み)。 ```css selector-for-YuGothic{ font-family:"游ゴシック","YuGothic","Hiragino Kaku Gothic ProN","Hiragino Kaku Gothic Pro","MS ゴシック",sans-serif; } selector-for-YuMincho{ font-family:"游明朝","YuMincho","Hiragino Mincho ProN","Hiragino Mincho Pro","MS 明朝",serif; } selector-for-Normal{ font-weight:normal; } selector-for-Bold{ font-weight:bold; } ``` IE8では、serif・sans-serifの指定を理解してくれずすべてMS Pゴシックで表示してしまうため、上記のようにフォールバックしました。等幅フォントの方を指定したのは、前で指定されたフォントとの字幅を近づけるためです。 #@font-faceとlocalを使った書き方+iOS7のヒラギノW2でLightに対応 iOS7では今のところCSSでヒラギノ角ゴシックW1とW2が使えるようです。 元のフォントはStd(AdobeJapan1-3)ですので、一部文字列「魹(U+9B79|とど)」「㐅(U+3405)」「逸(U+FA67)」などは表示できないかもしれません。 ```css selector-for-YuGothic{ font-family:"游ゴシック","YuGothic","Hiragino Kaku Gothic ProN","Hiragino Kaku Gothic Pro","MS ゴシック",sans-serif; } selector-for-YuMincho{ font-family:"游明朝","YuMincho","Hiragino Mincho ProN","Hiragino Mincho Pro","MS 明朝",serif; } selector-for-Normal{ font-weight:normal; } selector-for-Bold{ font-weight:bold; } ``` |
|
| 505位 |
|
|||
|
20:10:02 |
|
|
# よくある解説
* bindはajax等で変更した要素にはイベントが動かない * liveはajax等で変更した要素にもイベントが動く > [jQueryで追加した要素を操作するにはon()メソッドで](http://qiita.com/noboru/items/9c1017f952e37eb19b5a) # 問題 ```html <table> <tr> <td>ほげ</td> </tr> </table> ``` ```js $('table').bind('click', function(){ console.log('bind-table') }); $('tr').live('click', function(){ console.log('live-tr') }); $('td').live('click', function(){ console.log('live-td') }); ``` という状態の時、`td`をクリックした際にコンソールにどういう順番で出力されるか。 # 回答、、、の前に javascriptのイベントは本来、外側から内側(tableからtd)に向かい(キャプチャーフェーズ)、対象の要素まで到達すると、今度は外側に向かいます(バブリングフェーズ)。ところが、jqueryが出た当時のIEにはキャプチャーフェーズがなく、バブリングフェーズしかなかった、からなのか、jqueryのイベント動作もバブリングフェーズだけになりました。なので、jqueryで捕捉できるイベントは、内側から外側に向けてのバブリングフェーズだけです。 # 回答 ```js bind-table live-td live-tr ``` の順に出力されます。 ## tdが最初じゃないの? * 内側からイベントが動くんじゃないの? ここで最初の話に戻りますが、なぜliveでイベントをセットするとajaxで書き換えた要素に対してもイベントが動き、bindでイベントセットするとajaxで書き換えた要素に対してイベントが動かないか。という話なのですが。 実はliveはbindとは違い最初のセレクタで一致した要素に対して **イベントセットしてません。** ```js $('#id').bind(); //#idにイベントをセット $('#id').live(); //#idにはイベントをセットしてない ``` ## じゃあどこに? * デフォルトでは`document` * 正確には`context` ```js $(selector, context);//←これ ``` `context`にエレメント以外を渡すか何も渡さないとdocumentがセットされます。 (セレクタやjqueryオブジェクトを渡してもcontextは変わらない) ```js $(selector).context;//document $(selector, '#id').context;//document $(selector, $('#id')).context;//document $(selector, $('#id')[0]).context;//#id ``` ## つまり、 `table tr td` にイベントをセットしたとみせかけて、実際には `table document(tr) document(td)` にイベントをセットしていたことになります。 `document`はDOMのトップレベルオブジェクトですから、当然一番外側になり、イベント伝播は最後になります。また、セレクタ要素に対してはイベントセットしていないことになるので、ajaxで書き換えられようが、関係なくイベントが動くということになります。 ## あれ・・・? 最初のセレクタ無駄じゃない? ```js $(selector) //selector要素を走査 .bind(); //その走査した要素にイベントセット $(selector) //selector要素を走査 .live(); //その走査した要素にイベントセットしない→走査する必要なくない? ``` 無駄ですね。かつ、半強制的にdocumentに対してセットしているのもいかがなものか。 また、走査したものを使わないということは、Traversing系メソッドのチェーンに使いづらい(使えない)、という話にもなります。 ## そういうわけで、delegateが出来た ```js $('table').delegate('td', 'click', function(){ console.log("table - td"); }); ``` delegateは(だいたい)デリゲートと読み、意味は「委譲」と訳されます。 上記でいうと、tdのイベントをtableに委譲しています。 イベント自体はtableに、発火点はtdにすることで、td(tr)の要素がajaxで書き換わっても、バブリングフェーズでイベントをキャッチするのはtableなので、イベントが動きます。また、`table`を走査して`table`にイベントセットしているので、無駄になりません。また、このように委譲することで、tableの中にtdが大量にあっても、イベントセットはtableの1つだけになり、パフォーマンス的にも優れています。 ちなみに、 ```js //こう書けば、 $(document).delegate('td', 'click', function(){ console.log("fire!"); }); //以下と同じことになります。 $('td').live('click', function(){ console.log("fire!"); }); ``` # イベントセット方法多すぎ! * で、onが出来ました。 * 今まで書いてきたイベントセット方法は全てonで出来るようになります。 ## bind→on ```js $('td').bind('click', fn); $('td').on('click', fn); ``` * ほぼ同じですね。新しく覚えなおすことはありませんし、bindと書くよりも、onと書いた方が短いので、onを使いましょう。 ## delegate→on ```js $('table').delegate('td', 'click', fn); $('table').on('click', 'td', fn); ``` * 引数の順番が変わりました。 * 基本的には第1引数がイベント名、その後ろにセレクタがあればそのセレクタが発火点ということだけ覚えておけばほぼbindと同じですので、delegateの書式を覚えるよりも簡単です。なので、onを使いましょう。 ## live→on ```js $('table').live('click', fn); $(document).on('click', 'table', fn); ``` * ある意味一番ややこしいですが、 **liveがdocumentにイベントをセットしている** ということさえ理解していれば、delegateと同じになります。また、イベントセット箇所が明示的になるのでonを使いましょう。 * というか、もう1.9以降、使用不可です。 # 以上を踏まえて * 最初の問題を書き換えるとこうなります。 ```js $('table').on('click',function(){ console.log('table'); }); $(document).on('click', 'tr', function(){ console.log('tr'); }); $(document).on('click', 'td', function(){ console.log('td'); }); ``` あら不思議、どのように出力されるかは一目瞭然ですね。 # ちなみに * onが使えるようになってからは、bind、live、delegateはただの入り口です。 ```js //一部抜粋 bind: function( types, data, fn ) { return this.on( types, null, data, fn ); }, //liveは1.8.3まで live: function( types, data, fn ) { jQuery( this.context ).on( types, this.selector, data, fn ); return this; }, delegate: function( selector, types, data, fn ) { return this.on( types, selector, data, fn ); }, ``` jquery界隈の人たちが、イベントセットするのに、`on`を使えとよく言っている意味が分かったかと思います。 # ちなみにちなみに * clickもただの入り口です。というか見て分かる通り、だいたい入り口です。 ```js jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { // Handle event binding jQuery.fn[ name ] = function( data, fn ) { return arguments.length > 0 ? this.on( name, null, data, fn ) : this.trigger( name ); }; }); ``` this.onの引数を見ると分かると思いますが、onが作られる前はbindが使われていました。 # まとめ * clickとbindはほぼ一緒。というか、呼び出し方が違うだけ。 * bindとliveは書式が同じだけど、イベントのセット先が違う。 * liveとdelegateは書式が異なっていて、委譲してる点は同じだけど、delegateには無駄な走査が発生しない。 * on以外はonの入り口。 |
|
| 506位 |
|
|||
|
03:01:21 |
(メディアマックスジャパン株式会社 所属) |
|
## Hubotとはなにか?
Hubotは、文字列のコマンドを受け付け、指定されたコマンドに応じた処理を実行し、結果を表示する機能を提供するプログラムです。 この動作はシェルと同様なので、Hubotをシェルとみなすこともできます。 Hubotに対してコマンドを入力するのは、コンソールから、またはアダプタが提供されたチャットから行うことができます。単にHubotの動作を試したいだけならば、コンソールから試せます。 この記事では、Hubotをインストールし、コンソールから使ってみる手順を説明します。 ## Hubotのインストール node.js, redisが必要です。これらがインストールされてない場合は、まずインストールしてください。 OSXであれば、Homebrewを利用してインストールできます。 ```bash brew install node redis ``` あらかじめredisを起動しておきましょう。 ```bash redis-server & ``` Hubotのインストールは基本、公式サイトに書かれてるように、npmを使ってインストールします。 https://github.com/github/hubot/tree/master/docs ```bash npm install -g hubot coffee-script ``` Hubotをインストールすると、`hubot`コマンドが提供されます。`hubot`コマンドを使って、独自のHubot環境を構築できます。 ```bash hubot --create myhubot ``` このコマンドは、myhubotというディレクトリを作り、その中にHubot環境を作ります。 これでHubotを使う準備ができました。以下のコマンドを入力して、Hubotを起動してください。Hubotプロンプトが表示されます。 ```bash cd myhubot bin/hubot Hubot> ``` ## 組み込みのスクリプトを実行してみる Hubotへのコマンドの入力は、多くの場合、先頭に hubot と入力することで行います。`hubot help<Enter>`と入力してみましょう。 ```bash Hubot> hubot help Hubot> Events: debug - {user: <user object to send message to>} Hubot <user> doesn't have <role> role - Removes a role from a user Hubot <user> has <role> role - Assigns a role to a user Hubot <user> is a badass guitarist - assign a role to a user Hubot <user> is not a badass guitarist - remove a role from a user Hubot animate me <query> - The same thing as `image me`, except adds a few parameters to try to return an animated GIF instead. Hubot convert me <expression> to <units> - Convert expression to given units. Hubot die - End Hubot process Hubot echo <text> - Reply back with <text> Hubot fake event <event> - Triggers the <event> event for debugging reasons Hubot help - Displays all of the help commands that Hubot knows about. Hubot help <query> - Displays all help commands that match <query>. Hubot image me <query> - The Original. Queries Google Images for <query> and returns a random top result. Hubot map me <query> - Returns a map view of the area returned by `query`. Hubot math me <expression> - Calculate the given expression. Hubot mustache me <query> - Searches Google Images for the specified query and mustaches it. Hubot mustache me <url> - Adds a mustache to the specified URL. Hubot ping - Reply with pong Hubot pug bomb N - get N pugs Hubot pug me - Receive a pug Hubot show storage - Display the contents that are persisted in the brain Hubot show users - Display all users that Hubot knows about Hubot the rules - Make sure Hubot still knows the rules. Hubot time - Reply with current time Hubot translate me <phrase> - Searches for a translation for the <phrase> and then prints that bad boy out. Hubot translate me from <source> into <target> <phrase> - Translates <phrase> from <source> into <target>. Both <source> and <target> are optional Hubot what role does <user> have - Find out what roles are assigned to a specific user Hubot who has admin role - Find out who's an admin and can assign roles Hubot who is <user> - see what roles a user has Hubot youtube me <query> - Searches YouTube for the query and returns the video embed link. ship it - Display a motivation squirrel ``` コマンドリファレンスが表示されました。Hubotのコマンドの実体は、scripts ディレクトリにあるCoffeeScriptファイルです。scriptsディレクトリにファイルを追加することで、コマンドを追加することができます。以下では、今の時点で実行できるコマンドをいくつか実行してみます。 ``` Hubot> hubot animate me partyhard Hubot> http://images3.wikia.nocookie.net/__cb20070824174846/uncyclopedia/images/5/58/PARTYHARD4.gif#.png ``` インターネットからアニメーションgifを検索し、URLを表示します。 ``` Hubot> hubot mustach me obama Hubot> http://mustachify.me/2?src=http://www.tothetick.com/wp-content/uploads/2013/08/Obama4.jpg#.png ``` ひげをはやします。 ``` Hubot> hubot ping Hubot> PONG ``` ピンポンができます。 ``` Hubot> hubot pug bomb Hubot> http://24.media.tumblr.com/tumblr_maxm4ecVNq1qjmqavo1_500.jpg Hubot> http://29.media.tumblr.com/tumblr_lsvczkC8e01qzgqodo1_500.jpg Hubot> http://24.media.tumblr.com/tumblr_lhowz2fAnC1qaa50yo1_500.jpg Hubot> http://28.media.tumblr.com/tumblr_ltyzizUm3t1qb08qmo1_500.png Hubot> http://29.media.tumblr.com/tumblr_lsu0igEEtP1qiys5ao1_500.png ``` かわいいパグの画像を多数表示します。 ``` Hubot> hubot the rules Hubot> 1. A robot may not injure a human being or, through inaction, allow a human being to come to harm. 2. A robot must obey any orders given to it by human beings, except where such orders would conflict with the First Law. 3. A robot must protect its own existence as long as such protection does not conflict with the First or Second Law. ``` ロボット三原則を教えてくれます。 ``` Hubot> hubot time Hubot> Server time is: Tue Dec 03 2013 02:50:16 GMT+0900 (JST) ``` 現在時刻を教えてくれます。 ``` Hubot> ship it Hubot> http://d2f8dzk2mhcqts.cloudfront.net/0772_PEW_Roundup/09_Squirrel.jpg ``` かわいいリスの画像を表示します。 ``` Hubot> hubot die Hubot> Goodbye, cruel world. ``` プログラムを終了します。 以上です。Hubotは簡単に使えることがわかりましたね。ぜひインストールしてみてください。 |
|
| 507位 |
|
|||
|
10:03:43 |
(サイバーエージェント 所属) |
|
#async.js
主にnodeで使用されている、javascriptの非同期処理を扱うライブラリです。 フロントエンドでもゲーム作り等では重宝するんです。 その中でも使用率がかなり高い、waterfallとseries、そしてparallelの三つについて簡単にまとめました。 さらに、ECMAScript6からは非同期処理を扱うためのPromiseオブジェクトが標準で使用できます。 [ECMAScript6のアロー関数とPromiseまとめ - JavaScript](http://qiita.com/takeharu/items/c23998d22903e6d3c1d9) 技術情報のみつぶやくアカウント作成しました。JavaScriptなどの最新情報も追っていきます。 Twitter: [@takeharumikami] (https://twitter.com/takeharumikami) RSSはこちら Feedly: [Feedlyをフォロー] (http://cloud.feedly.com/#subscription%2Ffeed%2Fhttp%3A%2F%2Fqiita.com%2Ftakeharu%2Ffeed) RSS: http://qiita.com/takeharu/feed #waterfall まず、waterfallについてです。 配列の順番で、この場合は上の関数から実行されていきます。 この際に引数に与えられたcallbackの関数が実行されるまで、 次の関数の処理には移行しません。 そしてcallbackの関数の第一引数にはerrorを、 第二引数以降は次の関数に渡したい引数を選ぶ事ができます。 ```javascript: async.waterfall([ function(callback) { console.log('1'); setTimeout(function() { console.log('1 done'); callback(null, 1); }, 100); }, function(arg1, callback) { // arg1 === 1 console.log('2'); setTimeout(function() { console.log('2 done'); callback(null, 1, 2); }, 50); }, function(arg1, arg2, callback) { // arg1 === 1, arg2 === 2 console.log('3'); setTimeout(function() { console.log('3 done'); callback(null, 1, 2, 3); }, 10); } ], function(err, arg1, arg2, arg3) { // arg1 === 1, arg2 === 2, arg3 === 3 if (err) { throw err; } console.log('all done.'); console.log(arg1, arg2, arg3); }); //出力結果 1 1 done 2 2 done 3 3 done all done 1 2 3 ``` #series seriesの流れはwatarfallの制御の流れと同じです。 配列の順番に、この場合は上から順次実行されていきます。 そして、次の関数に処理が移るにはcallbackが呼ばれる必要があります。 違いはcallbackの引数に渡した値ですが、 最後にまとめてこの配列の順番のまま、配列に値が代入されています。 ```javascript: async.series([ function(callback) { console.log('1'); setTimeout(function() { console.log('1 done'); callback(null, 1); }, 100); }, function(callback) { console.log('2'); setTimeout(function() { console.log('2 done'); callback(null, 2); }, 50); }, function(callback) { console.log('3'); setTimeout(function() { console.log('3 done'); callback(null, 3); }, 10); } ], function(err, results) { if (err) { throw err; } console.log('all done'); console.log(results); }); //出力結果 1 1 done 2 2 done 3 3 done all done [1, 2, 3] ``` #parallel 最後にparallelです。 seriesやwaterfallと違い、並列に処理されます。 (厳密には並列ではないですが) ですから引数に渡されたcallbackでは次の関数が呼ばれません。 callbackに渡した引数はまとめて最後に配列となって、 この順番通りに代入されています。 ```javascript: async.parallel([ function(callback) { console.log('1'); setTimeout(function() { console.log('1 done'); callback(null, 1); }, 100); }, function(callback) { console.log('2'); setTimeout(function() { console.log('2 done'); callback(null, 2); }, 50); }, function(callback) { console.log('3'); setTimeout(function() { console.log('3 done'); callback(null, 3); }, 10); } ], function(err, results) { if (err) { throw err; } console.log('all done'); console.log(results); }); //出力結果 1 2 3 3 done 2 done 1 done all done [1, 2, 3] ``` parallelは並列処理なので、 配列に関数を代入しなくてもいいです。 そこで別パターンとしてオブジェクトの各valueに関数を代入する方法があります。 この場合の違いは二点です。 1:並列処理の開始が保証されない 2:callbackに渡した値が、最後にまとめてオブジェクトに代入されている 1は配列の時と違い、どこから実行されるか保証されていないということです。 配列のときは要素の0から実行されていきます。 2は下記を見てもらえればわかるのですが、 同じkeyに対応して渡された値が代入されています。 ```javascript: async.parallel({ first: function(callback) { console.log('1'); setTimeout(function() { console.log('1 done'); callback(null, 1); }, 100); }, second: function(callback) { console.log('2'); setTimeout(function() { console.log('2 done'); callback(null, 2); }, 50); }, third: function(callback) { console.log('3'); setTimeout(function() { console.log('3 done'); callback(null, 3); }, 10); } }, function(err, results) { if (err) { throw err; } console.log('all done'); console.log(results); }); // 出力結果 1 2 3 3 done 2 done 1 done all done { third: 3, second: 2, first: 1 } ``` # まとめ コールバック処理はjavascriptでは重要な要素ですよね。 asyncを使うとスパゲッティにもなりづらいですし、 うまく扱えるとかなり重宝しますので、ぜひぜひおすすめです。 # アカウント 技術情報のみつぶやくアカウント作成しました。JavaScriptなどの最新情報も追っていきます。 Twitter: [@takeharumikami] (https://twitter.com/takeharumikami) RSSはこちら Feedly: [Feedlyをフォロー] (http://cloud.feedly.com/#subscription%2Ffeed%2Fhttp%3A%2F%2Fqiita.com%2Ftakeharu%2Ffeed) RSS: http://qiita.com/takeharu/feed # おすすめの記事 ECMAScript6からは非同期処理を扱うためのPromiseオブジェクトが標準で使用できます。 [ECMAScript6のアロー関数とPromiseまとめ - JavaScript](http://qiita.com/takeharu/items/c23998d22903e6d3c1d9) 入門者がつまづく、thisの挙動を4種類に分けて簡単に学ぶならこれ。Apply, callの挙動までわかる。 [JavaScriptの「this」は「4種類」?」](http://qiita.com/takeharu/items/9935ce476a17d6258e27) JavaScriptでは関数型言語の一部の機能?実践的なJavaScriptの関数型とは。 [JavaScriptで関数型プログラミングの入門](http://qiita.com/takeharu/items/cf98d352ff574c5ac536) |
|
| 508位 |
|
|||
|
17:49:20 |
(フリーランス 所属) |
|
意識高い人は、コーディング規約作るけど、意識低い人はそれを採用しない。
これはどうしようもないので、もっと意識を高くし、コードフォーマッターを使って処理を自動化する。これで、(命名規則等を除き)見た目の部分でだいぶましになる。 コードフォーマッターには[Uncrustify](http://uncrustify.sourceforge.net/)を使う。 Xcodeを使っている場合は、[BBUncrustifyPlugin-Xcode](https://github.com/benoitsan/BBUncrustifyPlugin-Xcode)を利用するとXcodeのプラグインとしてUncrustifyを呼べるようになります。あとはショートカットキーとかを割り当てたりご自由に。 以下一例 例では、Objective-Cを用いているけど、UncrustifyはC, C++, C#, ObjectiveC, D, Java, Pawn, VALAに対応しています。 ```objective-c:Before.m #import "Test.h" @interface Test(Private) -(void)hoge; @end @implementation Test { NSString *str; int index; } -(void)hoge{ NSString *a = @"a"; NSString *bb = @"bb"; NSString *ccc = @"ccc"; NSArray *array = @[a,bb,ccc]; int i=1; i+=1; switch(i){ case 1: break; case 2 :{ } break; default: break; } } @end ``` ```objective-c:After.m #import "Test.h" @interface Test (Private) - (void)hoge; @end @implementation Test { NSString *str; int index; } - (void)hoge { NSString *a = @"a"; NSString *bb = @"bb"; NSString *ccc = @"ccc"; NSArray *array = @[a, bb, ccc]; int i = 1; i += 1; switch (i) { case 1: break; case 2: { break; } default: break; } } @end ``` 僕の好みは、`{`はメソッド宣言の場合のみ改行します(if, for等では改行しない)。また、変数や定数では型、変数名、=、右辺毎にそれぞれ左揃え、メソッド宣言の`-`や`+`の後にはスペース、演算子の前後にはスペース等々です。 以下に設定ファイルを載せる。 9割9分、 @dfurusaka@github さんの[Uncrustifyのセッティング](http://qiita.com/items/dd7c5ffdff27451dae16) シリーズを参考にしています。ありがとうございます。 ```cfg: ##### Uncrustify config ### Preprocessor ## http://qiita.com/items/dd7c5ffdff27451dae16 # #if、#ifdef、#ifndef〜#else〜#endifブロック内のプロプロセッサをインデントするかどうか。{ ignore, add, remove, force } pp_indent = add # ソースコードのインデントレベルに合わせてインデントするかどうか。{ true, false } pp_indent_at_level = true # pp_indent_at_level=falseの場合のインデント幅 { number } pp_indent_count = 4 # #if、#ifdef、#ifndef〜#else〜#endifブロック内のプリプロセッサの#の後から半角空白でインデントするかどうか { ignore, add, remove, force } pp_space = remove # #の後に加える半角空白数 { number } pp_space_count = 0 # #regionと#endregion (C#言語)、#pragmra region (C/C++) に対するインデント { number } pp_indent_region = 0 # #regionと#endregionの間のコードをインデントするかどうか { true, false } pp_region_indent_code = false # pp_indent_at_level=trueの場合: ファイルレベルにない'#if'、'#else'、'#endif'に対するインデント位置(N桁)を固定する { number } pp_indent_if = 0 # ファイルレベルにない'#if'、'#else'、'#endif'の間のコードをインデントするかどうか { true, false } pp_if_indent_code = true # '#define'をブレースレベルに合わせてインデントするかどうか { true, false } pp_define_at_level = false ### General ## http://qiita.com/items/d8998dd5e90697744e3c # 出力に用いる改行コード { auto, lf, crlf, cr } newlines = auto # 入力ファイルのタブサイズ { number } input_tab_size = 4 # 出力ファイルのタブサイズ { number } output_tab_size = 4 # 文字列エスケープ文字に対するASCIIコード (通常は'\'(92)、Pawnの場合'^'(94)) { number } string_escape_char = 92 # '>='と'>>='を'void f(list<list<B>>=val);'のテンプレートの一部と解釈させるかどうか { true, false } tok_split_gte = false # UTF-8ファイルのBOMコードへの対応 (推奨'remove') { ignore, add, remove, force } utf8_bom = add # 値128〜255を持つバイトを含むUTF-8でないファイルの場合、出力をUTF-8とするかどうか { true, false } utf8_byte = false # 出力エンコーディングを強制的にUTF-8にするかどうか { true, false } utf8_force = true ### Indent ## http://qiita.com/items/b3fee3560e4ae746b468 # インデントレベルのインデント幅 (半角空白数) (通常 2、3、4、8) { number } indent_columns = 4 # '('や'='の行送りの後のインデントを上書き。マイナスの場合はインデントを継続。 { number } indent_continue = 0 # コードをインデントする時にどのようにインデントするか。 { 0: 半角空白のみ 1: インデントはタブ、位置揃えは半角空白 2: インデントも位置揃えもタブ (タブストップ以外は半角空白) } indent_with_tabs = 0 # コードのインデントレベルでないコメントをタブ位置までタブ文字でインデントするか。falseの場合、半角空白のみ。 (indent_with_tabs=2の場合のみ) { true, false } indent_cmt_with_tabs = false # '\'-改行でされた文字列を位置揃えさせるかどうか。これ、いろいろ試したけど効かないな。 { true, false } indent_align_string = true # '{'をレベルからインデントさせる半角空白数 { number } indent_brace = 0 # ボディレベルからブレースをインデントさせるかどうか { true, false } indent_braces = false # 関数ブレースのインデントの無効化 (indent_bracesがtrueの時のみ) { true, false } indent_braces_no_func = true # クラスブレースのインデントの無効化 (indent_bracesがtrueの時のみ) { true, false } indent_braces_no_class = true # 構造体ブレースのインデントの無効化 (indent_bracesがtrueの時のみ) { true, false } indent_braces_no_struct = true # ブレースオーナーの文字数ベースでインデントする (例: 'if'なら空白3文字、'for'なら空白4文字) { true, false} indent_brace_parent = false # 'namespace'のボディをインデントさせるかどうか { true, false } indent_namespace = true # 名前空間ブロックをインデントさせる空白数 { number } 0=>ベースインデント幅に依存 indent_namespace_level = 0 # 名前空間のボディがこの数字よりも長い場合、インデントしない。 (indent_namespace=trueの場合のみ) (0: 無制限) { number } indent_namespace_limit = 0 # 'extern "C"'ボディをインデントさせるかどうか { true, false } indent_extern = true # 'class'ボディをインデントさせるかどうか { true, false } indent_class = true # クラス宣言の継承のコロンに続く文字をインデントさせるかどうか (indent_ctor_init_leading、indent_ctor_initと関連する) { true, false} indent_class_colon = true # コンストラクタのメンバーイニシャライザの先頭の':'の (仮想的な) インデント幅。 (indent_class_colon=trueの場合のみ) (デフォルト:2) { number } indent_ctor_init_leading = 4 # コンストラクタのメンバーイニシャライザの残りのリストに対する追加インデント (indent_class_colon=trueの場合のみ) { number } indent_ctor_init = 0 # 'else\nif'の'if'をインデントするかどうか { true, false } indent_else_if = false # '{'の後の行から始まる変数宣言のインデント幅の調整 (0: 調整なし、正値: カラム絶対値、負値: インデント相対値) { number } indent_var_def_blk = 0 # 複数行に続く変数宣言を位置揃え (align) せずインデントする { true, false } indent_var_def_cont = false # 関数定義のインデントを行頭から強制的に開始するかどうか { true, false } indent_func_def_force_col1 = false # 複数行に続く関数コールの引数リストをインデントするか、位置揃えするか { true, false } indent_func_call_param = false # 複数行に続く関数定義の引数リストをインデントするか、位置揃えするか { true, false } indent_func_def_param = false # 複数行に続くプロトタイプ宣言の引数リストをインデントするか、位置揃えするか { true, false } indent_func_proto_param = false # 複数行に続くクラス宣言内のコンストラクタの引数リストをインデントするか、位置揃えするか { true, false } indent_func_class_param = false # 複数行に続くクラスコンストラクタコールの引数リストをインデントするか、位置揃えするか { true, false } indent_func_ctor_var_param = false # 複数行に続くテンプレートクラス宣言の引数リストをインデントするか、位置揃えするか { true, false } indent_template_param = false # indent_func_xxx_paramオプションのインデント幅を2倍にする { true, false } indent_func_param_double = false # 関数宣言/プロトタイプの単独でぶら下がる'const'修飾子の絶対カラム位置 { number } indent_func_const = 0 # 関数宣言/プロトタイプの単独でぶら下がる'throw'修飾子の絶対カラム位置 { number } indent_func_throw = 0 # '->'もしくは'.'に続く位置揃えの半角空白数 (通常は0) { number } indent_member = 0 # コード直前にある、1行以上の一行コメント ('//') を位置揃えするための半角空白数 { number } indent_sing_line_comments = 0 # コードの後ろに続く一行コメント ('//') を相対的にインデントするかどうか { true, false } indent_relative_single_line_comments = false # 'switch'から'case'をインデントする半角空白数 (通常は、0か、indent_columnsで設定した値) { number } indent_switch_case = 4 # 'case'の中の他の行のインデントに影響を与えずに、'case'行だけを位置揃えするための半角空白数 (通常は0、インデント相対値) { number } indent_case_shift = 0 # 'case'の'{'をインデントする半角空白数 (通常は0、インデント相対値) { number } indent_case_brace = 0 # 行頭に見つけたコメントをインデントさせるかどうか { true, false } indent_col1_comment = true # gotoラベルのインデント方法 (正値: カラム絶対値、負値: インデント相対値) { number } indent_label = 1 # コロンが続くアクセス指定子の場合 (正値: カラム絶対値、負値: インデント相対値) { number } indent_access_spec = -4 # アクセス指定子の後のコードを1レベルインデントするかどうか (indent_access_specは0に強制的に上書きされる) { true, false } indent_access_spec_body = false # '('に続けて改行されている時、次行からは'('の後に整列するよう位置揃えする (非推奨) { true, false } indent_paren_nl = false # 改行後の')'のインデント方法 { 0: ボディレベルにインデントする 1: '('の位置に揃える 2: 直前の'{'レベルにインデントする } indent_paren_close = 0 # '()'内の改行直後の','のインデント方法 (trueの場合、'('の直下に位置揃えされる) { true, false } indent_comma_paren = false # '()'内の改行直後のブール演算子のインデント方法 (trueの場合、'('の直下に位置揃えされる) { true, false } indent_bool_paren = false # indent_bool_paren=true時の、最初の評価式のインデント方法 (trueなら次の行以降と位置揃えする) { true, false } indent_first_bool_expr = false # '['に続けて改行されている時、次行からは'['の後に整列するよう位置揃えする (非推奨) { true, false } indent_square_nl = false # ESQL/Cの'EXEC SQL'ボディの相対インデントを変更しない { true, false } indent_preserve_sql = false # '='に続くステートメントをインデントするか、位置揃えするか (falseもしくは'='直後に改行場合、インデントする) { true, false } indent_align_assign = true # Objective-Cブロックを通常ルールの代わりブレースレベルまでインデントするかどうか { true, false } indent_oc_block = false # メッセージ内のObjective-Cブロックをパラメータ名に相対的にインデントする (0: indent_oc_blockルール >0: インデント半角空白数) { number } indent_oc_block_msg = 0 # 継続するパラメータの最小インデント数 {number} indent_oc_msg_colon = 0 ### Alignment ## http://qiita.com/items/aecf5d72a55eef7ed6fe # インデントではないタブ文字を残すか { true, false } align_keep_tabs = false # 整列位置合わせにタブ文字を利用するか { true, false } align_with_tabs = false # 整列位置合わせで次のタブストップ位置にするか { true, false } align_on_tabstop = false # 数値を左揃えにするかどうか (align_struct_init_spanと関連) { true, false } align_number_left = false # プロトタイプ宣言と関数定義で、変数定義の位置を揃えるかどうか { true, false } align_func_params = true # 同じ名前を持つシングルライン関数のパラメータの位置を揃えるかどうか (関数名はすでにそれぞれ位置合わせされていなければならない。) { true, false } align_same_func_call_params = false # 変数定義でのポインタの'*'の位置揃えの方法 { 0: 型の一部、1: 変数名の一部、2: ダングリング } align_var_def_star_style = 2 # 変数定義での参照の'&'位置揃えの方法 { 0: 型の一部、1: 変数名の一部、2: ダングリング } align_var_def_amp_style = 2 # 構造体ビットフィールドの':'を位置揃えするかどうか { true, false } align_var_def_colon = true # 変数名の後の属性を位置揃えするかどうか { true, false } align_var_def_attribute = true # struct/enum/unionのメンバ変数定義を位置揃えするかどうか { true, false } align_var_def_inline = true # 変数定義の位置揃えに対するラインスパン (0: 揃えない) { number } align_var_def_span = 1 # 変数定義の位置揃えのカラム閾値 (0: 制限無し) { number } align_var_def_thresh = 0 # 変数定義の位置揃えのカラムギャップ { number } align_var_def_gap = 0 # 代入式の'='の位置揃えをするラインスパン (0: 揃えない) { number } align_assign_span = 1 # 代入式の'='の位置揃えをするカラム閾値 (0: 制限無し) { number } align_assign_thresh = 0 # enumの'='の位置揃えをするラインスパン (0: 揃えない) { number } align_enum_equ_span = 1 # enumの'='の位置揃えをするカラム閾値 (0: 制限無し) { number } align_enum_equ_thresh = 0 # struct/unionのメンバー定義の位置揃えをするラインスパン (0: 揃えない) { number } align_var_struct_span = 1 # struct/unionのメンバー定義の位置揃えをするカラム閾値 (0: 制限無し) { number } align_var_struct_thresh = 0 # struct/unionのメンバー定義のカラムギャップ { number } align_var_struct_gap = 0 # structのメンバ変数の指定初期化の位置揃えのラインスパン (0: 揃えない) { number } align_struct_init_span = 1 # typedefの型とシノニムの間の空白の最小数 { number } align_typedef_gap = 0 # 一行typedefの位置揃えをするラインスパン (0: 揃えない) { number } align_typedef_span = 1 # typedefされた関数とその他typedefの位置揃えの方法 { 0: ミックスして位置を揃えない、1: 開丸括弧と型の位置を揃える、2: 関数の型名とその他の型名の位置を揃える } align_typedef_func = 1 # typedefの''の位置揃えの方法 { 0: typedef typeで揃える、1: ''を型名の一部とする、2: '*'を型名の一部としてダングリングする } align_typedef_star_style = 2 # typedefの'&'の位置揃えの方法 { 0: typedef typeで揃える、1: '&'を型名の一部とする、2: '&'を型名の一部としてダングリングする } align_typedef_amp_style = 2 # 行末コメントを位置揃えするラインスパン (0: 揃えない) { number } align_right_cmt_span = 1 # If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment { true, false } align_right_cmt_mix = false # 行末コメントがこの桁数以上コードと離れている場合、位置揃えする (0: 揃えない) { number } align_right_cmt_gap = 1 # 行末コメントの位置揃えするカラム位置 (嬉しい副産物 'pulls in' コメント) (0: 揃えない) { number } align_right_cmt_at_col = 0 # プロトタイプ宣言の位置揃えのラインスパン (0: 揃えない) { number } align_func_proto_span = 2 # プロトタイプ宣言の返値型と関数名の間のギャップカラム { number } align_func_proto_gap = 0 # 'operator'キーワードでプロトタイプ宣言を位置揃えするかどうか { number } align_on_operator = true # プロトタイプと変数宣言のミックス整形するかどうか (trueの場合、align_var_def_XXXオプションがalign_func_proto_XXXオプションの代わりに使用される。) { true, false } align_mix_var_proto = false # 一行関数を関数プロトタイプと同様に位置揃えするかどうか (align_func_proto_spanを使用) { true, false } align_single_line_func = true # 一行関数のオープンブレースを位置揃えするかどうか (align_single_line_func=trueの場合のみ) (align_func_proto_spanを使用) { true, false } align_single_line_brace = true # align_single_line_braceに対するギャップ { number } align_single_line_brace_gap = 0 # バックスラッシュ-改行で囲われた複数行マクロを位置揃えするかどうか (ただし、マクロが複数行コメントを内包している場合、うまく動かない) { true, false } align_nl_cont = true # プリプロセッサ定義のボディの位置揃えのスパン (0: 揃えない) { number } align_pp_define_span = 1 # プリプロセッサ定義のラベルと値の間の空白の最小幅 { number } align_pp_define_gap = 0 # マクロ関数とマクロ変数を一緒に位置揃えするかどうか { true, false } align_pp_define_together = false # '<<'で開始する行を前の行の'<<'の位置に揃えるかどうか (デフォルト: true) { true, false } align_left_shift = true # Objective-Cのメッセージ仕様宣言のスパン (0: 揃えない) { number } align_oc_msg_spec_span = 0 # Objective-Cのメッセージコールにおけるパラメータ':'の位置揃えのスパン (0: 揃えない) { number } align_oc_msg_colon_span = 0 # trueの場合、最初のパラメータの位置に後続パラメータの位置を揃える (メッセージが短くとも) { true, false } align_oc_msg_colon_first = false # Objective-Cの'+'もしくは'-'宣言行のパラメータを':'で位置揃えするかどうか { true, false } align_oc_decl_colon = false ### Position ## http://qiita.com/items/ce7269fe4aa9f34e9a8d # 式が折り返す時の算術演算子の位置 { ignore, join, lead, lead_break, lead_force, trail, trail_break, trail_force } pos_arith = trail # 式が折り返す時の代入演算子の位置 '{'が続く'='には影響しない { ignore, join, lead, lead_break, lead_force, trail, trail_break, trail_force } pos_assign = trail # 式が折り返す時のブール演算子の位置 { ignore, join, lead, lead_break, lead_force, trail, trail_break, trail_force } pos_bool = trail # 式が折り返す時の比較演算子の位置 { ignore, join, lead, lead_break, lead_force, trail, trail_break, trail_force } pos_compare = trail # 式が折り返す時の三項演算子 (b ? t : f) の位置 { ignore, join, lead, lead_break, lead_force, trail, trail_break, trail_force } pos_conditional = ignore # 式が折り返す時の','の位置 { ignore, join, lead, lead_break, lead_force, trail, trail_break, trail_force } pos_comma = trail # コンストラクタのメンバ初期化リストにおける','の位置 { ignore, join, lead, lead_break, lead_force, trail, trail_break, trail_force } pos_class_comma = lead # コンストラクタとメンバ初期化の間の':'の位置 { ignore, join, lead, lead_break, lead_force, trail, trail_break, trail_force } pos_class_colon = lead ### Break line ## http://qiita.com/items/ee95487e79a2ca7fb375 # 連続してもよい改行数。 (0: 制限なし) { number } nl_max = 0 # 関数プロトタイプ宣言の後の改行数 (別のプロトタイプ宣言が続く場合) (0: 変更なし) { number } nl_after_func_proto = 1 # 関数プロトタイプ宣言の後の改行数 (別のプロトタイプ宣言が続かない倍) { number } nl_after_func_proto_group = 2 # 複数行の関数ボディの'}'の後の改行数 { number } nl_after_func_body = 2 # クラス宣言内の複数行の関数ボディの'}'の後の改行数 { number } nl_after_func_body_class = 2 # 一行関数ボディの'}'の後の改行数 { number } nl_after_func_body_one_liner = 1 # 複数行コメントの前の最小改行数 ('{'もしくは別の複数行コメントの後の場合、適用されない。) { number } nl_before_block_comment = 0 # 1行Cコメント ('/* ... */') の前の最小改行数 '{'もしくは別の1行Cコメントの後の場合、適用されない。 { number } nl_before_c_comment = 0 # C++コメント ('// ...') の前の最小改行数 '{'もしくは別のC++コメントの後の場合、適用されない。 { number } nl_before_cpp_comment = 0 # 複数行コメントの後に改行を強制するかどうか { false, true } nl_after_multiline_comment = true # struct/enum/unionの定義の'}'もしくは';'の後の改行数 { number } nl_after_struct = 2 # class定義の'}'もしくは';'の後の改行数 { number } nl_after_class = 2 # 'private:'、'public:'、'protected:'、'signals:'、'slots:'ラベルの前の改行数 '{'の後の場合、改行数は変更されない。 (0: 変更無し) { number } nl_before_access_spec = 1 # 'private:'、'public:'、'protected:'、'signals:'、'slots:'ラベルの後の改行数 (0: 変更無し) { number } nl_after_access_spec = 1 # 関数定義と関数コメントの間の改行数 (0: 変更無し) { number } nl_comment_func_def = 1 # '}'が続かないtry-catch-finallyブロックの後の改行数 (0: 変更無し) { number } nl_after_try_catch_finally = 2 # プロパティ、インデクサ、イベントの定義の前後の改行数 (0: 変更無し) { number } nl_around_cs_property = 0 # get/set/add/removeハンドラの間の改行数 (C#言語) (0: 変更無し) { number } nl_between_get_set = 0 # プロパティと'{'の間に改行を挿入/削除する (C#言語) { ignore, add, remove, force } nl_property_brace = ignore # '{'の後の改行を削除するかどうか { false, true } eat_blanks_after_open_brace = true # '}'の前の改行を削除するかどうか { false, true } eat_blanks_before_close_brace = true # プリプロセッサ以外の改行をどのぐらい積極的に削除するか 0: 変更しない 1: 他の設定でハンドルされない改行を可能な限り削除する 2: 全ての改行を一度削除して、設定から完全に整形しなおす { number } nl_remove_extra_newlines = 0 # 'return'文の前に改行を挿入するかどうか ('{'直後の'return'以外) { false, true } nl_before_return = true # 'return'文の後に改行を挿入するかどうか ('}'が直後にある'return'以外) { false, true } nl_after_return = false # Java注釈文の後に改行を挿入するかどうか 改行後の注釈文にのみ適用される { ignore, add, remove, force } nl_after_annotation = ignore # 2つの注釈文の間に改行を挿入するかどうか { ignore, add, remove, force } nl_between_annotation = ignore # 関数宣言の')'と'{'の間に改行を挿入するかどうか { ignore, add, remove, force } nl_fdef_brace = force ### Code transformation ## http://qiita.com/items/d1289c116451b709f610 # 単文の'do'/'while'文に'{}'を追加/削除する { ignore, add, remove, force } mod_full_brace_do = add # 単文の'for'文に'{}'を追加/削除する { ignore, add, remove, force } mod_full_brace_for = add # 単文の関数定義に'{}'を追加/削除する (Pawn) { ignore, add, remove, force } mod_full_brace_function = ignore # 単文の'if'文に'{}'を追加/削除する 'else'を含む場合、'{}'は削除されない。 { ignore, add, remove, force } mod_full_brace_if = add # 全ての'if'/'elseif'/'else'文のブロックを'{}'で囲うかどうか (1つでも'{}'で囲う必要があれば、全て'{}'で囲われ、全てが囲う必要がなければ、'{}'は削除される) { false, true } (mod_full_brace_ifを上書き) mod_full_brace_if_chain = false # mod_full_brace_xxxのボディがN行より長い場合、'{}'は削除しない { number } mod_full_brace_nl = 0 # 単文の'while'文に'{}'を追加/削除する { ignore, add, remove, force } mod_full_brace_while = add # 単文の'using'文に'{}'を追加/削除する (C#言語) { ignore, add, remove, force } mod_full_brace_using = ignore # 'return'文の不要な'()'を追加/削除する { ignore, add, remove, force } mod_paren_on_return = remove # オプショナルなセミコロンをリアルなセミコロンに修正するかどうか (Pawn) { false, true } mod_pawn_semicolon = false # 'while'と'if'文のブール値の前後に括弧を追加する { false, true } mod_full_paren_if_bool = false # 余分なセミコロンを削除する { false, true } mod_remove_extra_semicolon = true # 関数ボディの行数が指定数を超えるような長い関数の場合、'}'の後にコメントが付く { number } mod_add_long_function_closebrace_comment = 0 # 'switch'文のボディの行数が指定数を超えるような長いボディの場合、'}'の後にコメントが付く { number } mod_add_long_switch_closebrace_comment = 0 # #ifdefのボディの行数が指定数を超えるような長いボディの場合、#endifの後にコメントが付く { number } mod_add_long_ifdef_endif_comment = 0 # #ifdefもしくは#elseのボディが指定数を超えるような長いボディの場合、#elseの後にコメントが付く { number } mod_add_long_ifdef_else_comment = 0 # 連続する1行の'import'文をソートするかどうか (Java言語、D言語) { false, true } mod_sort_import = false # 連続する1行の'using'文をソートするかどうか (C#言語) { false, true } mod_sort_using = false # 連続する1行の'#include'文もしくは'#import'文をソートするかどうか (Objective-C言語) コードを破壊する可能性があるので、一般的にソートしなおすのは良くない { false, true } mod_sort_include = false # '{}'で囲われた'case'の後にある'break'文を'}'の内側に移動する { false, true } mod_move_case_break = true # '{}'で囲われた'case'文の前後に'{}'を追加/削除する もし、ブロック内に変数宣言がない場合に限り、'{}'を削除できる { ignore, add, remove, force } mod_case_brace = ignore # 関数の最後の文に現れる、'return;'を削除するかどうか { false, true } mod_remove_empty_return = true ### Comment transformation ## http://qiita.com/items/cfeda8a49cf8efe48406 # コメントを一定の幅で折り返す (桁数) { number } cmt_width = 240 # コメントリフロー (テキストの流し込み、再配置) (0: 折り返しのみ (リフローしない) 1: リフローも折り返しもしない 2: フルリフロー) (0と2の差がわからない) { number } cmt_reflow_mode = 1 # 複数行コメントの変更 (cmd_width、キーワード置換、先行文字列など含む) を無効化する { false, true } cmt_indent_multi = false # ブロックになっているCコメント ('/* ... */') をグループ化するかどうか { false, true } cmt_c_group = false # 結合したCコメントの最初の行に、'/*'単体を付けるかどうか { false, true } cmt_c_nl_start = false # 結合したCコメントの最後の行に、'*/'単体を付けるかどうか { false, true } cmt_c_nl_end = false # C++コメントをCコメントに変更するかどうか { false, true } cmt_cpp_to_c = false # ブロックになっているC++コメント ('// ....') をグループ化するかどうか (cmt_cpp_to_cがtrueの場合のみ) { false, true } cmt_cpp_group = false # 結合したC++コメント ('// ....') の最初の行に、'/*'単体を付けるかどうか (cmt_cpp_to_cがtrueの場合のみ) { false, true } cmt_cpp_nl_start = false # 結合したC++コメント ('// ....') の最後の行に、'*/'単体を付けるかどうか (cmt_cpp_to_cがtrueの場合のみ) { false, true } cmt_cpp_nl_end = false # コメント行にスター '*' を続けて付けるかどうか { false, true } cmt_star_cont = true # 連続するコメント行の開始位置と'*'の間に挿入する空白数 { number } cmt_sp_before_star_cont = 0 # 連続するコメント行の'*'とコメントの間に挿入する空白数 { number } cmt_sp_after_star_cont = 0 # '*'で始める複数行コメントに対し、最初と最後のコメント行が同じ長さの場合に、先頭にある空白を削除するかどうか (デフォルト: true) { false, true } cmt_multi_check_last = true ### Line split ## http://qiita.com/items/20a8143322e16d969ca8 # コード幅の制限桁数 { number } code_width = 240 # 長い'for'文を';'の位置でスプリットするかどうか { false, true } ls_for_split_full = true # 長い関数のプロトタイプ宣言/呼び出しを','の位置でスプリットするかどうか { false, true } ls_func_split_full = true # code_widthでスプリットするかどうか (だいたいcode_widthで少しはみ出す場合あり) { false, true } ls_code_width = true ### Spacing ## http://qiita.com/items/7b6564cebc60e26701f2 # 算術演算子 '+'、'-'、'/'、'*'等の前後に空白を挿入/削除する { ignore, add, remove, force } sp_arith = add # 代入演算子 '='、'+='等の前後に空白を挿入/削除する { ignore, add, remove, force } sp_assign = add # C++11ラムダ式の外部変数キャプチャの'='演算子の前後に空白を挿入/削除する (sp_assignを上書き) { ignore, add, remove, force } sp_cpp_lambda_assign = ignore # C++11ラムダ式の変数キャプチャの']'と引数リストの'('の間に空白を挿入/削除する { ignore, add, remove, force } sp_cpp_lambda_paren = remove # プロトタイプ宣言の'='の代入演算子の前後に空白を挿入/削除する { ignore, add, remove, force } sp_assign_default = remove # '='、'+='等の代入演算子の前に空白を挿入/削除する (sp_assignを上書き) { ignore, add, remove, force } sp_before_assign =add # '=', '+='等の代入演算子の後に空白を挿入/削除する (sp_assignを上書き) { ignore, add, remove, force } sp_after_assign = add # enumの'='代入演算子の前後に空白を挿入/削除する { ignore, add, remove, force } sp_enum_assign = add # enumの'='代入演算子の前に空白を挿入/削除する (sp_enum_assignを上書き) { ignore, add, remove, force } sp_enum_before_assign = add # enumの'='代入演算子の後に空白を挿入/削除する (sp_enum_assignを上書き) { ignore, add, remove, force } sp_enum_after_assign = add # プリプロセッサの'##'連結演算子の前後に空白を挿入/削除する (デフォルト: add) { ignore, add, remove, force } sp_pp_concat = add # プリプロセッサの'#'文字列化演算子の後に空白を挿入/削除する ('#@'文字化演算子にも適用される) { ignore, add, remove, force } sp_pp_stringify = remove # プリプロセッサの'#'文字列化演算子の前に空白を挿入/削除する (例: '#define x(y) L#y') { ignore, add, remove, force } sp_before_pp_stringify = ignore # '&&'と'||'の前後に空白を挿入/削除する { ignore, add, remove, force } sp_bool = add # '<'、'>'、'=='等の比較演算子の前後に空白を挿入/削除する { ignore, add, remove, force } sp_compare = add # '('と')'の内側に空白を挿入/削除する { ignore, add, remove, force } sp_inside_paren = remove # 入れ子の括弧の間に空白を挿入/削除する { ignore, add, remove, force } sp_paren_paren = ignore # 入れ子の括弧の内側の空白の数を同じにするか、しないか { true, false } sp_balance_nested_parens = false # ')'と'{'の間に空白を挿入/削除する { ignore, add, remove, force } sp_paren_brace = ignore # ポインタの'*'の前に空白を挿入/削除する { ignore, add, remove, force } sp_before_ptr_star = add # 変数名が後に続かないポインタの'*'の前に空白を挿入/削除する 'ignore'の場合、sp_before_ptr_starが代わりに使用される。 { ignore, add, remove, force } sp_before_unnamed_ptr_star = add # ポインタの''と''の間に空白を挿入/削除する { ignore, add, remove, force } sp_between_ptr_star = remove # 単語が続く場合、ポインタの'*'の後に空白を挿入/削除する { ignore, add, remove, force } sp_after_ptr_star = remove # 関数のプロトタイプ宣言や定義が続く場合、ポインタの'*'の後に空白を挿入/削除する { ignore, add, remove, force } sp_after_ptr_star_func = remove # (関数型の)開丸括弧が続く場合、ポインタの'*'の後に空白を挿入/削除する { ignore, add, remove, force } sp_ptr_star_paren = remove # 関数のプロトタイプ宣言や定義が続く場合、ポインタの'*'の前に空白を挿入/削除する { ignore, add, remove, force } sp_before_ptr_star_func = add # 参照の'&'の前に空白を挿入/削除する { ignore, add, remove, force } sp_before_byref = add # 変数名が後に続かない参照の'&'の前に空白を挿入/削除する 'ignore'の場合、spbefore_byrefが代わりに使用される。 { ignore, add, remove, force } sp_before_unnamed_byref = remove # 単語が続く場合、参照の'&'の後に空白を挿入/削除する { ignore, add, remove, force } sp_after_byref = remove # 関数のプロトタイプ宣言や定義が続く場合、参照の'&'の後に空白を挿入/削除する { ignore, add, remove, force } sp_after_byref_func = remove # 関数のプロトタイプ宣言や定義が続く場合、参照の'&'の前に空白を挿入/削除する { ignore, add, remove, force } sp_before_byref_func = add # 型と単語の間に空白を挿入/削除する (デフォルト: force) { ignore, add, remove, force } sp_after_type = force # D言語のテンプレート ('template Foo('と'class Foo(') において、丸括弧の前に空白を挿入/削除する { number } sp_before_template_paren = 0 # 'template'と'<'の間に空白を挿入/削除する ignoreの場合、sp_before_angleが使用される。 { ignore, add, remove, force } sp_template_angle = force # '<>'の前に空白を挿入/削除する { ignore, add, remove, force } sp_before_angle = add # '<'と'>'の内側に空白を挿入/削除する { ignore, add, remove, force } sp_inside_angle = remove # '<>'の後に空白を挿入/削除する { ignore, add, remove, force } sp_after_angle = add # 'new List();'のように、'<>'と'('の間に空白を挿入/削除する { ignore, add, remove, force } sp_angle_paren = remove # 'List m;'のように、'<>'と単語の間に空白を挿入/削除する { ignore, add, remove, force } sp_angle_word = add # '>>'の'>'と'>'の間に空白を挿入/削除する (C++/C#のテンプレート関連のみ) (デフォルト: add) { ignore, add, remove, force } sp_angle_shift = add # 'foo >'の'> >'の間の空白の削除を許可する (C++11のみ) (デフォルト: false) このオプションなしで、sp_angle_shiftは空白を削除できない。 { true, false } sp_permit_cpp11_shift = false # 'if'、'for'、'switch'、'while'の'('の前に空白を挿入/削除する { ignore, add, remove, force } sp_before_sparen = add # if条件の'('と')'の内側に空白を挿入/削除する { ignore, add, remove, force } sp_inside_sparen = remove # if条件の')'の前に空白を挿入/削除する (sp_inside_sparenを上書き) { ignore, add, remove, force } sp_inside_sparen_close = remove # if条件の'('の後に空白を挿入/削除する (sp_inside_sparenを上書き) { ignore, add, remove, force } sp_inside_sparen_open = remove # 'if'、'for'、'switch'、'while'の')'の後に空白を挿入/削除する { ignore, add, remove, force } sp_after_sparen = add # 'if'、'for'、'switch'、'while'の')'と'{'の間に空白を挿入/削除する { ignore, add, remove, force } sp_sparen_brace = add # 'invariant'と'('の間に空白を追加/削除する (D言語) { ignore, add, remove, force } sp_invariant_paren = ignore # 'invariant (C) c'の')'の後に空白を追加/削除する (D言語) { ignore, add, remove, force } sp_after_invariant_paren = ignore # 'if'、'for'、'while'と同じ行の';'のみの空のステートメントの前に空白を挿入/削除する { ignore, add, remove, force } sp_special_semi = add # ';'の前に空白を挿入/削除する (デフォルト: remove) { ignore, add, remove, force } sp_before_semi = remove # 空でない'for'文の';'の前に空白を挿入/削除する { ignore, add, remove, force } sp_before_semi_for = remove # 'for'文の空のパートの';'の前に空白を挿入/削除する { ignore, add, remove, force } sp_before_semi_for_empty = ignore # コメントが後に続く場合を除いて、';'の後の空白を挿入/削除する (デフォルト: add) { ignore, add, remove, force } sp_after_semi = add # 空でない'for'文の';'の後に空白を挿入/削除する (デフォルト: force) { ignore, add, remove, force } sp_after_semi_for = add # 'for'文の空のパートの最後のセミコロンの後に空白を挿入/削除する (for ( ; ; )) { ignore, add, remove, force } sp_after_semi_for_empty = ignore # '['の前に空白を挿入/削除する ('[]'以外) { ignore, add, remove, force } sp_before_square = remove # '[]'の前に空白を挿入/削除する { ignore, add, remove, force } sp_before_squares = ignore # 空でない'['と']'の内側に空白を挿入/削除する { ignore, add, remove, force } sp_inside_square = ignore # ','の後に空白を挿入/削除する { ignore, add, remove, force } sp_after_comma = add # ','の前に空白を挿入/削除する { ignore, add, remove, force } sp_before_comma = remove # 開丸括弧とカンマの間に空白を挿入/削除する ('(,'と'( ,') { ignore, add, remove, force } sp_paren_comma = ignore # 可変パラメータの'...'の前に空白を挿入/削除する (非句点が先行している時) { ignore, add, remove, force } sp_before_ellipsis = add # 'class'の':'の後に空白を挿入/削除する { ignore, add, remove, force } # sp_after_class_colon = ignore # 'class'の':'の前に空白を挿入/削除する { ignore, add, remove, force } # sp_before_class_colon = ignore # 'case'の':'の前に空白を挿入/削除する (デフォルト: remove) { ignore, add, remove, force } sp_before_case_colon = remove # 'operator'と演算子記号の間に空白を挿入/削除する { ignore, add, remove, force } sp_after_operator = remove # オペレータシンボルと開丸括弧の間に空白を挿入/削除する (operator ++()) { ignore, add, remove, force } sp_after_operator_sym = remove # キャストの後に空白を挿入/削除する (C言語/D言語) ('cast(int)a' vs 'cast(int) a'、'(int)a' vs '(int) a') { ignore, add, remove, force } sp_after_cast = add # キャストの括弧の内側に空白を挿入/削除する { ignore, add, remove, force } sp_inside_paren_cast = remove # キャストの型と開丸括弧の間に空白を挿入/削除する (C++言語) ('int(exp)' vs 'int (exp)') { ignore, add, remove, force } sp_cpp_cast_paren = remove # 'sizeof'と'('の間に空白を挿入/削除する { ignore, add, remove, force } sp_sizeof_paren = remove # タグキーワードの後に空白を挿入/削除する (Pawn) { ignore, add, remove, force } sp_after_tag = ignore # enumの'{'と'}'の内側に空白を挿入/削除する { ignore, add, remove, force } sp_inside_braces_enum = add # struct/unionの'{'と'}'の内側に空白を挿入/削除する { ignore, add, remove, force } sp_inside_braces_struct = add # '{'と'}'の内側に空白を挿入/削除する { ignore, add, remove, force } sp_inside_braces = add # '{}'の内側に空白を挿入/削除する { ignore, add, remove, force } sp_inside_braces_empty = add # 戻り値の型と関数名の間に空白を挿入/削除する ポインタ型の戻り値を除いて、最低でも空白1文字が強制的に挿入される。 { ignore, add, remove, force } sp_type_func = add # 関数宣言の関数名と'('の間に空白を挿入/削除する { ignore, add, remove, force } sp_func_proto_paren = remove # 関数定義の関数名と'('の間に空白を挿入/削除する { ignore, add, remove, force } sp_func_def_paren = remove # 空の関数'()'の内側に空白を挿入/削除する { ignore, add, remove, force } sp_inside_fparens = remove # 関数の'('と')'の内側に空白を挿入/削除する { ignore, add, remove, force } sp_inside_fparen = remove # 関数タイプの最初の括弧の内側に空白を挿入/削除する ('void (*x)(...)') { ignore, add, remove, force } sp_inside_tparen = remove # 関数タイプの括弧どうしの間に空白を挿入/削除する ('void (*x)(...)') { ignore, add, remove, force } sp_after_tparen_close = remove # 関数コールパートで、']'と'('の間に空白を挿入/削除する { ignore, add, remove, force } sp_square_fparen = remove # 関数の')'と'{'の間に空白を挿入/削除する { ignore, add, remove, force } sp_fparen_brace = add # 関数コールで関数名と'('の間に空白を挿入/削除する { ignore, add, remove, force } sp_func_call_paren = remove # パラメータなしの関数コールで関数名と'()'の間に空白を挿入/削除する 'ignore'の場合、sp_func_call_parenが使用される。 { ignore, add, remove, force } sp_func_call_paren_empty = remove # 関数コールでユーザ関数名と'('の間に空白を挿入/削除する 設定ファイルの中に'set func_call_user _'のように、ユーザ関数であることであるキーワードを設定する必要がある。 { ignore, add, remove, force } sp_func_call_user_paren = remove # コンストラクタ/デストラクタと開丸括弧の間に空白を挿入/削除する { ignore, add, remove, force } sp_func_class_paren = remove # 'return'と'('の間に空白を挿入/削除する { ignore, add, remove, force } sp_return_paren = remove # 'attribute'と'('の間に空白を挿入/削除する { ignore, add, remove, force } sp_attribute_paren = remove # '#if defined (FOO)'の'defined'と'('の間に空白を挿入/削除する { ignore, add, remove, force } sp_defined_paren = remove # 'throw'と'('の間に空白を挿入/削除する ('throw (something)') { ignore, add, remove, force } sp_throw_paren = add # 'throw'と'('以外の文字の間に空白を挿入/削除する ('@throw [...]') { ignore, add, remove, force } sp_after_throw = add # 'catch'と'('の間に空白を挿入/削除する ('catch (something) { }') ignoreの場合、sp_before_sparenが使用される。 { ignore, add, remove, force } sp_catch_paren = add # 'version'と'('の間に空白を挿入/削除する ('version (something) { }') (D言語) ignoreの場合、sp_before_sparenが使用される。 { ignore, add, remove, force } sp_version_paren = ignore # 'scope'と'('の間に空白を挿入/削除する ('scope (something) { }') (D言語) ignoreの場合、sp_before_sparenが使用される。 { ignore, add, remove, force } sp_scope_paren = ignore # マクロと値の間に空白を挿入/削除する { ignore, add, remove, force } sp_macro = add # マクロ関数の')'と値の間に空白を挿入/削除する { ignore, add, remove, force } sp_macro_func = add # 一行に'else'と'{'がある場合、間に空白を挿入/削除する { ignore, add, remove, force } sp_else_brace = add # 一行に'}'と'else'がある場合、間に空白を挿入/削除する { ignore, add, remove, force } sp_brace_else = add # 一行に'}'とtypedefの型名がある場合、間に空白を挿入/削除する { ignore, add, remove, force } sp_brace_typedef = add # 一行に'catch'と'{'がある場合、間に空白を挿入/削除する { ignore, add, remove, force } sp_catch_brace = add # 一行に'}'と'catch'がある場合、間に空白を挿入/削除する { ignore, add, remove, force } sp_brace_catch = add # 一行に'finally'と'{'がある場合、間に空白を挿入/削除する { ignore, add, remove, force } sp_finally_brace = add # 一行に'}'と'finally'がある場合、間に空白を挿入/削除する { ignore, add, remove, force } sp_brace_finally = add # 一行に'try'と'{'がある場合、間に空白を挿入/削除する { ignore, add, remove, force } sp_try_brace = add # 一行に'get'/'set'と'{'がある場合、間に空白を挿入/削除する { false, true } sp_getset_brace = add # '::'演算子の前に空白を挿入/削除する { ignore, add, remove, force } sp_before_dc = remove # '::'演算子の後に空白を挿入/削除する { ignore, add, remove, force } sp_after_dc = remove # 名前付き配列イニシャライザ':'の前後に空白を挿入/削除する (D言語) { ignore, add, remove, force } sp_d_array_colon = ignore # '!' (not) 演算子の後に空白を挿入/削除する (デフォルト: remove) { ignore, add, remove, force } sp_not = remove # '~' (invert) 演算子の後に空白を挿入/削除する (デフォルト: remove) { ignore, add, remove, force } sp_inv = remove # アドレス演算子'&'の後に空白を挿入/削除する (デフォルト: remove) 参照型の一部である'&'の後のスペーシングには影響しない。 { ignore, add, remove, force } sp_addr = remove # '.'や'->'演算子の前後に空白を挿入/削除する (デフォルト: remove) { ignore, add, remove, force } sp_member = remove # '' (dereference) 演算子の後に空白を挿入/削除する (デフォルト: remove) ポインタ型の一部である''の後のスペーシングには影響しない。 { ignore, add, remove, force } sp_deref = remove # 単項演算子'+'、'-'の後に空白を挿入/削除する ('x = -5'、'y = +7') (デフォルト: remove) { ignore, add, remove, force } sp_sign = remove # 単項演算子'++'、'--'の後に空白を挿入/削除する ('(--x)'、'y++;') (デフォルト: remove) { ignore, add, remove, force } sp_incdec = remove # 行末のバックスラッシュ('\')-改行の前に空白を挿入/削除する (デフォルト: add) { ignore, add, remove, force } sp_before_nl_cont = add # スコープ定義'+'、'-'の後に空白を挿入/削除する ('-(void) foo;'、'+(int) bar;') (Objective-C言語) { ignore, add, remove, force } sp_after_oc_scope = add # メッセージ仕様宣言の':'の後に空白を挿入/削除する (Objective-C言語) ('-(int) f:(int) x;' vs '-(int) f: (int) x;') { ignore, add, remove, force } sp_after_oc_colon = remove # メッセージ仕様宣言の':'の前に空白を挿入/削除する (Objective-C言語) ('-(int) f: (int) x;' vs '-(int) f : (int) x;') { ignore, add, remove, force } sp_before_oc_colon = remove # 不変ディクショナリ式における':'の後に空白を挿入/削除する (Objective-C言語) ('NSDictionary *test = @{@"foo" :@"bar"};') { ignore, add, remove, force } sp_after_oc_dict_colon = add # 不変ディクショナリ式における':'の前に空白を挿入/削除する (Objective-C言語) ('NSDictionary *test = @{@"foo" :@"bar"};') { ignore, add, remove, force } sp_before_oc_dict_colon = remove # メッセージ送信式の':'の後に空白を挿入/削除する (Objective-C言語) ('[object setValue:1];' vs '[object setValue: 1];') { ignore, add, remove, force } sp_after_send_oc_colon = remove # メッセージ送信式の':'の前に空白を挿入/削除する (Objective-C言語) ('[object setValue:1];' vs '[object setValue :1];') { ignore, add, remove, force } sp_before_send_oc_colon = remove # メッセージ仕様宣言の(type)の後に空白を挿入/削除する (Objective-C言語) ('-(int)f: (int) x;' vs '-(int)f: (int)x;') { ignore, add, remove, force } sp_after_oc_type = remove # メッセージ仕様宣言の最初の(type)の後に空白を挿入/削除する (Objective-C言語) ('-(int) f:(int)x;' vs '-(int)f:(int)x;') { ignore, add, remove, force } sp_after_oc_return_type = remove # '@selector'と'('の間に空白を挿入/削除する (Objective-C言語) ('@selector(msgName)' vs '@selector (msgName)') 同時に@protocol()ブロックにも提供される。 { ignore, add, remove, force } sp_after_oc_at_sel = remove # '@selector(x)'と続く単語の間に空白を挿入/削除する (Objective-C言語) ('@selector(foo) a:' vs '@selector(foo)a:') { ignore, add, remove, force } sp_after_oc_at_sel_parens = add # '@selector'の丸括弧の内側に空白を挿入/削除する (Objective-C言語) ('@selector(foo)' vs '@selector( foo )') Also applies to @protocol() constructs { false, true } sp_inside_oc_at_sel_parens = remove # ブロックポインタのキャレットの前に空白を挿入/削除する (Objective-C言語) ('^int (int arg){...}' vs. ' ^int (int arg){...}') { ignore, add, remove, force } sp_before_oc_block_caret = remove # ブロックポインタのキャレットの後に空白を挿入/削除する (Objective-C言語) ('^int (int arg){...}' vs. '^ int (int arg){...}') { ignore, add, remove, force } sp_after_oc_block_caret = remove # メッセージ内のレシーバとセレクタの間に空白を挿入/削除する (Objective-C言語) { ignore, add, remove, force } sp_after_oc_msg_receiver = add # @propertyの後に空白を挿入/削除する (Objective-C言語) { ignore, add, remove, force } sp_after_oc_property = add # 三項演算子の':'の前後に空白を挿入/削除する { ignore, add, remove, force } sp_cond_colon = add # 三項演算子の'?'の前後に空白を挿入/削除する { ignore, add, remove, force } sp_cond_question = add # 'case'とケースラベルの間のスペーシングを固定する ('ignore'と'force'のみ有効) { ignore, add, remove, force } sp_case_label = add # '..'演算子の前後に空白を挿入/削除する (D言語) { ignore, add, remove, force } sp_range = ignore # 'for'分の':'の後に空白を挿入/削除する ('for (TYPE VAR : EXPR)') (Java言語) { ignore, add, remove, force } sp_after_for_colon = add # 'for'分の':'の前に空白を挿入/削除する ('for (TYPE VAR : EXPR)') (Java言語) { ignore, add, remove, force } sp_before_for_colon = add # 'extern (C)'の'extern'と'('の間に空白を挿入/削除する (D言語) { ignore, add, remove, force } sp_extern_paren = ignore # C++コメントの開始の後に空白を挿入/削除する ('// A' vs '//A') { ignore, add, remove, force } sp_cmt_cpp_start = add # '#else'もしくは'#endif'とそれに続くコメントの間に空白を挿入/削除する { ignore, add, remove, force } sp_endif_cmt = add # 'new'、'delete'、'delete[]'の後に空白を挿入/削除する { ignore, add, remove, force } sp_after_new = add # 行末コメントもしくは文中コメントの前に空白を挿入/削除する { ignore, add, remove, force } sp_before_tr_emb_cmt = add # 行末コメントもしくは文中コメントの前に挿入する空白の数 { ignore, add, remove, force } sp_num_before_tr_emb_cmt = 0 # Javaアノテーションと開丸括弧の間に空白を挿入/削除する (Java言語) { ignore, add, remove, force } sp_annotation_paren = ignore ``` |
|
| 509位 |
|
|||
|
18:48:00 |
|
|
APNS(Apple Push Notification Service)を使ってプッシュ通知がうまく送信できないとき、エラーをみても情報が少なく解決するのに頭を悩ませることが多々あるかと思います。
その際に、陥りやすいミスを列挙しました。送信できない場合は以下の点をチェックしてみてください。 ## 1.証明書は正確に生成できているか APNS用の証明書を作る際、コマンドラインで複数のコマンドを実行することによって証明書を生成するため、間違ったコマンドを入力していると正しい証明書が生成されません。 筆者は以下のコマンドで生成されることを確認していますので、記載しておきます。aps_production_ck.pemがAPNSでプッシュ通知を送る際に必要な証明書です。(productionは本番環境/developmentはテスト環境) - aps_production_identity.cert ... iOS Dev Centerからダウンロードした証明書 - aps_production_key.p12 ... aps_production_identityをインストールしてキーチェーンストアから書きだした鍵 ``` openssl x509 -in aps_production_identity.cer -inform der -out aps_production_cert.pem openssl pkcs12 -nocerts -out aps_production_key.pem -in aps_production_key.p12 cat aps_production_cert.pem aps_production_key.pem > aps_production_ck.pem ``` 証明書を生成するスクリプトを公開しました。 http://qiita.com/0x0c@github/items/0514e8b11f84fc0fde36 証明書を生成するアプリを公開しました。 https://itunes.apple.com/jp/app/pempem/id876280659?mt=12 ## 2.証明書のパスワードは間違っていないか これはサーバサイドでの話です。 証明書のパスワードが間違っている場合、送信ができませんのでパスワードをチェックしましょう。 ## 3.(Development環境の時)ProductionがEnabledになっていないか iOS Dev CenterのApp IDs画面からプッシュ通知を送ろうとしているアプリの状況を確認しましょう。Push Notificationの欄でProduction(本番環境)がEnabledになっている場合、Development(開発環境)の証明書を使ってもプッシュ通知を送ることができません。 ## 4.Provisioning fileを最新版にする iOS Dev CenterのApp IDsからPush NotificationをEnabledにしたあと、ローカルに保存されているProvisioning fileを最新版にしないと、アプリ側でプッシュ通知がONになりません。XcodeのWindow->OrganizerからProvisioning fileをRefreshしてください。 もしくは、ダウンロードしなおしてインストールし直してください。 ## 5.AdHocで配信しているアプリはPush通知を受信しない AdHocのProvisioning fileはDevelopment向けではないため、AdHocで配信しているアプリはPush通知を受診することができません。 なので、実機転送したアプリ出ないとPush通知のテストはできません。 TestFlightを使ってアプリを配信していると、陥る罠だと思います。 **プッシュ通知を送る際の証明書はProductionのものを使うと動く可能性があるようです。こちらの環境では確認できていません。** ## 6.Device Tokenの取得方法が間違っている iOS8からDevice Tokenの取得の方法が変わりました。 そのため、iOS7で動いていたコードでもiOS8では正しく動作しません。 新しいDevice Tokenの取得方法にコードを書き直す必要があります。 その際、iOS8とそれ以前とで条件分岐する必要があります。 Device Tokenを取得するライブラリを書きましたのでご活用ください。 https://github.com/0x0c/M2DPushNotificationManager ## 7.Device Tokenが混ざっていると飛ばない Development環境のtokenとProduction環境のtokenを混ぜて送信すると動かないようです。 環境を切り替える際はテーブルをトランケートするか、別々のテーブルを使用したほうが良さそうです。 (@hodadeさんより) ## 8.大量に送信するとTCPコネクションが切断される プッシュ通知を大量に送るとAppleのサーバがTCPコネクションを切断してくるため、再送制御が必要になります。 ## 9.証明書が失効していないか? 受託や他社との共同プロジェクトの場合、Developerアカウントへのアクセス権限がないことがあるかと思います。 その際、証明書の失効期限を確認する手段が限られ、気がついたら失効していた!なんてことが発生することがあります。 --------------------------------------- 間違っている情報があったら指摘していただけると幸いですm(_ _)m |
|
| 510位 |
|
|||
|
11:32:10 |
|
|
rvmからrbenvに切り替えてみたのでrbenvがどういう方法でRubyバージョンを指定したり、状況によって切り替えることができるのか確認してみた。基本 https://github.com/sstephenson/rbenv/ の要約および動作確認、バージョンは0.4.0。 ## shims ~/.rbenv/shimsにあるスクリプトが使うコマンド(irb, gem, rake, rails, rubyなど)を振り分けてくれる。shimsが振り分けた先を知るには```rbenv which```を使う。 ``` $ rbenv which ruby ~/.rbenv/versions/1.9.3-p392/bin/ruby ``` **bundle**などgemでインストールして新しいコマンドが増える場合は~/.rbenv/shimsに振り分けるスクリプトを置かないといけないので```rbenv rehash```を唱える必要がある。 ## RBENV_VERSION ドキュメントによると **RBENV_VERSION** 環境変数が使うバージョンを決める。従ってコマンドラインからだと以下で切り替えられる。 ``` $ export RBENV_VERSION=1.9.3-p392 $ ruby -v ruby 1.9.3p392 ... ``` ## global/local/shell 実際のところRBENV_VERSIONは直接意識しなくてもいいようになっている。 使うデフォルトのバージョンを固定したい場合は **global** を使う。すると *~/.rbenv/version* に使うバージョンが書き込まれる。 ``` $ rbenv global 1.9.3-p392 ``` プロジェクトで特定のバージョンを使いたいときは、 **local** を使う。すると、カレントディレクトリの *.ruby-version* に使うバージョンが書き込まれる。 ``` $ rbenv local 1.8.7-p371 ``` コマンドラインから一時的に切り替えたい場合は **shell** を使う。すると、環境変数 *RBENV_VERSION* に設定される。 ``` $ rbenv shell 2.0.0-p0 ``` localおよびshellに指定したバージョンは--unsetオプションにより取り消せる(globalは取り消せなかった、後述のsystemに戻すことで代用できる)。なおlocalの場合は *.ruby-version* ファイルが削除される。 ``` $ rbenv local --unset $ rbenv shell --unset ``` 念のため実験したところ優先順位はちゃんとshell > local > globalの順で適応された。つまりは 環境変数 *RBENV_VERSION* > カレントディレクトリの *.ruby-version* > *~/.rbenv/version* の順に優先されることになる。 ## system ディストリビューションなどで例えば/usr/bin/rubyにRubyがインストールされていることがある。こういったRubyは **system** というキーワードで指定することができる。 ``` $ rbenv shell system $ rbenv local system $ rbenv global system ``` ```rbenv global --unset```の代わりとして```rbenv global system```が利用できる。 ## 古い.rbenv-version 互換性のために.rbenv-versionから.ruby-versionを使うようになっている。 * .rbenv-versionと.ruby-versionの両方が存在する場合 * .ruby-versionが優先される * ```rbenv local```使った場合は.ruby-versionのみ更新される * ```rbenv local --unset```使った場合は.ruby-versionも.rbenv-versoinも削除される * .rbenv-versionだけがある場合 * ```rbenv local```を実行すると.rbenv-versionが削除され.ruby-versionが作成される。 * ```rbenv local --unset```使った場合は.rbenv-versoinが削除される。 |
|
| 511位 |
|
|||
|
00:00:28 |
(Kindness 所属) |
|

## 枕詞 Vim Advent Calendar 2012 373日目……じゃなくて、 JavaScript - Client Side - Advent Calendar 2013の8日目担当の[Arc Cosine](https://twitter.com/ArcCosine)です。 まだVim初心者なので、Vim Advent Calendarに参加出来ませんが、いつか参加できるようになりたいです。 さて、今回のJavascript Client Sideは、最近、(俺の中で)大人気のDropbox Datastoreについてです。 ##時間がない人用アジェンダ このエントリに書いてあること 1. [Dropbox Datastore API](https://www.dropbox.com/developers/datastore)を使えるようになるまでのチュートリアル 2. Dropbox Datastore APIのJavaScriptコードサンプル 3. リアルタイム同期をお手軽に見れるという事 4. リリースにDropboxを使うと楽だという事 5. 独自ドメインで運用する場合は[Gehirn](http://www.gehirn.jp/rs2.html) オススメ(魔法石85個以下) 6. 実際に動くアプリは[こちら](https://dl.dropboxusercontent.com/u/335896/clonequill/index.html) 7. ソースコードは[こちら](https://github.com/ArcCosine/clonequill) ## Dropbox Datastore APIとは 言うまでも無いことではありますが、デキる社会人や学生はDropboxを活用しています(Arc Cosine調べ)。 しかしながら、Dropbox Datastore APIはと言うと、なかなか使われていないイメージです。 今年の7月にリリースされたAPIなので仕方がないかもしれません。 参考:[オフラインWebアプリを実現するDropboxの新API「Datastore API」。Dropboxは実質的にBaaS市場へ参入した](http://www.publickey1.jp/blog/13/webdropboxapidatastore_apidropboxbaas.html) ものすごく簡単に説明すると、key-valueのデータ保存がAPI経由で出来るようになりました。 これまで、データ保存と言えば、ファイルへの保存(テキストファイルやXMLファイル等)やデータベースへの保存(MySQLやPostgress等)、そしてドキュメント形式(MongoDB等)での保存といった形式がありましたが、Dropbox Datastoreは、それらの概念の延長線上にあるものです。 key-valueと言えば、JavaScriptでお馴染みの連想配列です。それと同じ感覚でデータを保存できるというのは、Javascripterとしては非常に扱いやすい方式であると言えます。 JSON.parseやevalなどを使わなずに、データ保存出来るのがとても嬉しいです。 対応ブラウザは下記の通り IE10、Safari 6、Firefox、Google Chrome. Operaが記載されていませんが、Operaでも使えます。 Operaが記載されていませんが、Operaでも使えます(大事なことなので二度書きました)。 仕様はこんな感じです。 |項目|サイズ| |:----------------------------------:|:-----:| |最大レコードサイズ |100KiB | |最大レコード数 |100,000| |最大データサイズ |10MiB | |Maximum size of a single sync() call|2MiB | KiB(kibibyte),MiB(mebibyte)という珍しい単位を使っているのが印象的ですね。 最後の行は英語力()な人なので翻訳できませんでしたが、同期関数の呼び出しサイズだと思います、多分。 参考:[Datastore API documentation - Dropbox](https://www.dropbox.com/developers/datastore/docs/js) ## 実際に、Dropbox Datastore APIへの登録を行なう。 まずは、開発者用のページへアクセスしましょう。 [Developers - Dropbox](https://www.dropbox.com/developers)  **App Console** を選択します。  右上の **Create app** をクリックします。  **Dropbox API app** を選択します。  今回は、Fileも扱いたいので、 **Files and datastores** を選択しましたが、場合によっては **Datastores only** を選択しても良いです。  Appフォルダの下のみのアクセス権が必要なので、Yesを選択します。  アプリの名前を入力し(今回僕はclonequillという名前にしました)、Create appをクリックします。 尚、注意点として、アプリ名に「drop」やDropboxに似た感じの言葉を入れると弾かれるっぽいので気をつけてください。[ガイドライン](https://www.dropbox.com/developers/reference/branding)に書いてありました。  これで登録はほぼ終わりです。 自分以外も使用する可能性がある場合は、 **Enable additional users** をクリックすると100人まで使用する事が出来ます。 申請すれば、ちゃんとしたアプリとして扱ってもらえて、人数制限が無くなります。 申請は **Apply for production** をクリックしてページ遷移後、下記のフォームから行います。  このアプリがDropboxをどう扱うのか、どうやったらそのアプリを試せるのかを問われていますので、頑張って英語で書きます。 拙い英語でも、なんとかなりましたので、その辺はチャレンジしてみてください。 どうしても英語が書けない場合は、丁寧で簡潔で分かりやすい日本語を書いて、翻訳サイトで変換し、違和感を感じる所を自分のフィーリングで修正すれば大丈夫です。 ## 認証ページ指定 認証ページは、App Consoleから指定出来ます。 先ほど作成したアプリのリンクをクリックすると、Settings タブがあり、その中に **OAuth redirect URIs** があります。 ここに、認証ページのURIを入力します。 認証には、httpsが有効になるサーバが必要です。 localhostを指定する場合は、https無効でも大丈夫です。  SSL証明書インストールしたサーバとか用意できねえよ!!! という貧乏人でも、作成したファイルをDropboxのPublicフォルダに突っ込めば、簡単に動作確認する事が出来ます。 DropboxのPublicフォルダはリリースが超ラクチンなので、オススメします。 なにせ、Dropboxにファイルコピーするだけですからね。 `リリースコマンド例: cp -r ~/TestFolder/ ~/Dropbox/Public/` その他にも、Google Driveにコピーして公開という方法もありますが、今回は省略します。興味がある方はやり方を探して試してみてください。 ## 独自ドメインで運用する場合  独自ドメインで運用しようとするとSSL証明書をインストールしたサーバを用意しなければなりませんが、[Gehirnのサーバ](http://www.gehirn.jp/rs2.html)が非常に安価に使うことができるのでそこをオススメします(ステマ)  初期費用(1,050)+年間使用料(3,780)=4,830円。初年度の使用料が **魔法石85個以下** と考えたら非常に安いですね! Gehirnの中の人は、是非このキャッチフレーズ使ってください(使えない)。 月額換算だと400円ちょっとです。初年度は松屋の牛めし野菜セット1食分で運用できます。 2年目以降は月額315円なので、スタバコーヒー1杯分で運用出来ます。 ここまで安いレンタルサーバーはなかなか無いですよね。 学生のお小遣いでもなんとかなりそうな額です。 SSL証明書はStart SSLをチョイスすれば、無料で取得できます。 参考:[無料のSSL証明書StartSSLを活用する](http://qiita.com/k-shogo/items/870b6d3939dd08da2de4) 尚、GehirnへのStart SSL証明書のインストールは、中の人が詳しく書いてくださっていますので、それを参考にしてください 参考:[無料SSL証明書+Gehirn RS2でセキュアなドメイン環境をキメる!](http://isid.ai/tips/2011/11/01/488) ## Dropbox Datastore APIの使い方 基本的には[チュートリアル](https://www.dropbox.com/developers/datastore/tutorial/js)の通りにコードを書けば誰でも簡単にアクセスする事が出来ます。 とは言え、現代人は忙しいので、一々リンク先を見なくても良いように、ここにサンプルコードを貼り付けますね。 これをそのままコピペしたら使えるかというとそうでもないので、チュートリアルをキチンと見てください(ヲィ #### HTML ```html:sample.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>sample</title> </head> <body> <div id="list-datas"></div> <input type="button" value="login" /> <script src="//www.dropbox.com/static/api/dropbox-datastores-1.0-latest.js" type="text/javascript"></script> <script src="js/cq_auth.js"></script> <script src="js/cq_list.js"></script> <!-- 他にも色々必要なjsをここに --> </body> </html> ````` 特になにもひねっていません。場合によっては、dropbox-datasotres-1.0-latest.jsをダウンロードしてローカルに置いても良いでしょう。 呼び出しを//から始める事で、http/httpsを気にせずにコードが書けるって所がちょっとしたテクニックです。 ### 認証部分 ```javascript:cq_auth.js //Set auth class. function CQAuth(){ this.init(); } CQAuth.prototype = { init: function(){ var DROPBOX_APP_KEY = "SET_YOUR_APP_KEY"; //Set your app key this.client = new Dropbox.Client({key: DROPBOX_APP_KEY}); // OAuth check this.client.authenticate({interactive:false}, function (error) { if( error ){ alert('Authentication error: ' + error); } }); }, login: function(){ this.client.authenticate(); }, logout: function(){ this.client.signOut({mustInvalidate: false}, function(error){ if( error ){ alert('Singn out error: ' + error); } }); location.reload(); }, isLogin: function(){ return this.client.isAuthenticated(); } } //Entry Point //* var auth = new CQAuth(); if( auth.isLogin() ){ alert("Login Success"); } var login=document.getElementById("login"); document.addEventListener(login,"click", function(){ auth.login(); }); //*/ ``` 認証関係の処理コードは全部Dropboxに丸投げできます。 チュートリアルのコードにはログアウトのコードが無いのですが、↑のコード通りに書けば、ログアウト出来ます。APIドキュメント見れば誰でも書けますけれどねw ### データ取得 データ取得のコードです。 ```javascript:cq_list.js //Set list class. function CQList(){ this.init(); } CQList.prototype = { init : function(){ this.client = null; this.table = null; }, load: function(client){ this.client = client; }, create: function(){ var _self = this; this.client.getDatastoreManager().openDefaultDatastore(function (error, datastore) { if (error) { alert('Error opening default datastore: ' + error); } _self.table = datastore.getTable('quill'); _self.update(); datastore.recordsChanged.addListener(function(){ _self.update() }); }) }, update: function() { // clear lists. document.getElementById('list-datas').innerHTML=""; var records = this.table.query(); var list = document.createDocumentFragment(); // Add an item to the list for each task. for (var i = 0, iz = records.length; i < iz; i++) { var record = records[i]; var li = list.appendChild(document.createElement("li")); li.innerHTML=record.get("title"); } document.getElementById('list-datas').appendChild(list); } } // Entry Point /* var auth = new CQAuth(); var list = new CQList(); if( auth.isLogin() ){ list.load(auth.client); list.create(); //Do Code. } //*/ ``` サンプルなので、色々と端折っていますが、基本的には、datastore.getTable()でtableを取得し、tableから、query()でrecordを取得して、recordにget()する事で、データを取り出します。 珍しいコードとして、datastore.recordsChanged.addListenerという部分がありますが、これは **リアルタイム同期** の仕組みです。 具体的には、PCとスマートデバイスで同じDatastoreを見ていた場合、PCで更新した内容がリアルタイムでスマートデバイスに更新されるという動きをします。 もちろん、その逆も然り。後で紹介する、Clone Quillで実際に試すことが出来ます(オープンマーケティング) ### データ保存 データの保存は、tableのinsert()で出来ます。 ```javascript:save.js var auth = new CQAuth(); var list = new CQList(); if( auth.isLogin() ){ list.load(auth.client); list.create(); var table=list.table; var title = "ABC"; var content = "DEF"; var share = "private"; table.insert({ title: title, content: content, share: share }); } ``` insertの部分を見ると分かる通り、連想配列そのままで保存出来てるってのが最高に<del>『ハイ!』ってやつだアアアアアア!</del>素敵です。 ### データ更新 データの更新は、recordのset()で出来ます。 ```javascript:update.js //一括更新の場合 var auth = new CQAuth(); var list = new CQList(); if( auth.isLogin() ){ list.load(auth.client); list.create(); var table = list.table; var records = table.query(); for( var i=0, iz = records.length; i<iz; i++ ){ var record = records[i]; record.set('share','public'); } //こんなことも出来る var record=records[0]; var id = record.getId(); save(id); function save(rid){ var record = table.get(rid); record.set('share','private'); } } ``` いずれにせよ、詳しいことは[チュートリアル](https://www.dropbox.com/developers/datastore/tutorial/js)のコードを読むのが一番早いです。 ## Dropbox Datastore APIを使って、quill.toクローン作った 先日、Clone QuillというWebアプリケーションを作り公開しました。 これは、知る人ぞ知るquill.toというメモサービスのクローンもどきです。 尚、quill.toは[今年の五月](http://blog.livedoor.jp/quillstaff/archives/1611421.html)にお亡くなりなりました。残念です。 今回作ったClone Quillは、Dropbox Datastore API経由でデータ保存しています。 つまり、データ保存先をDropboxに丸投げしています。 普通はちゃんとしたDBやらなんやらを用意するのですが、それを用意していない=サーバ側コードゼロ!という点が新しいです(別に新しくない)。 基本的にはメモ内容は非公開設定なのですが、Dropboxを経由して公開設定にする事も出来ます。 [Clone Quill](https://dl.dropboxusercontent.com/u/335896/clonequill/index.html)  オレオレ仕様なので、色々独自仕様です。 今回は、下記のような事にこだわりました。 * モバイルファースト * iPhone等のスマートデバイスからの入力を想定 * 申し訳程度のレスポンシブデザイン * まあ、モバイルファーストだしやっておけーみたいなっ * **キーボードファースト** * リスト部分の操作はキーボードで操作(J/Kでの上下移動等)出来るようになっている * ?キーでショートカット一覧が出る * 隠しショートカットもあるらしい * 隠しショートカットはWindowsとMacで動きが違っていて、笑える * 公開/非公開機能 * 公開リンクは、Dropboxにテキストファイルを吐き出してそのリンクを習得している * 尚、生成したテキストファイルは削除されない模様 * 今後の更新を待つべし * Twitter Bootstrapライクなボタンデザイン * **ぱくった** * Font Awesomeカスタマイズ * 最近は素敵なサービスがいっぱいある * 参考:[http://fontello.com](http://fontello.com) * 敢えてjQuery * モバイルファーストなら、jQuery Mobileだろ、JKって感じなのだが、敢えてjQueryを使った。 * 単にjQuery 2.xを使いたかっただけ。 * と言っても、jQuery 2.xで搭載した機能は一切使ってないとかなんとか * jQueryだと、touchstartがpreventDefault()できない事がある * CSSにcursor:pointerを指定すると解決する。これテストに出るからメモしておくこと * $(document).on(eventName,selector,callback)が面白い * node削除しても、ちゃんとイベント復活する * liveからの移行 * この機能により、datastore.recordsChanged.addListenerと結びついているレンダリング処理とイベント処理の分離が可能になった 総括としては、試験的な事がたくさん出来て楽しかったです。 やはり、コード書くのは楽しいですね。 ## 薀蓄はいいからコード晒せ [ArcCosine/clonequill - GitHub](https://github.com/ArcCosine/clonequill) どうぞ、お納めください、ユーザ様。 ライセンスは、Public DomainとNYSLのダブルライセンスです。 お好きな方をお選びください。 ちなみに、このダブルライセンスは高度なジョークなので、分かる人だけ笑ってください(意味なしジョークが一番面白い)。 js/cq_auth.jsのDROPBOX_APP_KEYをご自身のアプリキーに切り替えて、Dropboxの公開フォルダへコピペすればすぐに動かす事が出来ます。 なお、そのままコピーしても、OAuth認証で弾かれるますのでご注意を。 詳しくはREADME.md読んでください。 ## 最後に リアルタイム同期をDropbox Datastore APIでお手軽に作れたり、サーバを用意しなくても色々なサービスを公開できたりと、実に素晴らしい時代になったなと思いました。 少しだけお時間のある方は、Dropboxの利用例として、サーバ側コードを一切書いてないこの[クソゲー](https://dl.dropboxusercontent.com/u/335896/yoke/index.html)で遊んでみてください。6面クリアができたら凄い(笑)。<del>スペースキー押しっぱでクリア出来るクソゲー</del> 長文でしたが、ここまで読んで下さり、ありがとうございました。 以上です。 明日は[yuku_t](http://qiita.com/yuku_t)さんです。 |
|
| 512位 |
|
|||
|
13:11:34 |
|
|
# お断り
*情報が古いです。更新の予定はありません* *未だにストックされる方がいらっしゃいますが、古い情報です。ご自身で最新情報を確認してください。* 一部は[PopperTools](http://qiita.com/moriturus/items/74ce8a9e6ed0575aa65f)としてライブラリを公開しています # 以下古い内容 iOSアプリの開発にはjavascript/HTML5/CSSやRubyなど様々な言語が利用可能ですが、漢ならやっぱりObjective-Cネイティブでしょ! Objective-CはCの完全上位互換なので、Cで使える技の数々はもちろん利用可能です。 加えて動的にメソッドを追加できるカテゴリや、プロトコルorデリゲートなど便利な機能がたくさんあるので、ガンガン使って行きましょう! 以下私が普段使っているマクロ・カテゴリ・ライブラリをまとめます。 一部過去の投稿を焼き直したものですがどうかご容赦を>< ##マクロ 前提として、デバッグビルドにはDEBUGプリプロセッサ定数が宣言されているとします。 ```c:debugMacros.h /* デバッグログ用マクロ。 NSLog()の使いすぎは明らかなパフォーマンスの低下を招くので、 リリースビルドからは自動的に取り除かれるようにします。 */ #ifdef DEBUG #define DEBUGLOG(…) NSLog(__VA_ARGS__) #else #define DEBUGLOG(…) ; #endif /* ソース上の行番号を出力します。 ランタイムエラーが出るけど、どこまで正常に実行できているか 検証する際によく用います。 */ #ifdef DEBUG #define LINE() NSLog(@"%d",__LINE__) #else #define LINE() ; #endif /* 実行された際、どの関数orメソッド内にいるか出力します。 同じようなログを出力しまくっていると、 どのメソッドで出力されたものかわからなくなることがありますが、 一番最初にこいつを出力しておけば一目瞭然です。 上のLINE()マクロと組み合わせてもいいですね。 */ #ifdef DEBUG #define FUNC() NSLog(@"%s",__PRETTY_FUNCTION__) #else #define FUNC() ; #endif /* 上3つのような単純なデバッグログでは物足りない場合、こいつを使います。 引数に入れた引数・返り値なしのblocksを同期的に処理します。 デバッグビルドでのみ画面上にビルド番号を表示させたりできます。 メモリリークには気をつけましょう。 */ #ifdef DEBUG #define DEBUGEXEC(__BLOCK,...) { dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ __BLOCK(__VA_ARGS__); });} #else #define DEBUGEXEC(__BLOCK,...) ; #endif ``` NSLog()やBlocksを使っている以外は、ピュアなCやC++でも利用可能なテクニックです。 というか超古典的な常套手段ですね。 ##カテゴリ 前述のように、Objective-Cはメソッドを動的に(実行時に)追加することができます。 例えば、NSStringはアップルが提供するFoundationに組み込まれている(=一般デベロッパが作ったものではない)クラスですが、これにオリジナルの機能を追加することができます。 超便利! 素敵! Objective-Cデベロッパでは使っていない人はいないんじゃないかというくらい便利すぎる機能です。 はまるとなんでもカテゴリ化させてしまうようになるのが玉に瑕? ###NSURL NSURLクラスに、開発上よく使うディレクトリへのショートカットを追加します。 ただのラッパメソッドですが、あるとないとでは気持ち的にも実作業的にもかなり差が出るはずです。 ```Objective-C:NSURL+LibraryHandling.h @interface NSURL (LibraryHandling) //<アプリケーション>/Library/へのショートカット + (NSURL*)appLibraryDirectoryURL; //<アプリケーション>/Library/Caches/へのショートカット + (NSURL*)appLibraryCacheDirectoryURL; //<アプリケーション>/Documents/へのショートカット + (NSURL*)userDocumentsDirectoryURL; @end ``` ```Objective-C:NSURL+LibraryHandling.m @implementation NSURL (LibraryHandling) + (NSURL*)appLibraryDirectoryURL { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); NSString* path = [paths objectAtIndex:0]; NSURL* url = [NSURL fileURLWithPath:path isDirectory:YES]; return url; } + (NSURL*)appLibraryCacheDirectoryURL { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString* path = [paths objectAtIndex:0]; NSURL* url = [NSURL fileURLWithPath:path isDirectory:YES]; return url; } + (NSURL*)userDocumentsDirectoryURL { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString* path = [paths objectAtIndex:0]; NSURL* url = [NSURL fileURLWithPath:path isDirectory:YES]; return url; } @end ``` ###UIColor UIColorは組み込み以外の色(`[UIColor blackColor]`など__*以外*__)は、RGBAの色要素を0.0から1.0の範囲で指定して色を作ります。 様々な色指定の方法に慣れた方なら大丈夫ですが、個人的にはHTML/CSSのように16進数で指定したほうがパッと見でどんな色かわかりやすい気がするので、指定できるようにします。 ```Objective-C:UIColor+HexColoring.h @interface UIColor (HexColoring) //RGBAを16進数型(ffa500ffなど)で指定します + (UIColor*)colorWithHex:(uint32_t)hex; @end ``` ```Objective-C:UIColor+HexColoring.m @implementation UIColor (HexColoring) + (UIColor*)colorWithHex:(uint32_t)hex { CGFloat red = ((hex & 0xFF000000) >> 24) / 255.0f; CGFloat green = ((hex & 0x00FF0000) >> 16) / 255.0f; CGFloat blue = ((hex & 0x0000FF00) >> 8) / 255.0f; CGFloat alpha = (hex & 0x0000000FF) / 255.0f; return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; } ``` ###NSDate NSDateに[UTC(協定世界時)](http://ja.wikipedia.org/wiki/%E5%8D%94%E5%AE%9A%E4%B8%96%E7%95%8C%E6%99%82)と[ISO8601](http://ja.wikipedia.org/wiki/ISO_8601)のサポートを組み込みます。 ```Objective-C:NSDate+UTC_ISO8601.h @interface NSDate (UTC_ISO8601) #pragma mark - UTC //現在のUTCを返します + (NSDate*)UTC; //指定した日付をUTCに変換します + (NSDate*)UTCWithDate:(NSDate*)date; //NSDateのインスタンスをUTCに変換します - (NSDate*)convertToUTC; #pragma mark - ISO8601 //ISO8601形式の文字列を出力します - (NSString*)ISO8601String; //ISO8601形式の文字列から日付を作ります + (NSDate*)dateFromISO8601String:(NSString*)string; @end ``` ```Objective-C:NSDate+UTC_ISO8601.m @implementation NSDate (UTC_ISO8601) #pragma mark - UTC + (NSDate*)UTC { return [[NSDate UTCWithDate:[NSDate date]]; } + (NSDate*)UTCWithDate:(NSDate*)date { NSTimeZone* localTimeZone = [NSTimeZone localTimeZone]; NSTimeZone* utcTimeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"]; NSInteger currentGMTOffset = [localTimeZone secondsFromGMTForDate:date]; NSInteger gmtOffset = [utcTimeZone secondsFromGMTForDate:date]; NSTimeInterval gmtInterval = (NSTimeInterval)(gmtOffset - currentGMTOffset); return [[[NSDate alloc] initWithTimeInterval:gmtInterval sinceDate:date] autorelease]; } - (NSDate*)convertToUTC { return [NSDate UTCWithDate:self]; } #pragma mark - ISO8601 - (NSString*)ISO8601String { NSDateFormatter* formatter = [[NSDateFormatter alloc] init]; formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; NSString* dateString = [formatter stringFromDate:self]; [formatter release]; return dateString; } + (NSDate*)dateFromISO8601String:(NSString*)string { NSDateFormatter* formatter = [[NSDateFormatter alloc] init]; formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; NSDate* date = [formatter dateFromString:string]; [formatter release]; return date; } @end ``` ###NSMutableArray NSMutableArrayにシャッフル(ランダマイズ)を追加します。 ```Objective-C:NSMutableArray+Randomize.h @interface NSMutableArray (Randomize) - (void)randomize; @end ``` ```Objective-C:NSMutableArray+Randomize.m @implementation NSMutableArray (Randomize) - (void)randomize { NSUInteger count = [self count]; for (NSUInteger i = 0; i < count; ++i) { NSUInteger j = (NSUInteger)(arc4random() % (i+1)); [self exchangeObjectAtIndex:i withObjectAtIndex:j]; } } @end ``` ##ライブラリ 世の中にはたくさんのライブラリがあります。 その多くがオープンソースで、ライセンスにさえ気をつければ商用非商用関わらず自由に使うことができます。 ###[BlocksKit](https://github.com/zwaldowski/BlocksKit/) *サブプロジェクトを含めてBSD License,MIT License,Public Domain* 多くのクラスにBlocks構文によるサポートを追加します。 例えば、UIAlertViewのデリゲートメソッドを実装せずに、ボタンタップ時のハンドリングが可能になります。 *詳細:[iOSコーディングスタイルを変えてしまうBlocksKitの紹介](http://d.hatena.ne.jp/h_mori/20120220/1329691866)* ###[EBPurchase](https://github.com/ebutterfly/EBPurchase) *MIT License* In App Purchaseの実装に必要なすべてが網羅されています。 基本的にはひな形orサンプルなのですが、ここに必要なメソッドやデリゲート処理を追加していくだけで、IAP実装が完成してしまいます。 ###[SVProgressHUD](https://github.com/samvermette/SVProgressHUD/),[MBProgressHUD](https://github.com/jdg/MBProgressHUD) *どちらもMIT License* 進行状況を知らせたり、処理中であることを知らせるためのプログレスバー(バーじゃないですが)を表示します。 名前は似ていますが、できることや使い方が微妙に違いますので、それぞれのプロジェクトを参照してください。 個人的な実感では、SVProgressHUDは簡単に使えて軽量、MBProgressHUDはすこしややこしいけど機能が豊富といった感じでしょうか。 ###[JSONKit](https://github.com/johnezang/JSONKit/) *BSD Lisence or Apache Lisenceのデュアルライセンス* 私はデータをJSONで持つことが多いのですが、JSONKitは体感、[実証データ](http://www.bonto.ch/blog/2011/12/08/json-libraries-for-ios-comparison-updated/)共に最速です。 検証環境にもよるのかもしれませんが、Cで実装された[yajl](http://lloyd.github.com/yajl/)よりも速い。とにかく速い。 NSDictionaryクラスのPlist読み書きより速いのはちょっとおもしろいですね。 __遅いのはバグ。速いのは正義。__すばらしい。 ###[FMDB](https://github.com/ccgus/fmdb) *MIT Lisence* Objective-CによるSQLiteのラッパです。 AppleがCoreDataをプッシュするのはわかるんですが、どうも速度的には(バックエンドにSQLiteを使っても)SQLite単独で使用したほうが速いようなので、なかなか抜け出せない。 このFMDBと[SQLite最新版](http://www.sqlite.org/)を組み合わせれば全文検索まで簡単にできちゃいます。すごい! __*詳細: [iOS で SQLite – FMDB の使い方](http://akabeko.me/blog/2011/11/ios-sqlite-fmdb/)*__ ###[AFNetworking](https://github.com/AFNetworking/AFNetworking/) *MIT Lisence* NSURLRequestなどネットワーキング系クラスのサポートライブラリ。 ファイルやUIImageViewの画像を非同期で取得したり、JSONでのRESTをしたり、とにかくネットワークに関してはとりあえずこれを入れとけ状態。 開発も活発なので、ウォッチしていて損はないでしょう。 ###[ObjectAL for iPhone](https://github.com/kstenerud/ObjectAL-for-iPhone) *Apache 2.0 Lisence* 音声再生は、だいたいのプロジェクトにおいてはAVAudioPlayerで十分なのですが、ゲームやずばり音をテーマにしたアプリなどでは、レイテンシの問題が付き纏います。 そこでOpenALの出番なのですが、OpenALはC言語で実装された、少々レガシーなライブラリです。 そこでObjective-Cからも使いやすく、モダンなラッピングをしたのがこのObjectAL。 OpenALの3Dサウンドなんかも簡単に使えます。 ##終わりに Ojbective-CネイティブによるiOSアプリ制作は、軽量言語による制作とは違った魅力があります。 iOSの深淵を覗けるのはObjective-Cだけだし、なにより最速です。 クリティカルな部分はCやC++で書けばもっと速くなります。 __遅いのはバグ。速いのは正義。__ どうかこれからもObjective-Cネイティブ開発が盛り上がりますように。 |
|
| 513位 |
|
|||
|
20:07:22 |
|
|
Hamcrest の Matcher にどんなものがあるかメモ。
標準でどんな Matcher があるか知っておかないと、ついつい車輪の再発明をしてしまう(というか、してしまった)。 なので、とりあえず `org.hamcrest.Matchers` に定義されているメソッドをひと通り確認した。 ただし、使い方がよくわからないやつ(`iterableWithSize()`)、他のメソッドで事足りるやつ(`both()`, `either()`, `any()`)、個人的に使わなさそうなやつ(`hasXPath()`)は省いている。 #環境 ##バージョン ###JUnit 4.11 ###Hamcrest 1.3 ##build.gradle ```groovy:build.gradle apply plugin: 'java' repositories { mavenCentral() } dependencies { testCompile 'junit:junit:4.11', { transitive = false } testCompile 'org.hamcrest:hamcrest-all:1.3' } ``` ```text: >gradle dependencies testCompile - Compile classpath for source set 'test'. +--- junit:junit:4.11 \--- org.hamcrest:hamcrest-all:1.3 ``` JUnit 4.11 はデフォルトだと hamcrest-core の 1.3 に依存している。 今回は別途 hamcrest-all を依存関係に追加するので、 `transitive = false` を指定して、推移的な依存関係の解決を明示的に解除している。 #基本的な比較 Matcher ##is() : 同じ値であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("hoge", is("hoge")); } } ``` - 指定した値が等しいことをチェックする。 - 比較には、 `equals(Object)` メソッドが使用される。 ##not() : 否定する ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void 値を否定する() { assertThat("hoge", is(not("HOGE"))); } @Test public void Matcherを否定する() { assertThat("hoge", is(not(containsString("xx")))); } } ``` - 引数で指定した値(Matcher)を否定する。 ##nullValue() : null であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat(null, is(nullValue())); } } ``` - 値が null であることをチェックする。 ##notNullValue() : null でないことをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("not null", is(notNullValue())); } } ``` - 値が null でないことをチェックする。 ##equalTo() : 同じ値であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("hoge", is(equalTo("hoge"))); } @Test public void テストは失敗する() { assertThat("hoge", is(equalTo("xxx"))); } } ``` - 動作は `is()` と同じ。 - 英語の文法的に `is()` だと不自然なときは `equalTo()` を使う感じかと。 ##comparesEqualTo() : compareTo() で比較する ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { private Hoge one = new Hoge(); private Hoge other = new Hoge(); @Test public void テストは成功する() { assertThat(one, is(comparesEqualTo(other))); } @Test public void テストは失敗する() { assertThat(one, is(other)); } public static class Hoge implements Comparable<Hoge> { @Override public int compareTo(Hoge o) { return 0; } @Override public boolean equals(Object other) { return false; } @Override public int hashCode() { return super.hashCode(); } } } ``` - `equals()` メソッドではなく、 `compareTo()` メソッドで比較し、 `0` が返される場合にチェックが OK となる。 ##sameInstance() : 同じインスタンスであることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { Hoge hoge = new Hoge(); assertThat(hoge, is(sameInstance(hoge))); } private static class Hoge {} } ``` - インスタンスが同じであることをチェックする。 ##instanceOf() : 指定したクラスのインスタンスであることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { private Hoge hoge = new Hoge(); private Fuga fuga = new Fuga(); @Test public void テストは成功する() { assertThat(hoge, is(instanceOf(Hoge.class))); assertThat(fuga, is(instanceOf(Hoge.class))); } @Test public void テストは失敗する() { assertThat(hoge, is(instanceOf(String.class))); } private static class Hoge {} private static class Fuga extends Hoge {} } ``` - インスタンスが、指定したクラス、またはそのサブクラスのインスタンスであることをチェックする。 ##hasToString() : toString() で比較する ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { private static Hoge hoge = new Hoge(); public static class 値を指定する { @Test public void テストは成功する() { assertThat(hoge, hasToString("hoge")); } @Test public void テストは失敗する() { assertThat(hoge, hasToString("xxxx")); } } public static class Matcherを指定する { @Test public void テストは成功する() { assertThat(hoge, hasToString(containsString("og"))); } @Test public void テストは失敗する() { assertThat(hoge, hasToString(containsString("xx"))); } } private static class Hoge { @Override public String toString() { return "hoge"; } } } ``` - `toString()` した結果をチェックする。 #文字列比較に関係する Matcher ##startsWith() : 指定した文字で始まることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("hoge", is(startsWith("h"))); } @Test public void テストは失敗する() { assertThat("hoge", is(startsWith("x"))); } } ``` - 指定した文字列で始まることをチェックする。 ##endsWith() : 指定した文字で終わることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("hoge", is(endsWith("e"))); } @Test public void テストは失敗する() { assertThat("hoge", is(endsWith("xxx"))); } } ``` - 指定した文字列で終わることをチェックする。 ##containsString() : 指定した文字が含まれることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("hoge", is(containsString("og"))); } @Test public void テストは失敗する() { assertThat("HOGE", is(containsString("og"))); } } ``` - 指定した文字列が含まれることをチェックする。 ##equalToIgnoringCase() : 大文字・小文字を区別せずに比較する ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("hoge", is(equalToIgnoringCase("HOGE"))); } } ``` - 大文字・小文字を区別せずに文字列を比較する。 ##equalToIgnoringWhiteSpace() : 大文字・小文字・ブランク文字を無視して比較する ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("h o g\te", is(equalToIgnoringWhiteSpace(" H\tO G E "))); } } ``` - 次のことを無視して比較する。 - 大文字・小文字。 - ブランク文字(半角・全角スペース、タブ)の違い。 - 文字列の間にあるブランク文字の数。 - 前後のブランク文字。 ##isEmptyString() : 空文字であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("", isEmptyString()); } } ``` - 文字列が空文字であることをチェックする。 ##isEmptyOrNullString() : 空文字または null であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("", isEmptyOrNullString()); assertThat(null, isEmptyOrNullString()); } } ``` - 文字列が空文字または null であることをチェックする。 ##stringContainsInOrder() : 指定した順序で文字列が現れることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Arrays; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("HogeFugaPiyo", is(stringContainsInOrder(Arrays.asList("Hog", "eFug", "aPiyo")))); } @Test public void テストは失敗する() { assertThat("HogeFugaPiyo", is(stringContainsInOrder(Arrays.asList("Fuga", "Piyo", "Hoge")))); } } ``` - 指定した文字列が、指定した順序で現れることをチェックする。 #数値比較に関係する Matcher ##closeTo() : 指定した範囲の値であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat(0.5, is(closeTo(1.0, 0.5))); } @Test public void テストは失敗する() { assertThat(0.4, is(closeTo(1.0, 0.5))); } } ``` - 第一引数で指定した値を基準として、第二引数で指定した値の範囲に数値が収まることをチェックする。 - 上記例の場合、値が `0.5 ~ 1.5` ならテストが成功する。 - BigDecimal 版もある。 ##greaterThan() : 指定した値より大きいことをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat(10, is(greaterThan(9))); } @Test public void テストは失敗する() { assertThat(10, is(greaterThan(10))); } } ``` - 指定した値より大きいことをチェックする。 ##greaterThanOrEqualTo() : 指定した値以上であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat(10, is(greaterThanOrEqualTo(10))); } @Test public void テストは失敗する() { assertThat(10, is(greaterThanOrEqualTo(11))); } } ``` - 指定した値以上であることをチェックする。 ##lessThan() : 指定した値より小さいことをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat(10, is(lessThan(11))); } @Test public void テストは失敗する() { assertThat(10, is(lessThan(10))); } } ``` - 指定した値より小さいことをチェックする。 ##lessThanOrEqualTo() : 指定した値以下であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat(10, is(lessThanOrEqualTo(10))); } @Test public void テストは失敗する() { assertThat(10, is(lessThanOrEqualTo(9))); } } ``` - 指定した値以下であることをチェックする。 正直、この辺は普通に比較演算子使った方が読みやすい気がする。。。 #Collection・Iterableに関係する Matcher ##contains() : 全ての要素が等しいことをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Arrays; import java.util.List; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { private static List<String> list = Arrays.asList("hoge", "fuga", "piyo"); public static class 値を渡す { @Test public void テストは成功する() { assertThat(list, is(contains("hoge", "fuga", "piyo"))); } @Test public void 要素数が異なるので_テストは失敗する() { assertThat(list, is(contains("hoge", "fuga"))); } @Test public void 含まれる要素が異なるので_テストは失敗する() { assertThat(list, is(contains("hoge", "fuga", "PIYO"))); } @Test public void 順序が異なるので_テストは失敗する() { assertThat(list, is(contains("hoge", "piyo", "fuga"))); } } public static class Matcherを渡す { @Test public void テストは成功する() { assertThat(list, is(contains(equalTo("hoge"), containsString("ug"), startsWith("pi")))); } @Test public void 要素数が異なるので_テストは失敗する() { assertThat(list, is(contains(equalTo("hoge"), containsString("ug")))); } @Test public void 条件を満たさないMatcherが含まれるので_テストは失敗する() { assertThat(list, is(contains(equalTo("HOGE"), containsString("ug"), startsWith("pi")))); } @Test public void 順序が異なるので_テストは失敗する() { assertThat(list, is(contains(containsString("ug"), equalTo("hoge"), startsWith("pi")))); } } } ``` - `Iterable` インターフェースを実装したクラス(List とか)の要素が、指定した内容で完全に一致することをチェックする。 ##containsInAnyOrder() : 順序を無視して全ての要素が含まれることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Arrays; import java.util.List; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { private static List<String> list = Arrays.asList("hoge", "fuga", "piyo"); public static class 値を渡す { @Test public void テストは成功する() { assertThat(list, is(containsInAnyOrder("hoge", "piyo", "fuga"))); } @Test public void 要素数が異なるので_テストは失敗する() { assertThat(list, is(containsInAnyOrder("hoge", "fuga"))); } @Test public void 含まれる要素が異なるので_テストは失敗する() { assertThat(list, is(containsInAnyOrder("hoge", "PIYO", "fuga"))); } } public static class Matcherを渡す { @Test public void テストは成功する() { assertThat(list, is(containsInAnyOrder(containsString("ug"), startsWith("pi"), equalTo("hoge")))); } @Test public void 要素数が異なるので_テストは失敗する() { assertThat(list, is(containsInAnyOrder(containsString("ug"), equalTo("hoge")))); } @Test public void 条件を満たさないMatcherが含まれるので_テストは失敗する() { assertThat(list, is(containsInAnyOrder(containsString("ug"), equalTo("HOGE"), startsWith("pi")))); } } } ``` - 要素の順番は無視して、 `Iterable` インターフェースを実装したクラス(List とか)の要素に、指定した要素が全て含まれることをチェックする。 ##hasItem() : 指定した要素を持つことをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Arrays; import java.util.List; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { private static List<String> list = Arrays.asList("hoge", "fuga", "piyo"); public static class 値を指定する { @Test public void テストは成功する() { assertThat(list, hasItem("hoge")); } @Test public void テストは失敗する() { assertThat(list, hasItem("xxx")); } } public static class Matcherを指定する { @Test public void テストは成功する() { assertThat(list, hasItem(containsString("og"))); } @Test public void テストは失敗する() { assertThat(list, hasItem(containsString("xxx"))); } } } ``` - `Iterable` を実装したクラスの要素に、指定した値が含まれることをチェックする。 ##hasItems() : 指定した要素を全て含むことをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Arrays; import java.util.List; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { private static List<String> list = Arrays.asList("hoge", "fuga", "piyo"); public static class 値を指定する { @Test public void テストは成功する() { assertThat(list, hasItems("hoge", "piyo")); } @Test public void テストは失敗する() { assertThat(list, hasItems("xxx")); } } public static class Matcherを指定する { @Test public void テストは成功する() { assertThat(list, hasItems(containsString("og"), endsWith("ga"))); } @Test public void テストは失敗する() { assertThat(list, hasItems(containsString("xxx"))); } } } ``` - `hasItem()` の、要素を複数指定できる版。 ##hasSize() : 指定したサイズであることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Arrays; import java.util.List; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { private static List<String> list = Arrays.asList("hoge", "fuga", "piyo"); public static class 値を指定する { @Test public void テストは成功する() { assertThat(list, hasSize(3)); } } public static class Matcherを指定する { @Test public void テストは成功する() { assertThat(list, hasSize(lessThan(4))); } } } ``` - 指定したコレクションのサイズをチェックする。 ##empty() : 空であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Collections; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat(Collections.emptyList(), is(empty())); } } ``` - 指定したコレクションが空であることをチェックする。 ##emptyCollectionOf() : 指定したクラスの空コレクションであることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Collections; import java.util.List; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { List<String> list = Collections.emptyList(); assertThat(list, is(emptyCollectionOf(String.class))); } } ``` - コレクションが、指定したクラスの空のコレクションであることをチェックする。 ##emptyIterable() : 空の Iterator であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Collections; import java.util.Iterator; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { MyIterable iterable = new MyIterable(); assertThat(iterable, is(emptyIterable())); } public static class MyIterable implements Iterable<MyIterable> { @Override public Iterator<MyIterable> iterator() { return Collections.emptyIterator(); } } } ``` - `Iterable` を実装したクラスが空であることをチェックする。 ##emptyIterableOf() : 指定したクラスの空の Iterator であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Collections; import java.util.Iterator; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { MyIterable iterable = new MyIterable(); assertThat(iterable, is(emptyIterableOf(MyIterable.class))); } public static class MyIterable implements Iterable<MyIterable> { @Override public Iterator<MyIterable> iterator() { return Collections.emptyIterator(); } } } ``` - `Iterable` を実装したクラスが、指定したクラスの空の Iterator であることをチェックする。 #Map に関係する Matcher ##hasEntry() : 指定したエントリを持つことをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.HashMap; import java.util.Map; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { private static Map<String, String> map = new HashMap<>(); static { map.put("hoge", "HOGE"); map.put("fuga", "FUGA"); map.put("piyo", "PIYO"); } public static class 値を指定する { @Test public void テストは成功する() { assertThat(map, hasEntry("hoge", "HOGE")); } @Test public void テストは失敗する() { assertThat(map, hasEntry("xxxx", "HOGE")); } } public static class Matcherを指定する { @Test public void テストは成功する() { assertThat(map, hasEntry(containsString("og"), endsWith("E"))); } @Test public void テストは失敗する() { assertThat(map, hasEntry(containsString("xx"), endsWith("E"))); } } } ``` - 指定したエントリがマップに含まれることをチェックする。 ##hasKey() : 指定したキーを持つことをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.HashMap; import java.util.Map; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { private static Map<String, String> map = new HashMap<>(); static { map.put("hoge", "HOGE"); map.put("fuga", "FUGA"); map.put("piyo", "PIYO"); } public static class 値を指定する { @Test public void テストは成功する() { assertThat(map, hasKey("hoge")); } @Test public void テストは失敗する() { assertThat(map, hasKey("xxxx")); } } public static class Matcherを指定する { @Test public void テストは成功する() { assertThat(map, hasKey(containsString("og"))); } @Test public void テストは失敗する() { assertThat(map, hasKey(containsString("xx"))); } } } ``` - 指定したキーをマップが持つことをチェックする。 ##hasValue() : 指定した値を持つことをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.HashMap; import java.util.Map; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { private static Map<String, String> map = new HashMap<>(); static { map.put("hoge", "HOGE"); map.put("fuga", "FUGA"); map.put("piyo", "PIYO"); } public static class 値を指定する { @Test public void テストは成功する() { assertThat(map, hasValue("HOGE")); } @Test public void テストは失敗する() { assertThat(map, hasValue("xxxx")); } } public static class Matcherを指定する { @Test public void テストは成功する() { assertThat(map, hasValue(endsWith("E"))); } @Test public void テストは失敗する() { assertThat(map, hasValue(endsWith("xxx"))); } } } ``` #JavaBeans に関係する Matcher ##hasProperty() : 指定したプロパティを持つことをチェックする ```java:Hoge.java package sample.hamcrest; public class Hoge { private int value; public Hoge(int value) { this.value = value; } public int getValue() { return this.value; } public void setValue(int value) { this.value = value; } } ``` ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { private static Hoge hoge = new Hoge(10); public static class 値を指定する { @Test public void テストは成功する() { assertThat(hoge, hasProperty("value")); } @Test public void テストは失敗する() { assertThat(hoge, hasProperty("xxx")); } } public static class Matcherを使って値もチェックする { @Test public void テストは成功する() { assertThat(hoge, hasProperty("value", greaterThan(9))); } @Test public void テストは失敗する() { assertThat(hoge, hasProperty("value", equalTo(11))); } } } ``` - 指定した名前の JavaBeans プロパティを持つことをチェックする。 - Matcher を渡すことで、値もチェックすることができる。 - 指定した値が、マップのバリューに存在することをチェックする。 ##samePropertyValuesAs() : 全てのプロパティが等しいことをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { Hoge one = new Hoge(10, "one"); Hoge other = new Hoge(10, "one"); assertThat(one, is(samePropertyValuesAs(other))); } @Test public void テストは失敗する() { Hoge one = new Hoge(10, "one"); Hoge other = new Hoge(99, "other"); assertThat(one, is(samePropertyValuesAs(other))); } public static class Hoge { private int i; private String value; public Hoge(int i, String value) { this.i = i; this.value = value; } public int getI() { return this.i; } public void setI(int i) { this.i = i; } public String getValue() { return this.value; } public void setValue(String value) { this.value = value; } } } ``` - JavaBeans プロパティを比較する。 #Matcher を組み合わせる ##allOf() : Matcher を AND 条件で結合する ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("hoge", is(allOf(startsWith("h"), containsString("og"), endsWith("e")))); } @Test public void テストは失敗する() { assertThat("hoge", is(allOf(startsWith("h"), containsString("xx"), endsWith("e")))); } } ``` - 引数で渡した Matcher が全て満たされることをチェックする。 ##anyOf() : Matcher を OR 条件で結合する ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("hoge", is(anyOf(startsWith("h"), endsWith("xxx")))); } @Test public void テストは失敗する() { assertThat("yyyy", is(anyOf(startsWith("h"), endsWith("xxx")))); } } ``` - 引数で渡した Matcher の内、最低でも1つは条件を満たすことをチェックする。 #配列に関係する Matcher ##array() : 配列の各要素を Matcher で検証する ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(array(equalTo("hoge"), startsWith("f"), endsWith("o")))); } @Test public void 要素数に対してMatcherの数が異なるので_テストは失敗する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(array(equalTo("hoge"), startsWith("f")))); } @Test public void 条件を満たさないMatcherがあるので_テストは失敗する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(array(equalTo("hoge"), startsWith("f"), equalTo("PIYO")))); } } ``` - 配列1つずつに対して Matcher を適用し、全ての Matcher が条件を満たすことをチェックする。 ##arrayContaining() : 指定した要素と配列の要素が全て等しいことをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { public static class 値を渡す { @Test public void テストは成功する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayContaining("hoge", "fuga", "piyo"))); } @Test public void 要素数が異なるので_テストは失敗する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayContaining("hoge", "fuga"))); } @Test public void 条件を満たさないMatcherがあるので_テストは失敗する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayContaining("HOGE", "fuga", "piyo"))); } } public static class Matcherを渡す { @Test public void テストは成功する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayContaining(equalTo("hoge"), containsString("ug"), endsWith("o")))); } @Test public void 要素数が異なるので_テストは失敗する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayContaining(equalTo("hoge"), containsString("ug")))); } @Test public void 条件を満たさないMatcherがあるので_テストは失敗する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayContaining(equalTo("hoge"), containsString("ug"), endsWith("xxx")))); } } } ``` - 配列の中身が、引数で渡した値(Matcher)と完全に一致することをチェックする。 ##arrayContainingInAnyOrder() : 順序を無視して、指定した要素が全て配列に含まれることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { public static class 値を渡す { @Test public void テストは成功する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayContainingInAnyOrder("hoge", "piyo", "fuga"))); } @Test public void 指定した要素が全て含まれていないので_テストは失敗する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayContainingInAnyOrder("hoge", "piyo"))); } } public static class Matcherを渡す { @Test public void テストは成功する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayContainingInAnyOrder(equalTo("piyo"), equalTo("hoge"), endsWith("a")))); } @Test public void fugaにマッチするMatcherが存在しないので_テストは失敗する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayContainingInAnyOrder(equalTo("piyo"), equalTo("hoge"), endsWith("yo")))); } } } ``` - 要素の順番は無視して、配列の要素に指定した値が全て含まれることをチェックする。 ##arrayWithSize() : 配列のサイズをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { public static class 値を渡す { @Test public void テストは成功する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayWithSize(3))); } @Test public void サイズが異るので_テストは失敗する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayWithSize(2))); } } public static class Matcherを渡す { @Test public void テストは成功する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayWithSize(lessThan(4)))); } @Test public void 条件を満たさないので_テストは失敗する() { String[] array = {"hoge", "fuga", "piyo"}; assertThat(array, is(arrayWithSize(greaterThan(3)))); } } } ``` - 配列のサイズが指定した値と同じであること(Matcher の条件を満たすこと)をチェックする。 ##emptyArray() : 配列が空であることをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat(new String[] {}, is(emptyArray())); } } ``` - 指定した配列が空であることをチェックする。 ##hasItemInArray() : 配列の中に指定した値が存在することをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Arrays; import java.util.List; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { private static String[] list = {"hoge", "fuga", "piyo"}; public static class 値を指定する { @Test public void テストは成功する() { assertThat(list, hasItemInArray("hoge")); } @Test public void テストは失敗する() { assertThat(list, hasItemInArray("xxx")); } } public static class Matcherを指定する { @Test public void テストは成功する() { assertThat(list, hasItemInArray(containsString("og"))); } @Test public void テストは失敗する() { assertThat(list, hasItemInArray(containsString("xxx"))); } } } ``` - 配列の要素に、指定した値が含まれることをチェックする。 #その他 ##describedAs() : テストに失敗したときのメッセージを上書きする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テスト1() { assertThat("hoge", is(equalTo("HOGE"))); } @Test public void テスト2() { assertThat("hoge", is(describedAs("HOGE [%0, %1]", equalTo("HOGE"), "param1", "param2"))); } } ``` ```text:テスト1の実行結果 java.lang.AssertionError: Expected: is "HOGE" but: was "hoge" ``` ```text:テスト2の実行結果 java.lang.AssertionError: Expected: is HOGE ["param1", "param2"] but: was "hoge" ``` - テストが失敗したときの説明メッセージを、指定したものに置き換える。 - `%0` という形で埋め込みパラメータが使える。 ##isIn() : 配列かコレクションで指定した要素のいずれかに一致することをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Arrays; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class HamcrestTest { public static class コレクションを指定する { @Test public void テストは成功する() { assertThat("hoge", isIn(Arrays.asList("hoge", "fuga", "piyo"))); } @Test public void テストは失敗する() { assertThat("xxxx", isIn(Arrays.asList("hoge", "fuga", "piyo"))); } } public static class 配列を指定する { @Test public void テストは成功する() { assertThat("hoge", isIn(new String[] {"hoge", "fuga", "piyo"})); } @Test public void テストは失敗する() { assertThat("xxxx", isIn(new String[] {"hoge", "fuga", "piyo"})); } } } ``` - 指定したコレクション(配列)の中に、 `assertThat()` の第一引数で指定した要素が存在することをチェックする。 ##isOneOf() : 可変長引数で指定した値のいずれかに一致することをチェックする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("hoge", isOneOf("hoge", "fuga", "piyo")); } @Test public void テストは失敗する() { assertThat("xxxx", isOneOf("hoge", "fuga", "piyo")); } } ``` - 可変長引数で指定した値の中に、 `assertThat()` の第一引数で指定した要素が存在することをチェックする。 ##anything() : 全ての値にマッチする ```java: package sample.hamcrest; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Arrays; import org.junit.Test; public class HamcrestTest { @Test public void テストは成功する() { assertThat("hoge", is(anything())); assertThat("fuga", is(anything())); assertThat(123, is(anything())); assertThat(Arrays.asList("a", "b", "c"), is(anything())); } } ``` - 全ての値にマッチする。 |
|
| 514位 |
|
|||
|
10:54:32 |
|
|
# 書いてる人
プログラミング学習サービスやら、ペットサロン予約サービス、風俗検索サービスなど色々とやっている「かずきち」です。 ■運営サービス一部 http://crazy-wp.com/ http://webukatu.com/ 新宿のホストから不動産・保険の営業を経て、HTMLって何?という状態から3ヶ月独学でプログラミングやデザインを学び、IT業界で1年間実務経験を積んで年収は1本超え。現在は起業家としてサービス運営やら不動産運営をしています。 Qiita内にそれ系の記事も書いてます。 <a href="http://qiita.com/kazukichi/items/7379b75fba2f90d3cf45" target="_blank">エンジニアで稼ぐために大切な13のコト</a> <a href="http://qiita.com/kazukichi/items/aeba286c2a750081e5c0" target="_blank">WEBサービスで起業したい人に読んで欲しい18のコト</a> #基本的な仕組みに関して ```php <?php //処理内容 ?> ``` ※<?php ?>で1ブロック。複数ブロックがあっても全て繋がってるのでブロックをまたいで処理可能 ※「;」で終わるまでは改行や空白・スペースは無視される ※1つのファイルにphpのみ記述する(htmlなどない)なら最後の「?>」は省略してもいい ##phpのコンパイルと実行 phpプログラムは「レキサー」と呼ばれるものでphpプログラムを解析し、構文をばらばらにのトークンへ分解する。 そして、「パーサー」と呼ばれるものがトークンをオペコードへ変換。 そのオペコードが実行される。 ##echoの仕組み echoは評価値を文字列型へキャストしてから出力している (なので、falseなど論理値は表示されない)(小数1.0は1になる) ※小数点も表示したいならprintfを使う ##issetとemptyの違い emptyは、0、”"、nullがセットされていても、falseと評価。 issetは、nullの場合にfalseと評価 ``` 例)変数に0が入っていた場合 emptyだと変数の値が0だと空だと判断される。でもissetだとセットされていると判断される。 ``` #エラー関係 エラーは大まかに3種類。 1.コンパイル出来ない(実行以前で止まる) ➡ シンタックス(パース)エラー(syntax error) 2.実行が停止するもの ➡ フェイタルエラー(fatal error) 3.実行は継続されるが警告のでるもの。 ➡ 警告(warning)、注意(notice) 1は文法の間違いなどでコンパイル出来ないなど。 2は定義してない関数を呼び出そうとした場合など。 3は初期化してない変数をいきなり使ったりなど。 ###エラーの重要度高い順 E_PARSE E_ERROR E_WARNING E_NOTICE E_DEPRECATED E_DEPRECATEDはコーディング規約に関しての問題や将来的に廃止になる(非推奨)機能が使われている場合に発生。 ##ログを取る ログというのはプログラムがどこまで動いたかを記録したもの。 エラーになった時にどこでエラーになっているのか判断しやすくなる。 ```php <?php error_reporting(E_ALL); //E_STRICTレベル以外のエラーを報告する ini_set('display_errors','On'); //画面にエラーを表示させるか ini_set('log_errors','On'); //ログを取るか ini_set('error_log','php.log'); //ログの出力ファイルを指定 ?> ``` ##@「アットマーク」でエラー非表示 phpでの@(アットマーク)はエラー制御演算子で、関数や変数の前につける。 これを付けた関数や変数でエラーを出力しないようにする。 #変数に関して 変数名の前に「$」をつけて宣言する。変数名の大文字小文字は区別される。 ```php $a = 1; ``` ##可変変数 変数の中に変数がある感じのもの。 ```php <?php $a = 1; $b = 'a'; echo $$b; ``` ※$bが評価されて中に文字列「a」が入ってるので、「$a」になってまたそれが評価されて中身の「1」が表示される。 ##変数のスコープ javaでいうところの変数の前につくprotectedやprivateみたいなもの。 変数が参照(使える)範囲のこと。 ###グローバルスコープ <?php ?>のブロック中に書かれたものは別のブロックやファイルでも使える。 ###ローカルスコープ 関数やクラスのメソッド内で宣言されたものはその中だけしか使えない。 ###スーパーグローバル変数 どこからでも使える変数。PHP実行時に自動的に定義される。 GETやPOSTなどもこのスーパーグローバル変数。 ##定数 ```php define('MOMO','甘い'); ``` 甘いという文字列が入ったMOMOという名前の定数を定義してる。 ※定義可能なのは整数、小数、文字列のみ。配列やオブジェクトはダメ。 ※定数名は大文字、小文字区別される。 ##型 phpには8つの型がある。メソッドによって自動でキャストされたりする(動的型付け言語)ので、どのタイミングでどうなるのかを把握しとく必要がある。 ###自動キャスト ・フォームから入力されてきた値は全て「文字列」なので注意!! ・if文などで比較する時も「$value = 1」とすると文字列の中身を整数へ自動キャストして比較している!! ・比較する時に数値っぽい文字列は数値へ自動キャストされる。 ※"012345"と"12345"を「==」で比較すると「同じ」とみなされる ###ヒアドキュメント 長い文字列を""や''で変数に格納するのは面倒なので、そういう時に使える。 「変数 = <<<EOI」の後に文字を入れて、「EOI;」で閉じる。 文中に「{}」をつけて変数を書けば展開される。 ```php <?php $a = 1; $value = <<<EOI あいうえお かきくけこ {$a} さしすせそ。 EOI; ?> ``` ※「EOI」じゃなくてもなんでもいい ※閉じる時の任意の文字列(この場合は「EOI;」)のある行には他の文字や空白などが含まれてるとエラーになる ###論理型 BOOLEAN。真偽値を扱うもの。 if文などで論理型と論理型以外を比較すると自動的に論理型へキャストされて比較される。 整数0や空文字''、文字列の0、要素数が0の配列、nullは「false」と同じ。 ###null nullが代入されてるもの、値が何もはいってないもの、unset()されてるものは「null」 nullが入っている変数は「定義された変数」とみなされる。 ###文字列 「.」で連結できる ```php $a = 'aiu'; $b = 'eo'; $c = $a.$b; ``` #if文以外の条件式記述方法 if文を書くより分量少なくてすむ。 ```php $value = isset($a) ? $a : 'none'; ``` ※もし、変数aが空じゃなければ変数aを代入。空なら文字列noneを代入する。 ###ページを移動する headerファンクションはhtmlが出力される前に実行しないとダメ。 ```php:ページを移動する header('Location:移動先のURL'); ``` ###他のphpファイルを読み込む ```php //一度きりの読み込みなら <?php include_once('check.php'); ?> //何度も読み込めるもの <?php include('check.php'); ?> ``` その他に ```php //一度きりの読み込みなら <?php require_once('check.php'); ?> //何度も読み込めるもの <?php require('check.php'); ?> ``` もある。違いは、requireの場合は読み込みエラーになったら実行停止するが、includeは警告のみ。 check.phpを1回だけ読み込ませる。 なので、各ページで同じ処理をする場合は1つのPHPファイルにその処理を書いて、 各ページでそのファイルを読み込む様にすればいい。 ```php <?php require('check.php'); ?> ``` でもいい。違いはよく分からない。 #配列 phpの配列はハッシュやマップなどの区別はなく、全部「連想配列」。 どんな型の値でも入れられる。 キーが重複してた場合は上書きされる。 ###配列の初期化 ```php $arr[]; ``` ブラケット「[]」をつけると、その変数は「配列」として初期化される。 ###色々な配列の書き方 ```php 1. $array1[0] = "あいうえお"; $array1[1] = "かきくけこ"; $array1[2] = "さしすせそ"; 2. $array2[] = "あいうえお"; $array2[] = "かきくけこ"; $array2[] = "さしすせそ"; 3. $array3 = array("あいうえお", "かきくけこ", "さしすせそ"); 4. $array4["a"] = "あいうえお"; $array4["ka"] = "かきくけこ"; $array4["sa"] = "さしすせそ"; 5. $array5 = array( "a" => "あいうえお", "ka" => "かきくけこ", "sa" => "さしすせそ" ); ``` ###配列への値の追加・削除 ```php:追加 $value = array('a','b',7); //もしくは &value[] = 'd'; ``` ```php:配列の末尾に追加 array_push($value, 'a'); ``` ```php:配列の末尾を削除 array_pop($value); ``` ###連想配列を使う ```php:連想配列として追加 $items = array('ケーキ'=>'あまい', 'レモン'=>'すっぱい'); ``` ```php:連想配列の値を取り出す $items['ケーキ']; //「あまい」が取り出される ``` ```php:連想配列の値を全部取り出す foreach($items as $key => $value { printf($key.'は'.$value.'です'); } ``` #時刻を取得する ```php print(date(‘G’).’時’.date(‘i’).’分’.date(‘s’).’秒’); ``` またはもっと簡単にするなら ```php print(‘現在は’.date(‘G時 i分 s秒’).‘です’); ``` という感じで、 dateの中にパラメータを「G」とか「M」とか入れれば、「年」や「月」が取得できる。 ※Warningが表示された場合 PHP5.1.0から「タイムゾーン」を設定する必要が有るため。Dateを使う前に <?php date_default_timezone_set(‘Asia/Tokyo’); ?> を追加すればOK。 ##時刻を日本時間に変更する php.iniの設定を以下に変える ``` date.timezone = Asia/Tokyo ``` #フォーム関係 ##フォームの値を取得する ###複数選択のチェックボックスやリストボックスの値を取得 値は2次元配列で入っている。 ```html <input type=”checkbox” name=”reserve[]” value=”男”> <input type=”checkbox” name=”reserve[]” value=”女”> ``` という形で、name属性を配列にして、下のように2次元配列の形で取得する。 ```php:複数選択のボックスの値を取得 $_POST['reserve'][0] $_POST['reserve'][1] ``` ##フォームに入力された値を表示する時のセキュリティ対策 フォームからjsなどのスクリプトをそのまま送られると実行されてしまうので、回避する。 ラジオボタンやチェックボックスにも使うこと! ```php:htmlspecialcharsファンクション $value = htmlspecialchars($_POST['name属性の名前', ENT_QUOTES,'UTF-8'); ``` 「ENT_QUOTES」はシングルクウォーテーションも変換して回避するよ。というもの。 定数なので、ただの「3」でもいい。 ##フォームの値をチェック、変換する ###半角の「数字」かチェックする ```php:半角数字かチェックする if(is_numeric($age)){ //処理 } ``` ###全角英数字を半角英数字へ変換する ```php:全角英数字を半角英数字へ変換する mb_convert_kana($_POST['age'],'a','UTF-8'); ``` 「a」だと半角英数字へ。「n」にすると全角数字➡半角数字へ変換する。 他にも「」に何を入れるかで変換の仕方が違うので調べてみてね。 ###フォームに入力された内容が正しい形式か、正規表現を使ってチェックする ```php:例)郵便番号を正規表現を使ってチェックする if(preg_match("/|A|d{3}¥-¥d{4}¥z/", $zip)){ //処理 }else{ $error['zip'] = "郵便番号の形式が違います。"; } ``` ##チェックボックス・ラジオボタンやリストボックスの値を記憶しておく ```html+php <select name="prefecture" id="prefecture"> <option value="">都道府県の選択</option> <?php foreach($prf as $key => $value){ ?> <option value="<?php print($value); ?>" <?php if (isset($_POST['prefecture']) && $_POST['prefecture'] == $value){ print " selected"; }?>><?php print($value); ?></option> <?php } ?> </select></td> ``` という感じで、 もしPOSTに値が入っていて、かつ、その値がボックスのvalueと同じなら属性にselectedをつける。 とやればいい。 ※チェックボックスやラジオボタンはselectedのかわりに「checked」をつければいい。 #次ページへ値を渡す ###aタグで渡す ```php <a href="hoge.php?text=".urlencode('テスト')." /> ``` ###headerファンクションで渡す ```php $url = 'negotiate.php?food_id='.$_GET['food_id']; header("Location:$url"); ``` ※headerファンクションが実行されるまでに文字列などが出力されてしまっていると実行されない。 「Location : $url」という形で間空けてあると実行されないので注意!! #Cookie(クッキー)を使う ###Cookieを使ってフォームに入力された値を記憶しておく クッキーは時間指定をしてあげて、その時間が経ったらブラウザ側で破棄される仕組み。 1週間自動ログイン機能などに使える。 【処理順序】 1. まず、クッキーがあるかどうか確認 2. なければ、あげる(作る) 3. あれば、読み込む ```php:Cookieを使ってフォーム内容を記憶 //ログインフォーム.php if(isset($_COOKIE['名前'])){ $value = $_COOKIE['名前']; }else{ } <input type="checkbox" name="save" value="on" /><label for="save">1週間ログインしたままにする</label> //ログイン後フォーム.php if($save == 'on'){ setcookie('クッキー名',保存する内容,破棄までの時間(time()+60*60*24*7)); }else{ //空の値を入れる事でcookieを破棄する setcookie('クッキー名',''); } ``` 「setcookie」はヘッダーが送信される前に渡さないとエラーになるので注意。 timeは60秒×60分×24時間×7日で1週間になります。 #セッション(SESSION)を使う セッションはブラウザが閉じられるまでの間だけ保存できる仕組み。 会員登録時のフォームデータ記憶などに使える。 クッキーを使って、IDを受け渡して、次回訪問時にそのIDを元にデータを引っ張ってくる。 クッキーがOFFになってると使えない!! ``` 1.セッションスタートする(session_start();) 2.セッション(例えば、$_SESSION[‘counter’])があるかどうか(issetで調べる) 3.なければ、セッションを作る($_SESSION[‘counter’]=0などとして) 4.セッションが必要なければ削除する。(session_unset();) ``` ※毎ページでセッションスタートさせてデータ取り出しする必要があるので、セッションスタートを省略したいならphp.iniファイルを書き換える。 ※違うPCでセッションIDが読み取られれば、そのIDを使ってデータ取得出来てしまうので、 他にもIPアドレスを調べたり、ブラウザ種類でマッチさせたりと確認して認証する必要が有る。 #データベースへのアクセス PHPではMySQLデータベースへのアクセスには、PEAR DB、 PEAR MDB 、PEAR MDB2 、MySQL拡張モジュール、mySQLi 、PDO など色々な方法がある。 ##PDOを使ったMySQL pdoはjavaのやつみたいにDBがポスグレだったりmysqlだったりしても同じ形式で書けば、変換してクエリーを出してくれる仕組み。 Windowsで使うにはphp.iniの設定が必要。 Pdoを使うにはpdoクラスのオブジェクトを作成して使う。 ##dbへの接続 ```php $dsn = 'mysql:dbname=test;host=localhost;charset=utf8'; $user = 'root'; $password = ''; $options = array( // SQL実行失敗時に例外をスロー PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // デフォルトフェッチモードを連想配列形式に設定 PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // バッファードクエリを使う(一度に結果セットをすべて取得し、サーバー負荷を軽減) // SELECTで得た結果に対してもrowCountメソッドを使えるようにする PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, ); // PDOオブジェクト生成 $dbh = new PDO($dsn, $user, $password, $options); ``` ※文字コードは「UTF-8」じゃなくて「utf8」だから注意!! ##SQL文(クエリー)作成 ```mysql $stmt = $db->prepare('INSERT INTO message (name,body) VALUES (:name,:comment)'); ``` ##プレースホルダに値をセット ```php $stmt->bindParam(':name', $name, PDO::PARAM_STR); $stmt->bindParam(':comment', $comment, PDO::PARAM_STR); ``` ##SQL文実行 ```mysql $stmt->execute(array($username,$message)); ``` ##結果を1件ずつ処理 ```php while($row = $stmt->fetch(PDO::FETCH_ASSOC)){ //処理内容 } ``` ##dbを閉じる PHPスクリプトが終了すればデータベースへの接続は閉じられますが、明示的に閉じる場合には作成したPDOクラスのオブジェクトに"NULL"を代入します。 ```php:dbを閉じる $dbh = null; ``` ##SQL文書いて処理を行う 1回だけ使用するようなSQL文をデータベースへ送信するにはPDOクラスで用意されている"query"メソッドを使います。 ```php:1回だけ使う場合のSQL文 $sql = 'select id, name from shouhin'; $stmt = $dbh->query($sql); ```` ##preparedstatementを使ったSQL文 繰り返し使いそうなSQL文はプリペアードステートメントというSQL文のひな形をまず作ってから、 値を入れて実行する。 値の入れ方は2通り。「?」を使うものと「:name」などの名前つきパラメータを使う方法がある。 ```php:?を使う場合 $sql = 'select id, name from mypdo where id > ? AND id < ?'; $stmt = $dbh->prepare($sql); $stmt->execute(array(2, 4)); ``` ```php:名前つきパラメータを使う場合 $sql = 'select id, name from mypdo where id > :kagen AND id < :jyougen'; $stmt = $dbh->prepare($sql); $stmt->execute(array(':kagen'=>2, ':jyougen'=>4)); ``` ##SELECTで結果を取得する PDO::FETCH_NUMを指定した場合は下記のようになります。 ``` $sql = 'select id, name from shouhin'; $stmt = $dbh->query($sql); ``` ```php $result = $stmt->fetch(PDO::FETCH_ASSOC); print($result[0]); print($result[1]); ``` PDO::FETCH_ASSOCを指定した場合は下記のようになります。 ```php $sql = 'select id, name from shouhin'; $stmt = $dbh->query($sql); ``` ```php $result = $stmt->fetch(PDO::FETCH_ASSOC); print($result['id']); print($result['name']); ``` PDO::FETCH_BOTHを指定した場合(又は引数を省略した場合)は、どちらの形式でも利用が可能です。 ##取得したデータをフェッチしてループで使う ```php <?php while($table2 = $stmt2->fetch(PDO::FETCH_ASSOC)){ ?> 内容 <?php } ?> ``` ##INSERTで挿入したデータのIDを取得する ```php $id = $stmt->lastInsertId(); ``` ##ページング機能をつける SELECTのクエリーの最後に「LIMIT 10」という形でつけると、1ページに表示される件数が10件になる。 ```php:ページング <?php $page = $_GET['page']; //最初のページで10件表示させるため if($page == ''){ $page = 1; } $page = max($page, 1); //maxはどっちか大きい方を返すファンクション。この場合1より小さければ1が返る。 //最終ページを取得する $sql = 'SELECT COUNT(*) AS cnt FROM テーブル名'; $stmt = $dbh->prepare($sql); $table = mysql_fetch_assoc($stmt); $maxPage = ceil($table['cnt'] / 10); $page = min($page, $maxPage); //端数が出ても切り上げさせる。2.4ページ➡3ページになる。 $start = ($page -1) * 10; $sql = 'SELECT * FROM テーブル名 ORDER BY date LIMIT'.$start.',10'; ?> //html部分 //1ページ目なら前ページへは出さず、maxページなら次へページは出さないようにする <?php if($page > 1){ ?> <a href="現在のページパス?page=<?php print($page -1); ?>">前のページへ</a> <?php } ?> <?php if($page < $maxPage){ ?> <a href="現在のページパス?page=<?php print($page +1); ?>">次のページへ</a> <?php } ?> ``` LIMITが「1,5」「5,5」「10,5」と増えていって表示する件数を指定できる。 #文字列操作 ##文字を切り取る・切り抜く・抜き出す ```php $text = 'abcdef'; $result = substr($text, -2); // "ef" を返す $result = substr($text, -3, 1); // "d" を返す ``` 「-(マイナス)」で指定すれば、後ろから何文字を抜き出す。 引数を二つ指定すれば、「何文字目から」「何文字分を」抜き出す。 ##文字を丸める(一定文字以上は...にして省略するとか) ```php mb_strimwidth($a,0,10,'...','UTF-8') ``` ※変数aの中の文字列を0から10まで(日本語は2つで1文字)表示し、それ以上のものは..にする。 文字化けする場合は、最後に文字コードをつける。 ※Mysqlと違って’UTF-8’と書くので注意!!(''を忘れないこと。大文字で書くこと。) #ファイル操作 ###ファイル読み込み ```php $data = file_get_contents("読み込むファイルパス"); print($data); //そのままファイルの中身が見れる ``` ###読み込みと表示を同時に行う場合 ```php readfile("パス"); ``` ###ファイルに追記 ```php $data = file_get_contents('読み込むファイルパス'); //getでファイル読み込んで $data .= '内容'; //dataに追記して file_put_contents('書き込むファイルパス',$data); //putして保存する ``` ###RSSやWebAPIなどで使われているXMLファイルを読み込む ※PHP5から使えるようになったファンクション ```php $xml = simplexml_load_file('読み込むxmlファイルのURL'); //今回の場合、ファイル内の入れ子になった要素を指定してる foreach($xml -> channnel -> item as $item){ //処理内容 }; ``` ###ファイル書き込み ```php $success = file_put_contents('書き込むパス','書き込む内容'); ``` ※パスワードなど見られたら困るファイルはドキュメントルート(httpdocs,htdocsなど)の外に設置すること。 (URL打ち込むと見れちゃうので) ##画像ファイルなどのアップロード ###アップロードされた画像の受信 ```php //1.フォームを用意(methodは必ずpost。enctypeは下記のようにする) <form action="" method="post" enctype="multipart/form-data"> //2.フォームから送られたファイルを受信 $file = $_FILES['my_img']; //3.受信したファイルからいろいろな情報が取れるので、いろいろやる $file['name'] //ファイル名 $file['type'] //ファイルタイプ(拡張子) ※アバウトなので拡張子判定には使わない方がいい $file['tmp_name'] //アップロードした一時保管ファイル場所 $file['error'] //エラー内容 $file['size'] //ファイルサイズ //4.自分のサーバへ送る $filepath = '送り先ファイルパス'; move_uploaded_file($file['tmp_name'],'/picture/'.$filepath); //移動元のファイルパスと移動先パス //上のファンクション結果はboolean値で返ってくる ``` ###画像ファイルの移動 フォーム画面から確認画面へ移る際、指定された画像を一時保管からサーバへ移すが、 確認画面から「戻る」を押した場合、ファイルが残ってしまうので、 いったん、一時保管からサーバ上の一時保管へ移し、登録完了時にサーバ一時保管からサーバの画像フォルダへ移動し、 戻るボタンを押した際には、サーバの一時保管ファイル内画像を消去する時などに使える。 ```php rename("/tmp/file.txt", "/home/hoge/dir/file.txt"); ``` 移動元パスと移動先パスを入れる。 ```html+php:ユーザ登録画面 $("#form_email").keyup(function(){ var email = $('#form_email').val(); $.ajax({ type: "POST", url: "json.php", data: 'email='+email, //成功時のコールバック success: function (data, textStatus, xhr) { if(data[0].count == 0){ $("#email_rst").text('OK'); }else{ $("#email_rst").text('NG'); } } }); }); ``` #カレンダー作成 ``` <?php // 現在の年月を取得 $year = date('Y'); $month = date('n'); // 月末日を取得 $last_day = date('j', mktime(0, 0, 0, $month + 1, 0, $year)); $calendar = array(); $j = 0; // 月末日までループ for ($i = 1; $i < $last_day + 1; $i++) { // 曜日を取得 $week = date('w', mktime(0, 0, 0, $month, $i, $year)); // 1日の場合 if ($i == 1) { // 1日目の曜日までをループ for ($s = 1; $s <= $week; $s++) { // 前半に空文字をセット $calendar[$j]['day'] = ''; $j++; } } // 配列に日付をセット $calendar[$j]['day'] = $i; $j++; // 月末日の場合 if ($i == $last_day) { // 月末日から残りをループ for ($e = 1; $e <= 6 - $week; $e++) { // 後半に空文字をセット $calendar[$j]['day'] = ''; $j++; } } } ?> <?php echo $year; ?>年<?php echo $month; ?>月のスケジュール <br> <br> <table class="calender"> <tr class="week"> <th class="sunday">日</th> <th>月</th> <th>火</th> <th>水</th> <th>木</th> <th>金</th> <th class="saturday">土</th> </tr> <tr> <?php $today = date('j'); ?> <?php $cnt = 0; ?> <?php foreach ($calendar as $key => $value): ?> <td <?php if($value['day'] == $today) echo 'class="today"'; ?>> <?php $cnt++; ?> <?php if(!empty($value['day'])): ?> <a href=""> <span class="day"><?php echo $value['day']; ?></span> </a> <?php endif; ?> </td> <?php if ($cnt == 7): ?> </tr> <tr> <?php $cnt = 0; ?> <?php endif; ?> <?php endforeach; ?> </tr> </table> ``` #WEBプログラミングから起業までを一貫して学べるオンライン動画総合学習サービス『ウェブカツ!!』 様々なプログラミング学習サイトやサービスでは正直な所、自分でWEBサービスを作れるようにはならないため、「自分でWEBサービスを作れるようになる!」をゴールとしたオンライン動画総合学習サービス『ウェブカツ!!』を立ち上げました。興味ある方は登録よろしくお願いいたします。 http://webukatu.com/ |
|
| 515位 |
|
|||
|
22:14:04 |
|
|
#####mysql -u (ユーザー名) = 起動
#####mysql -u (ユーザー名) -p = パスワードを入力して起動 #####use (データベース名) = データベースを切り替えるコマンド #####set password for root@localhost=password('設定したいパスワード'); #データの中身の確認 select * from (テーブル名) #データベースを作る create database (データベース名); #####grant all on (データベース名).* to dbuser@localhost identified by '設定したいパスワード';作業ユーザーを作る #####テーブルを作る(例文) create table users ( id int not null auto_increment primary key, name varchar(255), email varchar(255) unique, password char(32), score double, sex enum('male', 'female') default 'male', memo text, created datetime, key score (score) ); #データベースの中身の確認 show databases; #データベースの中にあるテーブルの確認 show tables; #####show indexes from (テーブル名)特定のテーブルにどういった索引が設定されているか #####drop table users; #####desc users=テーブルの構造を見る事ができる #####insert into users =values ();レコードの挿入(挿入するレコード一覧) #####select (フィールド名) from (テーブル名);テーブル内のデータの確認 #####select (フィールド名) from (テーブル名) \G テーブル内のデータを縦に表示にする #####select (フィールド名) from (テーブル名)where(フィールド名)(条件) #####whereに指定する条件の種類= #####否定= !=,<> #####曖昧な検索の時はlikeを使う ##### like #####'%~'=無制限の任意の文字 #####_の場合は任意の一文字 #####between () and () でその間の条件を指定する ##### in('値','値');でその2つの条件を指定する #####and = 共通の条件を指定する #####or = またはの条件を指定する #####select * from (テーブル名) order by (フィールド名);小さい順に #####select * from (テーブル名) order by (フィールド名) desc;大きい順に #####select * from (テーブル名) limit (数字);件数の制限 #####select * from (テーブル名) limit (数),(数);数字は添字、開始位置の指定 #####select count(*) from(テーブル名) ;レコードの総件数を調べる場合 #####select distinct (フィールド名) from (テーブル名) ; #####select max(フィールド名) from (テーブル名) ;最大値,最小値,平均,合計(max,min,avg,sum) #####select sum(フィールド名) from (テーブル名) group by (フィールド名);チームごとに集計する場合 #####select rand();乱数を表示させる 組み合わせselect * from (テーブル名) order by rand() limit 1; #####select (フィールド名),length(フィールド名)from(テーブル名) ;文字数の長さを調べる #####select concat(フィールド名, '(', フィールド名, ')') from users;文字数を連結させる #####select concat(フィールド名, '(', フィールド名, ')') as (ラベル名) from users;ラベルをつける #####select now();現在時刻を表示する #####レコードの更新 #####update (テーブル名) set = 'kimura@dotinstall.jp' where id = (数字); #####delete from (テーブル名) where (フィールド名) <= 3.0;レコードの削除 #####alter table (テーブル名) add (追加するフィールド名) varchar(255) ;テーブルの構造を変更させたい場合,フィールドを足す、どのフィールドの下にのせるかを付ける場合はafter (フィールド名)を指定する #####alter table (テーブル名) change (フィールド名)(フィールド名) varchar(100);フィールドの数値を変更する #####alter table (テーブル名) add index email (email);index(索引)を追加する #####alter table (テーブル名) drop index email;index(索引)を消去する #####alter table (テーブル名) rename (変更するテーブル名);table名を変える #####select users.name, posts.title from users, posts where users.id = posts.user_id; #####複数のtableにまたがって指定していく場合は(table).(field) #####select users.name, posts.title from users, posts where users.id = posts.user_id order by posts.created #####desc; #####order byを組み合わせをした場合 #####mysql -u (ユーザー) -p (データベース名) < ファイル名;=外部のテキストファイルにコマンドを保存して#####使う方法 #####例 mysql -u dbuser -p blog_app < commands.sql; #####mysqldump -(ユーザー名) -p (データベース名) >データベース名.dump.sql=バックアップをとる #####例 mysqldump -u dbuser -p blog_app > blog_app.dump.sql #####mysql -(ユーザー名) -p (データベース名) <データベース名.dump.sql=復元 #####例 mysql -u dbuser -p blog_app < blog_app.dump.sql |
|
| 516位 |
|
|||
|
01:57:31 |
(ShouldBee 所属) |
|
もはや常識だと思うが、ウェブシステム開発にあたって3つの環境はそれぞれ独立して用意しておくといい。(独立といっても同じサーバにあっても構わないこともある。)
* 開発サーバ: 開発者用テスト環境 * ステージングサーバ: 顧客用レビュー環境 * プロダクションサーバ: 本番環境 ## 開発サーバ 開発者がコミットした内容を開発者が反映し、開発者がテストする環境。基本的に顧客や運用チームに見せることはない。 ローカル開発環境とは別に用意するのがポイント。「ローカルだと動くんだけど」病にいち早く気がつくために重要。ローカルはしばしば、開発者がライブラリへのリンクや各種設定を無意識のうちに行なっていて、そのためローカルでは動くがサーバでは動かないということがある。 この病の典型的な例としては次のようなものがある。 * Case-Insentive環境(ローカル)で動くが、Case-sensitive環境(リモート)で動かない。パスを指定したファイル名の大文字小文字など。 * コミット漏れ。 * コードベースで管理されていないライブラリへのリンク 開発サーバは、個人によるプロジェクトであろうが、チームによるプロジェクトであろうが、ローカル環境とは独立した環境として用意しておくことが望ましい。 ## ステージングサーバ ステージングは、運用担当や実際のユーザ(すなわち顧客)がテストを行う環境。ステージングには、開発サーバでテストが完了した機能・特徴が提供(デプロイ)される。 ステージングでのテストはデバッグのためのテストではなく(デバッグは既に開発サーバで完了していなければならない)、要件を満たしているか、実際の運用をするにあたって支障がないかなどを確認・レビューする。このレビューの結果、顧客からフィードバックが行われることがある。 ## プロダクションサーバ プロダクションは、システムが製品として提供され、運用担当や実際のユーザ(顧客)がシステムを利用する本番環境。ステージングでテスト(レビュー)が完了した機能・特徴が提供される。 |
|
| 517位 |
|
|||
|
13:04:35 |
(Freelancer 所属) |
|
OpenEars は Politepix 社より提供されている **フリーの iOS 向け音声認識/音声合成(Text to Speech, TTS)ライブラリ** です。
[OpenEars](http://www.politepix.com/openears)  **話した言葉を認識**したり、 **入力した文字列を読み上げ** たり(mac の say コマンドみたいなもの)することができます。 試してみたところ超簡単に使えたので、自分のアプリに OpenEars を導入する方法を紹介します。 (2014.3.31追記) **OpenEars 最新バージョンでの音声認識の導入方法については次の記事をご参照ください:『[OpenEars 1.6で音声認識を行う](http://d.hatena.ne.jp/shu223/20140126/1391077538)』** ##音声合成の導入方法 フレームワーク追加、ヘッダインポートといった一般的なライブラリの導入手順をのぞけば、基本的には **メソッドを1つ呼ぶだけ** で導入できます。 ###<b>1. フレームワークをプロジェクトに追加</b> 解凍したフォルダ配下にある **Frameworkフォルダごと** プロジェクトに追加します。フォルダには OpenEars.framework、Slt.framework ほか、言語モデルや辞書が入っています。 また、依存フレームワークである - AVFoundation.framework - AudioToolbox.framework をプロジェクトに追加しておきます。 ###<b>2. ヘッダをインポート</b> ```` #import <Slt/Slt.h> #import <OpenEars/FliteController.h> ```` ###<b>3. オブジェクトを生成</b> FliteController, Slt オブジェクトを生成します。 プロパティを定義しておき、 ```` @property (strong, nonatomic) FliteController *fliteController; @property (strong, nonatomic) Slt *slt; ```` alloc / init します。 ```` self.fliteController = [[FliteController alloc] init]; self.slt = [[Slt alloc] init]; ```` ###<b>4. say: メソッドをコール</b> あとは音声合成で発声させたい文字列を渡して `say:` メソッドをコールするだけです。 ```` [self.fliteController say:@"Hello world!" withVoice:self.slt]; ```` ###<b>(補足その1)日本語を発声させる</b> OpenEars (少なくとも上記の使い方では)日本語には対応していませんが、ローマ字的に文字列を渡すことでそれっぽくすることはできます。 ```` [self.fliteController say:@"Ai ou es apuri kaihatsu. Tatsujinn no recipe hyaku. Hatsubai chuu" withVoice:self.slt]; ```` ###<b>(補足その2)声を変える</b> Bitbucket に [openearsextras](https://bitbucket.org/Politepix/openearsextras/src) というリポジトリがあり、ここに声を変えるためのデータ(.framework として提供されている)が置いてあります。 たとえば上記では SLT.framework を用いましたが、openearsextras の中にある [Awb.framework](https://bitbucket.org/Politepix/openearsextras/src/9f15cdb9e746732402e928424a46debb9d79ecc7/FliteVoices/Awb.framework?at=master) を用いると、 **男性の声** で読み上げられます。 ###品質について 音声合成の品質は、英語の場合でも「そこそこ」です。抑揚が変。まだドキュメントを詳しく読んでないのですが、細かいチューニングができるようになっているかもしれません。 ##音声認識の導入方法 以前 [VocalKitの使い方](http://d.hatena.ne.jp/shu223/20110227/1299368179) という記事を書きましたが、執筆時から3年近く経っているので、内容が古くなっている可能性があります。(VocalKit のリポジトリも2年以上更新されていません。) この OpenEars は VocalKit と同様に音声認識エンジンとして CMU Sphinx を使用しているので、その代替になりそうです。 実装方法は下記の通り。音響モデルや言語辞書の設定があったり、音声認識の各種ステータスを受け取るためにプロトコルの実装が必要だったりして一見煩雑ですが、 **要は startListeningWith~ メソッドを呼んで認識結果を受け取るだけ** (発話の検出とかは勝手にやってくれる)なので、とてもシンプルです。 ###※2014.1.30追記 本記事にあるコードは **OpenEars最新バージョンにおいてビルドエラーが起きる** 箇所があります。最新版を用いた音声認識の実装方法については下記記事も併せてご参照ください。 - [OpenEars 1.6で音声認識を行う](http://d.hatena.ne.jp/shu223/20140126/1391077538:title:bookmark) ###<b>1. フレームワークをプロジェクトに追加</b> ここは音声合成と同様です。 ###<b>2. ヘッダをインポート</b> ```` #import <OpenEars/LanguageModelGenerator.h> #import <OpenEars/PocketsphinxController.h> ```` ###<b>3. プロトコルへの準拠</b> @interface で、音声認識の各種ステータスを受け取るための OpenEarsEventsObserverDelegate への準拠を宣言しておき、 ```` <OpenEarsEventsObserverDelegate> ```` 各種メソッドを実装します。 ```` - (void)pocketsphinxDidReceiveHypothesis:(NSString *)hypothesis recognitionScore:(NSString *)recognitionScore utteranceID:(NSString *)utteranceID { NSLog(@"The received hypothesis is %@ with a score of %@ and an ID of %@", hypothesis, recognitionScore, utteranceID); } ```` OpenEarsEventsObserverDelegate プロトコルには多くのメソッドが定義されていますが、 **全部 optional** なので、とりあえず認識結果を受け取るための上記メソッドを実装しておくだけでも雰囲気はつかめるかと思います。 ###<b>4. オブジェクトを生成</b> 各種プロパティを定義しておき、 ```` @property (strong, nonatomic) NSString *lmPath; @property (strong, nonatomic) NSString *dicPath; @property (strong, nonatomic) PocketsphinxController *pocketsphinxController; @property (strong, nonatomic) OpenEarsEventsObserver *openEarsEventsObserver; ```` 言語モデル/辞書のパスと、各種オブジェクトを保持しておきます。`delegate` プロパティのセットも忘れずに。 ```` NSString *resorcePath = [[NSBundle mainBundle] resourcePath]; self.lmPath = [NSString stringWithFormat:@"%@/%@", resorcePath, @"OpenEars1.languagemodel"]; self.dicPath = [NSString stringWithFormat:@"%@/%@", resorcePath, @"OpenEars1.dic"]; self.pocketsphinxController = [[PocketsphinxController alloc] init]; self.openEarsEventsObserver = [[OpenEarsEventsObserver alloc] init]; [self.openEarsEventsObserver setDelegate:self]; ```` ###<b>5. 認識スタート</b> `startListeningWithLanguageModelAtPath:` メソッドをコールすると音声入力がスタートし、あとは勝手に発話を検出(一定レベルの音声入力を検出して発話開始と判定、一定時間の空白を検出して発話終了と判定)して認識してくれます。 ```` - (void)startListening { [self.pocketsphinxController startListeningWithLanguageModelAtPath:self.lmPath dictionaryAtPath:self.dicPath languageModelIsJSGF:NO]; } ```` 発話を検出するたびに認識処理が走り、手順3で実装した `pocketsphinxDidReceiveHypothesis:recognitionScore:utteranceID:` が呼ばれます。Hypothesis が候補の文字列、recognitionScore はそのスコアです。 ###<b>(補足その1)認識終了</b> `stopListening` メソッドを呼ぶと認識処理(音声入力の待ち受けと、認識処理)が終了します。 ```` [self.pocketsphinxController stopListening]; ```` ###<b>(補足その2)言語モデルの動的生成</b> LanguageModelGenerator を用いると、認識したい言葉の配列から言語モデルを動的に生成することができます。 認識したい言葉(単語/フレーズ)の配列を生成し、 ```` NSArray *words = @[ @"SUNDAY", @"MONDAY", @"TUESDAY", @"WEDNESDAY", @"THURSDAY", @"FRIDAY", @"SATURDAY", @"QUIDNUNC", @"CHANGE MODEL", ]; ```` `generateLanguageModelFromArray:withFilesNamed:` メソッドをコールします。 ```` LanguageModelGenerator *lmGenerator = [[LanguageModelGenerator alloc] init]; NSError *error = [lmGenerator generateLanguageModelFromArray:words withFilesNamed:@"OpenEarsDynamicGrammar"]; if (error.code != noErr) { NSLog(@"Error: %@",[error localizedDescription]); } else { NSDictionary *languageGeneratorResults = [error userInfo]; self.lmPath = [languageGeneratorResults objectForKey:@"LMPath"]; self.dicPath = [languageGeneratorResults objectForKey:@"DictionaryPath"]; } ```` NSError の `userInfo` に処理結果が入っている、というのはちょっと変な感じがしますが。。 |
|
| 518位 |
|
|||
|
22:43:00 |
|
|
サーバを使ったiPhoneアプリを作りたかったので、簡単にサーバ側作れないか調べてみました。結論として結構簡単に出来る事が分かったのでメモメモ。 ##参考にした記事 [Sinatra+ActiveRecord+SQLite3で,軽量なWeb-DB連携例 | tamo's blog](http://tamosblog.wordpress.com/2012/10/26/sinatra/) 大分参考にさせて頂きました。ありがとうございます! 仕事ではMySQLを使う事が多いので、SQLite3ではなくMySQLを使う事にしました。 ##手順 1. Ruby,MySQLをインストール 2. データベースを作成 3. Gemfileを作成とインストール 4. database.ymlを作成 5. Rubyファイルを作成 6. 実行 7. 動作確認 ## 1. Ruby,MySQLをインストール 結構な量になったので別記事にまとめました。ここが一番面倒(´Д`) - [Ruby,MySQLの環境を構築する(Mac版) #MySQL #Mac #Ruby #homebrew - Qiita](http://qiita.com/items/e2cf0fa27f9763d3bdd4) - [Ruby,MySQLの環境を構築する(Linux版) #MySQL #Ruby #Linux - Qiita](http://qiita.com/items/c9f3d8ca69a4ab0b27cb) ##2. データベースを作成 MySQLの初歩的なコマンドを知っていれば、ここは問題ないと思います。 ActiveRecordの流儀で、テーブル名は複数形にする必要がある事だけ注意! サンプルとして、タイトル, 詳細文, 作成日時, 更新日時を持つ、topicsテーブルを生成してみます。 ```mysql: mysql> create database hoge; Query OK, 1 row affected (0.00 sec) mysql> use hoge Database changed mysql> create table topics ( -> id INT UNSIGNED NOT NULL AUTO_INCREMENT, -> title VARCHAR(255) NOT NULL, -> description VARCHAR(255) NOT NULL, -> created_at DATETIME NOT NULL, -> updated_at DATETIME NOT NULL, -> PRIMARY KEY(id) -> ); Query OK, 0 rows affected (0.03 sec) mysql> desc topics; +-------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | title | varchar(255) | NO | | NULL | | | description | varchar(255) | NO | | NULL | | | created_at | datetime | NO | | NULL | | | updated_at | datetime | NO | | NULL | | +-------------+------------------+------+-----+---------+----------------+ 5 rows in set (0.00 sec) ``` ###サンプルデータを登録 動作確認をする為のサンプルデータを入れておきます。 ```mysql: mysql> insert into topics values(1, "title1", "description1", now(), now()); Query OK, 1 row affected (0.00 sec) mysql> insert into topics values(2, "title2", "description2", now(), now()); Query OK, 1 row affected (0.01 sec) mysql> insert into topics values(3, "title3", "description3", now(), now()); Query OK, 1 row affected (0.00 sec) mysql> select * from topics; +----+--------+--------------+---------------------+---------------------+ | id | title | description | created_at | updated_at | +----+--------+--------------+---------------------+---------------------+ | 1 | title1 | description1 | 2013-05-19 21:29:21 | 2013-05-19 21:29:21 | | 2 | title2 | description2 | 2013-05-19 21:29:29 | 2013-05-19 21:29:29 | | 3 | title3 | description3 | 2013-05-19 21:29:38 | 2013-05-19 21:29:38 | +----+--------+--------------+---------------------+---------------------+ 3 rows in set (0.01 sec) ``` ##3. Gemfileを作成とインストール 次に、使用するgemファイルを管理するファイルを作ります。 ```bash: # ひな形ファイルの作成 $ bundle init Writing new Gemfile to /home/u1/hoge/Gemfile # 編集 $ vi Gemfile source "https://rubygems.org" gem "activerecord" gem "mysql2" gem "sinatra" # 必要なGemのインストール $ bundle install --path vendor/bundle ``` ActiveRecordとMySQLを使うのでそれぞれのgemを記入。 RubyからMySQLを使いたい時はmysql2というのを使うようです。 参考:[RubyでMySQLに繋ぐためのruby-mysqlとmysql2 - tagomorisのメモ置き場](http://d.hatena.ne.jp/tagomoris/20111210/1323502295) ##4. database.ymlを作成 プログラムからDBにアクセスする為に、DB情報を書いた設定ファイルを作ります。 各値は、自分の環境に合わせて設定して下さい。 ``` development: adapter: mysql2 database: hoge host: localhost username: root password: xxxxxx encoding: utf8 ``` ##5. Rubyファイルを作成 ようやくコードを書きます。長かった。。 使うのはタイトルにある通りSinatraです。 ```ruby: # coding:utf-8 require 'active_record' require 'mysql2' require 'sinatra' # DB設定ファイルの読み込み ActiveRecord::Base.configurations = YAML.load_file('database.yml') ActiveRecord::Base.establish_connection('development') class Topic < ActiveRecord::Base end # 最新トピック10件分を取得 get '/topics.json' do content_type :json, :charset => 'utf-8' topics = Topic.order("created_at DESC").limit(10) topics.to_json(:root => false) end # トピック投稿 post '/topic' do # リクエスト解析 reqData = JSON.parse(request.body.read.to_s) title = reqData['title'] description = reqData['description'] # データ保存 topic = Topic.new topic.title = title topic.description = description topic.save # レスポンスコード status 202 end ``` 基礎的な機能として、 - 最新トピック10件分を取得する GET /topics.json - トピックを登録する POST /topic を作ってみました。 ##6. 実行 書いたコードを動かします。ここでもちょっとはまった事があったので注意!(参考:[Sinatraがデフォルトでは外部から繋がらなくなってたよ #Sinatra #Ruby - Qiita [キータ]](http://qiita.com/items/b86b21f6ed39f4c10d5d)) ```bash: $ bundle exec ruby hoge.rb -o 0.0.0.0 [2013-05-19 21:37:56] INFO WEBrick 1.3.1 [2013-05-19 21:37:56] INFO ruby 1.9.3 (2013-02-06) [x86_64-linux] == Sinatra/1.4.2 has taken the stage on 4567 for development with backup from WEBrick [2013-05-19 21:37:56] INFO WEBrick::HTTPServer#start: pid=10667 port=4567 ``` ##7. 動作確認 ###最新のトピックを取得 プログラムを動かしてるサーバの /topics.json にリクエストを送って動作確認をします。仮にlocalhostで動かした場合は、http://localhost:4567/topics.json GETリクエストを送ると、以下のようなJSON形式のレスポンスが返ってきます。これをベースに変更を加えていけば使えそう。 ```javascript: [ { "created_at": "2013-05-19T21:29:38+09:00", "description": "description3", "id": 3, "title": "title3", "updated_at": "2013-05-19T21:29:38+09:00" }, { "created_at": "2013-05-19T21:29:29+09:00", "description": "description2", "id": 2, "title": "title2", "updated_at": "2013-05-19T21:29:29+09:00" }, { "created_at": "2013-05-19T21:29:21+09:00", "description": "description1", "id": 1, "title": "title1", "updated_at": "2013-05-19T21:29:21+09:00" } ] ``` ###トピックの登録 http://localhost:4567/topic に、以下のようなBodyのPOSTリクエストを送ります。すると202レスポンスが返ってきます。 ちなみにPOSTリクエストを送るのは、[Chrome拡張機能のDevHTTPClient](https://chrome.google.com/webstore/detail/dev-http-client/aejoelaoggembcahagimdiliamlcdmfm)を使ってます。 ```javascript: { "title": "title4", "description": "description4" } ``` ちゃんと登録されてるかを、GET /topics.json で確認。 ```javascript: [ { "created_at": "2013-05-19T21:48:56+09:00", "description": "description4", "id": 4, "title": "title4", "updated_at": "2013-05-19T21:48:56+09:00" }, { "created_at": "2013-05-19T21:29:38+09:00", "description": "description3", "id": 3, "title": "title3", "updated_at": "2013-05-19T21:29:38+09:00" }, { "created_at": "2013-05-19T21:29:29+09:00", "description": "description2", "id": 2, "title": "title2", "updated_at": "2013-05-19T21:29:29+09:00" }, { "created_at": "2013-05-19T21:29:21+09:00", "description": "description1", "id": 1, "title": "title1", "updated_at": "2013-05-19T21:29:21+09:00" } ] ``` ##おわり いやーほんとに簡単に書けますね。 あとは、Sinatra と ActiveRecord を勉強しながらちゃんと実装していこうと思います。あと色々自動化していきたいなー。 何か間違ってる所とかありましたらご指摘お願いしますm(_ _)m |
|
| 519位 |
|
|||
|
11:57:12 |
(株式会社Subot 所属) |
|
--------------------- 2014/04/23追記 [別記事にshenzhenでipaファイル作成する方法を書きました。](http://qiita.com/k_kinukawa/items/8718aba35060cf838ffa) ↑のほうが簡単です。 --------------------- DeployGateでiOSアプリを配布できるようになりましたが、配布の度にGUIからちまちまアーカイブするのはだるいです。 だるくて仕事やる気がなくなりSNSをダラダラ眺めていたら、[make adhocだけで.ipaをサッと作る](http://blog.ishkawa.org/blog/2013/02/27/make-ipa/)という記事が流れてきたのでDeployGateで配布するところまで出来るようにしてみました。 まず、AdHoc配布用にプロビジョニングプロファイルを作成します。 iOS Developer CenterのCertificates, Identifiers & Profilesから、 [Provisioning Profiles] > [Distribution]でAd Hocのプロビジョニングプロファイルを作成してください。 作成したらダウンロードします。 ダウンロードしたhoge.mobileprovisionは、~/Library/MobileDevice/Provisioning\ Profiles/に放り込んでおきます。 次に、Makefileを作成します。 Makefileの作り方は参考にしたページほぼそのままです。 自分の場合、こんな感じになりました。 プロジェクト名がhogeの場合、 ```bash:Makefile default: xcodebuild -sdk iphoneos -target hoge -configuration Release build adhoc: if [ -a build]; then rm -R build; fi; xcodebuild -sdk iphoneos -target hoge -configuration Release clean build CODE_SIGN_IDENTITY="iPhone Distribution" PROVISIONING_PROFILE="12345678-FFFF-0000-9999-012345678901" xcrun -sdk iphoneos PackageApplication build/Release-iphoneos/hoge.app -o $(PWD)/build/hoge.ipa --embed hoge.mobileprovision dgate push k_kinukawa build/hoge.ipa ``` になります。 PROVISIONING_PROFILE=に続く文字列は、hoge.mobileprovisionをテキストエディタで開き、UUIDという項目をコピペします。 ```xml:hoge.mobileprovision <key>UUID</key> <string>12345678-FFFF-0000-9999-012345678901</string> ``` UUIDを指定した.mobileprovisionが~/Library/MobileDevice/Provisioning\ Profiles/に存在しないと、ビルド時にエラーが出ます。 ```bash Check dependencies Code Sign error: No matching provisioning profile found: Your build settings specify a provisioning profile with the UUID “12345678-FFFF-0000-9999-012345678901”, however, no such provisioning profile was found. CodeSign error: code signing is required for product type 'Application' in SDK 'iOS 7.0' ** BUILD FAILED ** The following build commands failed: Check dependencies (1 failure) make: *** [adhoc] Error 65 ``` 最後の一行でDeployGateにipaファイルを投げています。 [DeployGateでプロジェクトは作成しておいてください。](http://qiita.com/k_kinukawa/items/bbb3e98239e51fc3c803) [dgateコマンドをインストール](https://deploygate.com/docs)しておく必要があります。 ```bash #macの場合 $curl https://deploygate.com/install.sh | /bin/sh ``` これでmake adhocすればビルド後にアーカイブ、DeployGateへ送信して自動配布、までが1コマンドで実行できるようになりました。 ```bash $ make adhoc if [ -a build]; then rm -R build; fi; /bin/sh: line 0: [: missing `]' xcodebuild -sdk iphoneos -target hoge -configuration Release clean build CODE_SIGN_IDENTITY="iPhone Distribution" PROVISIONING_PROFILE="12345678-FFFF-0000-9999-012345678901" Build settings from command line: CODE_SIGN_IDENTITY = iPhone Distribution PROVISIONING_PROFILE = 12345678-FFFF-0000-9999-012345678901 SDKROOT = iphoneos7.0 === CLEAN TARGET hoge OF PROJECT hoge WITH CONFIGURATION Release === .... .... .... ** BUILD SUCCEEDED ** xcrun -sdk iphoneos PackageApplication build/Release-iphoneos/hoge.app -o /Users/kenji.kinukawa/hoge/build/hoge.ipa --embed hoge.mobileprovision dgate push k_kinukawa build/hoge.ipa Push app file successful! Name : hoge Owner : k_kinukawa Package : com.kkinukawa.hoge Revision : 3 URL : https://deploygate.com/users/k_kinukawa/apps/com.kkinukawa.hoge?key=**************************************** ``` jenkinsと組み合わせると更に便利そうですね! |
|
| 520位 |
|
|||
|
17:14:12 |
(wantedly 所属) |
|
##はじめに Devise gem で出来ること - webの会員登録のフォームを簡単に作れる(新規登録画面・login画面・etc...) - sign_in filterなど会員サイトを作るときに必要なhelperが多く用意されている - facebookなどのominiauthを使うことができる こんなことが出来るDevise gemを使ってみたい!ってことで調べたもののまとめです。今回は[公式ドキュメン](https://github.com/plataformatec/devise)を調べましたので書いてあることは公式とほぼ変わらないと思います。⬇から早速説明になります。 ##deviseを使う divise3.0はrails3.2以上で動きます。Gemfileに追加してください ``` gem 'devise' ``` そしたらbundleコマンドを使ってinstallしてください deviseのインストールとGemfileの追加が終わったらgeneratorを走らせる必要があります。 ``` rails generate devise:install ``` genaratorはすべてのdeviseの設定オプションをインストールするので一回確認しといてください。これが終わったのならdevise modelを使う準備は完了です。 ``` rails generate devise MODEL ``` MODELの部分はユーザーのアプリケーションとして使う名前で置き換えてください。多くの場合はUseやAdminと言ったものが使われます。これはもし既に存在していなければ新規にmodelを作り、deviseのモジュール設定をしてくれます。次に```rake db:migrate```をしてmigrationファイルを作ります。またgeneratorはDevise controllerを示すように config/routes.rbを設定してくれます。 ( 注意)既にappを作り始めていたのならやり直してください。さもなければroute helperのエラーやloginできないといったエラーが出るでしょう。 ###controllerのfilterとhelpers DeviseはcontrollerとViewの中で使えるhelperを使うことができます。controllerをset upするときにbefore_filterを加えるだけでuserがauthenticateか判断することが出来ます。 ``` before_filter :authenticate_user! ``` userがsign_inしているか判断できるhelperもあります。 ``` user_signed_in? ``` 現在sign-inしているuserを得るhelperもあります。 ``` current_user ``` またuserの活動範囲を確認できます ``` user_session ``` userがsign-inしたり、パスワードを更新したらroot_pathにredirectされます。例えばuser:resourceならばuser_root_pathが存在すればそこに、なければdefaltのroot_pathが使われます。 ``` root to: "home#index" ``` また```after_sign_in_path_for```や```after_sign_out_path_for```を使ってredirect先を上書きすることも出来ます。 最後に、mailerの為にurl_optionをすべての環境に設定する必要があります。ここでは"config/environments/development.rb"に設定します。 ``` config.action_mailer.default_url_options = { :host => 'localhost:3000' } ``` (メモ)もしuserといった devise model を使わずに member を使うのならばhelperはこのようになります ```ruby: before_filter :authenticate_member! member_signed_in? current_member member_session ``` ##Modelの設定 modelの devise method は modules の設定を受けます。たとえば暗号化するアルゴリズムを使いたいのなら ``` devise :database_authenticatable, :registerable, :confirmable, :recoverable, :stretches => 20 ``` :stretchesを除き、:pepper, :encryptor, :confirm_within, :remember_for, :timeout_in, :unlock_in, other valuesで設定することが出来ます。devise:install が使われたときに作られたfileで詳細を見ることが出来ます。 ##強調するパラメーター あなたのviewをカスタマイズするときフォームに新しい特徴を追加することが出来ます。このsanitizationパラメーターはRails4になってmodelからcontrollerに移動しました。Deviseがコレに関する扱いをするものは同様にcontrollerに移動しています。たった3つのactionでどんなパラメーターでもmodelに伝えることが出来ます。それ故sanitizationを必要と知っています。名前とパラメーターの許可はdefaultではこのようになっています。 - sign_in((Devise::SessionsController#new) - emailのような承認キーがある場合のみ許可されます - sign_up(Devise::RegistrationsController#create) - 証明キーにpasswordとpassword_confirmationを追加するのを許可します - account_update(Devise::RegistrationsController#update) - 証明キーにpassword, password_confirmation, current_passwordを追加するのを許可します。(the lazy way™)のようなパラメーターを追加で許可したい場合はApplicationControllerに簡単なbefore_filterをすることでできます。 ``` class ApplicationController < ActionController::Base before_filter :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.for(:sign_up) << :username end end ``` defaltの設定を完全に変えたり、習慣的振る舞いを作るためにpass a blockを作ることも出来ます。 ```ruby: def configure_permitted_parameters devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email) } end ``` もしあなたが複数のDevise modelを持っているのならmodelごとに異なったsanitizerパラメーターをset upすることができます。その場合、私たちはDevise::ParameterSanitizerから継承し、自身のロジックを追加することを推奨します ```ruby: class User::ParameterSanitizer < Devise::ParameterSanitizer def sign_in default_params.permit(:username, :email) end end ``` このときにcontrollerに使うことを設定してください。 ```ruby: class ApplicationController < ActionController::Base protected def devise_parameter_sanitizer if resource_class == User User::ParameterSanitizer.new(User, :user, params) else super # Use the default one end end end ``` 上記の例では:usernameと:emailの両方を使うためのパラメーターを許可してます。パラメータを設定する愚かでない方法はcustom controllerの上にbefore_filterを定義することによって出来るでしょう。私たちは設定の仕方とcontrollerのカスタマイズ方法をsectionの流れに従って記述しました。 ##Viewの設定 私たちはDeviseを使い簡単にuser認証画面を作ることが出来ます。しかし、この方法でなくても自分で作ることも出来ます。Deviseはengineですから、すべてのViewはgemの内側にあります。これらのviewは作り始めを助けれくれますが、後に変えたいと思うでしょう。その場合はgeneratorを使うことでviewをあなたのアプリケーションにコピーしてくれます。 ``` rails generate devise:views ``` もしUserやAdminといったDevise modelがあるのならmodelもviewと同じように編集したいと思うでしょう。幸運なことにmodelを改造するのはviewよりも簡単です。``` "config.scoped_views = true"```を "config/initializers/devise.rb"の中に書くだけでいいのです。 これが終わると"users/sessions/new" や "admins/sessions/new"に基づいたviewを持つことができるようになります。 scopeの中にviewが見つからなかったら"devise/sessions/new"というdefaultのviewが使われます。また選んだviewを使うためにgeneratorを使うことも出来ます。 ``` rails generate devise:views users ``` ##controllerの設定 viewのcustomizationレベルが十分でないのなら、以下の手順ですべてのcontrollerをカスタマイズすることが出来ます。 1,Admins::SessionsController:のようなcustom controller を作ります。 ``` class Admins::SessionsController < Devise::SessionsController end ``` (注意)この例はapp/controller/admins/というディレクトリが既に作られているという前提で進めています 2,使うcontrollerのrouterを書きます ``` devise_for :admins, :controllers => { :sessions => "admins/sessions" } ``` 3.controllerに変化を加えると"devise/sessions"のviewが使えなくなるので忘れずに"devise/sessions"を"admins/sessions"にコピーしてください。 Deviseはsign_inが成功したか失敗したかflash messageでuserに伝えることが出来ることを覚えていてください。Deviseはあなたのアプリが適切なに"flash[:notice]"や"flash[:alert]"を呼び出すことを期待しています。すべてのflash hash を出力したり、Deviseのhash keyから:timedout keyを削除することの無いようにしてください。 ##Routesの設定 Deviseはdafalutのroutesがあります。もしそれらをカスタマイズする必要あるのならdevise_for methodを使ってください。これは :class_name, :path_prefixといったname_pathを変えることが出来ます。 ```ruby: devise_for :users, :path => "auth", :path_names => { :sign_in => 'login', :sign_out => 'logout', :password => 'secret', :confirmation => 'verification', :unlock => 'unblock', :registration => 'register', :sign_up => 'cmon_let_me_in' } ``` 詳細はdevise_forというdocumentationを確認してください。 もし "/sign_in"だけでなく"/users/sign_in"のようにもっと深く変更を加えたいのならdevise_scopeをrouterに加えてください。 ``` devise_scope :user do get "sign_in", :to => "devise/sessions#new" end ``` このように "/sign_in" をするとscopeは:userに限定されます。(注意)devise_scopeは通称なので気をつけてください ###I18n Deviseのflash message はI18nで使うことが出来ます。 ``` en: devise: sessions: signed_in: 'Signed in successfully.' ``` あなたはresourseに基づく固有のmessageをrouteの設定の中に書くことが出来ます ``` en: devise: sessions: user: signed_in: 'Welcome user, you are signed in.' admin: signed_in: 'Hello admin!' ``` Devise mailerはsubject messageに似たモノを使うことが出来ます ``` en: devise: mailer: confirmation_instructions: subject: 'Hello everybody!' user_subject: 'Hello User! Please confirm your email' reset_password_instructions: subject: 'Reset instructions' ``` local fileを見ることですべてのメッセージを確認できます。また、どのように翻訳されているか興味があるならこのwikiで確認することが出来ます。 https://github.com/plataformatec/devise/wiki/I18n ##Test helper Deviseはspec機能を使ったいくつかのhelperを持っています。この機能を使うには test/test_helper.rbfileに以下を付け加える必要があります。 ``` class ActionController::TestCase include Devise::TestHelpers end ``` もしあなたがRspecを使っているのならspec/support/devise.rb:の内側に以下を記述して下さい。 ``` RSpec.configure do |config| config.include Devise::TestHelpers, :type => :controller end ``` これでsign_inとsign_outメソッドを使う準備ができました。これはcontrollerと同じ様な機能を持っています。 ``` sign_in :user, @user # sign_in(scope, resource) sign_in @user # sign_in(resource) sign_out :user # sign_out(scope) sign_out @user # sign_out(resource) ``` あなたは2つのことを心にとどめておく必要があります 1.このhelperはcapybaraなどとは統一することが出来ません機能的なテストだけを使うことが出来ます。そのかわりuser formは既に埋まった状態で使うことが出来ます 2.もしDevise controllerやDeviseを継承したcontrolelrのテストをしたいのならば、使う前にrequestをする必要があります。Deviseはroutesから情報を受け取っているのでこれは不可欠です。しかし、testはrouterを使わないので使うことを明確に示すことが必要と成ります。例えば、user scopeのtestをしたいのならこのようにしてください ``` @request.env["devise.mapping"] = Devise.mappings[:user] get :new ``` ##Omniauth Deviseはominiauthのサポートを使ったユーザー認証を使用することが出来ます。使うにはconfig/initializers/devise.rbにominiauthの設定を明記してください。 ``` config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo' ``` ominiauthoの機能を使いたい場合は専用のwikiがあるのでこれを良く読んでください - https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview - [私がまとめたもの](http://qiita.com/sasa0721/items/e8574f9e21ffcaa988c4) ##multiple model設定 Deviseは好きな数だけDevise modelを作ることが出来ます。User model 以外にauthenticationとtime out機能を付け加えたAdmin modelを作りたいのなら以下のようにしてください ``` # Create a migration with the required fields create_table :admins do |t| t.string :email t.string :encrypted_password t.timestamps end # Inside your Admin model devise :database_authenticatable, :timeoutable # Inside your routes devise_for :admins # Inside your protected controller before_filter :authenticate_admin! # Inside your controllers and views admin_signed_in? current_admin admin_session ``` この代わりにDevise generatorを使うだけでも大丈夫です。 このようなmodelは異なったrouteを持つことを覚えておいてください。sign_inやsign_outなどと同じcontrollerを共有して使うことは出来ません。同じactionで異なったroleを使いたい場合は role-based approach を使うことを勧めます。 ##追加情報 #Heroku(重要) Rails3.1でDeviseをherokuで使いたいのならこのような設定が必要です ``` config.assets.initialize_on_precompile = false ``` その他の潜在的問題についてはコレを読んでください http://guides.rubyonrails.org/asset_pipeline.html |
|
| 521位 |
|
|||
|
12:45:21 |
( feedforce Inc. 所属) |
|
ちょっとこんがらがったので整理 # 先に結論 - 型指定なしで rescue すると、 `StandardError` のサブクラスのみ catch する - アプリケーションレベルでの標準的なエラーは `RuntimeError` のサブクラス - `RuntimeError` は、`StandardError` のサブクラス - なので、アプリケーション内で独自の例外を書く場合、普通は `RuntimeError` を使ったほうが良い - 1.8 系の場合 `Timeout::Error` は、`StandardError` のサブクラスではない # `raise` 関数に型を渡さない時に発生する例外は、 `RuntimeError` ```rb raise "エラーメッセージ" # => RuntimeError が発生 ``` ## `RuntimeError` って? > 特定の例外クラスには該当しないエラーが起こったときに発生します。 > また Kernel.#raise で例外クラスを指定しなかった場合も RuntimeError が発生します。 [class RuntimeError (Ruby 2.3.0)](http://docs.ruby-lang.org/ja/2.3.0/class/RuntimeError.html) # `rescue` でクラスを省略した時にcatchする例外は、 `StandardError` ```rb rescue => e # => StandardError を catch end ``` > 通常のプログラムで発生する可能性の高い 例外クラスを束ねるためのクラスです。 > StandardError とそのサブクラスは、 rescue 節でクラスを省略したときにも捕捉できます。 [class StandardError (Ruby 2.3.0)](http://docs.ruby-lang.org/ja/2.3.0/class/StandardError.html) > Rubyの組み込み例外は(SystemExit や Interrupt のような脱出を目的としたものを除いて) StandardError のサブクラスです。 [制御構造 (Ruby 2.3.0)](http://docs.ruby-lang.org/ja/2.3.0/doc/spec=2fcontrol.html#raise) # `Exception` の位置づけは? >全ての例外の祖先のクラスです。 [class Exception (Ruby 2.3.0)](http://docs.ruby-lang.org/ja/2.3.0/class/Exception.html) # `StandardError` は、 `Exception` のサブクラス - `Exception` は、システム関係の例外も含む - アプリケーションレベルの例外であれば、 `StandardError` を使うべきという思想らしい - そのため、 `rescue` で、型指定をしない場合は、 `StandardError` を継承している例外のみ `catch` する ```rb begin raise "エラーメッセージ" # catch する raise Exception.new("エラーメッセージ") # catch しない rescue => e p e.message end ``` # `RuntimeError` は `StandardError` のサブクラス - `StandardError` は、通常のプログラムで発生すエラーを意味するクラス - これを継承した場合は、通常のプログラムで発生すエラーを意味することになる - `RuntimeError` は、`StandardError` の中で、特定の例外クラスには該当しないエラーを意味するクラス - 「クラス指定のない `raise` 」専用のクラスといえる # `Exception` を `rescue` するとどんな時困るの? 例えばこんな時 ```rb 1.upto(10) do |i| begin p i sleep(1) rescue Exception => e p e.class p "catch!" end end ``` これを実行中に、ctrl-c で処理を停止しようとすると、 `Interrupt` 例外が発生して、処理が続行されちゃう けど、それって意図したことじゃないよね ```shell-session $ ruby 01.rb 1 2 ^CInterrupt "catch!" 3 4 5 ^CInterrupt "catch!" 6 7 8 9 10 ``` # 1.8 系での Timeout::Error の罠 1.8 系だとTimeout::Error が、StandardError ではなく、Interrupt を継承しているため、 型指定なしでの rescue では、catch されないという罠がある [Class: Timeout::Error (Ruby 1.8.7)](http://www.ruby-doc.org/stdlib-1.8.7/libdoc/timeout/rdoc/Timeout/Error.html) 例えば、こんな時に、タイムアウトが発生しても catch されない ```rb require 'net/http' begin Net::HTTP.start("test.net") do |http| ... end rescue =>e ... end ``` 例外の型指定は複数の型を指定できるので、こんな回避方法もある ```rb begin .... rescue Timeout::Error, StandardError =>e end ``` ちなみに 1.9 系以降では、StandardError になっているので、解決している [Class: Timeout::Error (Ruby 1.9.3)](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/timeout/rdoc/Timeout/Error.html) ### 参考 - [Rubyで自前の例外クラスを作るときExceptionではなくStandardErrorを継承する理由 - yarbの日記]( http://d.hatena.ne.jp/yarb/20121005/p1) - [【Ruby】Ctrl-cの強制停止はrescueで補足できる | one's world](http://no-zom.jp/blog/?p=588) - [Timeout::Errorに注意 - dreammindの日記](http://d.hatena.ne.jp/dreammind/20090217/1234813224) |
|
| 522位 |
|
|||
|
23:45:05 |
|
|
既存のGitレポジトリを、GithubやBitBucketのようなホスティングサーバに移行したり、逆にローカルサーバのGitBucketやGitLabなどに移行したい場合、(乗り換えなど)まあ単純にpushすればいいやんと思ったら、思うような結果にならなかったり、面倒な手順になってしまったりしてしまった。 どうも自分のワーキングのレポジトリから飛ばそうとすると、tagだったりbranchだったりが移行できていないかったりするのです。 ぐぐると、いったんローカルにリモートと同名のブランチ作って(checkoutして)から、push --all, --tags とかしてる奴とかありますがそれは面倒だなぁやだなぁみたいな。 最終的には、これが一番楽な手順かなと思う手順に行きつけたのでここに記す。 ``` $ git clone --mirror <SOURCE_REPOSITORY_URL> $ cd <REPOSITORY> $ git push --mirror <DESTINATION_REPOSITORY_URL> ``` mirrorオプションでcloneしてきて、mirrorオプションでpushする。これぐらいの手間なら許すとしよう。 [tanacasinoのメモ - Gitレポジトリを移行する方法](http://tanacasino.hatenablog.com/entry/2013/08/03/233427) |
|
| 523位 |
|
|||
|
12:48:10 |
|
|
## 日付をフォーマットする 日付をフォーマットして返す。フォーマット(第二引数)を省略した場合`2014-01-23 01:23:45.678`みたいな形で返す。 ```javascript:formatDate /** * 日付をフォーマットする * @param {Date} date 日付 * @param {String} [format] フォーマット * @return {String} フォーマット済み日付 */ var formatDate = function (date, format) { if (!format) format = 'YYYY-MM-DD hh:mm:ss.SSS'; format = format.replace(/YYYY/g, date.getFullYear()); format = format.replace(/MM/g, ('0' + (date.getMonth() + 1)).slice(-2)); format = format.replace(/DD/g, ('0' + date.getDate()).slice(-2)); format = format.replace(/hh/g, ('0' + date.getHours()).slice(-2)); format = format.replace(/mm/g, ('0' + date.getMinutes()).slice(-2)); format = format.replace(/ss/g, ('0' + date.getSeconds()).slice(-2)); if (format.match(/S/g)) { var milliSeconds = ('00' + date.getMilliseconds()).slice(-3); var length = format.match(/S/g).length; for (var i = 0; i < length; i++) format = format.replace(/S/, milliSeconds.substring(i, i + 1)); } return format; }; ``` ```javascript:e.g. formatDate(new Date('2014/1/23'), 'MM月DD日'); // 01月23日 ``` ## 日付を加算する ある日付に時間を加算した日付を返す。加算する単位(第三引数)を省略した場合、日付単位で加算する。 ```javascript:addDate /** * 日付を加算する * @param {Date} date 日付 * @param {Number} num 加算数 * @param {String} [interval] 加算する単位 * @return {Date} 加算後日付 */ var addDate = function (date, num, interval) { switch (interval) { case 'YYYY': date.setYear(date.getYear() + num); break; case 'MM': date.setMonth(date.getMonth() + num); break; case 'hh': date.setHours(date.getHours() + num); break; case 'mm': date.setMinutes(date.getMinutes() + num); break; case 'ss': date.setSeconds(date.getSeconds() + num); break; default: date.setDate(date.getDate() + num); } return date; }; ``` ```javascript:e.g. addDate(new Date('2014/1/23'), 1); // 2014/1/24 addDate(new Date('2014/2/23'), 1, 'MM'); // 2014/2/23 月が加算される ``` ## 2つの日付の差を計算する 第二引数の日付から第一引数の日付を引いた差を数値で返す。差の単位(第三引数)を省略した場合、差を日付単位で返す。 単位以下の差は切り捨て(差がマイナスの場合は切り上げ)。 ```javascript:dateDiff /** * 2つの日付の差を計算する * @param {Date} date1 日付1 * @param {Date} date2 日付2 * @param {String} [interval] 差の単位 * @return {Number} 2つの日付の差 */ var dateDiff = function (date1, date2, interval) { var diff = date2.getTime() - date1.getTime(); switch (interval) { case 'YYYY': var d1 = new Date(date1.getTime()); var d2 = new Date(date2.getTime()); d1.setYear(0); d2.setYear(0); var i; if (diff >= 0) { i = d2.getTime() < d1.getTime() ? -1 : 0; } else { i = d2.getTime() <= d1.getTime() ? 0 : 1; } return date2.getYear() - date1.getYear() + i; break; case 'MM': var d1 = new Date(date1.getTime()); var d2 = new Date(date2.getTime()); d1.setYear(0); d1.setMonth(0); d2.setYear(0); d2.setMonth(0); var i; if (diff >= 0) { i = d2.getTime() < d1.getTime() ? -1 : 0; } else { i = d2.getTime() <= d1.getTime() ? 0 : 1; } return ((date2.getYear() * 12) + date2.getMonth()) - ((date1.getYear() * 12) + date1.getMonth()) + i; break; case 'hh': return ~~(diff / (60 * 60 * 1000)); break; case 'mm': return ~~(diff / (60 * 1000)); break; case 'ss': return ~~(diff / 1000); break; default: return ~~(diff / (24 * 60 * 60 * 1000)); } }; ``` ```javascript:e.g. dateDiff(new Date('2014/1/1'), new Date('2012/1/2')); // 1 dateDiff(new Date('2014/1/1 12:00:00'), new Date('2012/1/2 00:00:00')); // 0 翌日だけど24時間経っていないので0 dateDiff(new Date('2014/1/1'), new Date('2013/1/1'), 'YYYY'); // -1 ``` ## CoffeeScript 以下CoffeeScriptでも書いてみたけれどあんまりテストはしていない ```coffeescript:formatDate ###* * 日付をフォーマットする * @param {Date} date 日付 * @param {String} [format] フォーマット * @return {String} フォーマット済み日付 ### formatDate = (date, format = 'YYYY-MM-DD hh:mm:ss.SSS') -> format = format.replace /YYYY/g, date.getFullYear() format = format.replace /MM/g, ('0' + (date.getMonth() + 1)).slice(-2) format = format.replace /DD/g, ('0' + date.getDate()).slice(-2) format = format.replace /hh/g, ('0' + date.getHours()).slice(-2) format = format.replace /mm/g, ('0' + date.getMinutes()).slice(-2) format = format.replace /ss/g, ('0' + date.getSeconds()).slice(-2) if format.match /S/g milliSeconds = ('00' + date.getMilliseconds()).slice(-3) length = format.match(/S/g).length format.replace /S/, milliSeconds.substring(i, i + 1) for i in [0...length] return format ``` ```coffeescript:addDate ###* * 日付を加算する * @param {Date} date 日付 * @param {Number} num 加算数 * @param {String} [interval] 加算する単位 * @return {Date} 加算後日付 ### addDate = (date, num, interval) -> switch interval when 'YYYY' date.setYear date.getYear() + num when 'MM' date.setMonth date.getMonth() + num when 'hh' date.setHours date.getHours() + num when 'mm' date.setMinutes date.getMinutes() + num when 'ss' date.setSeconds date.getSeconds() + num else date.setDate date.getDate() + num return date ``` ```coffeescript:dateDiff ###* * 日付を加算する * @param {Date} date 日付 * @param {Number} num 加算数 * @param {String} [interval] 加算する単位 * @return {Date} 加算後日付 ### dateDiff = (date1, date2, interval) -> diff = date2.getTime() - date1.getTime() switch interval when 'YYYY' d1 = new Date date1.getTime() d2 = new Date date2.getTime() d1.setYear 0 d2.setYear 0 if diff >= 0 i = d2.getTime() < d1.getTime() ? -1 : 0 else i = d2.getTime() <= d1.getTime() ? 0 : 1 return date2.getYear() - date1.getYear() + i when 'MM' d1 = new Date date1.getTime() d2 = new Date date2.getTime() d1.setYear 0 d1.setMonth 0 d2.setYear 0 d2.setMonth 0 if diff >= 0 i = d2.getTime() < d1.getTime() ? -1 : 0 else i = d2.getTime() <= d1.getTime() ? 0 : 1 return ((date2.getYear() * 12) + date2.getMonth()) - ((date1.getYear() * 12) + date1.getMonth()) + i; when 'hh' return ~~(diff / (60 * 60 * 1000)) when 'mm' return ~~(diff / (60 * 1000)) when 'ss' return ~~(diff / 1000) else return ~~(diff / (24 * 60 * 60 * 1000)) ``` ## 蛇足 [Moment.js](http://momentjs.com/)というライブラリを使う方法も書いた。 http://qiita.com/osakanafish/items/5ef636bbcb2c3ef94953 (2015/1追記)。 |
|
| 524位 |
|
|||
|
12:29:09 |
(コロプラ 所属) |
|
##データを保存できる領域 データは基本的にフラッシュドライブに保存します。 フラッシュドライブにある領域は大きく以下の3つ。 * アプリ領域 * OS領域 * 共有領域 主に使用するのは「アプリ領域」になります。 (プログラムからアクセスできる領域) ###アプリ領域の構成 アプリで参照できるのはアプリごとに割り振られたホームディレクトリに限られており、`/Applications/<GUID>/`以下がアプリ領域となります。 (ホームディレクトリは`NSHomeDirectory()`関数で確認できます) ホームディレクトリ以下はさらにいくつかのディレクトリに分かれていて、それぞれ目的が異なります。 | ディレクトリ | 説明 | |-----------|------| | /アプリ名.app | メインバンドル。アプリ自体のリソースファイルを保存する | | /Documents | アーカイブなどファイル単位で永続化する場合に使用 | | /Library/Caches | キャッシュ領域 | | /Library/Preferences | アプリケーションの設定情報を保存するディレクトリ。NSUserDefaultsで使用 | | /tmp | 一時ファイル保存場所 | ####アプリ名の取得方法 ```objc NSLog(@"AppName: %@", [[NSBundle mainBundle] bundlePath]); ``` ####ホームディレクトリ以下へのアクセス ```objc NSArray *paths = NSSearchPathForDirectoriesInDomains([ディレクトリタイプ], NSUserDomainMask, YES); ``` ディレクトリタイプに定数を渡すことで、上記のそれぞれのディレクトリパスを得ることができます。 * NSDocumentDirectory * NSCachesDirectory ##オブジェクトをシリアライズして保存する Objective-Cのオブジェクトをシリアライズして、それをファイルに保存する方法。 シリアライズはバイナリ化のこと。 iOSではアーカイブ、アンアーカイブと呼ぶ。 アーカイブするには`NSKeyedArchiver`クラス、アンアーカイブするには`NSKeyedUnarchiver`クラスを利用する。 ###アーカイブできるオブジェクトの種類 * NSArray * NSDictionary * NSString * NSDate * NSNumber * NSData * NSURL * UIView * UIViewController * ※NSCoding プロトコルに準拠しているクラス ##プロパティリストを使ってデータを保存する プロパティリストは、「.plist」の拡張子を持つXML形式のファイル。 通称plistファイルとも。 ###プロパティリスト形式のデータを保存する ```sample.m - (void)saveToPlistWithArray:(NSArray *)array { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *directory = [paths objectAtIndex:0]; NSString *filePath = [directory stringByAppendingPathComponent:@"data.plist"]; BOOL successful = [array writeToFile:filePath atomically:NO]; if (successful) { LOG(@"%@", @"データの保存に成功"); } } ``` ###プロパティリスト形式のデータを読み込む ```sample.m - (void)loadFromPlistWithArray { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *directory = [paths objectAtIndex:0]; NSString *filePath = [directory stringByAppendingPathComponent:@"data.plist"]; NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath]; if (array) { for (NSString *data in array) { LOG(@"%@", data); } } else { LOG(@"%@", @"データがありません。"); } } ``` ##NSUserDefaultsを使ってデータを保存する NSUserDefaultsに保存できるデータの型は以下。 * NSString * NSNumber * NSData * NSDate(日付) * NSArray * NSDictionary * NSURL |オブジェクトの型 | 保存メソッド | 取得メソッド | |--------------|------------|------------| | (id型) | setObject: forKey: | objectForKey: | |NSString | setObject: forKey: | stringForKey: | |NSArray | setObject: forKey: | arrayForKey: | |NSArray(文字列) | setObject: forKey: | stringArrayForKey: | |NSDictionary | setObject: forKey: | dictionaryForKey: | |NSData | setObject: forKey: | dataForKey: | |NSDate(日付) | setObject: forKey: | objectForKey: | |NSInteger | setInteger: forKey: | integerForKey: | |float | setFloat: forKey: | floatForKey: | |double | setDouble: forKey: | doubleForKey: | |BOOL | setBool: forKey: | boolForKey: | |NSURL | setURL: forKey: | URLForKey: | ### NSUserDefaultsに保存されている値を確認する 以下のようにすることで、保存されているすべてのキーと値を確認することが出来ます。 ```objc NSDictionary *dict = [NSUserDefaults.standardUserDefaults dictionaryRespresentation]; NSLog(@"Defaults: %@", dict); ``` また、以下のようにすることで、該当アプリで設定したもののみを確認することが出来ます。 ```objc NSDictionary *dict = [NSUserDefaults.standardUserDefaults persistentDomainForName:NSBundle.mainBundle.bundleIdentifier]; NSLog(@"Defaults: %@", dict); ``` ##Core Dataを使ってデータを保存する ※CoreDataに関しては別記事でまとめているので、以下を参照ください。 * [ [Objective-C] CoreDataを使う](http://qiita.com/edo_m18/items/8bc7615eb2f6d546705d) * [ CoreDataのマイグレーションをしようとしたときのメモ(未解決あり)](http://qiita.com/edo_m18/items/717fe32d744a10df7179) ##参考記事 * [iOS でデータを永続化する方法](http://d.hatena.ne.jp/glass-_-onion/20110904/1315142404) |
|
| 525位 |
|
|||
|
19:00:26 |
(FaithCreates Inc. 所属) |
|
ファイルの種類、タイムスタンプなんかで条件を指定してファイルを絞り込みたくなる時があるだろう。例えばこんな場合だ。
- 通常ファイルだけを別の場所に移動させる(ディレクトリはそのまま残す) - シンボリックリンクだけ削除する - タイムスタンプが1日以上古いファイルだけ削除する こういうときは普通は `find` コマンドでファイルを探すんだけど、zsh の「ファイル修飾子」を使うと1行で簡単にできるようになるので紹介する。 ## ファイル修飾子とは 「ファイル名修飾子」とは、ファイル名指定の後に付けるファイルの種類で絞り込みを行うための記号のこと。 例えばこんな感じ。 ``` % ls *(.) ``` この `(.)` の部分がファイル名修飾子になる。`.` 以外にも色々種類があるけど、よく使う、または役に立ちそうなのは以下。 | 記号 | 意味 | |:--------------------------|:------------------------------------------| | . | 通常ファイル | | / | ディレクトリ | | @ | シンボリックリンク | | * | 実行ファイル | | D | ドットで始まるファイルも含める | | ^ | 後ろの修飾子の意味を反転させる | | f<var>spec</var> | アクセス権で絞り込む | | m<var>[Mwhms]-n</var> | 修正時刻(modify time)で絞り込む | | a<var>[Mwhms]-n</var> | アクセス時刻(access time)で絞り込む | | c<var>[Mwhms]-n</var> | ステータス変更時刻(change time)で絞り込む | ## 使い方の例 簡単な使い方については例を見たほうが早いと思う。 まず、以下のようなファイル/ディレクトリがあったとする。 ``` file1.txt file2.txt file3.txt dir1/ (ディレクトリ) dir2/ (ディレクトリ) script1.sh* (実行可能ファイル script2.sh* (実行可能ファイル text -> file1.txt (シンボリックリンク) .hidden (. で始まるファイル) ``` ファイル修飾子で絞り込みを行った結果は以下の通り。 ```zsh # 通常ファイルのみ % echo *(.) file1.txt file2.txt file3.txt script1.sh script2.sh # ディレクトリのみ % echo *(/) dir1 dir2 # シンボリックリンクのみ % echo *(@) text # 実行可能ファイルのみ % echo *(*) script1.sh script2.sh # . で始まるファイルも含める % echo *(D) .hidden dir1 dir2 file1.txt file2.txt file3.txt script1.sh script2.sh text ``` もちろん `()` の前は `*` 以外に普通のファイル名指定、ワイルドカード指定ができる。 ```zsh # .txt で終わる通常ファイル % echo *.txt(.) file1.txt file2.txt file3.txt # dir1 という名前のディレクトリ % echo dir1(/) dir1 ``` 以降この記事の例では `()` の前は `*` を使うことにする。 ### 条件の反転 `^` を使うと意味が反転される。 ```zsh # 通常ファイル以外 % echo *(^.) dir1 dir2 text # シンボリックリンク以外 % echo *(^@) dir1 dir2 file1.txt file2.txt file3.txt script1.sh script2.sh ``` 残りの f, m, a, c は引数が必要になって使い方がちょっと複雑なので、個別に説明する。 ### ファイルのアクセス権での絞り込み f<var>spec</var> の形式で、ファイルのアクセス権(パーミッション)で絞り込める。 <var>spec</var> の部分を644とか755とかの8進数で書くと、そのパーミッションに一致(完全一致)するものだけが選ばれる。 3桁の数字のうち一部を `?` に置き換えると「その部分は何でもよい」という意味になる。 ```zsh # こんなふうにファイルが4つあったとする % ls -l -rw-r--r-- file1 -rw-r--r-- file2 -rw-rw-r-- file3 -rwxr-xr-x file4 # 644 なファイルのみ % echo *(f644) file1 file2 # 所有者のパーミッションが6のみ(グループ、その他はなんでもよい) % echo *(f6??) file1 file2 file3 ``` ### タイムスタンプでの絞り込み m<var>-n</var> の形式で修正時刻がn日以内であるもの、m<var>+n</var> の形式で修正時刻がn日より前(過去)であるもの、という意味になる。 日以外の単位を使いたい時は、`mh-n` のように `m` の後に単位を指定する。 単位として指定できる記号は以下。 | 記号 | 意味 | |:-----|:---------| | M | 月(30日) | | w | 週(7日) | | h | 時間 | | m | 分 | | s | 秒 | 使い方の例はこんな感じ。 ```zsh # 修正時刻が現在から3日以内であるもの % echo *(m-3) # 修正時間が現在から5時間以内であるもの % echo *(mh-5) # 修正時間が現在から10秒より前(過去)であるもの % echo *(ms+10) ``` また、`m` の代わりに `a` を使うと「修正時刻」ではなく「アクセス時刻」、`c` を使うと「ステータス変更時刻」という意味になる。 「修正時刻」とか「アクセス時刻」とかについて軽く説明しておく。 「修正時刻」はファイルを変更した時刻、「アクセス時刻」はファイル読み込みを行った時刻、という意味。「ステータス変更時刻」というのはちょっとややこしいけど、ざっくり言うとファイルの内容を変更した、またはファイル名、パーミッションなどを変更した時刻、という意味。 普通に使うときは `m` が一番役に立つと思う。 ## AND, OR `()` の中に修飾子を並べて書くとAND検索になる。 ```zsh # 通常ファイルかつパーミッションが755 % echo *(.f755) script1.sh script2.sh # ディレクトリかつパーミッションが755 % echo *(/f755) dir1 dir2 # ファイルの修正時刻が1日より過去かつ7日より最近である % echo *(m+1m-7) file1.txt file3.txt ``` `,` で連結するとOR検索になる。 ```zsh # 通常ファイルまたはディレクトリ % echo *(.,/) dir1 dir2 file1.txt file2.txt file3.txt script1.sh script2.sh # 「パーミッションが644の通常ファイル」、または「ディレクトリ」 % echo *(.f644,/) dir1 dir2 file1.txt file2.txt file3.txt ``` 最後の例のように、ORよりANDの方が結合度が上になる。 ## 忘れたときはタブ 「ファイル名修飾子」は他にも色々種類があって、なかなか覚えにくいと思う。そういうときは `(` の後にタブを押すと候補が補完されるので、それを見て選ぶといい。 ```zsh % echo *( [TAB]を押す % -- device files ) -- end of qualifiers * -- executable plain files + -- + command name - -- follow symlinks toggle . -- plain files / -- directories ... 略 ``` その他細かいことは man zshexpn の「FILENAME GENERATION」->「Glob Qualifiers」を読むともっと詳しく書いてある。 あんまりやり過ぎると逆にわけわかんなくなるけど、うまく使うとスマートに書けると思うので使ってみてください! |
|
| 526位 |
|
|||
|
01:16:50 |
|
|
自分が確認出来ているものをリストアップします
# AntTweakBar 古くからある GUI ライブラリ - http://anttweakbar.sourceforge.net/doc/  ## 特徴 - Windows/OSX/Linux 対応のクロスプラットフォーム仕様 - インターフェースは C だが内部的には C++ で実装 - Compatible/Core プロファイルに両対応 - DirectX9-11 にも対応 - ライセンスは zlib ## 長所 - ドキュメント(オンライン)及びサンプルが比較的揃っている - ひと通りの UI が揃っており、回転や方向を設定するための UI もある - エフェクトやパラメータエディタであれば間違い無くこれひとつで組み立てられる - GLUT/SDL1.2/SDL2/SFML/GLFW2 用のイベント処理をするためのラッパーがある - 軽量 ## 短所 - Makefile 型なので細かいビルドが難しい - パラメータ設定に癖がある - 特にグルーピングはなれないと難しいかもしれない - 日本語のようなマルチバイト文字に対応していない # GWEN Garry's Mod の開発元が開発しているライブラリ - https://github.com/garrynewman/GWEN  ## 特徴 - Windows/OSX/Linux 対応のクロスプラットフォーム仕様 - 比較的デスクトップに近い GUI を提供し、見た目が変更可能 - 1枚のテクスチャを GUI のスキンとして扱う - premake4 でビルドする - ライセンスは MIT ## 長所 - レンダリングエンジンの差し替えが可能 - デフォルトで以下のレンダリングエンジンを持つ - Allegro5 - CrossPlatform (OpenGL) - Direct2D/Direct3D - OpenGL - SFML/SFML2 - WindowsGDI - 凝った作りでなければこれひとつで GUI アプリケーションが出来る - マルチバイト文字に対応 ## 短所 - Windows 以外だとコンパイルに失敗する - 原因は Gwen::UnicodeString (std::wstring) のメソッドに C++11 なものに依存しているため - また MacOSX だと OpenGL のヘッダパスの違いを考慮していない - タグ付けがされておらず、開発版しか無い - ドキュメントがほとんどなく、サンプルがたより - 2014年以降更新されていないため保守が厳しい # libRocket モバイル方面で比較的使用実績のある UI ライブラリ - http://librocket.com/wiki ## 特徴 - Windows/OSX/Linux 対応のクロスプラットフォーム仕様 - HTML/CSS で記述する - CMake でビルドする - ライセンスは MIT ## 長所 - HTML/CSS のためビューの分離が明確にできる - 冒頭にある通りモバイルの実績がそれなりにある - デバッガがあり、ロジックは Python で拡張できる(未検証) ## 短所 - ドキュメントはひと通り揃ってるが、サンプルが少なく応用が難しい # CEGUI Ogre3D で真っ先にあげられる GUI ライブラリ - http://www.cegui.org.uk/wiki/index.php/Main_Page  ## 特徴 - Ogre3D との強い連携(CEGUI 単体利用も可能) - DirectX/OpenGL(Core含む)/OpenGLES に対応する - CMake でビルドする - ライセンスは MIT (0.5.0 までは LGPL) ## 長所 - ドキュメントとサンプルがかなりある - かなり柔軟な GUI 設計が可能 - 日本語などのマルチバイト文字表示に対応 ## 短所 - Windows 以外だと ICU ではなく旧式である libiconv を CEGUI_STRING_CLASS 関係なく強制的に使用する - 個人的には ICU にしてほしい感がある... - 10.8 にバンドルされてる libiconv だとリンクに失敗するので macports から入れること # imgui - https://github.com/AdrienHerubel/imgui  ## 特徴 - Box2D の新しいデモで使われる予定の UI ライブラリ - [Recast & Detour](https://github.com/memononen/recastnavigation) に含まれる GUI ライブラリから引っ張ってきたもの - ちなみに Recast & Detour の開発者は後述の milligui の開発者でもある - ライセンスは zlib ## 長所 - 小規模なため直接組み込みやすい - imgui.{cpp,h} と stb_truetype.h と imguiRenderGL2.{cpp,h} か imguiRenderGL3.{cpp,h} - OpenGL Core Profile に対応 - プリミティブを描画する API がある - 線分、矩形、角丸つき矩形、テキスト ## 短所 - ドキュメントがない - sample_gl[23].cpp が実質的なドキュメント - 初期化時フォントを読み込む必要があるのでフォント必須 - 日本語フォントを読み込ませても日本語表示に対応していない - GLEW に依存 - コミット数が非常に少なく、2013 年以降更新されていない ## その他 これの fork として現在 NanoUI を開発中。そのうち公開する予定 <blockquote class="twitter-tweet" lang="en"><p><a href="https://t.co/v1SmVuo1x8">https://t.co/v1SmVuo1x8</a> の NanoVG 使用 + 日本語表示対応版を内部設計と見た目をだいたいおなじにして NanoUI として自作した。そのうち github に上げる予定 <a href="http://t.co/uA9oaeenWM">pic.twitter.com/uA9oaeenWM</a></p>— しまぴょんさん@秋でもがんばらない (@shimacpyon) <a href="https://twitter.com/shimacpyon/status/516049724407697408">September 28, 2014</a></blockquote> # milligui 比較的最近になって開発が行われるようになった新しい GUI ライブラリ - https://github.com/memononen/milligui  ## 特徴 - [NanoVG](https://github.com/memononen/nanovg) と [NanoSVG](https://github.com/memononen/nanosvg) を使って描画する - nanovg は Core Profile 含むデスクトップの OpenGL 及び OpenGL ES 2/3 に対応する - 開発者は milligui と同じ - C言語で書かれている - このことは上記の nanovg/nanosvg も同様 - 開発中なためかライセンス不明 - 2014 年以降更新されていないためもしかしたら開発中断疑惑あり ## 長所 - 小規模のため直接組み込みやすい - src/mgui.{c,h} と lib/nanos?vg - imgui と違い日本語フォント読み込ませた上での日本語表示に対応 - これは [fontstash](https://github.com/memononen/fontstash) のおかげだろう - さらにいうと fontstash の開発者も milligui と同じである ## 短所 - 開発中のため API はまだ不安定 - 上記理由なためドキュメントなし - example/example.c が実質的なドキュメント # blendish [NanoVG](https://github.com/memononen/nanovg) を使って Blender 2.5 な UI を作るライブラリ。コミットログみると最初のコミットが2014年7月と執筆時点では非常に新しい - https://bitbucket.org/duangle/oui-blendish/overview  ## 特徴 - Blender な UI を作ることが出来る - NanoVG を使用している関係で日本語表示に対応している - その場合は [源真ゴシック](http://jikasei.me/font/genshin/) などの TTF フォントを使用すること - MIT ライセンス - 10/2 時点のバージョンでは nanovg をレポジトリ直下、すなわち blendish.h と同じディレクトリに配置する必要がある ## 長所 - ヘッダーのみのため組み込みやすい - 小さいながらも使用できる UI コンポーネントが多い ## 短所 - Blender な UI を好まない人にはつらい # ImGui (a.k.a dear, imgui) 機能豊富で実績が多い GUI ライブラリ github のトレンドに何度か上がってたので知ってる人知ってるかもしれない。名前が上記にあがってる https://github.com/AdrienHerubel/imgui とかぶってるので注意。 - https://github.com/ocornut/imgui どんな使い方ができるかは https://github.com/ocornut/imgui/issues/123 をみるとよいかもしれない。  2015 年後期に筆者が当該ライブラリ採用を行って現在も watch を続けている唯一のライブラリ ## 特徴 - 豊富な UI コンポーネント - MIT ライセンス ## 長所 - 使い勝手の良い UI コンポーネントが多くたいてい事足りる - カスタムレンダリングに対応している - NanoVG ほどではないがプリミティブな描画が可能な API がある - [簡易なノードエディタが作れるレベルには使い勝手あります](https://github.com/ocornut/imgui/issues/306) - 組み込みやすい - 初期と比較して必要なファイル数が若干多いがそれでも 10 ファイル未満 - カスタムアロケータも利用可能 - デモが非常に網羅的でやりたいことはデモのソースコードを見ればだいたい出来る - レンダラに依存しない - サンプルとして DirectX9 と OpenGL の2つがある - サンプルとして実装されているレンダラが多く、Vulkan にも対応している - OpenGL - OpenGL Core Profile - DirectX9 - DirectX10 - DirectX11 - Vulkan - 主要なライブラリと連携するサンプルも - SDL/SDL2 - Allegro5 ## 短所 - ~~ビットマップフォントを必要とするため、非 ASCII 圏にはつらい~~ - ~~英数字のみ、あるいはあらかじめ必要となる文字が決まっているならありか~~ - 上記は TTF 読み込みからのテクスチャアトラス化に対応したので解消された - ただちょくちょく文字が `?` になる問題がありこれの問題が対策が不明 # QtQuick Controls Qt 5.1 で導入されたコンポーネントで QtQuick の力を使っており、OpenGL で描画されている。 Qt 5.1 はやや古いマシンで遅くなる場合があり、デプロイが非常に面倒であったが Qt 5.2 で QtQuick のレンダリングエンジンの改良がなされたため実用的な速度で動くようになり、デプロイが容易になった。  ちなみに執筆者が開発している [VPVM](http://hkrn.github.io/MMDAI/) というアプリケーションも QtQuick Controls を使ってます。  ## 長所 - Qt との連携がすごく簡単 - QML+JavaScript の構成のため UI 変更の度にコンパイルを必要としない - これはつまり毎回動的に解釈されるというデメリットでもある - IME 対応が十分なされている ## 短所 - Qt が必要であり、かつ依存するライブラリが多くサイズが肥大化しがち - QtCore + QtGui + QtQml + QtQuick + QtNetwork + QtWidgets - 現時点で OpenGL Core Profile での動作をサポートしない # kiUi 5/8 に github の C++ のトレンドに上がったライブラリ。おそらくキーウィ (kiwi) とかけてる - https://github.com/novembermonk/kiui http://novembermonk.github.io/kiui/livedemo/kiui.html で emscripten を使った実際に動作するデモがあるので試すことが出来る ## 特徴 - テクスチャによるスキン変更が可能 - 入力に GLFW を、描画のバックエンドに NanoVG を使用したコードが最初から付属 - emscripten にはじめから対応している - C++ で書かれている - zlib ライセンス ## 長所 - 比較的豊富なコンポーネントを最初から擁している - (原理的に)プラットフォーム及びレンダリングエンジン中立 - とはいうものの GLFW を使う関係からか OpenGL 寄りの設計か - 外部依存性が少ない - NanoVG と YAML はソース組み込みで、外部のものは GLFW 以外なし - 日本語表示に対応している - NanoVG をバックエンドに採用しているから当然か - blendish と同じように使用する場合は [源真ゴシック](http://jikasei.me/font/genshin/) などの TTF フォントを使用すること - CMake でビルドできる ## 短所 - NanoVG 以外のバックエンドを書くのは難しいかも - まだ開発中でドキュメントは example から読み解く必要がありそう - 日本語入力は不可 # GXUI Google 社員によって現在開発中の主に Go で書かれている OpenGL の UI ライブラリ - https://github.com/google/gxui 評価するにはサンプル実装が少ないため一旦保留。現時点では以下でパッケージをインストールすればサンプルをビルドできる ``` go get code.google.com/p/freetype-go/freetype go get github.com/go-gl-legacy/gl go get github.com/go-gl/glfw/v3.1/glfw go get github.com/google/gxui ``` # Nuklear C 言語 (C89) のみで書かれている GUI ライブラリ  - nuklear.h でビルドできる - ただしロジックと描画命令のみ - レンダリングバックエンドは別に用意する必要がある - demo みると以下のライブラリを使用した連携例がある - Allegro5 - GLFW - SDL2 - X11 - カスタムアロケータも一応利用出来る - `vurtun/gui` -> `vurtun/zahnrad` の変遷歴がある - `Nuklear` になってから [stb](https://github.com/nothings/stb/) を明らかに意識した作りになってる - パブリックドメイン https://github.com/vurtun/nuklear # Game-GUI 未検証。C++ で書かれている - 「東京」の文字を表示させてる SS みると日本語含む UTF-8 の表示に対応しているようだ - Visual Studio のプロジェクトしかないことからクロスプラットフォーム対応の観点で怪しい - 少なくともビルドするには Windows 上で Visual Studio を使うしかない - MIT ライセンス https://github.com/sp4cerat/Game-GUI # NanoGUI 2015年 に登場した GUI ライブラリ。C++ で書かれており、cmake でビルドする https://github.com/wjakob/nanogui ([Go の移植版もある](https://github.com/shibukawa/nanogui-go)) - OpenGL 3.x かつ C++11 な環境が必要 - Eigen + GLFW + nanovg に依存する - OSX 上ではファイルダイアログのためのソースが用意されておりファイルダイアログが機能する - Roboto 固定のためフォントを変更する際は `theme.cpp` をいじる必要がある - 内部的には `nvgCreateFontMem` を呼んでるので原理的にはいじれば日本語含むマルチバイト文字に対応出来ると思われる - 静的ライブラリとしてビルドすると 4MB ほどの大きさに - MIT ライセンス  # NVIDIA Widgets 未検証。C++ で書かれており、cmake でビルドする https://code.google.com/p/nvidia-widgets/ - NVIDIA の SDK サンプルで使われている UI ツールキット - 最終更新が 2009 年でおそらくもう保守されていない - MIT ライセンス # その他 他にも同じ目的を持つライブラリとして [SFGUI](http://sfgui.sfml-dev.de/) と [Gorilla](https://github.com/betajaen/gorilla) があるけど両者とも特定のライブラリに強く依存している点 (SFGUI は [SFML2](http://www.sfml-dev.org/) に依存し Gorilla は [Ogre3D](http://www.ogre3d.org/) に依存する) で一旦選択肢から外した。 1 から作りたい場合は [NanoVG](https://github.com/memononen/nanovg) が OpenGL を使った UI の実装デザインの参考になるだろう。NanoVG については著者が書いた「[NanoVG で HTML5 Canvas の力を C/C++ にも](http://qiita.com/shimacpyon/items/b6c0f2e031a4f4d6aefa)」を参照すること。 |
|
| 527位 |
|
|||
|
02:49:25 |
(MediWeb 所属) |
|
2014/5 更新 バージョン覚えてないけど、多分Ruby:2.0、Rails:3.2 ## 前提 RailsのDBMSを初期設定のSQLiteからMySQLに変える方法です。[ここ](http://www.rubylife.jp/rails/model/index2.html)を参考にしました。 大事な所を他サイトに丸投げしたざっくりなまとめですが悪しからず… ## MySQL設定 ### インストール まずは、MySQLを[公式サイト](http://dev.mysql.com/)から[ダウンロード](http://dev.mysql.com/downloads/mysql/)します。 手順は[このサイト](http://www.dbonline.jp/mysqlinstall/install/index1.html)に詳しく書いてあるので参照してください。 ### 初期設定 インストールしたら、MySQLを起動して初期設定をします。 詳しくは[ドットインストール](http://dotinstall.com/lessons/basic_mysql_v2)でどうぞ。 ### 作業ユーザー作成 次に作業ユーザーを作成します。 以下を見ていただくと分かるのですが、作業ユーザーはdevelopment、test、productionの3つ必要になります。(developmentとproductionは同じユーザーでも設定上は大丈夫ですが、本番環境も見据えてproductionも独自のものを用意するといいでしょう。testは他のユーザーと同じにするとエラーが出ます) では、ターミナルでmysqlを起動してください。 で、作業ユーザーを作成します。ユーザー名は「'ユーザー名'@'localhost'」という書式になります。 ```Terminal mysql> create user [ユーザー名1] identified by 'パスワード1', [ユーザー名2] identified by 'パスワード2', [ユーザー名3] identified by 'パスワード3'; ``` 作成したユーザーの確認は以下のコマンドで確認できます。 ```Terminal mysql> select User,Host from mysql.user; ``` 以下のコマンドで作成したユーザーに権限を付与します。 ```Terminal mysql> grant all on *.* to '[ユーザー名]'@'localhost' ``` ## Rails設定 まず、Railsアプリを作成します。 ```ターミナル $ rails new [アプリ名] -d mysql ``` `-d mysql`と指定することでデフォルトDBMSがMySQLへ変える処理が行われます。 そんで「{RailsRoot}/config/database.yml」を開きます。 中身は以下のような内容になっていると思います。 ```[アプリのルート]/config/database.yml # コメント略 development: adapter: mysql2 encoding: utf8 reconnect: false database: [アプリ名]_development pool: 5 username: root password: host: localhost test: adapter: mysql2 encoding: utf8 reconnect: false database: [アプリ名]_test pool: 5 username: root password: host: localhost production: adapter: mysql2 encoding: utf8 reconnect: false database: [アプリ名]_production pool: 5 username: root password: host: localhost ``` この中のdevelopment、test、productionの項目をそれぞれ書き換えます。上で作成した作業ユーザーのユーザー名、パスワードをそれぞれ「username」と「password」の所に書いてください。 あと、環境に合わせてhostも書き換えます。 databaseの名前はデフォルトのままでも大丈夫ですが、必要とあれば書き換えてください。 そんで、`$ rake db:migrate`します。 これでOKのはず 適当にテストしたい方は以下のようにscaffold使って試してみると良いでしょう。 ```Terminal $ rake db:create $ rails g scaffold home name:string body:text email:string $ rake db:migrate ``` |
|
| 528位 |
|
|||
|
02:38:40 |
|
|
単純な文字列を渡すとかならともかく、オブジェクトをJSON形式で渡せると便利ですよね。それもいちいち通信してコールバックから貰うとかは面倒! ここでは簡単にできる2通りのやり方を紹介。 ##1. Viewに埋め込む 一番単純な方法。 ###ERB ```view.html.erb <%= javascript_tag do %> window.your_object = <%= raw @your_object.to_json %>; <% end %> ``` ###HAML ```view.html.haml = javascript_tag do board_event = #{raw @board_event.to_json}; ``` エスケープは一切してないので注意してください。 ##2. Gon gemを使う Viewに書くのが気持ち悪い場合、大量に渡すデータがある場合。[Gon gem](https://github.com/gazay/gon) を使うとスッキリControllerに書けます。 ###2-1.準備 ```Gemfile gem 'gon' ``` `$ bundle install`して、ヘッダに以下の一行を埋め込むと準備完了。 ```views/layouts/application.html.erb . . <%= include_gon %> #←これだけ! <%= javascript_include_tag "application" %> <%= stylesheet_link_tag "application" %> . . ``` ##2-2.サンプル controllerに適当に埋め込むだけ。 ```your_controller.rb def hoge . . gon.hoge = your_object . . end ``` ブラウザで開き、コンソールで`gon.hoge`を打つと、ちゃんとjsオブジェクトになっています。(DOMロード前に読み込めます) 普通にソース中に埋め込まれてるだけみたいです。 `gon.all_variables`で全ての変数を確認したり、`gon.clear`で初期化したりもできます。 オプションでgon.watchを使うことで、gon.hogeを書き換えるだけでブラウザをリロードするなくデータを更新したりもできるみたいです。 ##参考 [Rails Cast #324 Passing Data to JavaScript](http://railscasts.com/episodes/324-passing-data-to-javascript?language=ja&view=asciicast) |
|
| 529位 |
|
|||
|
16:05:54 |
|
|
プラグイン開発に関する詳細な情報は[gulpの公式ドキュメント](https://github.com/gulpjs/gulp/tree/master/docs)を参照のこと。また、開発したプラグインを公開する場合はgulp公式による[プラグイン開発ガイドライン](https://github.com/gulpjs/gulp/blob/master/docs/writing-a-plugin/guidelines.md)は必読(MUST readと書かれている)。[README規約](https://github.com/gulpjs/gulp/blob/master/docs/writing-a-plugin/readme-conventions.md)も見ておくとよい。 (2014/2/9追記:プラグイン開発ガイドラインの[日本語訳](http://qiita.com/morou/items/abc05013053f6110bcb1)を公開) なお、gulpプラグインはnode.jsパッケージの一種なので、公開する際の手順や[package.jsonの記述方法](https://npmjs.org/doc/json.html)などはnode.jsのドキュメントを参照のこと。 ## プラグインの基本構造 - gulpプラグインの実態(i.e. pipeに渡すもの)はnode.jsの[Stream.Transform](http://nodejs.org/api/stream.html#stream_class_stream_transform)のサブクラス - Stream.Transformとは、ストリームからの入力を受け取り、加工し、ストリームに出力するもの - Stream.Transformのサブクラスを直接作って実装しても良いが、gulpプラグインではStream.Transformラッパーである[through2](https://github.com/rvagg/through2)を利用するのが一般的 - through2がストリームの入出力部分を面倒見てくれるので「加工」の実装に注力するだけ - through2のオブジェクトを生成する時に「加工」用の関数を2つ渡す - transformFunction:入力ストリームからファイルを受け取る度に呼び出される。通常はファイル毎に行う処理を実装する。省略不可。 - flushFunction:出力ストリームへの書き込み(flush)前に一回だけ呼び出される。通常は最後にまとめて行う処理を実装する。省略可能。  ## transform関数 - 関数抜ける前に必ずcallbackを呼び出すこと(callback呼ばないとプラグイン呼び出し側に'end'イベントが発生しない) - 出力ストリームへの書き出しはthrough2.push(file)を呼び出す(複数ファイル書き出ししてもいい) - transformに渡されるfileはnull / buffer /streamの3種類ある - null:入力ストリームからデータを受け取らなかった場合 - buffer:入力ストリームのデータを1ファイル分バッファリングしてからtransformに渡される場合 - stream:入力ストリームのデータが順次transformに渡される場合 ## flush関数 - 関数抜ける前に必ずcallbackを呼び出すこと(callback呼ばないとプラグイン呼び出し側に'end'イベントが発生しない) - 出力ストリームへの書き出しはthrough2.push(file)を呼び出す(複数ファイル書き出ししてもいい) ## サンプルプラグイン サンプルとして、入力ストリームから2つのJSONファイルを受け取り、diffをとってその結果を出力するプラグインを作ってみる。 - diffの処理自体はnode.jsパッケージの[JsonDiffPatch](https://github.com/benjamine/JsonDiffPatch)を利用(出力の仕様などはJsonDiffPatchに従う) - プラグインに渡す引数は「出力ファイル名」のみ ```javascript var gutil = require('gulp-util'); var through = require('through2'); var jsonDiff = require('jsondiffpatch'); module.exports = function (outputFileName) { var file1, file2; // diff対象のファイル /* transform関数:ファイル毎に呼び出される */ function transform(file, encoding, callback) { // ファイルがnullの場合 if (file.isNull()) { // 次のプラグインに処理を渡すためにthis.push(file)しておく this.push(file); // callback()は必ず実行 return callback(); } // ファイルがstreamの場合(このサンプルプラグインはstreamに対応しない) if (file.isStream()) { // emit('error')を使って、プラグイン呼び出し側に'error'イベントを発生させる this.emit('error', new gutil.PluginError('gulp-diff', 'Streaming not supported')); // callback()は必ず実行 return callback(); } // fileをローカル変数に保持しておく if (!file1) { file1 = file; } else if (!file2) { file2 = file; } else { // 入力ファイルが3つ以上指定された場合はエラーとする this.emit('error', new gutil.PluginError('gulp-diff', 'File too much')); } // callback()は必ず実行 callback(); } /* flush関数:flush前に呼ばれる */ function flush(callback) { // ファイルが2つ指定されなかった場合 if (!file1 || !file2) { // エラーとする this.emit('error', new gutil.PluginError('gulp-diff', 'File too less')); } // ファイルの内容(file.contents)をJSONオブジェクトに変換 var json1 = JSON.parse(file1.contents.toString('utf8')); var json2 = JSON.parse(file2.contents.toString('utf8')); // diff実行 var diff = jsonDiff.diff(json1, json2); // 出力ファイルを生成(新規ファイル生成にはgulp-utilのFileを利用する) var output = new gutil.File({ cwd: file1.cwd, base: file1.base, path: file1.base + outputFileName, }); // ファイルのコンテンツにはnode.jsのBufferを使う output.contents = new Buffer(JSON.stringify(diff)); // ファイルを出力ストリームに流す this.push(output); // callback()は必ず実行 callback(); } /* through2オブジェクトを生成してreturn */ return through.obj(transform, flush); }; ``` |
|
| 530位 |
|
|||
|
15:13:29 |
(Freelancer 所属) |
|
[iOS7 から AVSpeechSynthesizer クラスが追加](http://d.hatena.ne.jp/shu223/20130924/1379990718) され、音声合成(読み上げ/Text to Speech)が手軽に使えるようになりました。
既に **日本語を含む36種の言語に対応したvoice** が用意されていて、音質もなかなかです。ひらがなも漢字も読んでくれるので、テキストをまるっと渡せば読んでくれます。  が、ひとつ物足りなかったのが、 **読み上げのチューニングができない** こと。 音声合成は古くからあるジャンルなので、もっと細かくチューニングできて、音声がきれいなサードパーティ製SDKがあるんじゃないかと思い、いろいろ調べてみました。 ##[AquesTalk2](http://www.a-quest.com/products/aquestalk.html) ###長所 - 評価版はすぐにDLして試せる - **日本語の特性を考慮した、非常に細かいチューニング** が可能 - アクセント - 無声化 - 区切り - ガ行鼻濁音 - 数値桁読み - etc... ###短所 - 音質が低い - phontというしくみがあるので入れ替え可能かも - 漢字が読めない - ひらがなに変換してやる必要がある - あまり更新されてない - サンプルの Deployment Target は 3.0。。 - 使用方法が面倒(APIがシンプルじゃない) - 例: **読み上げてもらう文字列を ShiftJIS に変換する必要がある** ```` char *sjis = (char*)[self.textView.text cStringUsingEncoding:NSShiftJISStringEncoding]; ```` ###その他 - ライセンスは5800円 - [オンラインで購入可](http://shop.a-quest.com/?mode=cate&cbid=1377248&csid=0) ##[iSpeech](https://www.ispeech.org/developers/iphone) ###長所 - **音質がいい** - 導入しやすい - Blocksを使った記述も可能 - 無料 - モバイル用SDKからの使用のみ。APIは従量課金っぽい ###短所 - **要ネットワーク** ###その他 - Evernote clearlyの読み上げ機能で[使用されている](http://blog.evernote.com/blog/2012/11/27/evernote-clearly-now-reads-pages-to-you-with-text-to-speech/) ##[Acapela](http://www.acapela-for-iphone.com/) ###長所 - 音質は標準(AVSpeechSynthesizer)と同じぐらい(※主観) - ひらがな、漢字をそのまま渡せる ###短所 - 漢字の読みが間違っていても、修正できない(辞書登録等の方法がない) - 評価版があまりメンテされてない - [ここ](http://www.acapela-for-iphone.com/download)からダウンロードできる最新版は1.304 - サンプルプロジェクトのコンパイラが古いものが指定されてるので、default compilerに変更する - 評価版はシミュレータでのみ動作するとのことなので、シミュレータで動かしてみると、HelloTTSサンプルは動くが、TTSDemoサンプルはエラーが出る(ここで評価をあきらめた) - ライセンス購入するともらえるSDKは、少なくとも1.4以上らしい ###その他 - 有料:アプリの売り上げの30%(Apple税を引いた残りの30%) - [うめのん](https://twitter.com/umekun123)さんの『[VoicePaper](http://voicepaperapp.com)』、『[音声文庫](https://itunes.apple.com/jp/app/id685782917)』といった読み上げ読書アプリは、Acapelaを使用しているとのこと。 (参考記事:[おかんでも使えるアプリ、青空文庫読み上げの音声文庫を作った](http://umenon.com/2013/08/16/aozorabunko/)) ##[ドキュメントトーカ](http://www.createsystem.co.jp/dtalkeriPhoneSDK.html) ###長所 - **日本語のイントネーションが自然** - **歌唱** させることも可能 - **男性の声** も選択可能 - 漢字ひらがな混じりOK - ユーザー辞書の登録も可能 - 導入が簡単(APIがシンプル) ###短所 - 音質はiOS標準(AVSpeechSynthesizer)に劣る - 実際にデバイスで動作できる有償版は、要問い合わせ - どれぐらいの価格帯なのかも不明(有償版は5000円〜50万円ぐらいの幅があるので、目安の価格ぐらいは知りたい) - 無料DLできる評価版はsimulatorでのみ動作する ##[NeoSpeech](http://www.neospeech.com/) フォームで諸々入力しないとSDKもらえないので試してないのですが、使ってみた人の話を聞いたところ、 - 日本語音声はiSpeechと同じ人の声 - API は ObjectiveC ではない とのこと。 ##Google TTS APIを利用するものなので、試してないのですが、下記のような参考になる記事があります。サンプルあり。 [【iOS】Google TTSでText To Speechを実装する](http://d.hatena.ne.jp/jonki/20130102) ##[VOICE TEXT](http://voicetext.jp/use_smartphone.html) - by HOYA - 要問い合わせ - 有償、かつ評価版をwebからダウンロードすることができないため、試していない ##[N2](http://www.kddilabs.jp/press/2013/0523-1.html) - by KDDI - 要問い合わせ - 有償、かつ評価版をwebからダウンロードすることができないため、試していない ##聴き比べ - iOS SDKの AVSpeechSynthesizer - iSpeech - AquesTalk2 - ドキュメントトーカ - Acapela を **聴き比べできるサンプルアプリ** を実装しました。  が、各社GitHubとかにSDKをアップしてるわけじゃないので、たぶん私がそれを再配布するのはアウトな気がする(未確認)ので、ローカルで試して結論を述べるにとどめておきます。 (というか、上述のまとめは、実装してみて聴き比べてみた上で書いたものです) ##結論 **音質の良い順** は、 iSpeech > 標準(AVSpeech)≒ Acapela > ドキュメントトーカ >> Aques iOS標準より明らかに音質がいいのは iSpeech ぐらいでした。 **日本語のイントネーションの自然さ** が良かったのは、ドキュメントトーカ、iSpeech、Aques(チューニングすれば)。 ただ、 - iSpeech はオフラインで使用できないこと - Acapela, DTalk 音質がApple標準と同等以下、かつ有料であること - Aquesは音質がかなり低いこと を加味すると、現実的には、 **とりあえずはiOS標準が第一候補** で、オンライン前提でもよければ **iSpeech** 、あとは、どうしてもイントネーションを調整したいとか、男性の声を使いたいとか、特殊なニーズに応じて検討、という感じです。 せっかくいろいろ調べたのに、なかなかしょっぱい結論となってしまいました。。 ##余談 ちなみに、今回TTSエンジンを検討したのは、 私の個人名刺に関係したアプリを作成するためでした。 <img src="http://cdn-ak.f.st-hatena.com/images/fotolife/s/shu223/20131128/20131128004555.jpg" width="300"> [iOS7対応の名刺をつくりました](http://d.hatena.ne.jp/shu223/20131118/1384757372) また準備が整ったら更新します。 ##関連ページ - [フリーの iOS 向け音声認識/音声合成ライブラリ『OpenEars』の使い方](http://qiita.com/shu223/items/eda02dc7d334c339ff64) |
|
| 531位 |
|
|||
|
16:40:58 |
|
|
git diff コマンドは、2つの commit の差分や、 working tree と index の差分などを表示するコマンドである。 ## 実行例 ### まだ index に add していないモノを表示 ``` git diff ``` index と working tree の差分を表示するコマンド。 _注意: 新規に作成したファイルはこのコマンドでは表示されない。新規に作成されたファイルは Git に track されていないので。_ ### 次の commit で反映される変更を表示 ``` git diff --cached ``` index と HEAD の差分を表示するコマンド。 commit 直前によく実行する。 ### 直前の commit による変更を表示 ``` git diff HEAD^ HEAD ``` HEAD と HEAD^ の差分を表示するコマンド。 commit 直後によく実行する。 ``` git diff HEAD^ HEAD --stat ``` `--stat` オプションを指定すると、差分の統計情報だけを表示する。 ## オプションについての補足 git diff コマンドは、 default では index と woking tree の差分を表示する。 `--cached` オプションを指定すると、 index と、ある commit の差分を表示する。 (commit を明示的に指定しない場合は HEAD との差分を表示する。) ## 設定 コンフィグファイルに以下の設定を書くことで、 git diff を含む Git の大半の出力に色がつく。 ``` [color] ui = true ``` ` ~/.gitconfig ` に上記設定を書き込むには、以下のコマンドを実行すれば良い。 ``` git config --global color.ui true ``` ## リファレンス * [git-diff](http://www.git-scm.com/docs/git-diff) * Git 公式ウェブサイトの中のマニュアルページ。 * [git diff の使い方がほんの少し理解できた - murankの日記](http://d.hatena.ne.jp/murank/20110320/1300619118) * git diff コマンドの使い方について、絵付きで分かりやすく書いてある。 * [Git - Git の設定](http://git-scm.com/book/ja/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%81%AE%E8%A8%AD%E5%AE%9A) * コンフィグファイルの設定について。 |
|
| 532位 |
|
|||
|
17:01:13 |
|
|
このドキュメントは、Node.jsが楽しくてたまらない人のための、さくらVPSでアプリケーションサーバーを構築するためのチュートリアルです。
**さくらVPS-Nodeチュートリアル**とでも呼ぶことにします。 このチュートリアルを完遂すると、Node.jsを用いたWebアプリケーションの公開サーバーを構築することができます。 また、デプロイにはDropboxによる同期を利用します。これは開発作業やメンテナンスを容易にします。 このチュートリアルは初学者向けを意図しており、構築するアプリケーション・サーバーも開発したWebアプリの個人レベルでの公開や、動作チェックなどを用途として意識しています。 そのため、あえてセキュリティ関連については触れていません。 このチュートリアルで重要なのはまずアプリケーションサーバーを構築することです。 確かなセキュリティを保ち続けるのは容易ではありませんし、一度確保したとしても日々新しい穴が見つかり続けます。 もしWebサービスとして公開するための本番環境として運用するなら、そこはググって調べてみて下さい。 それでは始めましょう。 ### やるべきこと 先に、これからやるべきことを一度確認しておきます。 1. **さくらVPSを契約する** 2. **いろいろな初期設定をする** 3. **Dropboxをインストールする** 4. **Node.jsをインストールする** 5. **nginxをインストールする** 6. **MongoDBをインストールする** 手順はたったの6つです。 このチュートリアルは、手順1と手順2を除けば、すべて任意・順不同で進められるようになっています。 たとえばNode.jsをすでにインストールしている人は、手順4を省略することができます。 ただ、手順1と手順2は必ず目を通し、やってないことがあればやって下さい。 ## 1. さくらVPSを契約する さくらインターネットのVPSを契約します。 以下のURLから契約して下さい。 > VPS(仮想専用サーバ)のさくらインターネット > [http://vps.sakura.ad.jp/](http://vps.sakura.ad.jp/) 契約するのは、1番安い、月額980円のプランで充分です。 リージョンは東京/大阪/石狩と選べますが、故郷が石狩の人だけ石狩を選択して下さい。故郷を大切にするのはいいことです。 他の人はおとなしく東京にしましょう。 クレジットカードで契約すれば、最初の2週間はお試し期間とされ無料になります。 その間にキャンセルすれば、お金は請求されません。このチュートリアルを進めて気に入れば、是非そのまま本登録しましょう。 月額980円とはいえ、何の制限もなく自分用のWebアプリをデプロイできる環境があるのはものすごく便利なことです。 ### 1-2. サーバーを起動する 契約してしばらくすると、「仮登録完了のお知らせ」メールが届きます。 このメールがきたら、もうサーバーで遊ぶことができます。 このメールには、サーバーのIPアドレスやrootユーザーのパスワードが書かれています。 今後しばらく使うことになるので、別のタブで開いておきましょう。また、大切に保管しましょう。 それではさっそく、サーバーを起動させてみましょう。 「VPSコントロールパネル」にログインし、「仮想サーバ操作」の「起動」を押します。 初回起動は少し時間がかかります。「稼働中」という表示になってから、15分ほど待ってから次の手順に移りましょう。 ### 1-3. とりあえずSSH接続を試す ターミナルで以下を実行します。 ``` $ ssh root@<接続先IPアドレス> ``` 今後、ターミナルでの操作は先頭に「$ 」をつけて表記します。 「$ 」を入力する必要はありません。 また、コマンドを「実行する」とは、コマンドを入力してEnterキーを押すことです。言わなくてもわかるか、それくらい。 `<接続先IPアドレス>`はお知らせメールに書かれているものに置き換えて下さい。 一例として僕の環境では、`ssh root@153.123.45.67`のようになります。 入力後、パスワードを求められます。これもお知らせメールに書かれているのでそれを見ながら入力して下さい管理ユーザの初期パスワードがそれです)。 (もし、`Are you sure you want to continue connecting (yes/no)?`と聞かれたら`yes`と入力してEnterを押して下さい) 接続に成功すると、 ``` [root@www4408uo ~]# ``` 入力待ち状態の表示がこんな感じに変わります(詳細の個人差はあります)。 この状態を、このチュートリアルでは今後「VPSにログインして〜」などと表現します。 そうでない通常の状態は、「ローカルマシンで〜」などと表現します。ローカルマシンとは、今あなたが使っているそのPCそのもののことです。 #### Windowsの人へ ターミナルとは、Mac版のコマンドプロンプトのことです。 このチュートリアルでは、手順の多くをCUI(コマンド)操作で行います。 しかしながら、Windowsのコマンドプロンプトからでは上の`ssh`コマンドが使えません。 Windowsでこのチュートリアルを進める場合は、[**Git**]([http://git-scm.com/](http://git-scm.com/))をインストールして下さい。 すると、「Git Bash」というアプリケーションが付随してインストールされます。 Git Bashはコマンドプロンプトがより強化されたものです。それであれば、Macユーザーと同じコマンドで手順を進められます。 ## 2. いろいろな初期設定をする ここでやることは2つです。 まず、普段使いのためのユーザーを作成します。 最初から用意されている「root」ユーザーは、システム全てを支配している管理ユーザーです。これを普段使いとして利用するのはおすすめしません。 もうひとつは、その後の作業に必要なツールが入っているか確認することです。 このチュートリアルで一番の要といっても差し支えないかもしれません(とはいえ、ほとんどの場合やることはありませんが)。 ### 2-2. ユーザーを作成する VPSにログインした状態で、以下を実行します。 ``` $ useradd <ユーザー名> -G wheel ``` `<ユーザー名>`の部分は自由に決めて下さい。半角英数小文字と「_」(アンダースコア)で構成した名前がいいでしょう。 普段あなたが使っているハンドルネームにでもすればいいと思います。 次に、そのまま以下を実行します。 ``` $ passwd <ユーザー名> ``` これは、ユーザー名に対してパスワードを設定するコマンドです。 `<ユーザー名>`は、先ほど決めたユーザー名を入力して下さい。 確認のためパスワードを2回尋ねられますので、2回とも同じものを入力して下さい。 ### 2-3. ユーザー設定ファイルを編集する 3つのファイルを編集します。 この手順では、ファイルの編集に「vi」というテキストエディタを使います。 操作がちょっと特殊です。馴染みがない人は以下の5つだけ覚えて下さい。 あとは普通のテキストエディタと同じように扱えるはずです。 - 入力モードとコマンドモードがある - 入力モードに入るにはiキー - コマンドモードに戻るにはEscキー - コマンドモードの状態で「:wq」と入力してEnterキーで「上書き保存して終了」 - コマンドモードの状態で「:q」と入力してEnterキーで「保存せずに終了」 それでは始めましょう。 #### /etc/pam.d/su 以下を実行します。 ``` $ vi /etc/pam.d/su ``` /etc/pam.d/su がviで開かれるので編集します。 **Before** ``` # auth required pam_wheel.so use_uid ``` この行をコメントインします。 **After** ``` auth required pam_wheel.so use_uid ``` こうなればOKです。 #### /etc/login.defs 以下を実行します。 ``` $ vi /etc/login.defs ``` etc/login.defs がviで開かれるので編集します。 末尾に以下の行を追加します。 ``` SU_WHEEL_ONLY yes ``` #### visudo 以下を実行します。 ``` $ visudo ``` viの設定ファイルがviで開かれるので編集します。 **Before** ``` # %wheel ALL=(ALL) ALL ``` この行をコメントインします。 **After** ``` %wheel ALL=(ALL) ALL ``` こうなればOKです。 これで設定の編集は完了です。 ### 2-4. ログイン(接続)しなおす お疲れ様でした。 無事にユーザーを作成して、設定も済ませたのでそのユーザーでログインしなおしましょう。 以下を実行します。 ``` $ exit ``` さくらVPSから切断され、ローカルマシンでの操作に戻ってきます。 そのまま、先ほどのユーザーでログインしなおします。 以下を実行します。 ``` $ ssh <ユーザー名>@<接続先IPアドレス> ``` パスワードを求められます。先ほど設定したパスワードを入力すると、ログインできると思います。 VPSへのログインに成功すると、以下のような表示に変わります。 ``` [axross@www4408uo ~]$ ``` rootからaxrossに変わってますね。 ### 2-5. 各種ツールがインストールされているか確認する この先必要になってくるのは、sudoとyumとgitという3つのツールです。 さくらVPSを契約した段階で、最初からインストールされているはずですが、念のため確認しておきましょう。 もしこれらがインストールされていない場合は、ググってインストールするか、さくらVPSのコントロールパネルからOSの再インストールを検討して下さい(適当)。 まずはsudoを確認します。以下を実行します。 ``` $ sudo -V ``` なにやら3行くらい出ますが、バージョン番号が表示されてればOKです。 次に、yumの確認のため以下を実行します。 ``` $ yum --version ``` 1行目にバージョン番号が出てればOKです。 最後にgitです。以下を実行します。 ``` $ git --version ``` 「git version ○○」みたいな表示になるはずです。 ## 3. Dropboxをインストールする Dropboxをインストールしておくことで、ローカルマシン(開発環境)で書いたコードをさくらVPS(公開環境)にデプロイする際の手間がなくなります。 ローカルマシンでコードを編集した時点で、さくらVPS側でも瞬時に変更が反映されるからです。 また、逆方向にも同期されますので、データベースの閲覧・編集といった保守作業も楽にできるようになります。 まず、VPSにログインした状態で以下を実行します。 ``` $ cd ~ && wget -O - "https://www.dropbox.com/download?plat=lnx.x86_64" | tar xzf - && ~/.dropbox-dist/dropboxd ``` 何やら処理がはじまって勝手にダウンロード・インストールされます。 インストールが終わると、以下のようなメッセージが表示されます。 ``` このコンピュータは Dropbox アカウントにリンクされていません... このデバイスをリンクするには、https://www.dropbox.com/cli_link?host_id=<ランダムな文字列> にアクセスしてください。 ``` これに書かれているURLをコピーして、ローカルマシンのブラウザでアクセスしましょう。 着く先はDropboxのストレージのWebです。パスワードを求められるので、Dropboxアカウントのパスワードを入力します。 これにより、VPSがDropboxアカウントで認証されます。 「このコンピュータは Dropbox にリンクされました。ようこそ、<名前> さん。」のようなメッセージが表示されればOKです。 Ctrl+Cを押して、インストーラを終了させます。 ### 3-2. 管理スクリプトをインストールする 以下のコマンドを実行します。 ``` $ mkdir -p ~/bin && cd ~/bin && wget "https://www.dropbox.com/download?dl=packages/dropbox.py" -O dropbox.py && chmod +x ~/bin/dropbox.py ``` ~/bin/にdropbox.pyというDropboxを管理するためのPythonスクリプトが置かれます。 ### 3-3. 管理スクリプトを使う ``` $ dropbox.py <コマンド> ``` Dropboxの管理スクリプトは、インストールの段階で実行権限を与えているのでそのままファイル名で呼んで実行することができます。 いくつかの操作を以下に記します。 - start - Dropboxを起動する - stop - Dropboxを終了させる - running - 起動しているか確認する - status - 同期状況を確認する - autostart - 自動的にDropboxが起動するように設定する 他にも、同期したくないファイルを設定できるexcludeコマンドなどもあります。詳しくは[こちら](http://www.dropboxwiki.com/tips-and-tricks/using-the-official-dropbox-command-line-interface-cli)。 とりあえずは、Dropboxを起動しておくために以下を実行しましょう。 ``` $ dropbox.py start ``` ### 3-4. lansyncをオフにする ``` $ dropbox.py lansync n ``` 同LAN内のマシンにpingを飛ばして同期を促す機能をオフにしておきます。 VPSではこの機能は不要などころか、他のVPS利用者にとって邪魔になるかもしれません。 設定の後はDropboxを再起動したいので、以下を実行しておきましょう。 ``` $ dropbox.py stop ``` Dropboxが終了したら、以下を実行してもう一度起動します。 ``` $ dropbox.py start ``` また、下記を実行すると、VPSへログインした際にも自動的にDropboxが起動するようになります。 ``` $ dropbox.py autostart y ``` ### 3-5. Dropboxを使う Dropboxは、ユーザーのホームディレクトリ直下にインストールされています(`~/Dropbox/`)。 今後、Node.jsのプロジェクト(jsファイルなど)をDropboxに入れることで、さくらVPS環境からもそれらを実行・閲覧・変更できます。 ## 4. Node.jsをインストールする このチュートリアルでは、Node.jsのインストールにバージョン管理ツールの「nodebrew」を利用します。 nodebrewを用いると、複数のバージョンのNode.jsをインストールしておいて切り替えたり、手軽に新しいバージョンのNode.jsをインストールしたりできます。 Node.jsは今も成長しており、盛んにアップデートされるためこれができるのは重要なことです。 ### 4-2. nodebrewをインストールする まず、VPSにログインした状態で以下を実行します。 ``` $ cd ~/ && curl https://raw.github.com/hokaccha/nodebrew/master/nodebrew | perl - setup ``` 自動的に必要なパッケージがダウンロード、インストールされます。 次に、PATHを通すために以下を実行します。 ``` $ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile && source ~/.bash_profile ``` きちんとnodebrewがインストールされているか、以下を実行することで確認できます。 ``` $ nodebrew -v ``` 1行目にバージョン番号が書かれていればOKです。 ### 4-3. nodebrewを使ってNode.jsをインストールする Node.jsをインストールするには、以下を実行します。 ``` $ nodebrew install-binary stable ``` 「stable」としているところは、インストールしたいNode.jsのバージョンです。stableであれば安定版の最新のものがインストールされます。 過去のバージョンや開発版のものをインストールしたい場合は、数字で直接指定することもできます。 その後、以下を実行します。 ``` $ nodebrew use stable ``` これにより、実際に「stable」バージョンのNode.jsを「使う」という指定ができます。 Node.jsのバージョンを切り替える際はこのコマンドを用います。もちろん、数字で指定することもできます。 最後に、Node.jsが無事にインストールされているか確認するために以下を実行します。 ``` $ node -v ``` バージョン番号が表示されればOKです。 (うっかり`-v`をつけずにnodeコマンドを実行してしまった場合は、`.exit`と入力してEnterを押せば終了できます) ## 5. nginxをインストールする 「nginx」はHTTP機能を持ったプロキシサーバーです。このチュートリアルではこれをリバースプロキシサーバーとして利用します。 ここではnginxの持つバーチャルホスト機能を利用します。 詳細は後で説明するとして、まずはインストールしましょう。 nginxのインストールには、以下を実行します。 ``` $ sudo yum install -y nginx ``` 「sudo」はこのコマンドの時だけ管理ユーザーで実行する、といった働きをするコマンドです。それに「yum」を組み合わせ、nginxを管理者権限でインストールしています。 この操作の後、パスワードを求められます。手順2-2で設定したユーザーパスワードを入力して下さい。 その後、以下の2つを実行します。 ``` $ sudo service nginx start ``` ``` $ sudo chkconfig nginx on ``` 1つ目のコマンドはnginxの起動です。 2つ目は、nginxをVPS起動時に同時起動(Windowsでいうスタートアップ)するようにするコマンドです。 ### 5-2. nginxのバーチャルホストの設定をする まずは今後ググる時のために、すべきことを整理しておきます。 Webアプリの割り振りには、「バーチャルホスト」と呼ばれる技術を利用します。 バーチャルホストとは、1つのマシン(サーバー)に対して、あたかも複数のIPアドレスまたはドメインが割り振られているように振る舞わせる技術のことです。 そして、その割り振り先にポートを指定できるので、複数のWebアプリを運用する場合はそれらをすべて違うポート番号でアクセスできるようにしておきます。そうすれば、「○○○.axross.org」はこのWebアプリへ、「△△△.axross.org」はこのWebアプリへ、と割り振ることができます。 1つのサーバーで複数のWebアプリを運用するので、負荷の大きいWebアプリを運用する際は気をつけて下さい。負荷が他のWebアプリにも影響します。 では、実際に設定する際の方法です。 以下を実行します。 ``` $ sudo vi /etc/nginx/conf.d/virtual.conf ``` sudoしているのでパスワードを求めれます。rootのパスワードを入力して下さい。 すると、virtual.conf という設定ファイルがviで開かれます。 まずは、以下の書き方を覚えて下さい。 ``` server { listen 80; server_name <ドメイン>; location / { proxy_pass http://127.0.0.1:<ポート番号>; } } ``` <ドメイン>と<ポート番号>は適宜置き換えて下さい。 たとえば僕が開発した「僕のはてブ」であれば、ドメインは「hatebu.axross.org」で、内部的には「4567」ポートで動かしています。 ですので、その場合は下記のようになります。 ``` server { listen 80; server_name hatebu.axross.org; location / { proxy_pass http://127.0.0.1:4567; } } ``` 必要のない設定などは「#」を行頭につけることでコメントアウトできます。 また、設定を編集した場合はnginxを再起動させる必要があります。それには以下を実行します。 ``` sudo service nginx restart ``` sudoしているのでrootのパスワードを求められます。そろそろsudoにも慣れてきたでしょうか。 ## 6. MongoDBをインストールする MongoDBはNode.jsのWebアプリでよく用いられるNoSQLデータベースです。 データの取り扱いがJSONによく似ていて、Javascriptで扱いやすいのが特徴です。 さっそくインストールしましょう。 VPSにログインした状態で以下を実行します。 ``` $ sudo vi /etc/yum.repos.d/mongodb.repo ``` mongodb.repo がviで開かれますので、以下のように書いて保存します。 ``` [mongodb] name=MongoDB Repository baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64/ gpgcheck=0 enabled=1 ``` 次に、以下を実行します。 ``` $ sudo yum install -y mongo-10gen mongo-10gen-server ``` yumを用いてMongoDBクライアントとサーバーがインストールされます。 ### 6-2. MongoDBサーバーを起動する 以下を実行します。 ``` $ sudo service mongod start ``` mongodサービスはMongoDBのサーバーデーモンです。`start`の他に、`stop`したり`restart`することもできます。 VPSを再起動した再などに同時にMongoDBサーバーも起動するには、以下を実行してchkconfigに登録します。 ``` $ sudo chkconfig mongod on ``` これでOKです。 ## 最後に お疲れ様でした。 このチュートリアルを終えたあなたの手元には、Node.jsアプリケーションサーバーとして構築されたさくらVPSがあるはずです。 開発したWebアプリを実験する環境として使ってもいいですし、バーチャルホストであることを利用してポートフォリオWebサイトにしても立ち上げてもいいかもしれません。 なんにせよ、まずは開発を楽しみましょう! このドキュメントを書き上げるのに参考にさせていただいた各種公式ドキュメントと、Qiita、Googleに深く感謝します。 |
|
| 533位 |
|
|||
|
15:49:00 |
|
|
コマンドラインツール作成支援のライブラリThorについて調べました。基本的な使い方について情報をまとめました。といっても、ほとんどホームページの基本情報を日本語訳しただけのようなものですが。 ### Thorの概要 Thorは、コマンドラインツールの作成を支援するライブラリです。 gitやbundlerのようにサブコマンドを含むコマンドラインツールを簡単に作成することができます。 - - - ### 基本的な使い方 Thorを継承したクラスのパブリックメソッドがコマンドになります。 MyCLI.start(ARGV)でコマンドラインの処理をスタートします。一般的にはbinフォルダの実行形式ファイル内でstart(ARGV)を実行します。 ```ruby:cli class MyCLI < Thor desc "hello NAME", "say hello to NAME" def hello(name) puts "Hello #{name}" end end MyCLI.start(ARGV) ``` これでhelloに引数を渡すと挨拶を返すコマンドラインの出来上がりです。 ```bash $ ruby ./cli hello "ruby" Hello ruby ``` また、Thorは自動的にヘルプも生成してくれます。コマンドの指定なしで実行するとヘルプを表示します。 ```bash $ ruby ./cli Tasks: cli hello NAME # say hello to NAME cli help [TASK] # Describe available tasks or one specific task ``` Rubyの通常のメソッドと同じように、引数の初期値を指定することも可能です。初期値を指定すると該当の引数の入力は任意になります。 ```ruby:cli class MyCLI < Thor desc "hello NAME", "say hello to NAME" def hello(name="Ruby") puts "Hello #{name}" end end ``` 引数を入力しない場合は初期値が使用されます。 ```bash $ ruby ./cli hello Hello ruby ``` - - - ### オプションの指定方法 コマンドのメタ情報としてオプションを指定することができます。 ```ruby:cli class MyCLI < Thor desc "hello NAME", "say hello to NAME" option :from def hello(name) puts "from: #{options[:from]}" if options[:from] puts "Hello #{name}" end end ``` オプションの値には、optionsハッシュにオプション名を指定することでアクセスできます。 オプションの値のデフォルトはStringですが、:typeを指定することで他のタイプを指定することができます。 :required => trueを指定することで特定のオプションを必須にすることができます。 ```ruby:cli class MyCLI < Thor option :from, :required => true option :yell, :type => :boolean desc "hello NAME", "say hello toNAME" def hello(name="Ruby") output = [] output << "from: #{options[:from]}" if options[:from] output << "Hello #{name}" output = output.join("\n") puts options[:yell] ? output.upcase : output end end ``` オプションに対して指定できるメタ情報は以下の通りです。 * :desc - オプションの説明。コマンドのヘルプに表示される。 * :banner - オプションの説明。 * :required - 必須。 * :default - オプションのデフォルト値。:requiredと:defaultを同時に指定することはできない。 * :type - オプションの値のタイプ。:string, :hash, :array, :numeric, :boolean * :aliases - オプションの別名。一般的にはオプションのショートバージョン指定のために使う。 :typeの指定には「オプション => タイプ」の省略表記を使用することができます。タイプには:requiredを指定することができます。この場合、そのオプションは:stringで必須になります。 クラス全体で共通のオプションをclass\_optionで指定することができます。 ```ruby:cli class MyCLI < Thor class_option :verbose, :type => :boolean desc "hello NAME", "say hello to NAME" options :from => :required, :yell => :boolean def hello(name) puts "> saying hello" if options[:verbose] output = [] output << "from: #{options[:from]}" if options[:from] output << "Hello #{name}" output = output.join("\n") puts options[:yell] ? output.upcase : output puts "> done saying hello" if options[:verbose] end desc "goodbye", "say goodbye to the world" def goodbye puts "> saying goodbye" if options[:verbose] puts "goodbye world" puts "> done saying goodbye" if options[:verbose] end end ``` - - - ## 参考 * [Thor - Home](http://whatisthor.com) |
|
| 534位 |
|
|||
|
10:32:26 |
(フリーランス 所属)
|
|
by @mixiappwchr
みなさん、最近はstoryboardを使ってバリバリ開発していることと思いますが、調子に乗っているとこのようになったりしませんでしょうか?  複雑過ぎでどうなっているかわかりません。。。! storyboardのsegueは直感的でわかりやすいのですが、そのままsegueでの遷移ばかり使っているすぐにスパゲッティなviewになります。 適切に複数のstoryboardに分けたり、xibとの使い分けを行えば良いのですが、めんどくさかったりします。 そもそもiOSの開発において画面を遷移するパターンは様々です。 * navigationControlerへのpush * presentViewControllerでの表示 * view単体でのaddSubview * 外部アプリからのscheme起動、またはその逆 これらの概念をもっとすっきりしたい、、そうだ!僕らにはあれがあるじゃないか!と思い立ち,ライブラリを作りました。 ## MYRoutes このライブラリは,web service開発では当たり前で使っているroutingの概念をiOS開発にも持ち込んで、 遷移の問題を単純化しようというライブラリです。 それだけでなく、遷移に関するコードをシンプルにまとめあげています。 https://github.com/nyankichi820/MYRoutes ```objcobjc [[MYRoutes shared] loadRouteConfig:@[ @[@"/nib/:message" ,@{@"nib":@"XIBTestViewController",@"class":@"MYViewController"}], @[@"/storyboard/first/:message" , @{@"storyboard":@"Main",@"identifier":@"First"}], ]]; ``` とルーティングの設定を行っておくと ```objc [[MYRoutes shared] dispatch:[NSURL URLWithString:@"/nib/hello?category_id=1"]] ``` とURLを渡してあげると,設定にそってViewを表示してくれます。 パラメーターも当然URLから引き渡すことができ, ```objc @[@"/nib/:message" ,@{@"nib":@"XIBTestViewController",@"class":@"MYViewController"}], ``` と設定されていると:messageといった記述をパラメーターをキャプチャする設定となり ```objc [routes dispatch:[NSURL URLWithString:@"/nib/hello?category_id=1"] ``` といったURLは指定のViewControllerにmessageプロパティに@"hello"がセットされた状態で表示されます。 またquery からも設定することができ,この場合はcategoryIdに@"1"がセットされます。URLなのでスネークケースで書くことが多いと思いますが、自動でキャメルケースに変更してsetします。 当然URL schemeからの起動にそのまま対応することができ ```objc - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { // dispatch from url scheme. example xxxx://tweet/view/id return [[MYRoutes shared] dispatch:url ]; } ``` とAppDelegateよりURLを渡してあげれば、適切に処理することが可能です。なによりコードが煩雑にならずにRouting設定で管理することが可能です。 逆に外部のアプリケーションの起動も同じくURLを渡せばOKです。 ```objc [routes dispatch:[NSURL URLWithString:@"http://www.yahoo.co.jp"]] ``` その他にも、URLからのルーティングではない、呼び出し方を便利にするメソッドも用意しています。 例えば、pushしたい場合にパラメーターもつけたい場合 ```objc NSDictionary *params = @{@"message":self.messageTexfield.text}; [routes pushViewController:@"ViewControllerIdnetifier" withStoryboard:@"StoryboardName" withParameters:params animated:YES completion:nil]; ``` と書くことでセットしたDictionaryのパラメータをもたせてpushすることが可能です。 またpush先のnavigationControllerは自動判別するため、現在のnavigationControllerが取得できない場所でも起動ができます。 まだ、カッとなって作ったばかりなので機能が貧弱でcocoapodsにも最新版がインデックスされてないのですが、よければ使ってみてください! ##### appwchr post ---- [iOSの開発をする上で絶対に使うべき!外せない!webサービス、開発ツール集](http://qiita.com/appwatcher/items/07a3babcb9b6cefb307e) [iOSでこんなアプリ,こんな機能を作りたかったらこれを見ろ!作りたいアプリに対応するクラス、フレームワーク、ライブラリのまとめ!] (http://qiita.com/appwatcher/items/b02255026a3ee6d95142) [注目のiBeacomなどの波に乗り遅れないために!iOSのBluetooth開発を容易にするライブラリを書きました。] (http://qiita.com/appwatcher/items/7491beffd7260b713542) [まだまだあった!iOSの開発を劇的に改善する最新のwebサービス、開発ツール集1] (http://qiita.com/appwatcher/items/f0024fe2ac34da345f04) [さらに快適なアプリ開発を!iOSの開発をもっと劇的に改善する最新のwebサービス、開発ツール集2] (http://qiita.com/appwatcher/items/c15d7311e71b4c2b77f1) [スパゲッティから脱出!iOS開発における遷移の問題をすっきり解決する便利ルーティングライブラリをご紹介] (http://qiita.com/appwatcher/items/259e8d13fff0547e90af) |
|
| 535位 |
|
|||
|
10:57:10 |
(コロプラ 所属) |
|
色々ややこしいBlocks。 宣言の仕方から、内部実装まで色々と調べたりしたのでメモ。 ##基本形 まずは基本形。 ```objc void (^blk)(void) { NSLog(@"in block"); }; ``` これをベースに展開していく、と覚えておくといい。 Blockは省略できる部分もある。 上記は以下のように省略できる。 ```objc (^blk)() { NSLog(@"in block"); }; ``` voidだから、ではなく、Block内の`return`文を読み取ってその値を返り値の型としてくれるらしい。 ちなみに、関数型ポインタの変形として覚えると覚えやすいかも、と思った。 ###関数型ポインタの例 ```objc int func(int i) { return i + 1; } int (*funcptr)(int) = &func; ``` この`int (*funcptr)(int) = &func;`という部分。 `*`を`^`にすればBlockになるイメージ。 ##型宣言 多分、実装する場合は宣言をして使うのが基本になると思う。 ```objc typedef void (^blk_t)(void); //`blk_t`が型となる ``` 実際に使う場合は以下。 ```objc blk_t blk = ^{ NSLog(@"in block"); }; ``` ##メソッドの引数宣言 ここがややこくなるところ。 ```objc - (void)hogeMethod:(void (^)(NSString *str))blk { NSString *test = @"argument"; blk(test); } ``` 基本的からだいぶ離れてしまった。 けど、Objective-Cのメソッド引数名はシグネチャのあとに来る、ということを考えると、こんな感じで考えるといいのではないかと。 まず最初にBlockの変数名が最後に来る。その後で、そのBlockの定義を基本形を元に書いていく。 ただ、すでに変数名は最後に書いてしまったので、そこだけ消去する。 そうするとメソッド引数の形が見えてくる。 ちなみに、型部分「`void (^)(NSString *str)`」はかっこでくくる必要がある。 分かりやすくすると↓な感じ。 ```objc // [Blockの型]には`void (^)(NSString *str)`が入る - (void)hogeMethod:([Blockの型])blk { // } ``` ##Block定義 こっちもだいぶややこい。 ```objc void (^anyBlk)(int) = ^void (int i) { printf("%d", i); } anyBlk(10); ``` `=`の左側はいわゆる変数宣言。(`int count = 5;`とかと同じ) ただ、その型部分がややこしいためとても複雑に見える。 型のみにすると ```objc void (^)(int i) = … ``` が型。確かに戻り値と引数の型をしっかりと宣言している。 そしてキャレット(`^`)の後に来るのが変数名、というわけ。 ただ、その型に代入する値としてのBlockもまた変な形なのでさらにややこしい。 これは、上記の「型」を持った値を代入している、と考えると少しは分かりやすいかも。 ```objc void (^anyBlk)(int i) = ^void (int i) ... ``` 頭の`^`はポインタの`*`のようにBlock型を表すものと考えればいいと思う。 で、最後に実際の関数部分を`{}`の中に書く、と。 ##Blockを関数の戻り値にする 基本的には`typedef`を使って型を定義しておいたほうが可読性が上がります。 ```objc typedef int (^blk_t)(int); blk_t func() { return ^(int i) { return i * i; }; } ``` ただ、型定義をしなくても一応使えるけど、もうなにがなんだかわからなくなるw ```objc int (^func())(int) { return ^(int i) { return i * i; }; } ``` これは、`int (^)(int)`という型のBlockを返す関数です。つまり型定義したほうと同じ。(関数名は`func`) Blockを知らずに`func`が関数名だと自信を持って言える人はいないと思います・・。 もはや宣言なのかすら怪しい状態です。やはり素直に型定義して使うほうがいいでしょう。 定義だけでもややこしいけど、コンパイラが解釈する内部実装はさらに変態的になっている模様・・。 ちょっと説明できる自信がないけど、分かっている範囲で備忘録として書いておこうと思います。 ---------- ##Blockの実装 「[エキスパート Objective-C プログラミング](http://www.amazon.co.jp/gp/product/4844331094/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4844331094&linkCode=as2&tag=edom18-22)」という書籍が実装についてとても詳しく解説してくれているので、そちらを読むと理解が早いと思います。 さて、Blockは大雑把に言えば(最終的に)ただの構造体です。 コンパイラが色々とややこしいところをすべて受け持ってくれて、やりたいことだけを記述できるようになっている、というわけです。 ###自動変数のキャプチャ Block外で宣言された変数を、Block内で使用することができます。 これは「自動変数のキャプチャ」という機能があって、これによって実現しています。 「キャプチャ」という名前から想像できるように、Block内部では実際の変数ではなくコピーが使われます。 ####キャプチャの例 ```objc int i = 5; void (^capTest)() = ^{ printf("%d", i); }; i = 10; capTest(); //=> 5になる ``` `i`に10を代入してから実行していますが、結果は`5`になります。 これは、Blockが定義された時点での`i`の値を「キャプチャ」しているために起こります。 ###__block修飾子 さらに、もしBlock内部でその変数を変更したい場合は`__block`修飾子を付けて宣言する必要があるのです。 さて、ではなぜこんなことが必要なんでしょうか。 コピーと書いたように、コンパイラはBlockを構造体として表します。 そしてその構造体を生成する際に、内部で使用している変数の値を「キャプチャ(コピー)」して、その構造体のメンバにします。 (構造体には関数へのポインタや、関数内で使用する変数を保持するメンバがあります) このコピー処理のために関数内で問題なく使えると同時に、変更ができなくなるわけですね。 (コピーしたものをいくら変更しても意味がない) Blockを使ったコードをC++に変換してみると、以下のような構造体が作られます。 ```sample.cpp struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_i_0 *i; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; ``` まだしっかり理解できてないのでコードのみの紹介ですが、Blockが構造体になる、という雰囲気が分かると思います。 さて、こうした複雑な仕組みのためにキャプチャが行われているわけです。キャプチャではなく、変数を自由に操作したい場合に必要なのが`__block`修飾子です。 さて、では`__block`を付けた変数はどうなるのでしょうか。 (ここはちょっとしっかり説明できる自信がありませんが)コンパイラはblock変数用の構造体を作ります。 そして、Blockを生成している関数内で該当の変数にアクセスがあった場合(つまりBlock外)も、Block内でアクセスがあった場合も問題がないように、変数へのポインタを制御してアクセス可能にします。 ###変数スコープを超えて Blockは値であるので、当然、引数に取ることも、関数の返り値にすることもできます。 ここでひとつ疑問がわきます。 とあるメソッドでBlockを返した場合、それがどこで使われるか分かりません。 さらに、そのBlock内ですでに終了した関数内の変数にアクセスした場合はどうなるでしょうか。 本来であれば、不正アクセスとして終了してしまいます。 しかし当然、Blockを使っている場合はそうはなりません。 Blockが戻り値として変える場合、自動的にBlockがヒープ領域にコピーされるようになっています。 そして先に書いた通り、Blockは構造体であるのでそのメンバを通して適切に変数にアクセスできる、というわけです。 ###変換されたソースを見る 実際に変換されたソースを見ると、なにが行われているかが分かると思います。 Blockを使った簡単なコードを書いて、以下のように`-rewrite-objc`オプションを指定して変換しているとどうなるかを見ることができます。 ```shell clang -rewrite-objc main.m ``` 数行のコードが、数百行まで膨れ上がります。 ちょっと行数が多いのでここには載せませんが、ごく簡単なサンプルを変換してみるとどういう動作をしているかが分かると思います。(上記のBlockの構造体もこれを利用して変換したコードを抜粋したものです) ごく簡単なサンプルを作って変換してみると、なにがどうなっているのか分かるのではないでしょうか。 |
|
| 536位 |
|
|||
|
03:28:36 |
(秘密結社シェルショッカー日本支部 所属) |
|
# シェルスクリプトは環境依存が激しいから……
などとよく言われ、敬遠される。それなら共通しているものだけ使えばいいのだが、それについてまとめているところがなかなかないので作ってみることにした。 ## 「どの環境でも使える=[POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/)で定義されている」と定義 「どの環境でも使える」とは、なかなか定義が難しい。あまりこだわりすぎると「古いものも含め、既存のUNIX全てで使えるものでなければダメ」ということになってしまう。しかし、私個人としては **今も現役(=メンテナンスされている)のUNIX系OSで使いまわせること** にこだわりたい。 とはいっても全てのOSやディストリビューションについて調べられるわけではないので、この記事では基本的に[最新のPOSIX](http://pubs.opengroup.org/onlinepubs/9699919799/)で定義されていることをもって、どの環境でも使えると判断するようにした。(飽くまで「基本的に」ということで) 従って、互換性確保のため、シェルの中で使ってよい機能は **Bourneシェルの範囲** ということにする。(bash,ksh,zsh,あるいはcsh等の拡張機能は使わないようにする) ## 随時バージョンアップ予定 新しいことを発見したり、教わったりしたら、随時この記事をバージョンアップしていこうと思うので、ツッコミ歓迎。 # 各論 ## 最終行の改行を省略したシェルスクリプトファイルにすべきではない シェルスクリプトの最後の行だからといって、行末のLF(0x0A)を省略するのは止めるべきだ。それは環境によって異なる動作を引き起こす原因になり得る。 例えば次のようにして、ヒアドキュメントセクションの終了宣言行で終わるシェルスクリプトを作ってみる。 ```sh: $ printf '#! /bin/sh\n' >> test.sh $ printf 'cat <<HEREDOC\n' >> test.sh $ printf ' hoge\n' >> test.sh $ printf 'HEREDOC' >> test.sh $ chmod +x test.sh $ ``` コードを見ればわかるように最後の行にだけ行末にLF(0x0A)を付けていないわけだが、一部の環境でこれを実行すると次のようになってしまう。 ```sh: $ ./test.sh hoge HEREDOC$ ``` ヒアドキュメントセクションの終了文字列と解釈されずに表示されてしまうのだ。他にも予期せぬ動作を招く恐れがあるので、最終行でもちゃんと行末には改行を付けよう。 ## シェルパターン シェルパターンとは、DOSで言うならワイルドカードといえば話が早いかもしれない。しかしUNIXのそれはもっと多機能で、ファイル名指定時のみならずcase文の条件指定時にも使えるし、何より`image[1-9][0-9][0-9].jpg`などと指定すればそのディレクトリーの中に存在する、"image100"から"image999"までのファイルを一括指定できるなど、正規表現ほどではないにしろ表現力が高いことが特長だ。 しかしこのブラケットに要注意。ブラケット記号の中に列挙した文字**「以外」を表す文字は、正規表現で馴染み深い`^`ではなく、`!`**である。 `^`は一応POSIXでも言及しているが、全ての実装で使えるとは限らない。この事実が厄介の元になっており、逆に言えば**`^`を「以外」の意味として解釈する環境もあれば、通常文字としてそのまま解釈する環境もある**ということだ。 従って、シェルパターンにおけるブラケットの中で`^`自身を文字として指定したければブラケット内の2文字目以降に記述すべきである。 ## シェル変数 まず、配列は使えない。従ってPIPESTATUSも使えない。 変数の中身を部分的に取り出す記述に関して使っても大丈夫なものに関しては、[POSIXのページ(2.6.2)](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02)を見るとまとまっている。 ### PIPESTATUS的な変数が必要な場合 例えばPIPESTATUSに依存したシェルスクリプトが既にあって、それをどの環境でも使えるように書き直したいと思った場合、実は可能だ。詳しいやり方については、別記事[「PIPESTATUSさようなら」](http://qiita.com/richmikan@github/items/44cbbde02bd130b2d910)を参照してもらいたい。 ### スコープ →local修飾子を参照 ## 正規表現 これは`AWK`、`grep`、`sed`等、コマンドによっても使えるメタキャラは違うし、`grep`なら`-E`オプションを付けるかどうかでも違うし、さらにGNU版でしか使えないものもあるので注意が必要。(*BSD上でもGNU版が採用されている場合もある。→grep参照) しかし、 **[正規表現メモ](http://www.kt.rim.or.jp/~kbk/regex/regex.html)** というスバラシいまとめページがあるのでここを見れば、使っても互換性が維持できるメタキャラがすぐわかる。 え、シェル変数の正規表現?それは一部シェルの独自拡張なので論外。 →ロケールも参照 ### ただし、文字クラスは使わない方が無難 [[:alnum:]]のように記述して使う「文字クラス」というものがある。これは正式名称を[POSIX文字クラス](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03_05)といい、その名のとおりPOSIX準拠であるのだが、一部の実装ではうまく動いてくれない。(Raspberry PiのAWKなど) まぁ、それはPOSIXに準拠してないそっちの実装が悪いといってしまえばそれまでなのだが、そもそも設定されているロケールによって全角を受け付けたり受け付けなかったりして環境の影響を受けやすいので使わない方がよいだろう。 ## 乱数 乱数を求めたい時、シェル変数のRANDOMを使うのは論外。それなら、とAWKのrand関数とsrand関数を使えばいいやと思うかもしれないがちょっと待った! 論より証拠。FreeBSDで次の記述を何度も実行してみれば、非実用的であることがすぐわかる。 ```sh:FreeBSD上でAWKを使って乱数を発生させると $ for n in 1 2 3 4 5; do awk 'BEGIN{srand();print rand();}'; sleep 1; done 0.0205896 0.0205974 0.0206052 0.020613 0.0206209 ``` つまり動作環境によっては乱数としての質が非常に悪いのだ。AWKが内部で利用しているOS提供のrand(3)とsrand(3)を、FreeBSDは低品質だったオリジナルのまま残し、新たにrandom(3)という別の高品質乱数源関数を提供することで対応しているのが理由なのだが。(Linuxではrand(3)とsrand(3)を内部的にrandom(3)にしている) ### /dev/urandomを使うのが現実的 ではどうすればいいか。POSIXで定義されているものではないが、`/dev/urandom`を乱数源に使うのが現実的だと思う。例えば次のようにしてod、sedコマンドを組み合わせれば0~4294967295の範囲の乱数が得られる。 ```sh:/dev/urandomからの乱数取得 $ od -A n -t u4 -N 4 /dev/urandom | sed 's/[^0-9]//g' ``` 最後のsedは、なぜ`tr -Cd '0-9'`にしないのか。理由は、→trコマンド参照 ### /dev/urandomをどうしても使いたくない場合 乱数の品質は/dev/urandomほど高くないものの、代替手段はある。psコマンドの結果は実行するたびに必ず変化するのでこれを種として取り入れる。 具体的には、プロセスID、実行時間、CPU使用率、メモリ使用量の各一覧あたりが刻々と変化するのでこれらを取得するとよいだろう。更に、現在日時も加え、これらに基づいて2^32未満の範囲でAWKのsrand()に渡す乱数の種を生成しているのが下記のコードだ。 ```sh:psコマンドを乱数の種として取り入れる LF=$(printf '\\\n_');LF=${LF%_} # sedで改行を扱うための定義 (ps -Ao pid,etime,pcpu,vsz; date) | # 乱数源(プロセス情報一覧+日時) od -t d4 -A n -v | # 数値化する sed 's/[^0-9]\{1,\}/'"$LF"'/g' | grep '[0-9]' | tail -n 42 | # 100000000未満の数字を sed 's/.*\(.\{8\}\)$/\1/g' | # 42個まで用意(2^32未満にするため) awk 'BEGIN{a=-2147483648;} # # 上の値を足してsigned long値を作る {a+=$1;} # END{srand(a);print rand();}' ``` ## ロケール ### ロケール環境変数によって動作が変わる可能性がある コマンドによっては、ロケール環境変数(`LANG`や`LC_*`)の内容によって動作が変わるものがある(環境によっては変わらないものもある)。具体的に何が変わるかといえば、主に文字列長の解釈や、出力される日付である。下記にそれらをまとめてみた。 * ロケール環境変数(`LANG`や`LC_*`)の内容によって、全角文字を半角の相当文字と同一扱いしたり、全角文字の文字列長を1とするもの * AWKコマンド、grepコマンド、sedコマンド等の正規表現(`[[:alnum:]]`、`[[:blank:]]`等々のキャラクタークラスや、`+`、`\{n,m\}`などの文字数指定子) * AWKコマンドの文字列操作関数(length、substr等) * wcコマンドの単語数(`-w`オプション) * などなど * ロケール環境変数(`LANG`や`LC_*`)の内容によって、デフォルトのフィールド区切り文字のが変わるもの * sort……`LANG`が設定されていると、その文字コードにおける全角スペースもデフォルトでスペース区切りと見なされる実装がある。 * ロケール環境変数(`LANG`や`LC_*`)の内容によって、出力される日付の書式が変わるもの * dateコマンド * lsコマンド * などなど * `LC_MONETARY`や`LC_NUMERIC`の影響を受けるもの * sort……`-n`を指定した場合に、桁区切りのカンマの影響を受けたり受けなかったりする。 ### ロケール環境変数の設定値は環境によってまちまち これも認識しておくべきことである。Linuxの多くのディストリビューションではインストール時に日本語を選択すると、日本語のロケールが設定されるが、皆が皆そうしているとは限らない。 ### 対策 全ての環境で動くようにするのであれば、ロケール設定無しの状態、すなわち英語で使うべきであろう。 ```sh:ロケール無し状態でのコマンド実行のしかた # 方法1. envコマンドで全てのロケールも含め環境変数を無効にした状態で実行する。 echo 'ほげHOGE' | env -i awk '{print length($0)}' # 方法2. LC_ALL=C及びLANG=Cを設定し、ロケール環境変数を全てCロケールにして実行する。 echo 'ほげHOGE' | LC_ALL=C LANG=C awk '{print length($0)}' # 方法3. 予めLC_ALL=CとLANG=Cを設定しておく export LC_ALL=C export LANG=C echo 'ほげHOGE' | awk '{print length($0)}' ``` ロケール系環境変数には現在、`LANGUAGE`と`LC_*`と`LANG`がある。このうち各種`LC_*`については`LC_ALL`の設定によって全て上書きされるが、`LANG`には効かないので`LC_ALL`と`LANG`を両方とも"C"にする。最初に列挙した`LANGUAGE`は最も強い効力を持つようだが、`LANG`や`LC_ALL`に"C"が設定されている場合は無視されるということである。→[GNU gettextドキュメント2.3.3項](http://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html#The-LANGUAGE-variable) ちなみに、いにしえの`export`は`=`を使えないということだが、[今どきのPOSIXのman](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#export)によれば使えることになっている。 ## `$((式))` よく「`expr`を使え」というが、`$((式))`はPOSIXでも指定されているので使ってもよいものとする。 ただ、数字の頭に"0"や"0x"を付けると、それぞれ8進数、16進数扱いされるので`expr`との間で移植をする場合は気を付けなければならない。(exprは数字の先頭に0が付いていても常に10進数と解釈される) ```sh:10進数、8進数、16進数として解釈される様子 $ echo $((10+10)) # 10進数の10たす10進数の10 20 $ echo $((10+010)) # 10進数の10たす8進数の10 18 $ echo $((10+0x10)) # 10進数の10たす16進数の10 26 ``` この問題は、異なる実装のAWK間にもあるので注意。→AWKコマンド参照 ## "["コマンド →testコマンド参照 ## AWKコマンド ### -0(マイナス・ゼロ) FreeBSD 9.xに標準で入っているAWKでは、`-1*0`を計算すると`-0`という結果になる。 ```sh:FreeBSD9.1で-1*0を計算させてみると $ awk 'BEGIN{print -1*0}' -0 $ ``` ところがこの挙動は同じFreeBSDでも10.xでは確認されていないし、GNU版AWKでも起こらないようだ。 このようにして、0であっても`-0`という二文字で返してくる場合のある実装もあるので、0を1文字と決め付けていると不具合を起こす場合がある。 #### マイナスを取り去るには 結果に0を足せばよいようだ。 ```sh:FreeBSD9.1で-1*0+0を計算させてみると $ awk 'BEGIN{print -1*0+0}' 0 $ ``` ### 0始まり即値の解釈の違い 頭に0が付いている数値を即値(プログラムに直接書き入れる値)として与えると、それを8進数と解釈するAWK実装もあれば10進数と解釈するAWK実装もある。 ```bash:FreeBSDのAWKで即値の010を解釈させた場合 $ awk 'BEGIN{print 010;}' 10 $ ``` ```bash:GNU版AWKで即値の010を解釈させた場合 $ awk 'BEGIN{print 010;}' 8 $ ``` どこでも同じ動きにしたければ、文字列として渡せばよい。すると10進数扱いになる。 ```bash:GNU版AWKでも文字列として"010"を渡せば10進数扱いされる $ awk 'BEGIN{print "010"*1;}' 10 $ echo 010 | awk '{print $1*1;}' 10 $ ``` ### length関数では配列の要素数を調べられない場合がある 今どきの大抵のAWKは、 ```awk:配列の要素数を調べられるlengthをサポートしてる場合 awk 'BEGIN{split("a b c",chr); print length(chr);}' ``` とやると、きちんと要素数(この例では3)を返してくれる。が、AWKの種類によっては、これに対応しておらずエラー終了してしまうものがある。 なので、例えば次のarlen()のように、配列の要素数を数える関数を自作してそちらを使うべきだ。 ```awk:配列の要素数を数える関数を自作しておく awk ' BEGIN{split("a b c",chr); print arlen(chr);} function arlen(ar,i,l){for(i in ar){l++;}return l;} ' ``` 幸いAWKの配列変数は、 **参照渡し** なので要素の中身が膨大だとしてもそれは影響しない。(要素数が大きい場合はやはり負担がかかると思うのだが……) 「length関数が使えるなら使いたい!」というワガママなアナタは、こうすればいい。 ```sh:lengthが使えるなら使いたいワガママなアナタへ # シェルスクリプトの最初で配列にlength使ってエラーにならないことを確認 case "$(awk 'BEGIN{a[3]=3;a[4]=4;print length(a)}' 2>/dev/null)" in 2) arlen='length';; # ←正しい数(=2)が返ってくれば"length" *) arlen='arlen' ;; # ←正しい数が返ってこねば独自関数"arlen" esac awk ' BEGIN{split("a b c",chr); print '$arlen'(chr);} # ←判定結果に応じて適宜選択される function arlen(ar,i,l){for(i in ar){l++;}return l;} ' ``` ### printf(およびsprintf)関数 →printfコマンド ### rand関数,srand関数は使うべきではない 詳しくは「乱数」のセクションを参照。 ### アクション記述を省略すべきではない AWKの基本文法は、各行に対するパターンとそれにマッチした時のアクションの記述から成っている。そしてアクションは省略可能で、省略した場合は`{print $0;}`を指定されたものと解釈されることになっている。 しかし、アクションを省略するとエラーになってしまう実装がある。Raspbienに載っているAWKでは次のようになってしまう。 ```sh:Raspbien上のAWKが起こすエラー $ echo HOGE | awk '1 END{exit;}' awk: line 1: syntax error at or near END $ ``` 回避策は、パターンを単独の行で記述するか、あるいはアクションを省略しないことなのだが、ワンライナーでも使えるのは後者だ。 ```sh:Raspbien上のAWKが起こすエラー $ echo HOGE | awk '{print;} END{exit;}' HOGE $ ``` アクションは省略すべきではないが、パターンは省略しても大丈夫だ。 ### 主に気を付けるべきは、gensub関数が使えないこと GNU版の独自拡張がいくつかあるが、中でも注意すべき点はgensub関数がそれであること。互換性を優先するなら多少不便かもしれないが関数とは言い難いインターフェースのsub関数やgsub関数を使う。あとは、[GNU AWKの`--posix`オプションに関するまとめ@kbkさん](http://www.kt.rim.or.jp/~kbk/gawk-30/gawk_15.html#SEC135)のページを見ればだいたいよいと思う。 ### 正規表現では`{数}`が使えない AWKの正規表現は、繰り返し指定が苦手。"?"(0~1個)と"*"(0個以上)と"+"(1個以上)は使えるが、2個以上の任意の数を指定するための`{数}`がない。GNU AWKにはあるんだけどね。その他は、[正規表現メモさんの記述](http://www.kt.rim.or.jp/~kbk/regex/regex.html#AWK)を見るのが便利。 ### 整数の範囲 例えば、あなたの環境のAWKは次のように表示されるだろうか? ```bash:浮動小数点表記になる例 $ awk 'BEGIN{print 2147483648}' 2.14748e+09 $ ``` 上記の例は、0x7FFFFFFF(符号付き4バイト整数の最大値)より大きい整数を扱えないAWK実装である。このようなことがあるので桁数の大きな数字を扱わせようとする時は注意が必要だ。 ### ロケール →ロケールを参照 ## bcコマンド POSIXに明記されているはずのコマンドなのだが、残念ながら一部のOSでは最小構成インストールでは省略されているものがある。Debian系Linuxディストリビューションの一部(Raspbianなど)やCygwinで確認している。 どの環境でも使えるシェルスクリプトにしたければ、bcコマンドを使わないという方法を取らざるを得ない。ただし、さすがにPOSIXのコマンドだけあり、省略されるOSでも(apt等により)大抵パッケージとして用意されているので、コメントやドキュメントでbcコマンドをインストールするよう促すのも手であろう。 ## dateコマンド 元々の機能が物足りないが故か、各環境で独自拡張されているコマンドの一つだが。互換性を考えるなら使えるのは * `-u`オプション(=UTC日時で表示) * `+フォーマット文字列`で表示形式を指定 の2つだけと考えるが無難だと思う。フォーマット文字列中に指定できるマクロ文字の一覧は、[POSIXのman](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/date.html)の"Conversion Specifications"の段落にまとめられている。 いろいろあるのだけど、UNIX時間(エポック秒とも呼ばれるUTC 1970/1/1 00:00:00からの秒数)との相互変換は無いようで、これが非常に残念。これさえできれば何とでもなるのに……。 だがこんなこともあろうかと、[相互変換コマンドutconv](https://gist.github.com/richmikan/8703117)を作っておいた。もちろんシェルスクリプト製だ。詳しくは、[シェルスクリプトで時間計算を一人前にこなす](http://qiita.com/richmikan@github/items/14e32d099bd9bcc7f8e8)を参照してもらいたい。 ## duコマンド `-h` オプションをつけた時の1列目表示が環境によって異なる。 ```sh:Linuxのduコマンド-hオプションの挙動 $ du -h /etc | head -n 10 112K /etc/bash_completion.d 12K /etc/abrt/plugins 4.0K /etc/statetab.d 4.0K /etc/dracut.conf.d 28K /etc/cron.daily 4.0K /etc/audisp 4.0K /etc/udev/makedev.d 36K /etc/udev/rules.d 48K /etc/udev 8.0K /etc/sasl2 ``` ```sh:FreeBSDのduコマンド-hオプションの挙動 $ du -h /etc | head -n 10 118K /etc/defaults 2.0K /etc/X11 372K /etc/rc.d 4.0K /etc/gnats 6.0K /etc/gss 30K /etc/security 40K /etc/pam.d 4.0K /etc/ppp 2.0K /etc/skel 144K /etc/ssh ``` 違いがわかるだろうか? 1列目(サイズ)が、前者は左揃えなのに後者は右揃えなのだ。なのでどの環境でも動くようにするには、1列目であっても行頭にスペースが入る可能性を考慮しなければならない。 例えば1列目の最後に単位"B"を付加したいとしたら、下記の1行目はダメで、2行目の記述が正しい。 ```sh:1行目の最後に"B"(単位)を付けたい場合 $ du -h /etc | sed 's/^[0-9.]\{1,\}[kA-Z]/&B/' # ←これでは不完全 $ du -h /etc | sed 's/^ *[0-9.]\{1,\}[kA-Z]/&B/' # ←こうするのが正しい $ du -h /etc | awk '{$1=$1 "B";print}' # ←折角の桁揃えがなくなるがまぁアリ ``` このようにして1列目にインデントが入るコマンドは結構あるし、インデントの幅も環境によりまちまちなので注意が必要だ。(例、 `uniq -c` 、`wc` などなど) ## echoコマンド 結論から言うと、どこでも動くよううにしたい場合、下記の項目に1つでも当てはまる時は**echoコマンドは使うべきではない。** * 先頭がハイフンで始まる可能性がある文字列 * エスケープシーケンスを含む可能性のある文字列 理由は次のとおりである。 ### 対応しているオプションが異なる 例えば、Linuxのechoコマンドは`-e`、`-n`オプションに対応しており、第一引数に指定すれば、それを文字列として表示せずに動作を変える。一方、FreeBSDのechoコマンドは`-n`のみに対応しており、また一方、AIXのechoコマンドはどちらにも対応していない。 というようにバラバラだからだ。 ### エスケープシーケンスに反応する実装がある 例えば`\n`は改行を意味するエスケープシーケンスであるが、FreeBSDのechoはそのまま“`\n`”と表示する。一方、Linuxのechoは`-e`オプションが付けられた時のみ改行に置換される。また一方、AIXのechoは常に改行に置換する。 **AIXのechoはデフォルトでエスケープシーケンスを解釈する**のだ。「それPOSIX的にどうなの?」と困惑するかもしれないが、[POSIXのechoのman](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html)にはちゃんとエスケープシーケンスの記述がある。 ### 対策 **どんな文字列が入っているかわからない変数を扱う場合**(ハイフンで始まらないとかエスケープシーケンスを含まないとわかっているならそのままでよい)、例えば次のようにprintfコマンドを使うなどして回避すること。 ```sh:echoのオプション反応問題を回避する方法の一つ(printfで代用) #! /bin/sh for arg in "$@"; do printf '%s\n' "$arg" done ``` ## envコマンド 今の環境変数の影響を一切受けずにコマンドを呼び出すために、``env -i <コマンド名>``のように-iオプションを使って起動したい場合があるが、ここで注意が必要だ。 大抵の実装では呼び出すコマンドパスを見つけるまでは環境変数PATHを覚えていてくれる。しかし一部の実装では-iオプションを付けると、コマンドパスを見つける前に環境変数PATHの内容を消してしまい、指定したコマンド起動に失敗してしまうことがある。 ```sh:envの-iオプションで環境変数PATH消去のタイミングが異なる。 # 多くの実装(外部コマンドのパスを見つけてから消去) $ env -i awk 'BEGIN{print "OK";}' OK $ # 一部の実装(外部コマンドのパスを見つける前に消去するのでエラーになる) $ env -i awk 'BEGIN{print "OK";}' env: awk: No such file or directory $ ``` この問題を防ぐには、既存の環境変数PATHを-iオプションの後ろで改めて指定するとよい。もちろんこの場合、環境変数PATHの値は呼び出し先コマンドに引き継がれることになるので注意すること。 ```sh:envの-iオプション指定時は、環境変数PATHだけ再定義するのが安全 $ env -i PATH="$PATH" awk 'BEGIN{print "OK";}' OK $ ``` ## execコマンド 注意すべきはexecコマンド経由で呼び出すコマンドに環境変数を渡したい時だ。 例えば、execコマンドを経由しない場合はこういう書き方ができる。 ```sh:コマンドの直前で環境変数を設定し、コマンドに渡す name=val awk 'BEGIN{print ENVIRON["name"];}' ``` これを実行すると、awkは`var`という文字列を表示する。 しかし、次のように書くと何も表示されないシェルがある。 ```sh:execの直前で環境変数を設定し、exec越しに呼び出すコマンドに渡す name=val exec awk 'BEGIN{print ENVIRON["name"];}' ``` 理由はというと、一部の環境のexecコマンドは、このようにして設定された環境変数を渡してくれないからだ。もしexecコマンド越しに環境変数を渡したいのであれば、事前にexportで設定しておくこと。 ```sh:exec越しに環境変数を渡したい場合の正しいやり方(1) export name=val exec awk 'BEGIN{print ENVIRON["name"];}' ``` あるいは、execの後、envコマンドを経由させるのでもよい。 ```sh:exec越しに環境変数を渡したい場合の正しいやり方(2) exec env name=val awk 'BEGIN{print ENVIRON["name"];}' ``` ## foldコマンド ### ファイル指定の`-` 一般的に、ファイル名として`-`を指定すると標準入力の意味と解釈されるが、本コマンドに対しては使わない方がよい。POSIXでもこのコマンドでもこのコマンドについて、そう解釈されると確かに書いてあるのだが、BSDの実装では真面目に"-"というファイルを開こうとしてエラーになってしまう。 ## grepコマンド > 俺は*BSDを使っているから、grepだってBSD版のはず。ここで使えるメタキャラはどこでも使えるでしょ。 と思っているアナタ。果たして本当にそうか確認してみてもらいたい。 ```sh:アナタのgrepはホントにBSD版? $ grep --version grep (GNU grep) 2.5.1-FreeBSD Copyright 1988, 1992-1999, 2000, 2001 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ ``` なんと、GPL排除に力を入れているFreeBSDのgrepはGNU版だ。関係者によれば、主に速さが理由で、grepだけは当面GNU版を提供するのだという。なので、POSIX標準だと思っていたメタキャラが実はGNU拡張だったということがある。(代表的なものは`\+`や`\?`や`\|`) POSIX標準grepで使える正規表現は、`-E`オプション無しの場合には[9.3 BRE](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03)で規定されているものだけ。`-E`オプション付きの場合には[9.4 ERE](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04)で規定されているものだけだ。[正規表現メモさんによる日本語解説](http://www.kt.rim.or.jp/~kbk/regex/regex.html#POSIX)が分かりやすいかもしれない。 ## headコマンド 大抵の環境には`-c`オプション(ファイルの先頭をバイト単位で切り出す)に対応しているのだが、POSIXでは規定されていないせいか正しく実装されていない環境も存在する。(AIXでは最後に余計な改行コードが付く) ちなみに、POSIXでもtailコマンドではきちんと規定されているので、headだけ規定されていないのはちょっと不思議だ。 さて、それでは`-c`オプションが使えない環境で何とかして同等のことができないものか……。大丈夫、ddコマンドでできる。 ```sh:ddコマンドでheadコマンドの-cオプション相当を実現する dd bs=1 count=$n 2>/dev/null # 標準入力の場合 dd if=/PATH/TO/TARGET_FILE bs=1 count=$n 2>/dev/null # ファイルの場合 ``` ddコマンドは標準エラー出力に動作結果ログを吐くので、head -c相当にするなら `2>/dev/null` などと書いて、ログを捨てること。 ## iconvコマンド POSIXに明記されているコマンドなのだが、初版より後に登場した新しいコマンドであり、一部のOSでは後から別途インストールしなければ使えない場合がある。比較的新しい実装としてはFreeBSD 9.0未満などが該当する。 古いUNIX系OSで動かされる可能性も考慮した上でどの環境でも使えるシェルスクリプトにしたければ、iconvコマンドを使わないという方法を取らざるを得ない。ただし、さすがにPOSIXのコマンドだけあり、(port等により)大抵パッケージとして用意されているので、コメントやドキュメントでiconvコマンドをインストールするよう促すのも手であろう。 ## if [ … ];then ~ else ~ fi構文 たまに、elseの時は何かしたいけどthenの時は何もしたくないということがある。そんな時、then ~ else の間に何も書かないとBash等一部のシェルではエラーを起こしてしまう。 ```sh:Bashでは次のコードはエラーになる if [ -s /tmp/hoge.txt ]; then # 1バイトでも中身があれば何もしない else # 0バイトだったら消す rm /tmp/hoge.txt fi ``` elifの後もelseの後も同様であるが、Bashでは何かしら有効なコードを置かないといけないのだ。(コメントを書いただけではダメ) ### 解決策 何らかの無害な処理を書けばいいのだが、一番軽いのはnullコマンドではないだろうか。つまり、こう書けばどの環境でも無難に動くようになるだろう。 ```sh:何もしたくなければnullコマンド":"を置くとよい(3行目に注目) if [ -s /tmp/hoge.txt ]; then # 1バイトでも中身があれば何もしない : else # 0バイトだったら消す rm /tmp/hoge.txt fi ``` 別の対策としては、条件を反転してそもそもelse節を使わずに済むようにするのもいいだろう。 ## ifconfigコマンド 実行中のホストに振られているIPアドレスを調べたい時にこのコマンドを使いたいことがあるが、各環境での互換性を確保するには2つのことに注意しなければならない。 ### パスが通っているとは限らない。 大抵の場合、ifconfigは/sbinの中にある。しかし **多くのLinuxのディストリビューションでは一般ユーザーにsbin系のパスが通されていない。** だから、このコマンドを互換性を確保しつつ使いたい場合は、環境変数PATHにsbin系ディレクトリー(`/sbin`、`/usr/sbin`)を追加しておく必要がある。 ### フォーマットがバラバラ ifconfigから返される書式が環境によってバラバラである。そこで、IPアドレスを取得するためのシェルスクリプトを[別Tips](http://qiita.com/richmikan@github/items/f32899c726b494828528)として書いてみたので参考にしてもらいたい。(IPv6も対応) ## killコマンド シグナルの種類は名称と番号のどちらでも指定できるわけだが、番号で指定する場合、[POSIXのman](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html)によれば、どの環境でも使える番号は下記のもの以外保証されていない。え、たったこれだけ!? | Signal No. | Signal Name | |:-----------|:------------| | 0 | 0 | | 1 | SIGHUP | | 2 | SIGINT | | 3 | SIGQUIT | | 6 | SIGABRT | | 9 | SIGKILL | | 14 | SIGALRM | | 15 | SIGTERM | もちろんシグナルの種類がこれだけしかないわけではない。ただ、 **その他のシグナルは名称と番号が環境によってまちまち** なのだ。(例えばSIGBUSは、FreeBSDでは10だが、Linuxでは7、といった具合) なので上記以外のシグナルを指定したい場合は名称("SIG"の接頭辞を略した文字列)で行うこと。使える名称自体は、[POSIXの記述でもご覧のとおり](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html)、豊富にある。 ### `-l`オプションで番号と名称の関係を調べられるとは限らない killコマンドで`-l`オプションを指定すれば、使えるシグナルの種類の一覧が表示されるのはご存知のとおり。しかし、番号と名称の対応がこれで調べられるわけではない。Linuxだと丁寧に番号まで表示されるが、FreeBSDでは単に名称一覧しか表示されない(一応順番と番号は一致してはいるのだが)。 ## local修飾子 シェル関数の中で用いる変数を、その関数内だけで有効なローカル変数にする場合に用いる修飾子だが、これはPOSIXでは規定されていない。しかし、関数内ローカルな変数は簡単に用意できる。小括弧で囲ってサブシェルを作ればその中で代入した値は外へは影響しないからだ。 よって、中身を丸ごと小括弧で囲った次のシェル関数で定義したシェル変数`$a`、`$b`、`$c`は、関数終了後に消滅するし、外部に同名の変数があっても中の値を壊すことはない。 ```sh:関数内のローカル変数を作る localvar_sample() { ( a=$(whoami) b='My name is' c=$(awk -v id=$a -F : '$1==id{print $5}' /etc/passwd) echo "$b $c." ) } ``` ## mktempコマンド **mktempコマンドはPOSIXで規定されていない!** だから、実際に使えない環境がある。 でもシェルスクリプトを本気で使いこなすにはテンポラリーファイルが欠かせず、そんな時に便利なコマンドがmktempなのだが……。どうすればいいだろうか。簡易的な対処と本格的な対処の2種類を用意した。 ### 簡易的なmktemp 簡易的なもの(一意性のみでセキュリティーは保証しない)なら下記のようなコードを追加しておけば作れる。 ```mktempコマンドが無い環境での簡易的な対策コード type mktemp >/dev/null 2>&1 || { mktemp_fileno=0 mktemp() { ( filename="/tmp/${0##*/}.$$.$mktemp_fileno" touch "$filename" chmod 600 "$filename" printf '%s\n' "$filename" ) mktemp_fileno=$((mktemp_fileno+1)) } } ``` mktempコマンドの有無を確認し、無ければコマンドと同じ使い方ができるシェル関数を定義するものだ。ただし引数は無視され、必ず/tmpディレクトリーに生成される。 ### 本格的なmktemp [POSIX版mktempコマンド](https://github.com/ShellShoccar-jpn/misc-tools/blob/master/mktemp)を作ってしまったので、これをダウンロードして使えばよい。 書式は[Coreutils版](https://www.gnu.org/software/coreutils/manual/html_node/mktemp-invocation.html#mktemp-invocation)に似せてある。ただし動作パフォーマンス確保のため、/binや/usr/binの中に元々のmktempが存在すればそちらを使う(execする)ようにしてあるので、あまり一般的でないオプションは使わない方がよい。 **「どの環境でも使える」という趣旨を理解せず、本投稿に寄せられた1番目のコメントの誘導には乗らない方がよい。** ## nlコマンド ### `-w`オプション POSIXでも規定されている`-w`オプションであるが、環境によって挙動が異なるので注意。尚、`-w`オプションはPOSIXでデフォルト値が設定されているため、**このオプションを記述しなくても同様の問題が起こるので注意!**(POSIXの範囲ではないので参考までにということになるが、catコマンドの`-n`オプションではこの問題は起こらないようだ。) `-w`とは行番号に割り当てる桁数を指定するものであるが、問題は指定した桁数よりも桁があふれてしまった時である。溢れた場合の規定は定義されていないので、実装によって解釈が異ってしまったようだ。 2つの実装を例にとるが、BSD版のnlコマンドでは、溢れた分の上位桁は消されてしまう。 ```sh:BSD版nlコマンドの場合 $ yes | head -n 11 | nl -w 1 1 y 2 y 3 y 4 y 5 y 6 y 7 y 8 y 9 y 0 y 1 y $ ``` 一方、GNU版のnlコマンドでは、溢れたとしても消しはせず、全桁を表示する。 ```sh:GNU版nlコマンドの場合 $ yes | head -n 11 | nl -w 1 1 y 2 y 3 y 4 y 5 y 6 y 7 y 8 y 9 y 10 y 11 y $ ``` 行番号数字の直後につくのはデフォルトではタブ("`\t`")なので、GNU版では桁数が増えるとやがてズレることになる。BSD版はズレることはない代わりに上位桁が見えないので、何行目なのかが正確にはわからない。 #### 対応方法 AWKコマンドの組み込み変数であるNRを使うとよい。さらに、次のようにしてprintf関数を併用すれば、GNU版nlコマンドと同等の動作をする。 ```sh:GNU版nlコマンドのデフォルトと同じ動作をする awk '{printf("%6d\t%s\n",NR,$0);}' ``` ### ファイル指定の`-` 一般的に、ファイル名として`-`を指定すると標準入力の意味と解釈されるが、本コマンドに対しては使わない方がよい。POSIXでもこのコマンドでもこのコマンドについて、そう解釈されると確かに書いてあるのだが、BSDの実装では真面目に"-"というファイルを開こうとしてエラーになってしまう。 ## odコマンド ### ファイル指定の`-` 一般的に、ファイル名として`-`を指定すると標準入力の意味と解釈されるが、本コマンドに対しては使わない方がよい。POSIXでもこのコマンドでもこのコマンドについて、そう解釈されると確かに書いてあるのだが、BSDの実装では真面目に"-"というファイルを開こうとしてエラーになってしまう。 ## printf(コマンドおよびAWKの中の関数) ### キャラクターコードの即値指定(16進数) 互換性を重視するなら、\xHHという16進表記によるキャラクターコード指定をしてはいけない。これは一部のprintfの独自拡張だからだ。代わりに\OOOという3桁の8進数表記を用いること。 ### キャラクターコードの即値指定(8進数) Mac OS X等一部のOS上のprintfでは、`\OOO`(OOOは任意の8進数)と同等の表現として`\0OOO`(3桁数字の手前に数字の0が付いている)という表現も認められている。しかしこれが厄介な問題を引き起こす。 例えば、`\040`に続いて数字の`1`を与えたかったら`\0401`と記述したいところだが、そうすると一部の環境では8進数で401に相当するコード(実際には0x01のStart Of Heading)を指定されたものと解釈してしまい、環境によって結果が変わってしまうのだ。 ```sh:先頭が0で始まる3桁8進数による文字指定は、直後の文字によっては御変換されることがある # FreeBSDの場合(問題なし) $ printf '\0401\n' 1 $ # Mac OS Xの場合(数字の1が出てこない!) $ printf '\0401\n' $ ``` この問題を回避するには、直後に半角数字が続く場合、その数字自体も`\049`のようにエスケープするのが無難だろう。 ### 負数の16進数表現 負の値を16進数に変換すると環境によって結果が異なる。例えば-1を16進数に変換すると次のとおりだ。 ```sh:printfの実装ビット数による違い(必ずしもOSのビット数とは一致しない) # 32ビット実装の場合 $ printf '%X\n' -1 FFFFFFFF $ # 64ビット実装の場合 $ printf '%X\n' -1 FFFFFFFFFFFFFFFF $ ``` 従って負の値を16進数に変換するのはあまり勧められないが、どうしてもしたいなら下8桁のみを取り出すべきだろう。もちろんその場合、-2147483648より小さい値は扱えない。 ## psコマンド 現在のpsコマンドは、オプションにハイフンを付けないBSDスタイルなど、いくつかの流派が混ざっているので厄介だ。 ### `-x`オプション 「制御端末を持たないプロセスを含める」という働きであるが、このオプションは使わない方がいい。そもそもPOSIXのmanにはないし、GNU版とBSD版では解釈が異なるっぽい。 例えばCGI(httpd)によって起動されたプロセス上で、`-a`も`-x`も付けず、自分に関するプロセスのみを表示しようとした場合、前者では表示されるものが後者では`-x`を付けた場合に初めて表示されるなどの違いがある。 互換性を重視するなら、大文字である`-A`オプションを用いてとにかく全てを表示(`-ax`に相当)させる方がよいだろう。 ### `-l`オプションはPOSIXでも明記されているが使うべきではない。 `-l`オプションは、lsコマンドの同名オプションのように多くの情報を表示するためのものである。POSIXのmanにも記載されているし、実際主要な環境でサポートされているので使っても問題なさそうだが、使うべきではない。理由は、表示される項目や順序がOSやディストリビューションによってバラバラだからだ。 ### 殆どの場合`-o`オプション必須 `-l`オプションを付けた場合の表示項目や順序がバラバラだと言ったが、実は付けない場合もバラバラだ。どの環境でも期待できる表示内容といえば、 * 1列目にPIDが来ること * 行のどこかにコマンド名が含まれていること くらいなものだ。互換性を維持しながらそれ以上の情報を取得しようとするなら、`-o`オプションを使って明確に表示させたい項目と順序を指定しなければならない。 `-o`オプションで指定できる項目一覧については[POSIXのman](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html)上の"STDOUT"セクション後半に記されている。(太小文字で列挙されている項目で、現在のところ"ruser"から"args"までが記されている) ### 補足1.親プロセスID(PPID)の値 Linuxでは、親プロセスIDが0になるのはPID=1のinitだけだ。しかし、FreeBSD等では他の様々なシステムプロセスの親も0になっている。これは、psコマンドの違いというよりカーネルの違いであるが、互換性のあるプログラムを書くときには注意すべきところだ。 ### 補足2.Cygwinのpsコマンド 2016年6月現在、Cygwinやgnupackで用意されているpsコマンドは残念ながらPOSIXのものとは非互換である。Windows配下で使うという事情により特別なものになっているようなのだが、-Aオプションも-oオプションもサポートされていない。manには記述があるのに使えないのは酷いと思うが仕方がない。 そもそもCygwinはPOSIX環境ではないからといって切り捨てるという考え方もあるのだが、対応をするのであればunameコマンドを使ってCygwinで動いていることを検出したら個別対応するコードにするしかない。 ## sedコマンド ### 最終行が改行コードで終わっていないテキストの扱い 試しに`printf 'Hello,\nworld!' | sed ''`というコードを実行してみてもらいたい。 ```bash:BSD版sedの場合 $ printf 'Hello,\nworld!' | sed '' Hello, world! $ ``` ```bash:GNU版sedの場合 $ printf 'Hello,\nworld!' | sed '' Hello, world!$ ``` と、このように挙動が異なる。最終行が改行コードで終わっていない場合、BSD版は改行を自動的に挿入し、GNU版はしないようだ。 純粋なフィルターとして振る舞ってもらいたい場合にはGNU版の方が理想的ではあるが、すべての環境で動くことを目標にするならBSD版のような実装のsedも無視するわけにはいかない。このようなsedをはじめ、AWKやgrep等、最終行に改行コードが挿入されてしまうコマンドでの対処法を[改行無し終端テキストを扱う](http://qiita.com/richmikan@github/items/5158eac47467d8a29056)というTipsにまとめたので見てもらいたい。 ### 使用可能なコマンド・メタキャラ これも、GNU版は独自拡張されているので注意。 コマンドに関して迷ったら[POSIXのman](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html)を見る。使用可能な正規表現については、[正規表現メモさんの記述](http://www.kt.rim.or.jp/~kbk/regex/regex.html#SED)が 便利。 ### ファイル指定の`-` 一般的に、ファイル名として`-`を指定すると標準入力の意味と解釈されるが、本コマンドに対しては使わない方がよい。POSIXでもこのコマンドでもこのコマンドについて、そう解釈されると確かに書いてあるのだが、BSDの実装では真面目に"-"というファイルを開こうとしてエラーになってしまう。 ### ロケール →ロケールを参照 ## sortコマンド →ロケールを参照 ## tacコマンド or tailコマンドで逆順出力 Tips的な話だが、ファイルの行を最後の行から順番に(逆順に)に並べたい時はtacコマンドやtailコマンドの`-r`オプションのお世話になりたいところであろう。しかし、どちらも一部の環境でしか使えないし、もちろんPOSIXにも載っていない。ではどうするか……。そんな時は、次のTipsがお勧めだ。 [「ファイルの逆順出力」@bsdhackさん](http://qiita.com/bsdhack/items/e23b4e6a6eec031e5d00) ## test("[")コマンド どんな内容が与えられるかわからない文字列(シェル変数など)の内容を確認する時、最近のtestコマンドなら ```bash:最近のtestコマンドはこう書いても問題無いものが多い # シェル変数$strの内容が"!"ならば"Bikkuri!"を表示する [ "$str" = '!' ] && echo 'Bikkuri!' ``` と書いても問題無いものが多い(さすがに$strが"("だった場合ダメなようだが)。しかし、古来の環境では > `[: =: unexpected operator` というエラーメッセージが表示され、正しく動作しないものが多い。これは$strに格納されている"!"が、評価すべき文字列ではなく否定のための演算子と解釈され、そうすると後ろに左辺ナシの`=`が現れたと見なされてエラーになるというわけである。 testコマンドを用いて、全ての環境で安全に文字列の一致、不一致、大小を評価するには、文字列評価演算子の両辺にある文字列の先頭に無難な一文字を置く必要がある。 ```bash:文字列評価演算子の両辺にある文字列の先頭に無難な1文字を置けば、どこでも正しく動く # シェル変数$strの内容が"!"ならば"Bikkuri!"を表示する [ "_$str" = '_!' ] && echo 'Bikkuri!' ``` もっとも、単に文字列の一致、不一致を評価したいだけなら、testコマンドを使わずに下記のようにcase文を使う方がよい。上記のような配慮は必要ないし、外部コマンド(シェルが内部コマンドとして持ってる場合もあるが)のtestコマンドを呼び出さなくてよいので軽い。 ```bash:case文で同等のことをする # シェル変数$strの内容が"!"ならば"Bikkuri!"を表示する case "$str" in '!') echo 'Bikkuri!';; esac ``` ## trコマンド このコマンドは各環境の方言が強く残るコマンドの一種で、無難に作るならなるべく使用を避けたいコマンドだ。 例えばアルファベットの全ての大文字を小文字に変換したい場合、 `tr '[A-Z]' '[a-z]'` ← System V系での書式(この場合は運よくどこでも動く) `tr 'A-Z' 'a-z'` ← BSD系、POSIXでの書式 という2つの書式がある。範囲指定の際にブラケット`[~]`が要るかどうかだ。BSD系の場合、ブラケットは通常文字として解釈されるので、これを用いると置換対象文字として扱われてしまう。しかしながら前者のブラケットは置換前も置換後も全く同一の文字なので幸いにしてどこでも動く。従って、このようなケースでは前者の記述をとるべきだろう。 しかし、-dオプションで文字を消したい場合はそうはいかない。 `tr -d '[a-z]''` ← System V系での書式(これはBSD系、POSIX準拠実装ではNG) `tr 'a-z'` ← BSD系、POSIXでの書式 POSIXに準拠してないSystem V実装が悪いと言ってしまえばそれまでなのだが、歴史の上ではPOSIXよりも早いのでそれをいうのもまた理不尽というもの。ではどうすればいいか。 答えは、「sedで代用する」だ。上記のように、全ての小文字アルファベットを消したいという場合はこう書けばよい。 `sed 's/[a-z]//g'` しかしながら、改行コードで終わっていないテキストデータを与えると改行を付け足してしまうsed実装があるので、そういう可能性のあるデータを扱いたい場合は更に対策が必要だ。→[改行無し終端テキストを扱う](http://qiita.com/richmikan@github/items/5158eac47467d8a29056) そこまでやるくらいだったら、範囲指定ではなく全部書いてしまえばいいと思うかもしれないが、もちろんそれでもいい。 `tr -d 'abcdefghijklmnopqrstuvwxyz'` ## trapコマンド →killコマンド参照 ## whichコマンド コマンドが存在すれば(パスが通っていれば)そのパスを返してくれるため、コマンドが無ければ無いなりにどの環境でも動くようなシェルスクリプトを書きた時などに重宝するコマンドだ。しかし、このwhichコマンドがPOSIX標準ではないというオチが待っている。 しかし諦めることはない。POSIXに存在するcommandという名のコマンドに`-v`オプションを付けると似た動きをするのでこれを使うとよい。 下記のコードをシェルスクリプトの冒頭に追記しておけば、whichコマンドが存在しない場合のみ、commandコマンドに基づいたシェル関数版whichが登録される。 ```sh:whichコマンドが無ければ同等品を追加するコード which which >/dev/null 2>&1 || { which() { command -v "$1" 2>/dev/null | awk 'match($0,/^\//){print; ok=1;} END {if(ok==0){print "which: not found" > "/dev/stderr"; exit 1}}' } } ``` command -vは組み込みコマンドが指定された場合でもコマンド名自身を返して正常終了するという点がwhichと異なるので、後ろのAWKで挙動を揃えている。 ## xargsコマンド ### 改行なし終端データの扱い 次の例を見てもらいたい。 ```sh:最後の単語を無視してしまうxargs実装(AIXなど) $ printf 'one two threee' | xargs echo one two $ ``` 単語が3つあるのだから、xargsはechoの後ろに“one”と“two”はもちろん、“three”も付けて実行してくれることを期待するが最後の“three”が無視されてしまっている。じつはこのxargs実装、最後の単語の後にも改行やスペース等の区切り文字を必要とするのである。 こういうxargs実装であっても確実に動作させるようにするには、例えばxargsの直前に`grep ^`などを挿んでデータの終端に確実に改行が付くようにしてやることだ。 ```sh:最後の単語を無視してしまうxargs実装にはgrepを挿むとよい $ printf 'one two threee' | grep ^ | xargs echo one two three $ ``` ### 空ループの有無 標準入力から入ってきた文字列を引数にしてコマンドに渡すためのコマンドであるが、標準入力から空白以外が含まれた行が1行も渡ってこなかった場合、引数無しでコマンドを実行するxargs実装もあれば、コマンドを実行しないxargs実装もある。 ```sh:xargs実装によって結果が変わる $ printf ' \n\n' | xargs echo 'foo' # FreeBSDの場合 $ $ printf ' \n\n' | xargs echo 'foo' # GNU版(多くのLinux)の場合 foo $ ``` xargsで呼び出される側のコマンドは引数0個で呼ばれるなど想定していない(Usageを表示したり戻り値0以外にしたりする)ものが多いので、前者の挙動の方が好ましいとは思うのだが、あるものはしょうがない。 一応、前者の動作に揃える`-r`オプションというものがある(最近のFreeBSD版もこれを認識する)のだが、そんなオプションはPOSIXでは規定されていないがゆえ、それを付けて互換性を向上させようとすると逆に全ての環境で動く保証がなくなってしまうのが皮肉なところ。 #### 対応方法 さてどうするか……、これは対症療法しかない。すなわち、 1. 引数0個で実行されてもエラー扱いしないようなコマンドにする。 1. コマンドがエラー動作することを想定するような後続の処理にする。 1. 標準入力に必ず有効かつ無害な行が入るようにする。 1. 呼び出されるコマンドに無害な引数を付けておく。 などを行う。 1番目の対処は、例えば呼び出すコマンドがrmなら`-f`オプションを付けてエラー扱いを抑止するという方法だ。 ```sh:対処方法1の例「rmコマンドをいちいちエラーで騒がせないようにする」 find . -name '*.tmp' | xargs rm -f ``` 2番目の対処は、例えば戻り値が0以外でも即エラー扱いしないとか、標準エラーに流れてくるエラーメッセージやUsageを/dev/nullに捨てるというものだ。 ```sh:対処方法2の例「rmコマンドがエラーで騒いでも無視する」 find . -name '*.tmp' | xargs rm 2>/dev/null ``` 3番目の対処は、例えば呼び出すコマンドがgrepなどのファイルを読み込むだけのものであれば使える方法だ。例えば/dev/nullを読み出しファイルとして、標準入力の最初(最後に付加すると改行なし終端テキストだった場合に不具合が起こる)に付加すればよい。 ```sh:対処方法3の例「grepに無害なファイル/dev/nullを読み込ませる」 find . -name '*.txt' | awk 'BEGIN{print "/dev/null"} 1' | xargs grep '検索キーワード' # grepの場合は後述の4番目の対処方法をお勧めする ``` 4番目の対処は、手段が若干異なるだけで目的は3番目と同じだ。先程のgrepの例ならこう書き直す。短く書けるし、先程紹介した対処方法よりもお勧めだ。 ```sh:対処方法4の例「grepに無害なファイル/dev/nullを読み込ませる(推奨)」 find . -name '*.txt' | xargs grep '検索キーワード' /dev/null ``` grepコマンドの場合は特にこちらを勧める。理由は、grepコマンドは、検索対象のファイルが1個だけ指定された場合と複数指定された場合で挙動を変えるからだ。具体的には、検索キーワードが見つかった時、1個だけだった場合はファイル名を表示しないのに対し、複数個だった場合には行頭にファイル名を併記する。 上記のように記述しておけば、grepコマンドは常に複数個指定されたと見なすので、findコマンドで見つかったファイルの数が1個であっても2個以上であっても、必ず行頭にファイル名を併記するようになり、動作が統一される。 ### 引数文字列の扱い xargsに`\\\'`という文字列を与えると、例えばFreeBSDのxargsとLinuxのxargsで異なった結果を返す。 ```sh:xargs実装による違い(最初のprintfは"\\\'"という文字列を生成している) # FreeBSD $ printf '\\\\\\'"' " | xargs printf ' $ # Linux $ printf '\\\\\\'"' " | xargs printf \' $ ``` 実はFreeBSDのxargsコマンドは、引数文字列を`$@`(ダブルクォーテーションなし)のように渡してシェルのエスケープ処理を受けるのに対し、Linuxの(GNU版の)xargsコマンドは`"$@"`(ダブルクォーテーションあり)のように渡すので、シェルのエスケープ処理を受けない。だから結果として、Linux上ではバックスラッシュが1個残るのだ。 ではどうするか。確実な方法は、エスケープ処理される文字を使わないことだ。バックスラッシュはいたしかたないとして、例えばシングルクォーテーションは`\047`などと表現した文字列がprintfに渡るようにすればよい。ただしバックスラッシュも、引数としてシェルに解釈される時やprintfに解釈される時などに変化を受けるので十分注意すること。 ## zcatコマンド zcatは、`gunzip | cat`相当だと思っている人も多いかもしれないが違う! それはGNU拡張であり、**本来のzcatは`uncompress | cat`相当である。** 従って、次のようにしてgzip圧縮されたデータを与えるとエラーを返すzcatコマンド実装がある。 ```sh:gzipに対応していないzcatコマンド $ echo hoge | gzip | zcat stdin: not in compressed format $ ``` 全ての環境のzcatコマンドを想定するなら、compressコマンドで圧縮したデータを与えること。 ```sh:zcatコマンドにはcompressコマンドで圧縮したデータを与えること $ echo hoge | compress | zcat hoge $ ``` ただし、compressコマンドは元データがファイルの場合、圧縮してもサイズが小さくならない場合に圧縮をしないので、次のような事故を起こさないように注意すること。(`compress -f`とすれば必ず作る) ```sh:compressコマンドはサイズが小さくならないと圧縮をしない $ echo 1 > hoge.txt $ compress hoge.txt -- file unchanged ← サイズが小さくならないので圧縮ファイルを作らなかった $ zcat hoge.txt.Z hoge.txt.Z: No such file or directory $ ``` |
|
| 537位 |
|
|||
|
21:01:32 |
(GMOペパボ 所属) |
|
Vagrantで作成した開発環境でCSSの更新が反映されない現象が起こることがあります。
なぜこのような現象が発生するかというと、静的なファイルのようにリクエストの処理にデータのアクセスを必要としないときには、ファイルを読み込むことなくカーネルの sendfile を使って ファイルを送るようになっているからです。 以下のようにして sendfileを使わないようにします。 ## apacheの場合 ```apache:httpd.conf EnableSendfile off ``` ## nginxの場合 ```nginx:nginx.conf sendfile off; ``` Webサーバーを再起動すればCSSの更新が適応されるようになると思います。 |
|
| 538位 |
|
|||
|
01:54:40 |
|
|
gitのリモートリポジトリが更新されているかどうかを確認する方法はいくつかあります。
##方法1: git fetch 後にdiffをとる ``` $ git fetch origin $ git diff origin/master ``` ##方法2: git ls-remote コマンドを使用する ``git ls-remote``を使用することでリモートリポジトリのコミットIDが取得できます。 リモートリポジトリの最新コミットID(HEAD)とローカルの最新コミットID(HEAD)を比較し、その2つが異なっていれば差分があると判断できます。 さらに、リモートのコミットIDが過去に存在しないものであれば、ローカルのリポジトリが古い(マージしていないコミットがリモートに存在する)ことになります。 ``` $ git ls-remote origin HEAD 78ddd44eb3b76017a55014f27d9f846054dfa52b HEAD $ git log -1 HEAD # or master commit 8741c1a1fd81e0e3620e7054f3731ad2338f25fa $ git log -1 origin/HEAD # or origin/master commit 8741c1a1fd81e0e3620e7054f3731ad2338f25fa ``` ##方法3: git remote show コマンドを使用する 以下のように実行結果の最終行に (local out of date) と表示されていればローカルの方が古いことになります。(ブランチが複数ある場合は複数行表示されます) ただし、リモートブランチと同名のブランチがローカルにも存在する場合のみ表示されます。 また、リモートブランチと異なる名前のブランチは upstream/tracking branch を設定していても表示されません。(残念…) ``` git remote show origin * remote origin Fetch URL: git://github.com/Shougo/unite.vim.git Push URL: git://github.com/Shougo/unite.vim.git HEAD branch: master Remote branch: master tracked Local branch configured for 'git pull': master merges with remote master Local ref configured for 'git push': master pushes to master (local out of date) ``` ---------- 以上の3種類が私の知っている方法です。 (他にもあるようでしたら教えて頂けるとありがたいです) 方法1が最もメジャーな方法かと思いますが、fetchを行う必要があるという点でやや面倒ではあります。とはいえ細かいdiffを確認する場合には必須ですが。 方法2/方法3はfetchが不要で、ざっくりと更新状況を見たい場合に利用できます。 特に方法3が分かりやすいですね。 ##方法4を提案 一応今回の本題…方法2,3の拡張版です。 チェックするリポジトリが1つ2つなら上記の方法でも良いかと思いますが、たくさんのリポジトリの更新状況を確認したい場合には少々面倒です。 そこで、今回は複数のリポジトリの更新状況をざっくり確認するコマンド``git-check``を作成しました。 まずは実行結果です。  こんな感じにざっくりと更新状況が確認できます。 submoduleが存在する場合はインデントされて表示されています。 (上記はneobundleで管理しているvimプラグインのリポジトリの更新状況を確認しています) ###導入方法 下記githubのリポジトリからcloneまたはダウンロードし、中にある``git-check``コマンドをPATHの通った所に置いて下さい。 [yonchu/git-check · GitHub](https://github.com/yonchu/git-check) ``` $ git clone https://github.com/yonchu/git-check.git $ mv git-check/git-check ~/bin ``` gitを使用しない場合は[ここ](https://github.com/yonchu/git-check/archive/1.2.tar.gz)から直接ダウンロードして下さい。 ###使用方法 ``` Usage: git-check [-s|-r|-w] [-f <Repositories list file>] [Directory ...] Options: -s : git status を確認 -r : リモートリポジトリの更新状況を確認 -w N : インデント幅 (Default: 4) -f [File] : リポジトリのディレクトリパスを記述したファイルを指定 ※-s/-r いずれも未指定の場合は両方表示します ``` ディレクトリは複数指定が可能で、ワイルドカードを使用することもできます。 関係のないファイルやディレクトリは無視しますのでワイルドカードでざっくりと指定すると良いでしょう。ディレクトリ未指定の場合はカレントディレクトリが対象になります。 ``-r``指定でリモートリポジトリの更新状況を確認できますが、他にも``-s``を指定することで``git status --short``の結果を表示することもできます。  また、``-f``を指定することでリポジトリの場所をファイルで指定することもできます。 ``` $ cat repos.txt ~/dotfiles ~/dotfiles.local ~/work/dev/myproject/* ~/work/dev/github/* ~/work/dev/gist/* ~/work/dev/github_tracking/* $ git-check -f repos.txt ``` よくチェックするものをファイル内に記述しておくことでチェックが楽になります。 ##蛇足 な、長い…方法1,2,3の説明が蛇足だった気がする… |
|
| 539位 |
|
|||
|
02:20:30 |
|
|
## 普通のname/valueペアをPOSTする ```bash curl -F "name1=value1" -F "name2=value2" http://yourdomain/execute.script ``` これが ```html <form method="POST" action="http://yourdomain/execute.script"> <input type="hidden" name="name1" value="value1"> <input type="hidden" name="name2" value="value2"> <input type="submit" value="submit"> </form> ``` みたいな感じになります。 ## 普通のname/valueペアに加えてファイルをPOSTする ```bash curl -F "name1=value1" -F "name2=value2" -F "profile_icon=@path/to/file.png" -F "zip_file=@path/to/zipfile.zip" http://yourdomain/execute.script ``` これが ```html <form method="POST" action="http://yourdomain/execute.script" enctype="multipart/form-data"> <input type="hidden" name="name1" value="value1"> <input type="hidden" name="name2" value="value2"> <input type="file" name="profile_icon"> <input type="file" name="zip_file"> <input type="submit" value="submit"> </form> ``` みたいな感じになります。 ## みたいな感じとは? multipart/form-dataなど、フォーム送信する際の方式がいくつかあり、厳密に同じか確認していません。 しかも、データを受け取って保存するところまで確認していません!がきっと大丈夫。と思いたい。 ## TIPS ファイル送信時、受け取り側のphpで`var_dump($_FILES);`のみを出力し、 ```bash curl -F "name1=value1" -F "name2=value2" http://yourdomain/execute.script > path/to/public/web/directory/result.html ``` などと指定しておくと、curlの結果がわかりやすかったです。 |
|
| 540位 |
|
|||
|
17:07:10 |
(えんかく.jp 所属) |
|
アップグレードすると挙動がよくわからくなりそうだったので、まっさらにして新しくOSを入れ直してみました。その時にいろんな記事を見たのですが、すでに古くなっていたりとかしたので、結構時間がかかりました。 そんなわけで環境設定の備忘録です。 ## この記事で行うこと + OS初期状態からのhomebrewを軸にした環境設定 + iterm2 - tmux - zsh - vimの環境設定 + anyenvを使った開発環境設定 ## この記事でわからないこと + boxenの使い方 ## 方針決定 最初に考えていたのは、[boxen](https://github.com/blog/1345-introducing-boxen)を使った自動インストールでした。 + [Mac OS Xの設定や管理を自動化するGitHub Boxen](http://www.infoq.com/jp/news/2013/02/GitHub-Boxen) + [Boxen使わなくても許されるのは2012年までだよね ](http://qiita.com/yuku_t/items/c6f20de0e4f4c352046c) + [もうすぐ 2014 年だけど boxen を独りで使ってみる](http://inokara.hateblo.jp/entry/2013/12/31/184645) OSをインストールして、さて入れようかと思ってもう少し記事を漁っていたところ、boxenよりもこちらがいいという記事がいくつか見つかりました。 + [BoxenやめてBrewfile+homebrew-caskにした](http://nomnel.net/blog/boxen-to-brewfile/) + [私もBoxen止めてBrewfile+brew-caskにした](http://masutaka.net/chalow/2014-01-25-1.html) どうも、boxenは個人で管理するには少しオーバースペックっぽいので、私もBrewfileによる管理でとりあえず進めることにします。 ※振り返って考えると、以下の作業をboxenとして全部設定するのがいいのかな…? ## ステップ1(homebrew bundleによるアプリインストール) homebrewを使ってまず必要なものをざっとインストールします。homebrew以前に準備しないといけないものも合わせていれます。 以下の順番で進めます。 + app storeからxcodeをインストール + アプリケーション->ユーティリティ->ターミナルを起動 + コマンドラインツールインストール ``` $ xcode-select —install ``` + homebrewインストール ``` $ ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go/install)" ``` + homebrewが正常に動作するかどうか確認 ``` $ brew doctor ``` + ~/dotfiles フォルダを作成 + ~/dotfiles/Brewfileを作成 ```Brewfile tap homebrew/binary tap phinze/homebrew-cask install ack install autoconf install binutils install git install gnu-sed install heroku-toolbelt install openssl install postgresql install w3m install wget install tmux # zshは/etcを参照しないようにする(参考:brew info zsh) install zsh --disable-etcdir install fontforge install reattach-to-user-namespace # macvimを入れる tap supermomonga/homebrew-splhack install --HEAD cmigemo-mk install --HEAD ctags-objc-ja install gettext-mk install --HEAD macvim-kaoriya linkapps # powerline用フォントを作成する tap sanemat/font install --powerline --vim-powerline ricty # 以下mac用アプリを入れる install brew-cask cask install dropbox cask install google-chrome cask install skype cask install vagrant cask install virtualbox cask install hipchat cask install iterm2 cask install kobito cask install bettertouchtool cask install alfred cask alfred link cask install KeyRemap4MacBook ``` + brew caskでインストールするアプリが /Applicationsに入るようにする ``` $ export HOMEBREW_CASK_OPTS="--appdir=/Applications" ``` + brewfileを使う。しばらく待つ。 ``` $ brew bundle ~/dotfiles/Brewfile ``` 参考記事:[BrewfileとHomebrew-caskでmacのセットアップ](http://qiita.com/macoshita/items/09c730e5a281897365eb), [homebrew-caskが良さそうなので導入してみた](http://blog.digital-bot.com/blog/2013/09/08/homebrew-cask/) ## ステップ2(iterm2設定、アプリによる操作カスタマイズ) iterm2というターミナルアプリはカスタマイズしやすくていいのですが、最初にいくつか設定しておかないと、快適環境にならないです。あわせてキーボードの操作についても、設定していきます。 以下の順番で進めます。 + brew bundleが成功していれば、パッチがあたったRictyフォントができているので、フォントファイルをコピーする。その後、フォントのキャッシュを作成する。 ``` $ cp -f /usr/local/Cellar/ricty/3.2.2/share/fonts/Ricty*.ttf ~/Library/Fonts/ $ fc-cache -vf ``` + iterm2の環境設定を行う * vimでpowerlineを使うために、フォントの変更 (iTerm->Preferences->Profiles->Text) Regular FontとNon-ASCII FontをRitch Regular for Powerlineに変更 * カラフルな表現にするために、ターミナル種別の変更(iTerm->Preferences->Profiles->Terminal) Report Terminal Typeをxterm-256colorに変更 * 矢印キーをvimで使うために、キーのプリセットを変更(iTerm->Preferences->Profiles->Keys) Load Presetで、xterm Defaultsを押下参考文献:[iTermでVimを使うと矢印キーでカーソル移動できない](http://tech.portalshit.net/2010/10/26/iterm-arrow-key-issue/) * zshでMETAキーを使うために、キーの設定を変更(iTerm->Preferences->Profiles->Keys) Left option key acts as を+ESCに変更 参考文献:[zshでよく使うキーバインドまとめ](http://qiita.com/takc923/items/08792b7188b5c11e0f21) * ※(バッドノウハウ)背景を変更(iTerm->Preferences->Profiles->Colors) Basic Colors->Backgroundを修正する 参考文献:[iTerm2上で256色のvimを使う](http://qiita.com/muniere/items/e6c5c48a541401ee5af3) * tmux上からコピー&ペーストを可能にするために、ターミナル<->クリップボードのアクセスを許可する※(セキュリティ面注意)(iTerm->Preferences->General) Selection->Allow clipboard access to terminal appsのチェックをONにする 参考文献:[iTerm2のクリップボードインテグレーション(OSC 52/PASTE64)のつかいかた](http://qiita.com/kefir_/items/1f635fe66b778932e278) ※バッドノウハウについて以下に引用しておきます。[iTerm2上で256色のvimを使う](http://qiita.com/muniere/items/e6c5c48a541401ee5af3) > iTerm2は色の設定を変更するまで再読み込みが行われないようで、ターミナルの種類を変更しただけでは256色になりません(本当の原因は不明です)。 + KeyRemap4MacBookでESCキーのカスタマイズを行う * [Vimを使う上でのIME(日本語入力)の取り扱い](http://rcmdnk.github.io/blog/2013/06/20/computer-mac-keyremap4macbook-vim/)を参考にして設定します。 * 操作方法が書いてないので補足すると、KeyRemap4MacBookのPreference->Misc & Uninstall の Custom Setting->Open private.xmlボタンを押下すると、Finderが開くので、private.xmlを編集して保存するとOKです。何も設定がない状態から追加した設定が以下になります。 ```private.xml <?xml version="1.0"?> <root> <item> <name>ESC to IME off (to English) + Esc + Esc</name> <appendix>Enable for all but HHK</appendix> <identifier>private.vim.ime_off_ESC</identifier> <only>TERMINAL, VI</only> <inputsource_only>JAPANESE</inputsource_only> <autogen> __KeyToKey__ KeyCode::ESCAPE, ModifierFlag::NONE, KeyCode::VK_CHANGE_INPUTSOURCE_ENGLISH, KeyCode::VK_CHANGE_INPUTSOURCE_JAPANESE, KeyCode::VK_CHANGE_INPUTSOURCE_ENGLISH, KeyCode::ESCAPE, KeyCode::ESCAPE </autogen> </item> <item> <name>Control + BRACKET_LEFT to IME off (to English) + Esc + Esc</name> <identifier>private.vim.ime_new</identifier> <only>TERMINAL, VI</only> <inputsource_only>JAPANESE</inputsource_only> <autogen> __KeyToKey__ KeyCode::BRACKET_LEFT, MODIFIERFLAG_EITHER_LEFT_OR_RIGHT_CONTROL|ModifierFlag::NONE, KeyCode::VK_CHANGE_INPUTSOURCE_ENGLISH, KeyCode::VK_CHANGE_INPUTSOURCE_JAPANESE, KeyCode::VK_CHANGE_INPUTSOURCE_ENGLISH, KeyCode::ESCAPE, KeyCode::ESCAPE </autogen> </item> </root> ``` ## ステップ3(zshへの切り替え) いままではデフォルトのシェル(bash)を使っていましたが、zshに切り替えます。 brew bundleでインストールしたファイルは、/usr/local/bin以下に入っていますが、シェルやパスについてはそのままなので変更する必要があります。 ステップ3からは、ターミナルをiterm2に変更しています。 参考記事:[zsh の設定](http://www.gfd-dennou.org/member/uwabami/cc-env/ZshConfig.html) + /etc/shellsの一番下に/usr/local/bin/zshを追加 ``` $ sudo vi /etc/shells ``` ```/etc/shells /bin/bash /bin/csh /bin/ksh /bin/sh /bin/tcsh /bin/zsh /usr/local/bin/zsh ``` + 利用するシェルを変更 ``` $ chsh -s /usr/local/bin/zsh ``` + ~/dotfiles/.zsh フォルダを作成 + ~/dotfiles/.zshenv ファイルを作成 ``` ~/dotfiles/.zshenv #! /usr/bin/env zsh export ZDOTDIR=${HOME}/dotfiles/.zsh source ${ZDOTDIR}/.zshenv ``` + シンボリックリンクをホームディレクトリに置く ``` $ ln -s ~/dotfiles/.zshenv ~/.zshenv ``` + ~/dotfiles/.zsh/.zshenv、~/dotfiles/.zsh/.zshsh を作成する ```~/dotfiles/.zsh/.zshenv #! /usr/bin/env zsh # -*- mode: sh; coding: utf-8; indent-tabs-mode: nil -*- # export LANG=ja_JP.UTF-8 path=( {/usr/local,/usr,}{/bin,/sbin}(N-/) ) typeset -gxU path manpath=( {/usr,/usr/local}/share/man(N-/) ) typeset -gxU manpath [ -z "$ld_library_path" ] && typeset -xT LD_LIBRARY_PATH ld_library_path [ -z "$include" ] && typeset -xT INCLUDE include typeset -xU ld_library_path include ## function: auto-zcompile & source function _auto_zcompile_source () { local A; A=$1 [[ -e "${A:r}.zwc" ]] && [[ "$A" -ot "${A:r}.zwc" ]] || zcompile $A >/dev/null 2>&1 ; source $A } [ -f $ZDOTDIR/proxy ] && \ _auto_zcompile_source $ZDOTDIR/proxy [ -d $HOME/bin ] && path=( $HOME/bin $path ) ### duplicate cleaning typeset -gxU path cdpath fpath manpath ld_library_path include export HOMEBREW_CASK_OPTS="--appdir=/Applications" ``` ```~/dotfiles/.zsh/.zshsh #! /usr/bin/env zsh # umask 022 # default umask bindkey -e # keybind -> emacs like setopt no_beep # beep を無効化 setopt auto_pushd # cd 時に Tab 補完 setopt pushd_to_home # pushd を引数無しで実行した時に pushd ~ とする setopt pushd_ignore_dups # ディレクトリスタックに重複する物は古い方を削除 DIRSTACKSIZE=20 limit coredumpsize 0 setopt correct # コマンドのスペル訂正 setopt rc_quotes # '' で ' を表現(エスケープをちょっとだけ省く) unsetopt correct_all # 全ての引数のスペル訂正: 無効化 setopt auto_resume # リダイレクトしてない suspend job を同じ操作で再開 setopt bg_nice # bg の nice を低くして実行 setopt notify # バックグラウンドジョブの状態変化を即時報告する setopt nohup # default は nohup ## functions treat as array typeset -Uga chpwd_functions typeset -Uga precmd_functions typeset -Uga preexec_functions ## utilities autoload -Uz colors; colors # 色指定を $fg[red] 等で行なえるように. HISTFILE=$ZDOTDIR/history/${USER}-zhistory HISTSIZE=100000 SAVEHIST=HISTSIZE setopt extended_history # コマンドの開始時刻と経過時間を登録 unsetopt share_history # ヒストリの共有 for GNU Screen setopt inc_append_history # 履歴を直ぐに反映 setopt hist_ignore_space # コマンド行先頭が空白の時登録しない unsetopt hist_ignore_all_dups # 重複ヒストリは古い方を削除 setopt hist_reduce_blanks # 余分なスペースを削除 setopt hist_no_store # historyコマンドは登録しない autoload -Uz history-search-end zle -N history-beginning-search-backward-end history-search-end zle -N history-beginning-search-forward-end history-search-end bindkey "^P" history-beginning-search-backward-end bindkey "^N" history-beginning-search-forward-end function history-all { history -E 1} # 履歴の一覧を出力 export LSCOLORS=ExFxCxdxBxegedabagacad export LS_COLORS='di=01;34:ln=01;35:so=01;32:ex=01;31:bd=46;34:cd=43;34:su=41;30:sg=46;30:tw=42;30:ow=43;30' export ZLS_COLORS=$LS_COLORS export CLICOLOR=true ## Options setopt auto_list # 補完候補を一覧で表示 setopt auto_param_slash # 補完候補がディレクトリの場合, 末尾に / を追加 setopt auto_param_keys # カッコの対応も補完 setopt list_packed # 補完候補をできるだけ詰めて表示 setopt list_types # 補完候補のファイル種別を識別 unsetopt list_beep # 補完の beep を無効化 setopt rec_exact # 曖昧補完を有効化 setopt interactive_comments # コマンドでも # 以降をコメントとみなす setopt magic_equal_subst # = 以降も補完(--prefix= 等) setopt complete_in_word # コマンドの途中でもカーソル位置で補完 setopt always_last_prompt # カーソル位置を保持してファイル名一覧を補完 ## style zstyle ':completion:*' menu select=2 # カーソルによる補完候補の選択を有効化 # 色指定に LS_COLORS を使用 zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS} # 種別を別々に表示させるため, グループを空白に zstyle ':completion:*' group-name '' # ディレクトリ名の補完 zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}' zstyle ':completion:*' keep-prefix # リモートディレクトリも補完 zstyle ':completion:*' remote-access true zstyle ':completion:*' completer \ _oldlist _complete _match _ignored _approximate _list _history ## 補完候補の追加 [ -d $ZDOTDIR/modules/completions ] && \ fpath+=( $ZDOTDIR/modules/zsh-completions/src $fpath) # [ -d $ZDOTDIR/functions ] && \ # fpath+=( $ZDOTDIR/functions $fpath) typeset -gxU fpath # autoload $ZDOTDIR/functions/*.zsh # 初期化 setopt prompt_subst # プロンプト定義内で変数置換やコマンド置換を扱う setopt prompt_percent # %文字から始まる置換機能を有効に unsetopt promptcr # 被る時は右プロンプトを消す setopt transient_rprompt # コマンド実行後は右プロンプトを消す autoload -Uz vcs_info zstyle ':vcs_info:*' enable git bzr svn hg zstyle ':vcs_info:*' formats '%s:%b' zstyle ':vcs_info:*' actionformats '%s:%b%a' zstyle ':vcs_info:(svn|bzr)' branchformat '%b:r%r' zstyle ':vcs_info:bzr:*' use-simple true function prompt_vcs_info(){ LANG=C vcs_info if [[ -n "$vcs_info_msg_0_" ]]; then ps_vcs_info="[%B%F{red}$vcs_info_msg_0_%f%b]" else ps_vcs_info='' fi } precmd_functions+=prompt_vcs_info function count_prompt_chars (){ print -n -P -- "$1" | sed -e $'s/\e\[[0-9;]*m//g' | wc -m | sed -e 's/ //g' } # precmd のプロンプト更新用関数 function update_prompt (){ ## プロンプト: 1段目左 local ps_user="%(!,%B%F{magenta}%n%b,%n)" local ps_host="%m" [[ -n ${SSH_CONNECTION} ]] && ps_host="%F{yellow}%m%f" local prompt_1st_left="[$ps_user@$ps_host$chroot_info]" ## プロンプト: 1段目右 local prompt_1st_right="[%F{white}%(5~,%-2~/.../%1~,%~)%f]" ## 1段目行の残り文字列の計算 local left_length=$(count_prompt_chars $prompt_1st_left) local right_length=$(count_prompt_chars $prompt_1st_right) local bar_rest_length=$[ COLUMNS - left_length - right_length -1 ] ## 1段目に水平線を引く local prompt_1st_hr=${(l:${bar_rest_length}::-:)} ## PROMPT の設定 # @see Zshをかわいくする.zshrcの設定 # URL: http://qiita.com/kubosho_/items/c200680c26e509a4f41c # 横幅等を調整. local ps_status="[%j]%(?.%B%F{green}.%B%F{blue})%(?!(*'-')%b!(*;-;%)%b)%f " local ps_mark="%(!,%B%F{magenta}#%f%b,%%)" PROMPT="$prompt_1st_left$prompt_1st_hr$prompt_1st_right-"$'\n'"$ps_status$ps_mark " PROMPT2='|%j]> ' SPROMPT="[%j]%B%F{red}%{$suggest%}(*'~'%)?<%b %U%r%u is correct? [n,y,a,e]:%f " # 右プロンプト RPROMPT="$ps_vcs_info" } precmd_functions+=update_prompt export MANPAGER='less -s' export PAGER='less -R' if whence lv >/dev/null ; then export PAGER=lv ; export LV="-c -T8192 -l" else alias lv=$PAGER fi whence vim >/dev/null && alias vi=vim export EDITOR=vi ## ls alias sl='ls' #fxxk!! export TIME_STYLE=long-iso export LS_OPTIONS="-N -T 0 --time-style=$TIME_STYLE" alias ls='ls -FG' alias la='ls -haFG' alias ll='ls -hlFG' alias lla='ls -hlaFG' alias lsd='ls -ld *(-/DN)' alias man='LANG=C man' alias vim=/Applications/MacVim.app/Contents/MacOS/Vim alias vi=vim PATH="/Applications/MacVim.app/Contents/MacOS:$PATH" if [ ! -f $ZDOTDIR/.zshenv.zwc -o $ZDOTDIR/.zshenv -nt $ZDOTDIR/.zshenv.zwc ]; then zcompile $ZDOTDIR/.zshenv fi if [ ! -f $ZDOTDIR/.zshrc.zwc -o $ZDOTDIR/.zshrc -nt $ZDOTDIR/.zshrc.zwc ]; then zcompile $ZDOTDIR/.zshrc fi ``` 挙動として、zsh起動時は、~/.zshenvが読み込まれ、環境変数ZDOTDIR(~/dotfiles/.zsh)が設定されます。 それ以降は、~/dotfiles/.zsh/.zshrc, ~/dotfiles/.zsh/.zshenvが読み込まれることになります。 ※ .zshenvと.zshrcを分ける理由は、[zsh, plenv(rbenv), parallel](http://qiita.com/rg_gs/items/5795fa777cac90d1359b)という記事が詳しいです。 ## ステップ4(anyenv導入) homebrewでは、rubyやrbenv等のパッケージは入れませんでした。xxenv系を調べているときに、[anyenv](https://github.com/riywo/anyenv)という複数の言語のバージョン管理をまとめて行える便利なものを見つけたので、こちらを採用することにします。 参考記事:[anyenvという**env系の簡易マネージャを作った](http://blog.riywo.com/2013/06/22/155804), [Installed anyenv](http://kazuomabuo.hatenablog.jp/entry/2013/09/01/144037) + ~/.anyenvにanyenvをインストールする ``` $ git clone https://github.com/riywo/anyenv .anyenv ``` + ステップ2で作成した~/dotfiles/.zsh/.zshrc, ~/dotfiles/.zsh/.zshenvを修正する ```~/dotfiles/.zsh/.zshenv(path部分を修正) path=( $HOME/.anyenv/bin {/usr/local,/usr,}{/bin,/sbin}(N-/) ) ``` ```~/dotfiles/.zsh/.zshrc(追加) eval "$(anyenv init -)" ``` + さきほどの修正を起動中のシェルに反映させる ``` $ exec $SHELL -l ``` + rbenv(ruby), plenv(perl), pyenv(python), ndenv(node)をインストール ``` $ anyenv install rbenv $ anyenv install plenv $ anyenv install pyenv $ anyenv install ndenv $ exec $SHELL -l ``` + 必要なバージョンをそれぞれインストール ``` $ rbenv install 2.0.0-p353 $ rbenv rehash $ rbenv global 2.0.0-p353 $ plenv install 5.16.3 $ plenv rehash $ plenv global 5.16.3 $ pyenv install 2.7.6 $ pyenv rehash $ pyenv global 2.7.6 $ ndenv install v0.10.25 $ ndenv rehash $ ndenv global v0.10.25 ``` これでインストールされました。 ※ (はまったところ) 例えばrubyで、 ``` $ gem install jekyll ``` としてモジュールをインストールした後に実行してもファイルがないと怒られました。env系の挙動がよくわかってなかったのですが、改めて、 ``` $ rbenv rehash ``` を実行しなければ反映されません。面倒な場合の解決策は、[rbenv で gem を使った時に rbenv rehash しなくて良くする](http://rhysd.hatenablog.com/entry/20120226/1330265121)が参考になりました。 ## ステップ4までのまとめ ここまでのディレクトリ構成をまとめておきます。 ``` ~ (ホームディレクトリ) + .zshenv (※1のシンボリックリンク) + .anyenv/ + dotfiles/ + Brewfile + .zshenv (※1) + .zsh/ + .zshrc + .zshenv ``` ## ステップ5(Vimのインストール) Vimをインストールします。インストール方針としてこんな感じです。 + NeoBundleでモジュール管理する + .vimrc1ファイルですべてを記述せずに、依存関係毎にファイルを分ける 参考文献:[.vimrcファイルを分割する](http://yuzuemon.hatenablog.com/entry/2013/11/17/005517) 、[「source ~/.vim/*.vim みたいなことをしたい」とあんちぽさんに質問された話](http://blog.glidenote.com/blog/2013/09/20/source-vim-files-with-runtime/) 最終的な構成はこのようになります。 ``` ~ (ホームディレクトリ) + .zshenv (※1のシンボリックリンク) + .vimrc (※2のシンボリックリンク) + .anyenv/ + dotfiles/ + Brewfile + .zshenv (※1) + .zsh/ + .zshrc + .zshenv + .vimrc (※2) + .vim/ + .vimrc + bundle/ + conf.d/ + : (以下、設定ファイル) ``` 順番に作成していきます。 + ~/dotfiles/.vimrcを作成 ```~/dotfiles/.vimrc " 最初に読み込む source ~/dotfiles/.vim/.vimrc " conf.d内にあるファイルを全部読み込む set runtimepath+=~/dotfiles/.vim/ runtime! conf.d/*.vim ``` + シンボリックリンクをホームディレクトリに置く ``` $ ln -s ~/dotfiles/.vimrc ~/.vimrc ``` + ~/dotfiles/.vim、~/dotfiles/.vim/conf.d、~/dotfiles/.vim/bundle ディレクトリを作る + NeoBundleをインストールします。 ``` $ git clone https://github.com/Shougo/neobundle.vim ~/dotfiles/.vim/bundle/neobundle.vim ``` + ~/dotfiles/.vim/.vimrcを作成します。これが最初に読み込まれるので、NeoBundleが読み込まれるようにします。 ```~/dotfiles/.vim/.vimrc set nocompatible set number set expandtab set tw=0 set tabstop=2 set shiftwidth=2 set softtabstop=2 set list set listchars=tab:▸\ filetype off if has('vim_starting') set runtimepath+=~/dotfiles/.vim/bundle/neobundle.vim/ endif call neobundle#rc(expand('~/dotfiles/.vim/bundle/')) filetype plugin indent on " Installation check. if neobundle#exists_not_installed_bundles() echomsg 'Not installed bundles : ' . \ string(neobundle#get_not_installed_bundle_names()) echomsg 'Please execute ":NeoBundleInstall" command.' "finish endif "git clone git://github.com/Shougo/neobundle.vim ~/dotfiles/.vim/bundle/neobundle.vim NeoBundle 'Shougo/neobundle.vim' ``` + 色の設定をします。 ```~/dotfiles/.vim/conf.d/colors-solarized.vim NeoBundle 'altercation/vim-colors-solarized' let g:solarized_termcolors=256 let g:solarized_termtrans=0 let g:solarized_hitrail=1 let g:solarized_visibility=1 syntax enable set background=light colorscheme solarized ``` + vim-powerlineの設定をしようと思ったんですが、最近は、lightlineがよさげということで、lightlineを導入します。参考文献: [lightline.vim作りました - プラグインの直交性について](http://d.hatena.ne.jp/itchyny/20130824/1377351527) ```~/dotfiles/.vim/conf.d/lightline.vim NeoBundle 'itchyny/lightline.vim' let g:lightline = { \ 'colorscheme': 'landscape', \ 'mode_map': { 'c': 'NORMAL' }, \ 'active': { \ 'left': [ [ 'mode', 'paste' ], [ 'fugitive', 'filename' ] ] \ }, \ 'component_function': { \ 'modified': 'MyModified', \ 'readonly': 'MyReadonly', \ 'fugitive': 'MyFugitive', \ 'filename': 'MyFilename', \ 'fileformat': 'MyFileformat', \ 'filetype': 'MyFiletype', \ 'fileencoding': 'MyFileencoding', \ 'mode': 'MyMode', \ }, \ 'separator': { 'left': '⮀', 'right': '⮂' }, \ 'subseparator': { 'left': '⮁', 'right': '⮃' } \ } function! MyModified() return &ft =~ 'help\|vimfiler\|gundo' ? '' : &modified ? '+' : &modifiable ? '' : '-' endfunction function! MyReadonly() return &ft !~? 'help\|vimfiler\|gundo' && &readonly ? '⭤' : '' endfunction function! MyFilename() return ('' != MyReadonly() ? MyReadonly() . ' ' : '') . \ (&ft == 'vimfiler' ? vimfiler#get_status_string() : \ &ft == 'unite' ? unite#get_status_string() : \ &ft == 'vimshell' ? vimshell#get_status_string() : \ '' != expand('%:t') ? expand('%:t') : '[No Name]') . \ ('' != MyModified() ? ' ' . MyModified() : '') endfunction function! MyFugitive() if &ft !~? 'vimfiler\|gundo' && exists("*fugitive#head") let _ = fugitive#head() return strlen(_) ? '⭠ '._ : '' endif return '' endfunction function! MyFileformat() return winwidth(0) > 70 ? &fileformat : '' endfunction function! MyFiletype() return winwidth(0) > 70 ? (strlen(&filetype) ? &filetype : 'no ft') : '' endfunction function! MyFileencoding() return winwidth(0) > 70 ? (strlen(&fenc) ? &fenc : &enc) : '' endfunction function! MyMode() return winwidth(0) > 60 ? lightline#mode() : '' endfunction ``` + 補完とスニペット機能を導入します。参考資料: [今しているneocomplcache + neosnippetの設定の自分的おすすめ](http://kazuph.hateblo.jp/entry/2013/01/19/193745) ```~/dotfiles/.vim/conf.d/neocomplcache_snippet.vim NeoBundle 'Shougo/neocomplcache' " Disable AutoComplPop. let g:acp_enableAtStartup = 0 " Use neocomplcache. let g:neocomplcache_enable_at_startup = 1 " Use smartcase. let g:neocomplcache_enable_smart_case = 1 " Set minimum syntax keyword length. let g:neocomplcache_min_syntax_length = 3 let g:neocomplcache_lock_buffer_name_pattern = '\*ku\*' " Define dictionary. let g:neocomplcache_dictionary_filetype_lists = { \ 'default' : '' \ } " Plugin key-mappings. inoremap <expr><C-g> neocomplcache#undo_completion() inoremap <expr><C-l> neocomplcache#complete_common_string() " Recommended key-mappings. " <CR>: close popup and save indent. inoremap <silent> <CR> <C-r>=<SID>my_cr_function()<CR> function! s:my_cr_function() return neocomplcache#smart_close_popup() . "\<CR>" endfunction " <TAB>: completion. inoremap <expr><TAB> pumvisible() ? "\<C-n>" : "\<TAB>" " <C-h>, <BS>: close popup and delete backword char. inoremap <expr><C-h> neocomplcache#smart_close_popup()."\<C-h>" inoremap <expr><BS> neocomplcache#smart_close_popup()."\<C-h>" inoremap <expr><C-y> neocomplcache#close_popup() inoremap <expr><C-e> neocomplcache#cancel_popup() NeoBundle 'Shougo/neosnippet' NeoBundle 'Shougo/neosnippet-snippets' let g:neosnippet#snippets_directory='~/dotfiles/.vim/bundle/neosnippet-snippets/neosnippets,~/dotfiles/.vim/snippets' " <TAB>: completion. " inoremap <expr><TAB> pumvisible() ? "\<C-n>" : "\<TAB>" inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<S-TAB>" " Plugin key-mappings. imap <C-k> <Plug>(neosnippet_expand_or_jump) smap <C-k> <Plug>(neosnippet_expand_or_jump) " SuperTab like snippets behavior. " imap <expr><TAB> neosnippet#jumpable() ? "\<Plug>(neosnippet_expand_or_jump)" : pumvisible() ? "\<C-n>" : "\<TAB>" imap <expr><TAB> pumvisible() ? "\<C-n>" : neosnippet#jumpable() ? "\<Plug>(neosnippet_expand_or_jump)" : "\<TAB>" smap <expr><TAB> neosnippet#jumpable() ? "\<Plug>(neosnippet_expand_or_jump)" : "\<TAB>" " For snippet_complete marker. if has('conceal') set conceallevel=2 concealcursor=i endif ``` + Vimを立ち上げます。 ``` $ vim ``` + Vim上で、:NeoBundleInstallを実行します。 + 素敵画面になればひとまず導入完了です。  ## ステップ6(tmuxのインストール) tmuxをインストールします。インストール方針としてこんな感じです。 + powerlineは導入しない + 色はVimにあわせる 参考文献:[Solarize color theme tmux configuration](https://github.com/altercation/solarized/tree/master/tmux) + iterm2 - vim - tmuxで、コピー&ペーストができるように設定する。参考文献:[tmuxのおすすめ設定](http://qiita.com/catatsuy/items/db0a471bf1eabaa21c36) + tmux上のvimでYankした時にクリップボードにコピーする、クリップボードからペーストする + tmuxinatorを導入する 参考文献:[Tmuxinatorで開発・運用を便利に](http://rrreeeyyy.com/blog/2013/02/25/14-tmuxinator/) 以下進めていきます。 + ~/dotfiles/.tmux.confを作成する ```~/dotfiles/.tmux.conf unbind C-b set-option -g prefix C-t bind r source-file ~/.tmux.conf bind e setw synchronize-panes on bind E setw synchronize-panes off # Base set-window-option -g utf8 on set -g status-utf8 on set-window-option -g mode-keys vi set-option -g mouse-select-pane on set-window-option -g mode-mouse on set -s escape-time 0 # Windows set-option -g base-index 1 bind-key c new-window bind Space choose-window # Panes bind b break-pane bind C-h select-pane -L bind C-j select-pane -D bind C-k select-pane -U bind C-l select-pane -R bind l resize-pane -R bind j resize-pane -D bind k resize-pane -U bind h resize-pane -L bind L resize-pane -R 10 bind J resize-pane -D 10 bind K resize-pane -U 10 bind H resize-pane -L 10 # Use vim keybindings in copy mode setw -g mode-keys vi #Views set-option -g default-terminal "screen-256color" set-option -g default-command "reattach-to-user-namespace -l zsh" set -g set-titles on set -g set-titles-string '#20(whoami)@#H:#20(pwd)' #### COLOUR (Solarized 256) # default statusbar colors set-option -g status-bg colour235 #base02 set-option -g status-fg colour136 #yellow set-option -g status-attr default # default window title colors set-window-option -g window-status-fg colour244 #base0 set-window-option -g window-status-bg default #set-window-option -g window-status-attr dim # active window title colors set-window-option -g window-status-current-fg colour166 #orange set-window-option -g window-status-current-bg default #set-window-option -g window-status-current-attr bright # pane border set-option -g pane-border-fg colour235 #base02 set-option -g pane-active-border-fg colour240 #base01 # message text set-option -g message-bg colour235 #base02 set-option -g message-fg colour166 #orange # pane number display set-option -g display-panes-active-colour colour33 #blue set-option -g display-panes-colour colour166 #orange # clock set-window-option -g clock-mode-colour colour64 #green set-option -g status-interval 10 set-option -g status-justify "centre" set-option -g status-left-length 60 set-option -g status-right-length 90 # 設定リロード bind r source-file ~/.tmux.conf ``` + シンボリックリンクをホームディレクトリに置く ``` $ ln -s ~/dotfiles/.tmux.conf ~/.tmux.conf ``` + ~/dotfiles/.zsh/.zshrcにpbcopyの設定を追加する 参考文献:[tmux で作業している時だけ zsh の設定を変えたいあなたに](http://qiita.com/catatsuy/items/dcd06d81fef7f7236ff5) ```~/dotfiles/.zsh/.zshrc(追加) if [ -n "$TMUX" ]; then alias pbcopy="reattach-to-user-namespace pbcopy" fi ``` + ~/dotfiles/.vim/conf.d/にclipboard用の設定を追加する 参考文献:[【Vim】【Mac】Vimのヤンクでクリップボードにコピーする](http://sea-mountain.hatenablog.jp/entry/20110421/1303362772) ```~/dotfiles/.vim/conf.d/clipboard.vim set clipboard=unnamed,autoselect ``` + tmuxinatorをインストールする。(rbenvからなので必要であれば、rehashする) ``` $ gem install tmuxinator $ rbenv rehash ``` + ~/dotfiles/.zsh/.zshrcにtmuxinatorの設定を追加する ```~/dotfiles/.zsh/.zshrc(追加) [[ -s $HOME/dotfiles/.tmuxinator/scripts/tmuxinator ]] && source $HOME/dotfiles/.tmuxinator/scripts/tmuxinator ``` + ディレクトリの追加と、シンボリックリンクの作成を行う ``` $ mkdir ~/dotfiles/.tmuxinator/ $ ln -s ~/dotfiles/.tmuxinator/ ~/.tmuxinator ``` + 設定ファイルを作成する(muxは、tmuxinatorの別名) ``` $ mux new default ``` ```~/dotfiles/.tmuxinator/default.yml # ~/.tmuxinator/default.yml name: default root: ~/ # Optional tmux socket # socket_name: foo # Runs before everything. Use it to start daemons etc. # pre: sudo /etc/rc.d/mysqld start # Runs in each window and pane before window/pane specific commands. Useful for setting up interpreter versions. # pre_window: rbenv shell 2.0.0-p247 # Pass command line options to tmux. Useful for specifying a different tmux.conf. # tmux_options: -f ~/.tmux.mac.conf # Change the command to call tmux. This can be used by derivatives/wrappers like byobu. # tmux_command: byobu windows: - editor: layout: main-vertical panes: - guard - guard - guard ``` + tmuxをtmuxinator経由で起動する ``` $ mux start default ``` + レイアウトされた画面が出てきたら完了。おつかれさまでした。  ## まとめ(雑感) 簡単にまとめるはずが、やたらと長くなってしまいました。生産性という面では環境設定にかける時間は少ないほどいいはずなので、自動化必須ですね。 数年前に比べると、コンパイルしないといけないものが減って、gitから直接インストールできるものが増えて、導入は楽になってる分、見た目や出来ることの幅が広がっている印象があります。 また、nodeとか、急に環境整備が進んでパッケージマネージャー含めて導入していかないといけないものとかも出てきてますね。 いまは最適だと思っていても、半年ぐらい経つともっと便利とか、すでに使ってないとか、流行り廃りが早いので、都度見直し必須かなと思いました。まずは2014年初頭の備忘録ということで。 |
|
| 541位 |
|
|||
|
18:58:25 |
(Akatsuki Inc. 所属) |
|
##前提 ### knifeとは Chefレポジトリを操作するためのツール。 chef solo環境で利用するのは主に以下の二つ ・knife cookbook(クックブックの作成) ・knife solo 前者は生のchef soloを利用する場合に用いる。 詳しくは以下。 Chefの基本 http://qiita.com/kidachi_/items/9d569b8673e70ef93f0e ### knife-soloとは 今回のトピックはこちら。 それ単体でリポジトリやクックブックの作成、実行まで一括して行ってくれる。 基本はchef soloを利用したい場合はこのknife soloに頼ればok。 ##knifeの初期設定 ``` $ knife configure ``` 諸々の質問事項は全てデフォルトでok。 初期化が完了すると ~/.chef/knife.rbに設定ファイルが保存される。 ##knife-solo knifeのプラグイン(gem)。 chef solo利用にあたり便利な機能をknifeに追加する。 手元(ホストOS)のレシピをリモートにrsyncでゲストOS側に転送した後 chef-soloを実行し、その出力を送り返してくれる便利機能などが付属。 chef-soloだとchefを流す対象(リモートのサーバ)にログインして そこでchef-soloする必要があるが、 knife-soloならローカルからホストを指定して流せば良いので手軽。 ###インストール ``` $ gem install knife-solo ``` ver0.3.0の場合は、knifeの設定ファイル~/.chef/knife.rbに knife-soloが利用するテンポラリディレクトリのパスを設定する。 ```knife.rb knife[:solo_path] = '/tmp/chef-solo' ``` ##knifeによるリポジトリの生成 ``` $ knife solo init chef-repo ``` chef-soloに最適なレイアウトでChefリポジトリを作ってくれる。 構成 ``` $ ls -1 chef-repo cookbooks/ data_bags/ nodes/ roles/ site-cookbooks/ ``` ## site-cookbooks配下に新たなcookbookを作成する ```sh $ knife cookbook create <cookbook_name> -o site-cookbooks ``` ※chef-repo/cookbooksとchef-repo/site-cookbooksの違い(推奨の使い方) 前者はダウンロードしてきたサードパーティのcookbooksを、 後者には自分で作成したcookbooksを入れる。 ## knifeの各設定 プロジェクト直下の`.chef/knife.rb`で設定する。 ※knife configureの際生成される`~/.chef/knife.rb`とは別なので注意! ```.chef/knife.rb log_level :info log_location STDOUT node_name 'taniguchidaiki' client_key '/work/OPServer/.chef/hoge.pem' validation_client_name 'chef-validator' validation_key '/etc/chef-server/chef-validator.pem' chef_server_url 'https://kidachi.local:443' syntax_check_cache_path '/work/OPServer/.chef/syntax_check_cache' role_path '/work/OPServer/roles' # 上記の通り、cookbooksでなくsite-cookbooks cookbook_path '/work/OPServer/site-cookbooks' data_bag_path '/work/OPServer/data_bags' knife[:solo_path] = '/tmp/chef-solo' ``` ##レシピ転送&リモート実行コマンド一覧 ``` # 指定hostに対してchef-soloをインストール # 以後リモートホスト上でchef-soloが通るように、いい感じに調整してくれる。 $ knife solo prepare <host> $ knife solo prepare <user>@<host> # <host>でchef-soloを実行させる $ knife solo cook <host> # run_listを個別に指定(ver0.3.0以降) $ knife solo cook <host> -o hello::default, nginx::default # <host>に転送したレシピ群を削除 $ knife solo clean <host> # 新規Chefリポジトリを作成 $ knife solo init chef-repo ``` ※knife-soloがssh経由でchef-soloを実行する際、sshに使われるログインユーザはsudoかつパスワードなしでchef-soloを実行できる権限を持っている必要あり(VagrantやEC2は初期状態でOK)。 ※複数サーバにknife-soloしたい場合は、 ``` $ echo node1 node2 node3 | xargs -n 1 knife solo cook ``` ##指定hostにchef-soloをインストール 実際にやってみる。 ``` $ knife solo prepare 192.168.33.10 Bootstrapping Chef... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 101 6790 101 6790 0 0 944 0 0:00:07 0:00:07 --:--:-- 17146 Downloading Chef 11.6.2 for el... Installing Chef 11.6.2 警告: /tmp/tmp.1zXqJZ9U/chef-11.6.2.x86_64.rpm: ヘッダ V4 DSA/SHA1 Signature, key ID 83ef826a: NOKEY 準備中... ########################################### [100%] 1:chef ########################################### [100%] Thank you for installing Chef! Generating node config 'nodes/192.168.33.10.json'... ``` 成功。 ###ホストOSからknife-soloする際の注意 通常の方法でssh出来るようにしておく。 →knife-soloの際、ホストからゲストへ通常のsshアクセス、つまり ``` $ ssh 192.168.33.10 ``` でsshできるようになっている必要がある。 ~/.ssh/configに以下追記。 ``` # ~/.ssh/config Host 192.168.33.* IdentityFile ~/.vagrant.d/insecure_private_key User vagrant ``` ##run_listの設定 chef-repos内のnodesディレクトリ内jsonファイル(今回は192.168.33.10.json)に run_list(実行したいレシピ名)を記述する。 ```102.168.33.10.json { "run_list":[ "recipe[apache]", "recipe[build-essential]", "recipe[chef_handler]", "recipe[git]", "recipe[iptables]", "recipe[mysql55]", "recipe[php54]", "recipe[postfix]", "recipe[runit]", "recipe[yum]" ] } ``` ##knife-solo実行 ※レシピは既成とする。 ``` $ knife solo cook 192.168.33.10 ``` いろいろエラーが出た。 ##エラー1 ``` WARNING: Local cookbook_path '/var/chef/cookbooks' does not exist WARNING: Local cookbook_path '/var/chef/site-cookbooks' does not exist WARNING: Local role_path '/var/chef/roles' does not exist WARNING: Local data_bag_path '/var/chef/data_bags' does not exist ``` path設定がデフォルトのままだったのでこけた。 Vagrantfileに適切なpathを設定し、vagrant reload。 ```Vagrantfile config.vm.provision :chef_solo do |chef| chef.cookbooks_path = "/work/chef-repo/cookbooks" chef.roles_path = "/work/chef-repo/roles" chef.data_bags_path = "/work/chef-repo/data_bags" # chef.add_recipe "mysql" # chef.add_role "web" # # # You may also specify custom JSON attributes: # chef.json = { :mysql_password => "foo" } end ``` ※ちなみにこの設定は~/.chef/knife.rbの設定に上書きされる。 ※今回は knife solo initを使っていなかったので、rolesとdata_bagsディレクトリがそもそも存在していなかった。chef-repo配下に自分で作成して解決。 ##エラー2 ``` Recipe: mysql55::default * package[mysql-server] action install ================================================================================ Error executing action `install` on resource 'package[mysql-server]' ================================================================================ Chef::Exceptions::Package ------------------------- Version 5.5.32-1.el5.remi of mysql-server not found. Did you specify both version and release? (version-release, e.g. 1.84-10.fc6) ``` ``` Recipe: php54::default * package[php] action install ================================================================================ Error executing action `install` on resource 'package[php]' ================================================================================ Chef::Exceptions::Package ------------------------- Version 5.4.20-1.el5.remi of php not found. Did you specify both version and release? (version-release, e.g. 1.84-10.fc6) ``` mysql、phpそれぞれ、レシピで指定したバージョンが見つからないと。 yumからinstall可能なmysqlをチェック ``` [vagrant@localhost ~]$ yum --enablerepo=epel,remi,rpmforge info mysql Loaded plugins: fastestmirror Determining fastest mirrors * base: ftp.nara.wide.ad.jp * extras: ftp.nara.wide.ad.jp * updates: ftp.nara.wide.ad.jp Available Packages Name : mysql Arch : x86_64 Version : 5.1.69 Release : 1.el6_4 Size : 907 k Repo : updates Summary : MySQL client programs and shared libraries URL : http://www.mysql.com License : GPLv2 with exceptions Description : MySQL is a multi-user, multi-threaded SQL database server. MySQL is a : client/server implementation consisting of a server daemon (mysqld) : and many different client programs and libraries. The base package : contains the standard MySQL client programs and generic MySQL files. ``` Versionが古い。またRepoがCentOSデフォルト(updates)になっている。 →epel/remiリポジトリを追加する。 ##リポジトリを追加/有効化する CentOSにyumリポジトリを追加 http://qiita.com/kidachi_/items/40f862181050b78b9cc7 //TODO 本当はここもChef化したい。 ###mysql,phpそれぞれをリポジトリから落とせるバージョンに最適化する。 ``` $ sudo vi chef-repo/cookbooks/mysql55//attributes/default.rb default['mysql55']['version'] = "5.5.34-1.el6.remi" default['mysql55']['server-id'] = "0" ``` ``` $ sudo vi chef-repo/cookbooks/php54//attributes/default.rb default['php54']['version'] = "5.4.20-1.el5.remi" ``` ###remiを常時有効化(enabled=1)にする。 //TODO 後々依存関係が出たりするので、常時有効はあまり良くないとか? ```/etc/yum.repos.d/remi.repo(ゲスト) [remi] name=Les RPM de remi pour Enterprise Linux 5 - $basearch baseurl=http://rpms.famillecollet.com/el5.$basearch/ http://iut-info.univ-reims.fr/remirpms/el5.$basearch/ enabled=1 priority=1 ``` ###Vagrantfile上でも各リポジトリを有効化。 ```Vagrantfile(ホスト) vmclient.vm.provision :chef_solo do |chef| ~ chef.add_recipe "yum::epel" chef.add_recipe "yum::remi" ~ end ``` 参考) Chef の サードパーティ Cookbook を利用して、yum のリポジトリを追加してみる http://girigiribauer.com/archives/1095 ##再度knife-solo実行 ``` $ knife solo cook 192.168.33.10 Running Chef on 192.168.33.10... Checking Chef version... Uploading the kitchen... Generating solo config... Running Chef... Starting Chef Client, version 11.6.2 Compiling Cookbooks... Recipe: chef_handler::default * remote_directory[/var/chef/handlers] action create ~ ~ Chef Client finished, 40 resources updated ``` 成功。 ```102.168.33.10.json { "run_list":[ "recipe[apache]", "recipe[build-essential]", "recipe[chef_handler]", "recipe[git]", "recipe[iptables]", "recipe[mysql55]", "recipe[php54]", "recipe[postfix]", "recipe[runit]", "recipe[yum]" ] } ``` これで、今後上記のミドルウェアは全てChefベースで展開できる。 ## 補足 vagrant関連記事は以下 Vagrantの基本 http://qiita.com/kidachi_/items/e63c1607705178aa257c saharaでVagrantの状態管理 http://qiita.com/kidachi_/items/ba365905b2a770c72be1 |
|
| 542位 |
|
|||
|
14:46:53 |
(株式会社キュリオシティソフトウェア 所属) |
|
## はじめに iOS5からUIViewControllerを自前のクラスにaddChildViewController:メソッドで追加できるようになった。 最近では、こういった自前コンテナは主にFacebookやPath2.0のスライドメニュー風のUI開発なんかに使われていると思う。 このaddChildViewController:メソッドの後には必ずdidMoveToParentViewController:メソッドを呼ぶという説明があるが、これの意味がよくわからなかったのでそのメモ。 例えば ```objectivec:didMoveToParentViewControllerの必要性を疑うコード //自前コンテナ(self)にchildViewControllerを追加 [self addChildViewController:childViewController]; //その後didMoveToParentViewControllerを実行しなければいけない [childViewController didMoveToParentViewController:self]; ``` この追加(add)したあとになぜ完了(didMove)を明示的に知らせないといけないのか謎という感覚。また、これがなくてもこちらの想定通り動くのでずっと疑問だった。 ### 結論としては 自前コンテナでaddChildViewController:を実行した後は、やはり必ずdidMoveToParentViewController:を呼ぶ。これはUIViewController自身が他のコンテナに追加(もしくは削除)されている最中かどうかを知ることで処理を分岐する事もできるようになっていて、それを判断するために完了したことを通知する必要がある。 ちなみに、正しくコードをかけていない場合、追加したViewControllerのviewWillAppear:が動作しない場合がある。 これはどのような事かというと、コンテナのviewを表示したいだけならばViewControllerのviewに対して子ViewControllerのviewをaddSubViewすればいいだけだと思ってしまう。しかしそのViewが表示されたかどうかという判断にはならないためaddChildViewControllerがそもそも必要になってくる。 ## 説明 ### UIViewControllerの追加サイクル そもそもUIViewControllerの追加には「開始」と「終了」がかならずセットで必要となる。 ・追加: addChildViewController: ・開始: willMoveToParentViewController:(add後に自動で呼ばれる) ・完了: didMoveToParentViewController:(任意のタイミングで呼ぶ) まず自前コンテナへの追加のためにaddChildViewController:した際に、ViewControllerの追加開始の合図であるwillMoveToParentViewControler:は自動で呼ばれる。 開始は自動で呼ばれるが、それが終了したことをコールするためのdidMoveToParentViewController:メソッドは自動で呼ばれないため必要となる。 ### 終了をコールしないと何に困るのか もともとUIViewControllerは自身が他のコンテナに追加(もしくは削除)されている最中かどうかを知ることで処理を分岐する事もできるようになっていて、これを判断するのに完了を呼ぶ必要がある。 ちなみに、追加されている途中かどうかはUIViewControllerのisMovingToParentViewControllerメソッドによって取得できる。 ### didMoveToParentViewController:がなぜ自動で呼ばれないようになっているか では、なぜdidMoveToParentViewController:が自動で呼ばれないかというと、画面遷移にトランジションを指定したい場合が想定されているからだと思う。 例えば次のコードのように、完了までに時間がかかる(実装者が時間を指定できる)場合に、完了のメソッドが明示的にコールできるようになっていることでその意味が出てくる。 ```objectivec: /*fromViewControllerはすでにコンテナに追加されているとする*/ //toViewControllerをコンテナに追加する [self addChildViewController:toViewController]; //fromからtoへ画面遷移のトランジションを行う [self transitionFromViewController:fromViewController toViewController:toViewController duration:1.0 options:UIViewAnimationOptionTransitionNone animations:^{ // ここで何かしらの遷移アニメーション } completion:^(BOOL finished) { // 追加完了を明示的に行う [toViewController didMoveToParentViewController:self]; }]; ``` ## まとめ 上記のように、画面遷移のトランジションを使わなくてもaddChildViewController:したらdidMoveToParentViewController:を呼び出すことを忘れないようにしたらよいでしょう。 もし、突っ込みどころ等あればコメントなり編集リクエストがあれば嬉しいです。 ## 参考 カスタムContainer View Controllerを作る(ルールが分かりやすく纏められている) http://qiita.com/edo_m18/items/8b6b457f82b185ab1f6a Cocoa練習帳: [iOS]独自のコンテナViewController http://www.bitz.co.jp/weblog/?date=20120923 isMovingToParentViewControllerメソッドについてはiOS View Controllerカタログの「ナビゲーションスタックの変化の監視」 https://developer.apple.com/jp/devcenter/ios/library/documentation/ViewControllerCatalog.pdf |
|
| 543位 |
|
|||
|
00:41:00 |
|
|
複数バージョンのRubyを管理をするためのツールとして``rbenv + ruby-build``の組み合わせは大分ポピュラーになってきました。 備忘録としてよく使うコマンドをまとめてみました。 ## セットアップ OSX + homebrew だったら以下の記事がおすすめ。 * [OS X で rbenv を使って ruby 1.9.3 の環境を作る](http://qiita.com/items/9dd797f42e7bea674705) ## よく使うコマンド(インストール編) OSXに入れる時は``CONFIGURE_OPTS``の設定を忘れないようにしましょう。 ```shell # インストール可能な一覧セットを得る $ rbenv install 1.9.2-p290 1.9.3-p327 . . # インストール ## 基本 $ rbenv install 1.9.3-p327 ## OSX向け、readlineやopensslをhomebrew経由で入れたものを使う $ brew install readline openssl $ CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline) --with-openssl-dir=$(brew --prefix openssl)" rbenv install 1.9.3-p286 ``` ## Rubyの切り替え ```shell # 1.9.3-p327に切り替え $ rbenv global 1.9.3-p327 # システムのデフォルトrubyに戻す $ rbenv global system # 特定のディレクトリ以下のRubyバージョンを切り替え $ rbenv local 1.9.3-p327 # ローカル設定を解除 $ rbenv local --unset ``` ## メンテナンス 特に ``rbenv rehash`` はよく使うので覚えておきましょう。 ``rvm``では不要なコマンドなので忘れがちです。 ```shelll # インストールしたrubyやgemのパスを通す $ rbenv rehash # インストール済みのRuby一覧を見る $ rbenv versions ``` ## アップデート ```shell # rbenv install の一覧を更新する $ brew upgrade ruby-build ``` 他にもよく使う物があればコメント等でご指摘下さい! |
|
| 544位 |
|
|||
|
01:04:11 |
|
|
AngularJSのようなクライアントMVCフレームワークを採用すると、クライアントサイドの規模が大きくなってくるので、できればJavaScriptじゃなくて型のあるプログラミング言語で開発したいですよね。
AngularJSは独自のクラスシステムを持っていないし、モデルやコントローラを実装するためにベースクラスを継承したりする必要もないので、altJSとの相性がよくて組み合わせやすいです。 altJSと言ってもたくさん種類がありますが、今回はTypeScriptを使ってAngularJSアプリを書くときのTipsやコツなどを紹介したいと思います。 # ベースとなるプロジェクトを作る AngularJSのコードを書くとき、JavaScriptであればおもむろに書き始めることも可能ですが、altJSを使う場合はコンパイルなどの手順が必要になるので、Gruntを使ったプロジェクトを作る必要があります。 Gruntfileを1から書いてファイルの配置などを考えるのはちょっと面倒なので、yeomanを使うか、どこかからひな形を持ってくるのが楽です。 ## yeomanを使う yeomanを使うならangular-generator-typescriptというのがあります。 * [AngularJS + Typescript用のyeomanのgenerator](http://qiita.com/tenntenn/items/06bbb9971e8a6c3ccc95) でもこれ9ヶ月間もメンテされてないので、TypeScriptのコンパイル通らないし、fork元で新しく追加されたテンプレートに追随できていないんですよね。 じゃあこれをforkして修正するか、もしくはfork元のgenerator-angularにTypeScript用のオプションを追加すればいいんじゃないか?とも思ったのですが、すでにそんなpull requestがありました。 * [Typescript support for generator-angular](https://github.com/yeoman/generator-angular/pull/313) 開発者が忙しくて放置されていたようですが、近いうちに取り込まれそうな雰囲気なので待つのがよさそう。 ## ひな形を使う ひな形を持ってくるならこちらがおすすめ。いろいろと参考にさせてもらいました。 * https://github.com/vvakame/angularjs-typescript この記事の内容をまとめたプロジェクトも用意しました。テストの設定がなかったりしてちょっと物足りないかもしれませんが、よければ参考にしてください。 * https://github.com/zoetrope/angular-typescript-sample # IDEを使う TypeScriptでの開発のメリットを存分に享受したいのであれば、Visual Studioや、IntelliJ IDEA/WebStormなどのIDEの利用は必須と言ってもいいでしょう。 IntelliJ IDEAから使う場合は、メニューから[File] -> [New Project...]を開き、プロジェクトのタイプにWebを選んで、前述したプロジェクトの含まれるディレクトリを指定するだけです。 これだけで、AngularJSのAPIもこんなふうに補完されます。  IntelliJ IDEAには、AngularJS用のプラグインも用意されていて(WebStormには標準搭載)、HTMLの編集中にdirectiveの補完が効くので便利です。(`$scope`のメンバも補完してくれるともっと便利なんですけどね) なお、IntelliJ IDEA 12はTypeScriptのGenericsに対応していない(ver.0.8相当)ので、IntelliJ IDEA 13の利用をおすすめします。 # Gruntfileをカスタマイズする yeomanで生成したりひな形に含まれているGruntfileはそのまま使ってもいいんですが、カスタマイズすることでさらに便利になります。 ## 型定義ファイル TypeScriptでは、既存のJavaScriptライブラリを利用するために型定義ファイル(拡張子が.d.tsのファイル)を用意する必要があります。 型定義ファイルを扱うリポジトリはDefinitelyTypedがメジャーです。AngularJSの型定義ファイルももちろん用意されています。 * https://github.com/borisyankov/DefinitelyTyped angular-generatorで生成したプロジェクトではDefinitelyTypedを丸ごと落としてきているのですが、これはちょっとおおげさな気がします。 そこで、型定義ファイルを取得するためのツールとしてtsdを使います。 (最近までtsdでangular-route.d.tsが取れなかったので、pull requestして取れるようにしてもらいました。) * [TypeScriptの型定義ファイル管理ツールtsdを使ってみた](http://qiita.com/grapswiz@github/items/72012f283de23385553a) Gruntからtsdを呼び出すには、将来的には[grunt-tsd](https://github.com/DefinitelyTyped/grunt-tsd)ってのが提供されるようですが、現状ではgrunt-execを使います。 grunt-execをインストールします。 ~~~ npm install grunt-exec --save-dev ~~~ あとは、Gruntfileでこんなタスクを用意するだけです。ファイルの置き場所はtsd-config.jsonで制御します。 ~~~js:Gruntfile.js exec: { tsd: { cmd: function () { return "tsd install jquery angular angular-resource angular-route"; } } } ~~~ ## TypeScriptコンパイル GruntでTypeScriptのコンパイルをするにはgrunt-typescriptを使い、Gruntfileに次のようなタスクを用意します。 ~~~js:Gruntfile.js typescript: { main: { src: ['dist/scripts/**/*.ts'], dest: 'dist/scripts/App.js', options: { target: 'es5', sourcemap: true, declaration: false } } } ~~~ ここで、srcとdestの指定方法に少しコツがあります。 まず、srcを指定するには次のような2つの方法があります。 * 1ファイルだけ指定して、そのファイル内に他のファイルへの参照(`/// <reference path="xxx.ts" />`)を書く。 * 配列で全ファイルを列挙する、またはワイルドカードで全ファイルを指定する。 これはどちらでも好みで選んでいいと思います。 destの指定方法も2種類あります。 * ディレクトリを指定すると、TypeScriptのソースと同じツリー構造でJavaScriptとmapファイルが生成される。 * ファイルを指定すると、すべてのTypeScriptのコンパイル結果が1つのJavaScriptとmapファイルにまとめられる。 次のsourcemapのところで説明しますが、destは1つのファイルにまとめておくのがおすすめです。 ## sourcemap TypeScriptでデバッグするときには、やっぱりsourcemapを使いたい。 TypeScriptのコンパイル結果をそのままHTMLから参照するのであれば特に問題ないのですが、TypeScriptでコンパイルした結果をminifyする場合は、2段階のsourcemapの解決が必要になるのでちょっとややこしくなります。 Gruntfileの設定例はこんなかんじになります。 ~~~js:Gruntfile.js copy: { scripts: { files: [ { expand: true, flatten: false, cwd: 'app/scripts', src: ['**/*.ts'], dest: 'dist/scripts' }, { expand: true, flatten: false, cwd: 'app/d.ts', src: ['**/*.d.ts'], dest: 'dist/d.ts' } ] } }, typescript: { main: { src: ['dist/scripts/**/*.ts'], dest: 'dist/scripts/App.js', options: { target: 'es5', sourcemap: true, declaration: false } } }, uglify: { dev: { options: { report: 'min', beautify:true, mangle: false, preserveComments: 'some', sourceMap: 'dist/scripts/App.min.js.map', sourceMapRoot: '', sourceMappingURL: 'App.min.js.map', sourceMapIn: 'dist/scripts/App.js.map', sourceMapPrefix: 1 }, files: { 'dist/scripts/App.min.js': [ 'dist/scripts/libs/angular/*.js', 'dist/scripts/libs/angular-route/*.js', 'dist/scripts/libs/angular-resource/*.js', 'dist/scripts/App.js' ] } } } ~~~ それぞれのタスクの説明をします。 * copy:scripts * TypeScriptのファイルをWebサーバから見える場所に置きます。(sourcemapで解決するときに使うので) * typescript:main * TypeScriptのコンパイルをして、App.jsとApp.js.mapを生成します。 * uglify:dev * App.jsとAngularJSやその他ライブラリのファイルを結合して、App.min.jsを生成します。 * sourceMapInオプションでApp.js.mapを指定し、App.min.jsからTypeScriptへのマッピングを解決するためのApp.min.js.mapを生成します。 uglifyのsourceMapInオプションは1ファイルしか指定できない(functionを指定すれば指定できる?)ようなので、TypeScriptのコンパイル結果は1つにまとめておくのがよいです。 これでChromeやIDEを使って、TypeScriptのソースにブレイクポイントを張ったりできるようになりました!  # コードの書き方 TypeScriptでAngularJSの基本的なコードの書き方は、generator-angularで生成されるコードが参考になります。 生成コードのテンプレートはこちら。 * https://github.com/anchann/generator-angular/tree/typescript/templates/typescript 基本は上記のコードを見れば分かると思うので、ここでは応用的な書き方について紹介したいと思います。 ## ScopeとController まずは、ScopeとControllerの書き方を3種類紹介したいと思います。 なお、説明のためにController内にビジネスロジック的なものを書いていますが、ちゃんとしたコードを書くときはビジネスロジックはModelに書くようにしてください。 ### 基本的な書き方 IScopeを継承した新しいinterfaceと、Controllerクラスを定義します。 JavaScriptと比べるとScopeの定義を書くのが面倒に感じるかもしれませんが、型チェックで得られるメリットは大きいので我慢して書きます。 ~~~ts:MainController.ts export interface MainScope extends ng.IScope { content: string; items: app.models.Item[]; add(item: string): void; } export class MainController { constructor(private $scope:MainScope) { $scope.items = [] $scope.add = (item:string) => { $scope.items.push(new app.models.Item(item)); } } } ~~~ Viewに公開するメンバ関数はコンストラクタの中で定義するので、コンストラクタは長くなりがちです。 なお、Viewに公開したくないメンバ変数やメンバ関数は、Controllerクラスのprivateメンバとして用意します。 ### angular.bindを使う Controllerのメンバ関数をangular.bindを使ってScopeにバインドする方法です。 ~~~ts:MainController.ts export interface MainScope extends ng.IScope { content: string; items: app.models.Item[]; add: Function; } export class MainController { constructor(private $scope:MainScope) { $scope.items = [] $scope.add = angular.bind(this, this.add) } add(item:string): void { this.$scope.items.push(new app.models.Item(item)); } } ~~~ bindを書く手間はありますが、コンストラクタがだらだらと長くなることはなくなりました。 ### Controller As を使う 最後に[AngularJS 1.2.0の"Controller As" Syntax](http://qiita.com/soundTricker/items/9a2a27281246ca7ce0b7)を使った方法です。 ~~~ts:MainController.ts export class MainController { content: string; items: app.models.Item[]; constructor(private $scope:ng.IScope) { this.items = [] } add(item:string): void { this.items.push(new app.models.Item(item)); } } ~~~ ScopeとControllerがまとまって、すっきりと書くことができました。ただし、HTML側の書き方が少し変わるので注意が必要です。 この書き方は、ScopeとControllerの役割分担ができないとか、ViewからControllerの中がすべて見えてしまう(privateつけても呼び出せる)とか、問題点を指摘されたりもしているので、採用する際には少し検討したほうがよさそうです。 ## Directive Directiveの実装は、JavaScriptの場合と同じように連想配列を返す書き方もできますが、IDirective interfaceを実装することもできます。 interfaceを使ったほうが型のサポートがあるので安心ですね。 なお、IDirectiveのメンバ関数やメンバ変数には`?`がついているので、必要のないメンバ関数・メンバ変数は実装しなくても大丈夫です。 ~~~ts:MyDirective.ts module app.directive { export class MyDirective implements ng.IDirective { restrict: string = "E"; templateUrl: string = "/views/xxx.html"; transclude: any = false; replace: boolean = false; scope: any = false; link(scope: ng.IScope, elem: any, attrs: ng.IAttributes, ctrl: any) { /* 処理を書く */ } } } angular.module("app.directive") .directive('myDirective', () => new app.directive.MyDirective()); ~~~ ## `$resource`へのメンバ関数の追加 サーバーサイドと通信するための`$resource`サービスは、標準でGET、POST、DELETEのHTTPメソッドが使えるようになっています。 それ以外のHTTPメソッド(PUTなど)を利用したい時は、自分で新しいメンバ関数を追加することになります。 しかし、TypeScriptでは新しいメンバ関数を追加しても、呼びだそうとすると「そんな関数知らないよ」とコンパイル時に怒られてしまいます。 そこでまず、IResourceClassを継承して、新しいメンバ関数を追加したinterfaceを用意します。 ~~~ts module app.service { export interface IUpdatableResourceClass extends ng.resource.IResourceClass { update(params: any, data: any, success?: Function, error?: Function): ng.resource.IResource; } } ~~~ 続いて、PUTメソッドを追加した`$resource`を返すサービスを用意します。 ~~~ts angular.module('app.service', ['ngResource']) .factory('Items', function ($resource: ng.resource.IResourceService) { return $resource("/api/items/:itemId", {}, {update: {method: 'PUT'}}); }); ~~~ コントローラにそのサービスを渡してあげます。 ~~~ts angular.module('app.controller').controller("MainController", ["$scope", "Items", ($scope: ng.IScope, Items: app.service.IUpdatableResourceClass):app.controller.MainController => { return new app.controller.MainController($scope, Items) }]); ~~~ これで、PUTメソッドを追加した`$resource`を、Controllerから使えるようになりました。 でも`$resource`のメンバ関数はany型の引数を渡せるようになっているのでちょっと頼りないですね。 より厳密に型チェックをしたいのであれば、ちゃんと引数や戻り値の型を定義したクラスを別途用意して`$resource`をラップしてしまいましょう。 # まとめ 一度TypeScriptで書き始めると、もう生JavaScriptは書きたくなくなります。ぜひお試しあれ。 |
|
| 545位 |
|
|||
|
19:45:39 |
(Wondershake, Inc. 所属) |
|
Rails Advent Calendar 9日目です。 どのくらい認知があるのか知りたい、という意味も込めての投稿です:-) Railsに標準で備わっているApplication Template(アプリケーションテンプレート)の概要とサンプルの紹介です。 ## RailsはGemが増えると最初の設定だるい よくRailsは「設定より規約」なんだよね、とか聞きますが、 たしかにRailsは仕組みさえ覚えてしまえば、 比較的簡単にデプロイまでできてしまいます。 難しい設定ファイルも書くことはとても少ないです。 特に最近はHerokuを中心とするPaaSの台頭でよりその恩恵を受けれている気がします。 また、Rubyのコミュニティがとても活発で、 優秀な方がたくさんいるので、めちゃんこ便利なGemがたくさん流通しています。 Githubをサーフィンしてたら、 「やっべー、このGemすげー、Gemfileに追加しよ!」 ってことは日常茶飯事です。 でも、そうやっているといつのまにかGemの設定ファイルがたくさん生まれます。 Capistranoならconfig/deploy.rb ResqueをGodで監視するならconfig/god/resque.rb 他に毎回application.rbに追加している設定もあるかもしれません。 そうすると、 ```bash rails new hogehoge ``` してから、実際にアプリケーションコード書き始めるまでの準備がとても長い… そしてコピペするとミスのなんのって。 やっぱこうなってきたら自動化したいですよね。 ## Application Templateを使おう RailsにはApplication Templateという機能があります。 どういうのかっていうと、 いつも使う設定をひとつのファイルにまとめておくと、 毎回それを参照することで自動的にファイルの生成や設定の追加をやってくれる、というものです。 文章にするとちょっと抽象的すぎますね。 たとえば、 ```ruby:app_template.rb # Gemfileにrainbowを追加(bundle installの前に実行される) gem 'rainbow' # どうせいつもすぐ消すし消しとこ! remove_file "public/index.html" ``` ```bash rails new new_project_name -m app_template.rb ``` とすると、自動的にrainbowが入った状態で、 public/index.htmlが存在しないプロジェクトが出来上がります。 --- もし、毎回は使わないけど、時々使いたいgemがあれば、 ```ruby:app_template.rb if yes?('Would you like to install nokogiri?') gem 'nokogiri' end ``` のように書くことも出来るし、gitのリモートリポジトリを指定したいときは、 ```ruby:app_template.rb @app_name = app_name @remote_repo = ask("git remote repo?") git :remote => "add origin #@remote_repo:#@app_name.git" ``` のようにインタラクティブなApplication Templateも書くことも出来ます。 --- 詳しい書き方については、 英語ですがRails Guidesに書いてあるのでそちらをご参照ください。 Rails Guides [Creating and Customizing Rails Generators & Templates > 8 Application Templates](http://guides.rubyonrails.org/generators.html#application-templates) Rails Guidesにも書いてあるんですが、 このApplication TemplateではThorの以下のメソッドも使えるので便利です。 Thor [Module: Thor::Actions](http://rdoc.info/github/wycats/thor/master/Thor/Actions) ## 作るのもいいけど…出来上がってるの無いの? あるある、あるよ。 Stackoverflow [What Rails application templates show best practices for setting up a new Rails 3 application?](http://stackoverflow.com/questions/4999207/rails-3-application-templates) でもすんげーたくさん詰まってて、 個々が使いやすいように、自分のために作るものだと思うので、 是非ちょっとずつ自分のAppcation Templateを作ってみては!とおもいます。 って言うだけじゃ説得力ないな、と思ったので自分もつくりました笑 [tachiba's application template](https://github.com/tachiba/rails3_template) スパゲッティになっちゃっているので、 設定の量が増えたらStackoverflowに紹介されているもののように、 ファイルを分割してapplyで参照するのが可読性も保ててよいのかと思います。 ## 自動化して楽しよう! やっぱRailsを使うなら実装を書くのに時間を割きたいな、と思います。 ほんで、これだけすらすら実装して、 デプロイできる環境が整っているので、 それこそゴミのようにたくさんサービスを作りたいわけです。 ちょっと最初は面倒なんですが、 自分のためのApplication Templateを作る、育てるのは楽しいと思うので、 是非作ってGithubで公開してみてください。 cf.) ちなみに、Rails ComposerというRails Apps Composer(Gem)を使ったテンプレートもあります。 http://railsapps.github.com/rails-composer/ ちょっと複雑そうに見えたので自分は逃げてしまいました:-( |
|
| 546位 |
|
|||
|
03:04:48 |
(Increments 所属) |
|
社内やオープンソースのプロジェクトに並行して参加していると、gitconfig の `user.name` や `user.email` をリポジトリごとに切り替えたくなることがある。リポジトリを作るたびに `git config user.name "My Name"` すればいいのだが、 `user.name` が存在しないか空文字列だと環境変数 `NAME` の値を暗黙的に使う仕様になっているため、設定をうっかり忘れてしまうとなかなか気づけない。名前やメールアドレスを間違えたまま何度もコミットしてしまうと修正が厄介である。
## Git 2.8以上 最近の Git で設定忘れを未然に防ぐには `git config --global user.useConfigOnly true` を実行する。これを設定するとユーザー情報について環境変数を暗黙に参照することがなくなる。グローバルな gitconfig で `user.name` と `user.email` を設定してあるなら削除しておく。こうすることで、各リポジトリ単位で `user.name` と `user.email` を設定することを強制できる。 ``` $ git config --global user.useConfigOnly true $ git config --global --unset user.name $ git config --global --unset user.email $ cd /path/to/my_repo $ git config user.name "My Name" $ git config user.email "me@example.com" ``` ## Git 2.7以下 以前の Git では `pre-commit` フックで設定の値を検証することになる。 `pre-commit` フックは `git commit` の直前に実行され、終了ステータスが非ゼロであれば `git commit` は中止される。 ```bash:pre-commit #!/bin/sh if [ -z "`git config --local user.name`" ]; then echo "fatal: user.name is not set locally" exit 1 fi if [ -z "`git config --local user.email`" ]; then echo "fatal: user.email is not set locally" exit 1 fi ``` フックスクリプトを実行可能にし、 $my_repo/.git/hooks ディレクトリに配置する。 ``` $ chmod a+x pre-commit $ mv pre-commit $my_repo/.git/hooks ``` これで、 $my_repo リポジトリにおいて `user.name` と `user.email` が設定されていなければ `git commit` が失敗するようになった。 ``` $ git commit -m "Add some files" fatal: user.name is not set locally $ git config user.name "My Name" $ git config user.email me@example.com $ git commit -m "Add some files" [master deadbeef] Add some files ... ``` `pre-commit` フックスクリプトをテンプレートに入れれば、新たに `git init` か `git clone` したリポジトリにはフックスクリプトがコピーされるようになる(参考:[gitのhooksを管理する(自分用テンプレートを使う)](http://qiita.com/quattro_4/items/59fdf8b9aa9ef48ecbdf))。 |
|
| 547位 |
|
|||
|
15:07:13 |
|
|
iOSアプリケーションにカスタムURLスキームを実装し、SafariからそのURLを開く際にまだユーザがそのアプリケーションをインストールしていなければそのURLを開けずにページエラーになってしまう。その場合はアプリケーションをインストールする導線を張りたい。 ## 対策1 workaroundとして以下のhackがある。 ```javascript setTimeout(function() { window.location = "http://itunes.com/apps/yourappname"; }, 25); window.location = "custom-uri://"; ``` ### hack1の説明 window.locationにてカスタムURLスキームを設定し、そのURLに対応したアプリケーションがない場合(Safari自身も対応していない場合)、``window.location = "custom-uri://"`` の処理で画面遷移はせず、Safariは現在のページにとどまるためsetTimeoutがすぐ処理され、``window.location = "http://itunes.com/apps/yourappname"`` によりStoreへ飛ばすコードが実行される。 これによりアプリがインストールしていない場合にStoreに飛ばすということが実現できる。 ### hack2の説明 SafariはURL ( ``custom-uri://`` ) を開くアプリケーションが見つからない場合にAlertを表示する。 Alertを表示後にさらに別のアプリケーション(上記の例ではApp Storeアプリが ``http://itunes.com/apps/yourappname`` をハンドリング)で開けるカスタムURLスキームを開くと、SafariがAlertを消す動きを見せるため、25msのような短い間隔であればユーザにAlertを見せないように振る舞うことができる。 逆にsetTimeoutで開くページがSafariで扱えるウェブページの場合Alertはキャンセルされず残ったままになるようだった。そのためApp Storeの前に自前のヘルプページを挟みたいという要件があるとsetTimeoutの間隔を短くするこのhackは使えずにAlertが残ったままになる。 ### 対策1の問題点 カスタムURLスキーム ( ``custom-uri://`` ) を実装したアプリケーションがインストール済みの場合のフローが問題になる。 インストール済みならURL ( ``custom-uri://`` ) をそのアプリケーションがフォラグランドになって処理を行うが、setTimeoutの処理はキャンセルされないので、ユーザがSafariに戻ったときにインストール済みにもかかわらず毎回 ``window.location = "http://itunes.com/apps/yourappname";`` が実行されストアのページが開れてしまうという問題が起きる。 ## 対策2 対策1の問題点を回避するworkaroundとして時刻をチェックするというhackがある。 ```javascript var checkedAt = new Date(); setTimeout(function() { var t = new Date() - checkedAt; if (t < 2000) { window.location = "http://itunes.com/apps/yourappname"; } }, 1000); window.location = "custom-uri://"; ``` ### hack3の説明 アプリケーションがURLを開いてフォラグランドになれば当然Safariがバックグランドに回るためSafariの実行が停止し、すぐにはsetTimeoutの処理は実行されない。 Safariに戻ったあとにこのsetTimeoutが実行されるがこの時には時間が経っているのでsetTimeout内で時刻をチェックすれば、インストール済みのフローではApp StoreのURLを開かないようにできる。 iPhone 4Sで試したところ500msくらいであればSafariがバックグランドに入る前にsetTimeoutが実行されてしまったので、1000msにしてみたがこの辺りはタイミング次第なので調整が必要かもしれない。 ### 対策2の問題点 対策1のhack2が使えないということになる。対策1のhack2ではアプリケーション未インストール時に表示されるAlertをsetTimeoutを25msにすることで即座にキャンセルしていたが、この対策を使うと1000msなのでその間1秒間ユーザにAlertを見せてしまうことになる。 ## まとめ 今のところこれ以外のworkaroundを知らないため全ての解決方法がわからない。対策1と対策2の問題を比較した場合、インストール前のフロー問題とインストール後のフローの問題のどちらに優先度を置くかによって判断が変わる。 個人的にはインストール前にちょっとAlertが表示されることよりも、インストール後に毎回ストアに遷移してしまうほうが問題だと感じているので対策2を選ぶのがよいと考えている。 ## 参考にしたもの * Stack Overflow - [Is it possible to register a http+domain-based URL Scheme for iPhone apps, like YouTube and Maps?](http://stackoverflow.com/questions/1108693/is-it-possible-to-register-a-httpdomain-based-url-scheme-for-iphone-apps-like/1109200#1109200) * Stack Overflow - [iPhone Safari: check if URL scheme is supported in javascript?](http://stackoverflow.com/questions/627916/iphone-safari-check-if-url-scheme-is-supported-in-javascript) * Safari Developer Guide - [Safari Web Content Guide: Handling Events](http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html) |
|
| 548位 |
|
|||
|
17:37:54 |
|
|
##はじめに パッケージ管理システムを混在させてはいけない。 MacにはHomebrew以外に、MacPorts、Finkといったパッケージ管理システムがある。 複数のパッケージ管理システムを混在させると、バージョン管理等が大変になるので止めるべき。 Homebrewを使うと、最初から入っているrubyやsvnとは違うディレクトリに新しくインストールされる。 コマンドの参照パスをそちらを優先するように設定することで、既存のソフトウェアに影響せず利用できる。 不要になればbrew uninstallで簡単に取り除ける。 ## Homebrewのインストール [Homebrew公式](http://brew.sh/) Homebrewへのパスをbash_profileに追記する。 ``` $ vim ~/.bash_profile export PATH=/usr/local:$PATH ``` Homebrewでインストールされるソフトウェアの保存先が無ければ作成する。 ``` $ sudo mkdir /usr/local/ ``` Homebrewをインストールする。 ``` $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" ``` 先ほど編集したbash_profileを再読み込み。 ``` $ source ~/.bash_profile ``` インストールできているか確認 ``` $ brew -v Homebrew 0.9.5 ``` |
|
| 549位 |
|
|||
|
21:16:24 |
(Aratana Inc. 所属) |
|
JSONファイル読み込みのおさらいメモ。
下記ファイルを読み込みます。 ```js:data.json [ {"id":1,"name":"田中"}, {"id":2,"name":"鈴木"}, {"id":3,"name":"佐藤"} ] ``` これを、id を id属性に。name を li タグ内に表示します。 ```html: <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script> <script> $(function() { $.getJSON("data.json" , function(data) { var ulObj = $("#demo"), len = data.length; for(var i = 0; i < len; i++) { ulObj.append($("<li>").attr({"id":data[i].id}).text(data[i].name)); } }); }); </script> </head> <body> <ul id="demo"></ul> </body> </html> ``` |
|
| 550位 |
|
|||
|
00:20:11 |
|
|
ローカルで動かしていたSinatraをさくらvpsで動かそうと思ったら、アクセスしても繋がらない問題に出くわしました(;´д`) 検索しても意外と情報が少なかったのでメモ残しときます。 ##症状 Sinatraを使ったコードを実行すると、デフォルトだと4567番ポートで立ち上がるのですが、アクセスしても繋がらない!! ```bash: $ bundle exec ruby hoge.rb [2013-05-17 23:58:37] INFO WEBrick 1.3.1 [2013-05-17 23:58:37] INFO ruby 1.9.3 (2013-02-06) [x86_64-linux] == Sinatra/1.4.2 has taken the stage on 4567 for development with backup from WEBrick [2013-05-17 23:58:37] INFO WEBrick::HTTPServer#start: pid=10077 port=4567 ``` 初めはiptablesの設定が悪いのかとか、別のところで何かブロックされてるんじゃないかとか悩んでいたのですが、iptablesの設定をオフにしても繋がらなかったので原因は別にある事が判明。netstatをしたら、localhostからのアクセスしかLISTENしていない事が分かりました。 ```bash: $ netstat -an # 必要部分だけ抽出 tcp 0 0 127.0.0.1:4567 0.0.0.0:* LISTEN ``` という事は、SinatraのListenAddressの設定がおかしい!! ##原因 以前Sinatraを利用した時は、特に何もしなくても外部から繋がっていたのでおかしいなと思いながら調べていると以下の記述を発見! >**:bind - server hostname or IP address** String specifying the hostname or IP address of the interface to listen on when the :run setting is enabled. The default value in the development environment is 'localhost' which means the server is only available from the local machine. In other environments the default is '0.0.0.0', which causes the server to listen on all available interfaces. 引用元:[Sinatra: Configuring Settings](http://www.sinatrarb.com/configuration.html) **なんとdevelopment環境だと、localhostからのアクセスしか受け付けないのがデフォルトらしい!!** でも今まではそんな事なかったし、日本語訳の資料にはこんな記述なかったので、最近の変更なのかなーとSinatraの変更履歴を見てみたところ >= 1.4.0 / 2013-03-15 ~ 省略 ~ * Default to only serving localhost in development mode. (Postmodern) 引用元:[sinatra/CHANGES at 1.4.2 · sinatra/sinatra · GitHub](https://github.com/sinatra/sinatra/blob/1.4.2/CHANGES) あったよー!ver.1.4.0からこのような仕様になったんですね。3月の変更だから結構最近。 ##対応方法 ListenAddressを設定するオプションがあるので、そちらを使えばOKです。(0.0.0.0 は全てのアドレスを指す) ###実行オプションを使う ```bash: $ bundle exec ruby hoge.rb -o 0.0.0.0 ``` ###rubyのコードの中で設定する ```ruby: set :bind, '0.0.0.0' ``` ## environment を変えてもいける ドキュメントに書かれている通り、ローカルからしか繋がらないのは 'development' の時だけなので、environmentに、'production' や 'test' を設定した時は何もしなくても外部から繋がります。 ###実行オプションを使う ```bash: $ bundle exec ruby hoge.rb -e production ``` ###rubyのコードの中で設定する ```ruby: set :environment, :production ``` |
|
| 551位 |
|
|||
|
18:05:37 |
(Coach United Inc. 所属) |
|
これから AngularJS でアプリケーションを作るぜ!という方向けの、環境構築メモです。 - モダンな開発をするために総合開発環境として Yeoman を利用 - おすすめとされる AngularJS アプリケーション構成も適用 - 最低限のUIライブラリはいるだろうということで UI Bootstrap(AngularJS用のBootstrap)を導入 - IDEとして [WebStorm](http://www.jetbrains.com/webstorm/) を利用 - ただこれは私がたまたま使ってるだけなので、使わなくてもOK ちなみに私が試した環境は、つぎのとおりです。 - Mac OS X 10.9.1 - node v0.10.25 - npm 1.3.24 ## YeomanとAngularJS雛形ジェネレータをインストール もしお使いの環境にまだ導入されてなければ npm でインストールします。 ``` $ npm install -g yo grunt-cli bower $ npm install -g generator-angular ``` ## AngularJSの雛形を生成 WebStormの新規プロジェクトを適当な名前で開き、ターミナルで(なければ普通に Mac のターミナルで適当なディレクトリを作成し配下へ移動して)、   次のようにコマンドを入力します。 ``` $ yo angular --minsafe [今回作るアプリケーション名] ``` ※ この `[今回作るアプリケーション名]App` が AnugularJS上のアプリケーション名となります(例:`Test`を指定した場合は`TestApp`)。後から変える場合は `index.html` `app.js` `main.js` を書き換えてください。 この時、インストールするモジュールを YES/NO で聞かれます。BootstrapとAngular系モジュールを選択して(つまり Sass だけ除いて)て導入します。まあ必要あれば Sass を導入しても構いません。  ※もし NO と答えても、`$ bower install bootstrap --save` のように後から Bower で簡単に追加インストールできます。 つづいて UI Bootstrap をインストールします。ターミナルで、 ``` $ bower install angular-bootstrap --save ``` `--save` を指定することにより `bower.json` でパッケージ管理されるようになります。パッケージ管理しておかないと、後で全モジュールを入れ直す場合やproduction用コード(後で説明)を作る場合に困るので、ちゃんとパッケージ管理しておきましょう(ちなみに、もしproduction用コードに含めないパッケージの場合は `--save-dev` を指定します)。 また、Bower で管理しているパッケージは `bower.json` ファイルで確認できます。  次に `app.js` を開き、利用するモジュールに `ui.bootstrap` を追加します。  `index.html` を開き、下記の1行を追加します(`UI Bootstrap` の JSファイルを読み込み)。 ``` <script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script> ``` --- **[2014/3/15追記]** `jquery.js` と `bootstrap.js` をインクルードしている2行を削除してください。Bootstrap と UI Bootstrap が競合するため(jQueryは bootstrap.js が不要ならこちらもいらないのでついでに削除)。 --- **[2014/4/8追記]** 手動で `index.html` を修正しなくても、次のようにコマンド叩くと自動で `index.html` を修正してくれるようです。 `$ grunt bower-install` --- それ以外も `lang="ja"` を追加したり、もろもろ自分ルール^^; と違うところを直します。 結果は次のとおり(面倒であればコピペして使ってもらって構いません。ただ`ng-app="[アプリケーション名]App"`は`app.js``main.js`に記載のものと合わせてください)。 ```html:index.html <!doctype html> <!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]--> <!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]--> <!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]--> <!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--> <head lang="ja"> <meta http-equiv="Content-Type" content="text/html" charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title></title> <meta name="description" content=""> <!-- Place favicon.ico and apple-touch-icon.png in the root directory --> <!-- build:css styles/vendor.css --> <!-- bower:css --> <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"> <!-- endbower --> <!-- endbuild --> <!-- build:css({.tmp,app}) styles/main.css --> <link rel="stylesheet" href="styles/main.css"> <!-- endbuild --> </head> <body ng-app="testApp"> <!--[if lt IE 7]> <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p> <![endif]--> <!-- Add your site or application content here --> <div class="container" ng-view=""></div> <!-- Google Analytics: change UA-XXXXX-X to be your site's ID --> <!--[if lt IE 9]> <script src="bower_components/es5-shim/es5-shim.js"></script> <script src="bower_components/json3/lib/json3.min.js"></script> <![endif]--> <!-- build:js scripts/vendor.js --> <!-- bower:js --> <script src="bower_components/angular/angular.js"></script> <script src="bower_components/angular-resource/angular-resource.js"></script> <script src="bower_components/angular-cookies/angular-cookies.js"></script> <script src="bower_components/angular-sanitize/angular-sanitize.js"></script> <script src="bower_components/angular-route/angular-route.js"></script> <script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script> <!-- endbower --> <!-- endbuild --> <!-- build:js({.tmp,app}) scripts/scripts.js --> <script src="scripts/app.js"></script> <script src="scripts/controllers/main.js"></script> <!-- endbuild --> </body> </html> ``` これで完了です! 試しに WebStorm のプレビュー機能で `index.html` を開いてみます。 レスポンシブにも対応していますね^^  もし WebStorm を利用していない場合でも、Grunt で確認できます。Macのターミナルで、 ``` $ cd Test $ grunt serve ``` そうすると標準のブラウザ?が次のように開きます。  いや〜、Grunt便利ですね。 プログラム構成的には AngularJS の推奨する構成になっており、Yeomanを利用した効率的な開発環境&Bootstrapで最低限の見た目はカバーできている環境の出来上がりです。 (私的には WebStorm の導入もおすすめです。有償ですが。) ## 環境ができたら TOPページのVIEWは `main.html` 、ロジックは `main.js` ですので(デフォルトでは上記プレビューの通り Yeoman のサンプルが記載されています)、これらに自分のアプリケーションを書いていけばOKです。 (`index.html` および `app.js` はアプリケーション全体の記載をするものなのでそうはいじらないはずです。それがAngulsrJSの思想みたい。) `index.html` は一度ブラウザに読み込まれれば再度サーバに取得することはなく、このブラウザ側の `index.html` を起点とし、必要に応じて `main.html` 等をロードする形式になります。 (`index.html`はディスパッチみたいな役割をし、ここ自体にはHTMLタグを書くことはない。) JSファイルとHTMLファイルを追加する場合は、  - ロジック(モデルとコントローラ)ファイルは app/scripts/controllers/ に配置します。 - VIEW(HTML)ファイルは app/views/ に配置します。 ロジックとVIEWを増やす場合(ページを増やす場合)は、`app.js` で新たに URL と Controller、View の対応づけを設定し、`index.html` で追加したJSファイルをインクルードします。 DirectiveやService用の構成を追加する場合は、次のように出来るようです。 [AngularJS Ninja](http://angularjsninja.com/blog/2013/08/31/yeoman-generator-for-angularjs/) ## production用(本番用)のファイルを作成する場合 まず、Bowerのコンポーネントを本番用にします。 ``` $ bower install --production ``` `--production` を指定することで、`bower.json` 中のDEV用パッケージが除かれ `bower_components` ディレクトリが展開されます。 次に、Gruntでビルドします。 ``` $ grunt build ``` `dist` ディレクトリに本番配信用のファイル(minifyされてる?)が展開されます。 ## 参考にしたサイト - [Yeomanを使ってAngularJSアプリのひな形をつくってみる](http://dev.classmethod.jp/client-side/yo-angular/) - [AngularJS Startup Advent Calendar 2013の5日目](http://blog.kinzal.net/post/68979324453/yeoman-angularjs-qiita-advend-calendar) - [Bower入門(基礎編)](http://yosuke-furukawa.hatenablog.com/entry/2013/06/01/173308) - [AngularJSとTwitter Bootstrapを組み合わせて使う](http://qiita.com/zoetro/items/eb3a44e4baa8c367b4aa) |
|
| 552位 |
|
|||
|
15:03:17 |
|
|
開発環境によっては、セキュリティー()を高めるために、利用可能なプロトコルがしぼられている場合があります。例えば、http/httpsは使えるけどgitプロトコルは使えないといった状況です。
ただし、イマドキのライブラリ管理系のツールを使うと、gitプロトコルを使ってgithubからcloneしたがるものが多く、当然ながらエラーになって困ります。 そんなとき、gitプロトコルでリクエストをする場合に、自動的にhttpsとして実行するように変更すると幸せになれます。 ``` git config --global url."https://".insteadOf git:// ``` JavaScriptのbowerやVimのNeoBundleとかで動作確認済みです。 お試しあれ。 参考 (http://stackoverflow.com/questions/4891527/git-protocol-blocked-by-company-how-can-i-get-around-that/10729634#10729634) |
|
| 553位 |
|
|||
|
22:11:04 |
(wow.inc 所属) |
|
blocksには数多くの落とし穴があります。
しかしそれを乗り越えることができれば、非常にスマートにかける状況はそれなりに増えると思います。 ※注意:ここではARC環境での話です。一部MRCと状況が異なる場合があるかもしれません。 <blocksはObjective-cのオブジェクトをstrong参照でキャプチャする> ここでいうキャプチャとは、ポインタをコピーしている、ということです。 なんで?と思われる方も多いと思います。しかし理由ははっきりしていています。 例えば、dispatch_afterで処理を遅延することを考えてみましょう。 ``` NSArray *sameArray = ...; int64_t delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ id obj = sameArray[2]; }); ``` ここでdispatch_afterに渡されたblocksは2秒後に遅延実行されます。 さて、sameArrayはARC管理オブジェクトですね。ということは、何もしなければ、2秒後には解放されてしまっているオブジェクトなんですね。しかし実際にはblocksがキャプチャ、つまりstrong参照を保持しているために、BAD_ACCESSにはならないんです。 blocksのキャプチャというのは非常に理にかなっているわけです。 さて、ここでさらに注意しなければならないことがあります。 <blocksがキャプチャするのはObjective-cのオブジェクトだけ> 例えばCGImageRefなんてどうでしょうか? そう、もちろんキャプチャしません。 つまり、先ほどのように遅延実行で、CGImageRefは参照こそできるが、strongではもてないんです。ということは簡単にBAD_ACCESSがおこります。つまり、自分でCGImageRetainしないといけないんですね。これは本当に注意が必要です。 <blocksの中でメンバ変数を使用すると、selfをstrongキャプチャする> これも恐ろしいです。仮にblocksをメンバにもつクラスがあり、そのblocksがselfをキャプチャすると、簡単に循環参照でメモリリークです。 ``` @interface Hoge : NSObject @property (nonatomic, copy) void (^blocks)(); @end @implementation Hoge { NSNumber *value; } - (id)init { if(self = [super init]) { value = @10; self.blocks = ^{ [value description]; //循環参照! }; } return self; } - (void)dealloc { printf("dealloc"); } @end ``` これをなんとかするには __block __weak Hoge *_self = self; などと一時変数としてweak参照の変数を使ったりすることで解決できますが、冗長でスマートではなく、私はあまり好きではありません。 <blocksの寿命は記述したスコープのみ> 厳密にはblocksによってはスタック領域に確保されるのではないblocksもあるのですが、 このように考えていた方が統一的で安全です。 ``` void (^blocks)(); { blocks = ^{};//寿命はこのスコープのみ! } //ここでは既に死んでいる blocks(); ``` このようなことをする場合は次のようにするのを推奨します ``` void (^blocks)(); { blocks = [^{} copy];//スタックからヒープに移動 } //ここではヒープにあるblocksを参照、またARCで自動破棄 blocks(); ``` copyすると、blocksはスタックメモリからヒープメモリへと移動できるので、安全に後から呼べるのです。 関数の戻り値としてblocksを返す場合(関数言語でいうところの高階関数)も同様に、 copyしておくことを推奨します。 また、クラスプロパティもcopy属性推奨です。 つまり、「スコープを出た後でも呼び出したいblocksはコピーする」というのが安全の指標となるでしょう。dispatch_afterなんかは、内部で受けとったblocksをコピーしているので、呼び出し側では特に気にせずつかえるのです。 <blocksはObjective-cオブジェクトである> 先の例からも分かる通りです。しかしまあARC下では参照が自然と消滅するため、それほど意識しなくても良いかもしれません(循環参照以外では)。 <blocksはキャプチャした変数をconst コピーする> ``` int a = 0; void (^blocks)() = ^{ a = 3; //constなコピーなのでだめ! }; ``` こういう場合に__blockを使います ``` __block int a = 0; void (^blocks)() = ^{ a = 3; //ok! }; ``` また、次のような使い方すら可能です。 ``` typedef int (^Hoge)(); Hoge function() { __block int n = 10; return [^{ return n--; } copy]; } int main(int argc, const char * argv[]) { @autoreleasepool { Hoge hoge = function(); for(int i = 0 ; i < 5 ; ++i) { printf("%d, ", hoge()); //10, 9, 8, 7, 6, } } return 0; } ``` __blockの変数はcopyされるとスタックからヒープに本体と一緒に移動できるんです。 じゃあ次の例ではどうでしょうか? ``` typedef int (^Hoge)(); Hoge function() { __block int n = 10; return [^{ return n--; } copy]; } int main(int argc, const char * argv[]) { @autoreleasepool { Hoge hoge1 = function(); Hoge hoge2 = [hoge1 copy]; for(int i = 0 ; i < 5 ; ++i) { printf("%d, ", hoge1()); } for(int i = 0 ; i < 5 ; ++i) { printf("%d, ", hoge2()); } //10, 9, 8, 7, 6, 5, 4, 3, 2, 1, } return 0; } ``` そう、__blockのついた変数はコピーしても共有されるんですね。面白いです。 ここまで読むと、blocksは主に循環参照で非常に欠陥が多く、弱点もおおいのです。 それも仕方の無いことで、ガベージコレクションのあるC#などは非常にうまく言語に匿名関数がなじんでいます。ネイティブ言語の宿命といえばそうなのかもしれませんね。 しかしながら ・コールバックとして ・visitorパターンの代用として ・strategyパターンの代用として ・関数型プログラミングとして 等非常に強力な場面も多いです。注意深く使っていきたいですね。 |
|
| 554位 |
|
|||
|
10:38:38 |
|
|
Capistranoを使うととデプロイが簡単だという話を良く聞くけれど、 Capistranoを使うことが難しかったのでメモ。 ##Getting Started ###インストール ``` $ gem install capistrano ``` ###実行する 設定ファイルを作成するコマンド ``` $ capify . ``` を実行すると、 ``` [working_dir]/Capfile [working_dir]/config [working_dir]/config/deploy.rb ``` が作成される ###タスクの基本 config/deploy.rbを空にして以下を記述する ```ruby:config/deploy.rb # localhost の ssh を許可しておくこと task :list, :hosts => "localhost" do run "ls" end ``` terminalで以下を実行する ``` $ cap list #=> localhostのファイル一覧が出力される ``` パスワードを問われるのはSSHで接続しているため ###:hostをまとめる デフォルトでは、全てのサーバー・全てのロールが実行される。 ロールを設定するのは局所的な動作を期待するため。 ```ruby:config/deploy.rb # :hostsをまとめることができる role :local, "localhost" task :list do run "ls" end ``` ###複数サーバーを設定する ```ruby:config/deploy.rb # 複数のサーバーに実行することができる role :local, "localhost", "anotherhost" task :list do run "ls" end ``` ###複数ロールを設定する ```ruby:config/deploy.rb # 複数ロールを持つことができる role :local, "localhost" role :another, "anotherhost" task :list, :roles => :local do run "ls" end task :list_another, :roles => :another do run "ls" end ``` ###タスクの説明 ```ruby:config/deploy.rb role :local, "localhost" # コマンド一覧に表示される説明 desc "Echo the server's host name" task :echo_hostname do run "echo `hostname`" end ``` ``` $ cap -vT #=> コマンド一覧 ... cap deploy:web:enable # Makes the application web-accessible again. cap echo_hostname # Echo the server's host name cap invoke # Invoke a single command on the remote servers. ... ``` ###コマンドを直接実行 ``` cap invoke COMMAND="echo 'Hello World'" #=> Hello, Wrold ``` ##デプロイ設定 ###アプリケーション名を設定 ```ruby:config/deploy.rb set :application => "myapplication" ``` ###リポジトリの設定 ```ruby:config/deploy.rb # リポジトリの場所を指定する set :repository, "ssh://localhost/var/git/myapplication.git" # リポジトリの種類を指定する set :scm, :git ``` ###デプロイ先の指定 ```ruby:config/deploy.rb set :deploy_to, "/var/www/rails/" role :app, "localhost", # アプリケーションサーバー role :web, "localhost", # Webサーバー role :db, "localhost", :primary => true # DBサーバー ``` DBはmaster/slaveがあればprimaryでコントロールできる 全て同じ場合は次のような書き方もできる。 ```ruby:config/deploy.rb server "localhost", :app, :web, :db, :primary => true ``` ###実行ユーザーの設定 ```ruby:config/deploy.rb set :user, "foo" ``` ###リポジトリユーザーの設定 ```ruby:config/deploy.rb set :scm_username, "foo" ``` ###スーパーユーザー権限で実行の設定 ```ruby:config/deploy.rb set :use_sudo, false #=> defaultではtrue ``` ##deployコマンド ``` cap deploy # Deploys your project. cap deploy:check # Test deployment dependencies. cap deploy:cleanup # Clean up old releases. cap deploy:cold # Deploys and starts a `cold' application. cap deploy:create_symlink # Updates the symlink to the most recently deployed... cap deploy:migrate # Run the migrate rake task. cap deploy:migrations # Deploy and run pending migrations. cap deploy:pending # Displays the commits since your last deploy. cap deploy:pending:diff # Displays the `diff' since your last deploy. cap deploy:restart # Blank task exists as a hook into which to install... cap deploy:rollback # Rolls back to a previous version and restarts. cap deploy:rollback:code # Rolls back to the previously deployed version. cap deploy:setup # Prepares one or more servers for deployment. cap deploy:start # Blank task exists as a hook into which to install... cap deploy:stop # Blank task exists as a hook into which to install... cap deploy:symlink # Deprecated API. cap deploy:update # Copies your project and updates the symlink. cap deploy:update_code # Copies your project to the remote servers. cap deploy:upload # Copy files to the currently deployed version. cap deploy:web:disable # Present a maintenance page to visitors. cap deploy:web:enable # Makes the application web-accessible again. cap invoke # Invoke a single command on the remote servers. cap shell # Begin an interactive Capistrano session. ``` ##Namespace Capistrano 2.x以降、全てのタスクはネームスペース化されており、モジュールとして分離できます。ここに処理をはさみ込むこともできます。 ```ruby:config/deploy.rb # デプロイ時に rake resque:work を動かすgodスクリプトを呼ぶ namespace :deploy do task :start, :roles => :app do run "god start resque-worker" end task :restart, :roles => :app do run "god restart resque-worker" end task :stop, :roles => :app do run "god stop resque-worker" end end ``` |
|
| 555位 |
|
|||
|
23:18:38 |
|
|
日本語を含むZIPファイルを圧縮したのと違うのOSで解凍する際、文字化けが発生することがあります。
以下調べた対処法を書いていきます。 Windowsで圧縮したZIPをMac、Linuxで解凍する場合、逆にMac等で圧縮したZIPをWindowsで解凍する場合の両方で文字化けは起こりえます。 ## 原因 Mac、Linuxではファイルを圧縮する際ファイル名をutf-8でエンコードするのに対し、Windowsではファイル名をCP932(Shift_JIS)でエンコードするのが原因。 [ZIP (ファイルフォーマット) - Wikipedia](http://ja.wikipedia.org/wiki/ZIP_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88)#Windows_.E5.9C.A7.E7.B8.AE.E3.83.95.E3.82.A9.E3.83.AB.E3.83.80) ## 対処法 ### Mac,Linux→Windows Macで作られたファイルをWindowsで開く場合 Windows標準の解凍機能を用いる場合、修正プログラム( http://support.microsoft.com/kb/2704299/ja )を使うことでWindows 7 または Windows Server 2008 R2では文字化けせずに解凍できるようになります。 Windows 8からは何もせずとも解凍できるようになっているようです。その他の環境の場合、[Explzh](http://www.ponsoftware.com/)等のフリーソフトを使うことをオススメします。 ### Windows→Mac,Linux Windowsで作られたファイルをMac、Linuxで開く場合 Mac標準のファイルアーカイバー、Ubuntuの日本語Remixのunzipを使っている場合、文字コードを自動で判別してくれるため対策の必要はありません。 それ以外の環境の場合、(Linux想定)以下の様な対処法があります。 * patchが当たった修正版unzipを使う * unarを使う * 化けたファイル名をconvmvで修正する ### unzip Ubuntuで化けるときはとりあえず http://www.ubuntulinux.jp/japanese の *Japanese Teamのパッケージレポジトリを追加する* に書いてあることを実行するのをオススメします。 unzipが修正され、ファイルアーカイバ等でShift_JISでファイル名がエンコードされたZIPファイルを開けるようになります。 他のディストリビューションだとソース落としてパッチを当ててビルドすることで修正版unzipを導入できますがそんなこと出来る人はこんなところきっと見ないので割愛。 ### [unar](http://unarchiver.c3.cx/commandline) 文字コード判別機能付き解凍コマンド WindowsのZIPファイルを解凍するだけでなく、色々な形式の圧縮ファイルを解凍できるコマンドです。 解凍するだけならunzipもtar xfももう要りません。 インストール方法は省略 ### convmv ファイル名をiconvを使ってOSのロケールに修正するコマンド 他の原因でファイル名が化けた際や、[Windows用にShift_JISで圧縮したファイルを作りたい場合](http://www.ftnk.jp/~fumi/cl/2007-06-29-2.html)にも 使えます。 使い方はこんな感じ ``` convmv -f utf8 -t shift_jis *.txt --notest ``` |
|
| 556位 |
|
|||
|
19:19:09 |
|
|
## config って?
開発環境ごとに定数を YAML ファイルで管理できるプラグインのこと ##公式リポジトリ https://github.com/railsconfig/config ##configをインストール Gemfile に config を記述 ````ruby:Gemfile gem 'config' ``` vendor/bundle 以下に config をインストール ``` $ bundle install --path vendor/bundle $ bundle exec rails g config:install create config/initializers/rails_config.rb create config/settings.yml create config/settings.local.yml create config/settings create config/settings/development.yml create config/settings/production.yml create config/settings/test.yml append .gitignore ``` gitignoreには自動で下記の内容が追加される ```.gitignore config/settings.local.yml config/settings/*.local.yml config/environments/*.local.yml ``` 使用したい環境に応じて編集するファイルを変更 | YAML(環境) |ファイルの場所 | |:-----------|:------------| | 全ての環境のYAML | config/settings.yml | | ローカル環境専用のYAML | config/settings.local.yml | | 開発環境専用のYAML | config/settings/development.yml | | テスト環境専用のYAML | config/settings/test.yml | | 本番環境専用のYAML | config/settings/production.yml | ###記述例 ```config/settings.yml twitter: follow_target_name: 'mossmoss' text: 'mossmossmossmossmoss' url : 'http://mossmossmossmossmoss.com/' ``` ###参照例 ``` Settings.twitter[:text] Settings.twitter['text'] Settings[:twitter][:text] ``` [settingslogic](https://github.com/binarylogic/settingslogic "settingslogic")もいいけど、わかりやすいしそこそこ大きな開発の時に使えそうなrails_configでした。 |
|
| 557位 |
|
|||
|
14:35:22 |
(D-ZERO Co.,Ltd. 所属) |
|
## 複数の非同期通信をする場合
適当に作るとこんな感じ。※変数宣言などアンチパターンなのはご愛嬌 ```js: var request = [ { url: 'hoge00.json', params: { hoge: 123, huga: 456 } }, { url: 'hoge01.json', params: { hoge: 246, huga: 912 } }, { url: 'hoge02.json', params: { hoge: 369, huga: 1368 } }, { url: 'hoge03.json', params: { hoge: 492, huga: 1824 } }, { url: 'hoge04.json', params: { hoge: 615, huga: 2280 } } ]; var results = []; var doneCount = 0; for (var i = 0; i <= request.length; i++) { $.ajax({ url: request[i].url, data: request[i].params, type: 'POST', dataType: 'json', success: function (responce) { results.push(responce); doneCount++; if (doneCount === resqest.length) { allDone(); } } }); } function allDone () { results; // => 結果の順番は通信が成功した順番 }; ``` 配列`request`に格納したURLの順番と結果の順番は同じとは限らない。 初心者がよくハマるポイントだけど、ほぼ一斉に非同期通信を行い($.ajaxメソッドを実行し)、通信が成功した順番に`success`コールバックが実行されるので、その成功した結果の順番に`results`がプッシュされるからである。 ## こんな感じにできたら理想的 ```js: var request = [ { url: 'hoge00.json', params: { hoge: 123, huga: 456 } }, { url: 'hoge01.json', params: { hoge: 246, huga: 912 } }, { url: 'hoge02.json', params: { hoge: 369, huga: 1368 } }, { url: 'hoge03.json', params: { hoge: 492, huga: 1824 } }, { url: 'hoge04.json', params: { hoge: 615, huga: 2280 } } ]; var jqXHR = $.multiAjax({ request: request, type: 'POST', dataType: 'json' }); jqXHR.done(function (responce) { responce; // => [00の結果, 01の結果, 02の結果, 03の結果, 04の結果] }); ``` ## 上記を実現するには ### 直列処理ならば... リクエストをひとつひとつ処理していく方法は、結構いろんなところで紹介されている。 - [jQueryでajax:非同期通信時、実行順番を保障する方法](http://kinopyo.com/blog/jquery-ajax-get-order-guranteed-async-transfer/) - [jQuery.ajax()の非同期通信で実行順序を保証する方法](http://www.koikikukan.com/archives/2012/10/11-000300.php) 基本的にはひとつずつAjax通信をして、そのコールバックで次のAjax通信を行なっている。もしくは`async`を`false`にして、非同期通信でなく完全な同期通信をしてしまう方法。これなら結果の順番は絶対変わらない。 でも、やりたいのはそうじゃなくて、通信を同時に一気に行いたい。通信時間短縮のためには『並列』というのが大事。それに加えて結果の順番も保証してほしい。 ### $.whenを使えば意外と簡単に実現できる。 ```js: var request = [ { url: 'hoge00.json', params: { hoge: 123, huga: 456 } }, { url: 'hoge01.json', params: { hoge: 246, huga: 912 } }, { url: 'hoge02.json', params: { hoge: 369, huga: 1368 } }, { url: 'hoge03.json', params: { hoge: 492, huga: 1824 } }, { url: 'hoge04.json', params: { hoge: 615, huga: 2280 } } ]; var jqXHRList = []; for (var i = 0; i <= request.length; i++) { jqXHRList.push($.ajax({ // $.ajaxの戻り値を配列に格納 url: request[i].url, data: request[i].params, type: 'POST', dataType: 'json' })); } // $.when関数を利用する // $.whenは可変長引数を取るので、apllyメソッドを利用して配列で渡せるようにする // $.whenのコンテキスト(applyの第一引数)はjQueryである必要があるので $ を渡す $.when.apply($, jqXHRList).done(function () { var json = []; var statuses = []; var jqXHRResultList = []; // 結果は仮引数に可変長で入る **順番は保証されている** // 取り出すには arguments から取り出す // さらにそれぞれには [data, textStatus, jqXHR] の配列になっている for (var i = 0; i < arguments.length; i++) { var result = arguments[i]; json.push(result[0]); statuses.push(result[1]); jqXHRResultList.push(result[3]); } json;// => リクエストの配列と同じ順番で結果を参照できる }); ``` こんなロジックで、先に記した理想に沿ったプラグインにするなど、いろいろ作ればいい。 ## 要するに`$.when.apply`便利 Ajaxに限らずアニメーションなども、このテクニックを使えば並列の処理が行える。 読み込む外部ファイルやアニメーション要素の数が動的に変化する場合、`$.when`のまま使うと可変長引数で渡すときに戸惑うが、`apply`を使えば第一引数にさえ気をつければ簡単だ。ついでに結果は順番が保証されている。 ## 仕様上の注意 配列の中身が一個だと結果が配列でなくなるので注意 ```js: $.when.apply($, [req, req, req]).done(function () { arguments; // => [[data, status, jqXHR], [data, status, jqXHR], [data, status, jqXHR]] }); $.when.apply($, [req]).done(function () { arguments; // => [data, status, jqXHR] }); ``` なんともありがた迷惑な仕様…。配列の個数が1以下になる可能性がある場合は適切な処置が必要。 * * * `$.when.apply`を積極的につかってみよう! |
|
| 558位 |
|
|||
|
13:09:04 |
|
|
git におけるリモートリポジトリの操作は、「リモート上の何かを直接操作する」のではなくて「ローカルの変更をリモートに送りこむ」と考えれば理解しやすいのかもしれない。
リモートのタグを削除する ------------------------ リモート origin のタグ TAGNAME を削除するには、次のようにする。 git tag -d TAGNAME git push origin :TAGNAME ローカルでタグを削除してから、リモートに「空のタグを送りこむ」という感じ。 リモートのブランチを削除する ---------------------------- リモート origin のブランチ BRANCHNAME を削除するには、次のようにする。 git branch -d BRANCHNAME git push origin :BRANCHNAME ローカルでブランチを削除してから、リモートに「空のブランチを送りこむ」という感じ。 追記(2012-09-13) ---------------- TAGNAME や BRANCHNAME の前にコロンを付けるかわりに、次のようにすることができるようです。こちらのほうが分かりやすいですね。 git push --delete origin TAGNAME git push --delete origin BRANCHNAME |
|
| 559位 |
|
|||
|
18:39:35 |
|
|
タイトルどおりのことをやった際の実装時メモ。
# Canvasから画像を取得 2通りの方法が見つかった。 1. Base64への変換 1. Blobへの変換 ## Base64への変換 `canvas.toDataURL()`を呼び出すだけで取得可能であるが、 画像サイズに比例した巨大な文字列となり、変換によって元サイズより33%データ量が増える。 ```js var base64= this.canvas.toDataURL('image/png'); ``` 取得したBase64を`<image>`の`src`に指定すれば画像表示され、 `<a>`の`href`に指定すればリンククリックでダウンロードできてこれはこれで便利。 ## Blobへの変換 `canvas.toBlob()`を呼び出すだけで取得可能。 Blob形式で表現すれば、`createObjectURL(blob)`によりURL参照が取得できるので、 画像サイズに依存せずメモリ使用量を抑えられるらしい。(未確認) これは便利と思いきや`canvas.toBlob()`はFirefoxでのみサポートされており、 Firefox以外ではBase64経由でBlobを作成する方法とならざる得ない。 ```js toBlob: function() { var base64 = this.canvas.toDataURL('image/png'); // Base64からバイナリへ変換 var bin = atob(base64.replace(/^.*,/, '')); var buffer = new Uint8Array(bin.length); for (var i = 0; i < bin.length; i++) { buffer[i] = bin.charCodeAt(i); } // Blobを作成 var blob = new Blob([buffer.buffer], { type: type }); return blob; } ``` ちなみにサイズを確認すると、確かにBase64は33%増量していた。 type | size -----|-----: base64.length | 132506 blob.size | 99361 # Serverへ送信する クライント側は Sencha Touch 2.3 を使用。 というのも2.3から XMLHTTPRequest Level 2(XHR2)を完全にサポートしたので、 バイナリデータも送信できるはずなのでその試験を兼ねて。 サーバは勉強も兼ねて`Sinatra`を利用。 1. Base64のまま送信する 2. Binaryに変換して送信する 3. Blobに変換して送信する ## 1. Base64版 * Base64は先頭のminetype(`data:image/png;base64,`の部分)は不要なため削除 * サーバ側はデコードしてファイルに書き出してやれば画像として保存される ### クライアント側 ```js var base64 = this.canvas.toDataURL('image/png'); var request = { url: 'http://localhost:4567/base64', method: 'POST', params: { image: base64.replace(/^.*,/, '') }, success: function (response) { console.log(response.responseText); } }; Ext.Ajax.request(request); ``` ### サーバ側 ```rb require 'base64' post '/base64' do File.open('imageBase64.png', 'wb') do|f| f.write(Base64.decode64(params['image'])) end "OK" end ``` ## 2. Binary版 * `xhr2: true`を明示的に指定する * `rawData`として渡す(dataだと文字列と認識される) * サーバ側はbodyをそのまま書き出してやると画像として保存される ### クライアント側 ```js toBinary: function(canvas) { var base64 = canvas.toDataURL('image/png'), bin = atob(base64.replace(/^.*,/, '')), buffer = new Uint8Array(bin.length); for (var i = 0; i < bin.length; i++) { buffer[i] = bin.charCodeAt(i); } return buffer; } // Binaryデータを作成 var buf = toBinary(); var request = { url: 'http://localhost:4567/binary', method: 'POST', xhr2: true, rawData: buf.buffer, success: function (response) { console.log(response.responseText); } }; Ext.Ajax.request(request); ``` ### サーバ側 ```rb post '/binary' do File.open('imageBin.png', 'wb') do|f| f.write(request.body.read) end "OK" end ``` ## 3. Blob版 * Blobの場合ファイルとして扱えるので、FormData形式で送信する * `rawData`として渡す * サーバ側はアップロードされたファイルを書き出す ### クライアント側 ```js var buf = toBinary(); // Blobを作成 var blob = new Blob([buf.buffer], { type: 'image/png' }); // FormDataとして送信 var fd = new FormData(); fd.append("image", blob); var request = { url: 'http://localhost:4567/blob', method: 'POST', xhr2: true, rawData: fd, success: function (response) { console.log(response.responseText); } }; Ext.Ajax.request(request); ``` #### サーバ側 ```rb post '/blob' do img = params[:image] File.open('imageBlob.png', 'wb') do|f| f.write(img[:tempfile].read) end "OK" end ``` # 参考 * [Canvas Images and Rails](http://rohitrox.github.io/2013/07/19/canvas-images-and-rails/) * [XMLHttpRequest2 に関する新しいヒント](http://www.html5rocks.com/ja/tutorials/file/xhr2/) * [XHR2 Uploads and Downloads - Touch 2.3.0 - Sencha Docs](http://docs.sencha.com/touch/2.3.0/#!/guide/xhr2_guide) |
|
| 560位 |
|
|||
|
00:58:09 |
|
|
[websocket-rails](https://github.com/websocket-rails/websocket-rails)を使ってRailsでWebSocketによるPush通知を実装する話をします。 websocket-railsを使って簡単な[デモ](http://naoty-timeline.herokuapp.com/)を作ってHerokuにアップしました。Twitter Streaming APIで受け取ったデータをWebSocketでリアルタイムにクライアントへ送るものです。ソースコードも[github](https://github.com/naoty/twitter_streaming_sample)にありますので参考にどうぞ。 ## セットアップ ```rb:Gemfile gem "websocket-rails" ``` ```bash $ bundle $ rails g websocket_rails:install ``` 最後のコマンドで設定ファイルを追加し、`application.js`をクライアント側のライブラリを`require`するように変更します。 開発環境ではRack::Lockを無効にしないとエラーになるので以下のようにしておきます。 ```rb:config/environments/development.rb TwitterStreamingSample::Application.configure do config.middleware.delete Rack::Lock end ``` ## 実装 WebSocket接続したクライアントに対して任意のタイミングでメッセージを送りたい場合、channelというものを使うと簡単に実装できます。 ```ruby:streamings_controller.rb def create # ... WebsocketRails[:streaming].trigger "create", tweet head :ok end ``` これで、"streaming"というチャネルに"create"というメッセージをtweetのデータとともに送信します。`WebSocketRails`という定数はどこからでもアクセス可能なので、任意のタイミングでチャネルにメッセージを送ることが可能です。 ```coffee:streamings.js.coffee dispatcher = new WebSocketRails("ws://#{localhost.host}/websocket") channel = dispatcher.subscribe("streaming") channel.bind "create", (tweet) -> # something ``` 次にクライアント側では、まずWebSocket接続を行います。WebSocket接続はwebsocket-railsが追加する`/websocket`というパスに対してリクエストを送ります。 接続が成功するとdispatcherと呼ばれるオブジェクトを返します。このオブジェクトはWebSocketサーバーとやり取りをする中心的なオブジェクトです。`dispatcher#subscribe(チャネル名)`で特定のチャネルを購読するオブジェクトを取得できるので、あとはメッセージを受け取ったときのコールバックを設定するだけです。 ### これだけ これだけでdispatcherでsubscribeしたクライアントすべてに対してRailsからPush通知ができます。 ## nginx 本番環境でnginxとともに運用する場合、nginx側の設定も必要です。nginxでWebSocketのプロキシを行うには1.3.13以降であることが必要です。 ```nginx:nginx.conf location /websocket { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; } ``` "Upgrade"というヘッダーはプロトコルをHTTPからWebSocketに変更するために必要な情報で、[RFC](http://tools.ietf.org/html/rfc6455#section-1.3)で定められています。 ## Heroku 最近、HerokuがWebSocketのサポートを開始しました。ただし、まだβ版での開始なので、デフォルトのままではWebSocketを利用できません。以下のコマンドを実行するとWebSocketの機能がオンになります。 ```bash $ heroku labs:enable websockets ``` --- ### 参考 - [websocket-rails/websocket-rails](https://github.com/websocket-rails/websocket-rails) - [WebSocket proxying](http://nginx.org/en/docs/http/websocket.html) - [Using WebSockets on Heroku with Ruby | Heroku Dev Center](https://devcenter.heroku.com/articles/ruby-websockets) |
|
| 561位 |
|
|||
|
21:54:14 |
(Nepula,Inc. 所属) |
|
一つの関数内で容量の大きなファイルを読み込み加工する処理を連続して行っていたらメモリが足りなくなった。
ARC ではスコープを外れ(て参照カウンタがゼロになっ)たオブジェクトは、すぐに破棄されると思っていたのでしばらくハマった。 ## 問題のソース(ARC使用) ローカルでもWebでも何でもいいけど、ファイルから無視できない程度の容量のデータの読み込みを繰り返す処理。 ```obj-c: - (IBAction)buttonDownWithArc:(id)sender { NSString* path = @".../bigdata.img"; for (int i = 0; i < 10000; i++) { NSData* data = [NSData dataWithContentsOfFile:path]; [NSThread sleepForTimeInterval:0.5]; data = nil; } } ``` これを Instruments でプロファイルするとこうなる。  じゃんじゃんメモリ確保してしまう(汗 ``data`` は ``nil`` にした時(あるいはスコープ外れた時)にメモリ解放されると思っていたのだが。 ちなみにこの関数の処理が終了すると、メモリが解放される。 ## 非ARC でやってみた メモリ管理をマニュアルでやったらどうなるかを確認した。 ```obj-c: - (IBAction)buttonDownNoArc:(id)sender { NSString* path = @".../bigdata.img"; for (int i = 0; i < 10000; i++) { NSData* data = [NSData dataWithContentsOfFile:path]; [NSThread sleepForTimeInterval:0.5]; [data dealloc]; data = nil; } } ``` この時のメモリ確保状況は、期待した通りになった。  メモリ使用量が線形に **増えない** ことが分かる。ARC 利用時にもこうなるようにしたい。 状況は、スコープ内変数の破棄が、関数を抜ける時に遅延されている。 ARC 周りの情報をいろいろ漁っていて、AutoReleasePool との関わりが怪しいと予想した。 * [[iOS5] ARC (Automatic Reference Counting) : Overview - iOS 開発ブログ Natsu's note ](http://blog.natsuapps.com/2011/11/ios5-arc-overview.html) より引用: > ###retain, release, autorelease, deallocはコンパイラのお仕事 > >ARCを利用する場合、コンパイラが > > * retain, release, autoreleaseを挿入してくれる(自分で呼んではいけない。コンパイラエラーになる)。 > * deallocを適切な位置に挿入してくれる(deallocのオーバーライドは可能。ただし[super dealloc]は不可能)。 > >ことになります。 コンパイラにより関数単位で ``@autoreleasepool { }`` が挿入されているとしたら、最初の図のような動きになるはず。ということは、for ループの中に @autorelease を持ってったらどうか? ## ARC + @autoreleasepool 版 for の中の処理を ``@autoreleasepool { }`` で括ってみた。 ```obj-c: - (IBAction)buttonDownWithArcAndAutoRelease:(id)sender { for (int i = 0; i < 100; i++) { @autoreleasepool { NSData* data = [NSData dataWithContentsOfFile:_path]; [NSThread sleepForTimeInterval:0.5]; } } } ``` すると、  やたー、期待する動きになったぞ。 ## まとめ とここまで調べて、しばらく Obj-C さわってなかったので埃をかぶっていた * [エキスパートObjective-Cプログラミング -iOS/OS Xのメモリ管理とマルチスレッド-](http://www.amazon.co.jp/gp/product/4844331094?ie=UTF8&camp=1207&creative=8411&creativeASIN=4844331094&linkCode=shr&tag=oku2008-22) を引っ張り出してきて読んだら、P.25 にまさにその事が書かれていて泣いた。 >とはいえ、autorelease されたオブジェクトが大量に発生した場合、NSAutoReleasePool のオブジェクトが破棄されない限り、それらのオブジェクトは release されないので、メモリ不足に陥る場合があります。典型的な例は、大量の画像をリサイズしながら読み込む場合でしょう。… > >```obj-c: for (int i = 0; i < 画像数; i++) { /* * 画像読み込み処理 * autoreleaseされたオブジェクトが大量発生。 * NSAutoReleasePoolのオブジェクトが破棄されないため * いずれメモリ不足発生! */ } ``` 勉強しなおします。。。 |
|
| 562位 |
|
|||
|
10:28:32 |
(GUNMA GIS GEEK 所属) |
|
[Multi-line strings in JavaScript and Node.js](http://tomasz.janczuk.org/2013/05/multi-line-strings-in-javascript-and.html)
面白い記述のしかたを発見。 ```html <html> <head> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> <script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script> <style> .title{ width:100%; height:240px; line-height:240px; text-align:center; background:#ccc; } </style> <body> <script> //ヒアドキュメント var heredoc = (function () {/* <div class="title"> <h1> <a href="${url}">${title}</a> </h1> </div> */}).toString().match(/(?:\/\*(?:[\s\S]*?)\*\/)/).pop().replace(/^\/\*/, "").replace(/\*\/$/, ""); var object = { url: "http://shimz.me/blog", title: "GUNMA GIS GEEK" } var html = jQuery.tmpl(heredoc, object); $('body').append(html); </script> </body> </htm> ``` |
|
| 563位 |
|
|||
|
14:03:56 |
|
|
今回は、zshの補完を強化する方法を幾つか紹介してみたいと思います。 ##補完候補をハイライトする ```~/.zshrc autoload -U compinit compinit zstyle ':completion:*:default' menu select=2 ``` ##補完候補をオプションやディレクトリで分けて表示する  ```~/.zshrc # 補完関数の表示を強化する zstyle ':completion:*' verbose yes zstyle ':completion:*' completer _expand _complete _match _prefix _approximate _list _history zstyle ':completion:*:messages' format '%F{YELLOW}%d'$DEFAULT zstyle ':completion:*:warnings' format '%F{RED}No matches for:''%F{YELLOW} %d'$DEFAULT zstyle ':completion:*:descriptions' format '%F{YELLOW}completing %B%d%b'$DEFAULT zstyle ':completion:*:options' description 'yes' zstyle ':completion:*:descriptions' format '%F{yellow}Completing %B%d%b%f'$DEFAULT # マッチ種別を別々に表示 zstyle ':completion:*' group-name '' ``` [zshの補完を強化する方法](http://qiita.com/items/21f569f3853beb0f3409)を見てください。bash 限定だった`git-completion`が進化した[zsh-completions](https://github.com/zsh-users/zsh-completions)などを使うことで、簡単に補完を強化することができます。 #オプションを補完するときの表示を分かりやすくする  ```~/.zshrc # セパレータを設定する zstyle ':completion:*' list-separator '-->' zstyle ':completion:*:manuals' separate-sections true ``` #色をつける 色をつけるには、[LS_COLORSを設定しよう](http://qiita.com/items/84fa4e051c3325098be3)で紹介されている方法が一番効果的だと思います。しかし、ツールを使用したくない場合は、以下のように個別に設定して行きましょう。  ```~/.zshrc # 名前で色を付けるようにする autoload colors colors # LS_COLORSを設定しておく export LS_COLORS='di=34:ln=35:so=32:pi=33:ex=31:bd=46;34:cd=43;34:su=41;30:sg=46;30:tw=42;30:ow=43;30' # ファイル補完候補に色を付ける zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS} ``` ちなみに、コマンドに色をつけるには、[ zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting)を使います。 #git-flowコマンドの補完 ``` mkdir ~/zsh/ && cd ~/zsh/ && git clone https://github.com/bobthecow/git-flow-completion.git && echo "source ~/zsh/.git-flow-completion.zsh" >> ~/.zshrc && exec $SHELL ``` # #記憶を補完する 記憶を補完するために以下を設定しておくと便利です。 ```~/.zshrc # cheat-sheet cheat-sheet () { zle -M "`cat ~/zsh/cheat-sheet.conf`" } zle -N cheat-sheet bindkey "^[^h" cheat-sheet git-cheat () { zle -M "`cat ~/zsh/git-cheat.conf`" } zle -N git-cheat bindkey "^[^g" git-cheat ``` #その他 ```~/.zshrc # 補完に関するオプション # http://voidy21.hatenablog.jp/entry/20090902/1251918174 setopt auto_param_slash # ディレクトリ名の補完で末尾の / を自動的に付加し、次の補完に備える setopt mark_dirs # ファイル名の展開でディレクトリにマッチした場合 末尾に / を付加 setopt list_types # 補完候補一覧でファイルの種別を識別マーク表示 (訳注:ls -F の記号) setopt auto_menu # 補完キー連打で順に補完候補を自動で補完 setopt auto_param_keys # カッコの対応などを自動的に補完 setopt interactive_comments # コマンドラインでも # 以降をコメントと見なす setopt magic_equal_subst # コマンドラインの引数で --prefix=/usr などの = 以降でも補完できる setopt complete_in_word # 語の途中でもカーソル位置で補完 setopt always_last_prompt # カーソル位置は保持したままファイル名一覧を順次その場で表示 setopt print_eight_bit #日本語ファイル名等8ビットを通す setopt extended_glob # 拡張グロブで補完(~とか^とか。例えばless *.txt~memo.txt ならmemo.txt 以外の *.txt にマッチ) setopt globdots # 明確なドットの指定なしで.から始まるファイルをマッチ bindkey "^I" menu-complete # 展開する前に補完候補を出させる(Ctrl-iで補完するようにする) # 色の定義 local DEFAULT=$'%{^[[m%}'$ local RED=$'%{^[[1;31m%}'$ local GREEN=$'%{^[[1;32m%}'$ local YELLOW=$'%{^[[1;33m%}'$ local BLUE=$'%{^[[1;34m%}'$ local PURPLE=$'%{^[[1;35m%}'$ local LIGHT_BLUE=$'%{^[[1;36m%}'$ local WHITE=$'%{^[[1;37m%}'$ # 範囲指定できるようにする # 例 : mkdir {1-3} で フォルダ1, 2, 3を作れる setopt brace_ccl # manの補完をセクション番号別に表示させる zstyle ':completion:*:manuals' separate-sections true # 変数の添字を補完する zstyle ':completion:*:*:-subscript-:*' tag-order indexes parameters # apt-getとかdpkgコマンドをキャッシュを使って速くする zstyle ':completion:*' use-cache true # ディレクトリを切り替える時の色々な補完スタイル #あらかじめcdpathを適当に設定しておく cdpath=(~ ~/myapp/gae/ ~/myapp/gae/google_appengine/demos/) # カレントディレクトリに候補がない場合のみ cdpath 上のディレクトリを候補に出す zstyle ':completion:*:cd:*' tag-order local-directories path-directories #cd は親ディレクトリからカレントディレクトリを選択しないので表示させないようにする (例: cd ../<TAB>): zstyle ':completion:*:cd:*' ignore-parents parent pwd # オブジェクトファイルとか中間ファイルとかはfileとして補完させない zstyle ':completion:*:*files' ignored-patterns '*?.o' '*?~' '*\#' ``` 最後に、zshをもっとカスタマイズしてみたい人は、[zsh Advent Calendar 2012](http://qiita.com/advent-calendar/2012/zsh)を参考にしてみるといいかもしれません。 |
|
| 564位 |
|
|||
|
19:52:05 |
(株式会社キッズスター 所属) |
|
RailsAdminは、簡単に、キレイな、扱いやすい管理画面を提供してくれるRailsエンジンです。  ## リソース * [Demo](http://rails-admin-tb.herokuapp.com) * [GitHub](https://github.com/sferik/rails_admin) * [RubyGems.org](http://rubygems.org/gems/rails_admin) * [The Ruby Toolbox](https://www.ruby-toolbox.com/projects/rails_admin) ## インストール RailsAdminは、管理画面にアクセスする管理者の管理に[Devise](https://github.com/plataformatec/devise)を利用しています。 その為、まずは、Deviseをインストールしておきましょう。 Gemfileに、deviseを追記します。 ```ruby:Gemfile gem 'devise' ``` インストールします。 ```bash $ bundle install $ rails g devise:install create config/initializers/devise.rb create config/locales/devise.en.yml =============================================================================== Some setup you must do manually if you haven't yet: 1. Ensure you have defined default url options in your environments files. Here is an example of default_url_options appropriate for a development environment in config/environments/development.rb: config.action_mailer.default_url_options = { :host => 'localhost:3000' } In production, :host should be set to the actual host of your application. 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root :to => "home#index" 3. Ensure you have flash messages in app/views/layouts/application.html.erb. For example: <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> 4. If you are deploying Rails 3.1 on Heroku, you may want to set: config.assets.initialize_on_precompile = false On config/application.rb forcing your application to not access the DB or load models when precompiling your assets. =============================================================================== ``` 上記の警告メッセージの内、フラッシュメッセージの表示ロジックのみ、レイアウトファイルに追記します。 ```ruby:app/views/layouts/application.html.erb <!DOCTYPE html> <html> <head> <title>SampleRailsAdmin</title> <%= stylesheet_link_tag "application", :media => "all" %> <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> </head> <body> <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> <%= yield %> </body> </html> ``` 本来であれば、RailsAdminをインストールする時に、関連する管理者モデルを自動生成してくれるはずなのですが、正常にジェネレーターが動作しないようなので、先にDeviseで管理者モデルを自動生成します。 ```bash $ rails g devise admin_user invoke active_record create db/migrate/20120802102400_devise_create_admin_users.rb create app/models/admin_user.rb insert app/models/admin_user.rb route devise_for :admin_users ``` 管理者は直接DBに登録するようにしたい為、管理者モデルファイルから`:registerable`と`:recoverable`の指定を外します。 ```ruby:app/models/admin_user.rb class AdminUser < ActiveRecord::Base # Include default devise modules. Others available are: # :token_authenticatable, :confirmable, # :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :rememberable, :trackable, :validatable # Setup accessible (or protected) attributes for your model attr_accessible :email, :password, :password_confirmation, :remember_me # attr_accessible :title, :body end ``` 同様に、マイグレーションファイルから関連するロジックをコメントアウトします。 ```ruby:db/migrate/20120802102400_devise_create_admin_users.rb class DeviseCreateAdminUsers < ActiveRecord::Migration def change create_table(:admin_users) do |t| ... ## Recoverable # t.string :reset_password_token # t.datetime :reset_password_sent_at ... end ... # add_index :admin_users, :reset_password_token, :unique => true ... end end ``` マイグレーションします。 ```bash $ rake db:migrate ``` さて、ここからRailsAdminのインストールです。 本来であれば、ここまで全てRailsAdminがお世話してくれるはずなんですけどね。 Gemfileに、rails_adminを追記します。 ```ruby:Gemfile gem 'rails_admin' ``` インストールします。 途中で、管理者モデルとして何を使うかと聞いて来るので`admin_user`を、管理画面をマウントするパスをどうするか聞いてくるので`admin`と答えます。 ```bash $ bundle install $ rails g rails_admin:install - Hello, RailsAdmin installer will help you sets things up! - I need to work with Devise, let's look at a few things first: - Checking for a current installation of devise... - Found it! - Looks like you've already installed it, good! - And you already set it up, good! We just need to know about your user model name... - We found 'admin_user' (should be one of 'user', 'admin', etc.) ? Correct Devise model name if needed. Press <enter> for [admin_user] > - Ok, Devise looks already set up with user model name 'admin_user': - Now you'll need an initializer... create config/initializers/rails_admin.rb - Adding a migration... create db/migrate/20120802103609_create_rails_admin_histories_table.rb ? Where do you want to mount rails_admin? Press <enter> for [admin] > gsub config/routes.rb route mount RailsAdmin::Engine => '/admin', :as => 'rails_admin' ``` RailsAdminが利用する履歴モデルのマイグレーションファイルが自動生成されているので、もう一度、マイグレーションします。 ```bash $ rake db:migrate ``` ## 利用方法 まず、管理者をDBに登録します。 ```bash $ rails c Loading development environment (Rails 3.2.7) 1.9.3p194 :001 > AdminUser.create(:email => "admin@test.com", :password => "hogehoge", :password_confirmation => "hogehoge") (0.1ms) begin transaction AdminUser Exists (0.1ms) SELECT 1 AS one FROM "admin_users" WHERE "admin_users"."email" = 'admin@test.com' LIMIT 1 SQL (1.9ms) INSERT INTO "admin_users" ("created_at", "current_sign_in_at", "current_sign_in_ip", "email", "encrypted_password", "last_sign_in_at", "last_sign_in_ip", "remember_created_at", "reset_password_sent_at", "reset_password_token", "sign_in_count", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [["created_at", Thu, 02 Aug 2012 10:46:25 UTC +00:00], ["current_sign_in_at", nil], ["current_sign_in_ip", nil], ["email", "admin@test.com"], ["encrypted_password", "$2a$10$spWm6E8m2s8w7OVDfaQo0Ogn1K4EbpVNP7wD3nUF476Evdtf6fKtC"], ["last_sign_in_at", nil], ["last_sign_in_ip", nil], ["remember_created_at", nil], ["reset_password_sent_at", nil], ["reset_password_token", nil], ["sign_in_count", 0], ["updated_at", Thu, 02 Aug 2012 10:46:25 UTC +00:00]] (0.8ms) commit transaction => #<AdminUser id: 1, email: "admin@test.com", encrypted_password: "$2a$10$spWm6E8m2s8w7OVDfaQo0Ogn1K4EbpVNP7wD3nUF476E...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: "2012-08-02 10:46:25", updated_at: "2012-08-02 10:46:25"> ``` サーバーを起動して、`http://localhost:3000/admin`にアクセスすると、ログイン画面が表示されます。 先程、登録した管理者のメルアドとパスワードを入力してログインすると、管理画面のダッシュボードが表示されます。 モデルは自動的にRailsAdminによって読み込まれる為、特に小難しい事さえしなければ、この状態でどんどん新しいモデルを管理出来るようになります。 使い勝手が知りたければ、公式に[Demo](http://rails-admin-tb.herokuapp.com)が用意されているので、こちらで試してみるのも良いでしょう。 ## あわせて読みたい * [rails_admin を使った管理画面の作成 | 篳篥日記](http://d.hatena.ne.jp/hichiriki/20120126) * [Rails3 で管理画面を作成するなら RailsAdmin で決まりかも | present](http://d.hatena.ne.jp/griefworker/20120628/rails_admin) |
|
| 565位 |
|
|||
|
12:37:04 |
(SAKURA Internet Inc. 所属) |
|
そろそろBoxenを試してみたいと思っていたのですが、試行錯誤できるよう何度も一からやり直せる環境がほしいと思っていました。Mac上のVirtualBoxにMavericksをインストールできないかとググってみると[OS X on OS X](http://ntk.me/2012/09/07/os-x-on-os-x/)というページを見つけました。このページの手順通り実行すればOKでした。ありがとうございます!
## Mavericksのライセンス http://images.apple.com/legal/sla/docs/OSX109.pdf B. Mac App Store License.のセクションに > (iii) to install, use and run up to two (2) additional copies or instances of the Apple > Software within virtual operating system environments on each Mac Computer you own > or control that is already running the Apple Software, for purposes of: (a) software > development; (b) testing during software development; (c) using OS X Server; or (d) > personal, non-commercial use. という項目があります。MavericksをApp Storeから入手した場合はApple Softwareを動かしているMac上の仮想環境に2つまでVMを作れるとのことです。 ## Mavericksのインストーラをダウンロード まだMavericksにアップグレードしていない場合は、App StoreでOSX Mavericksのインストーラをダウンロードだけしてインストーラが開始したら終了します。 既にMavericksにアップグレードした場合は、インストーラが削除されていますので、App StoreからOSX Mavericks 無料配信中のバナーをクリックしダウンロードボタンを押して再度ダウンロードします。 なお、私は今回試したのは後者のパターンです。 ## VirtualBox用イメージを作成 ```shell-session $ git clone https://github.com/ntkme/InstallESD.dmg.tool $ cd InstallESD.dmg.tool $ bin/iesd -t BaseSystem -i /Applications/Install\ OS\ X\ Mavericks.app/Contents/SharedSupport/InstallESD.dmg -o Output.dmg ``` ## VirtualBoxで新規VMを作成 * OSタイプ: Mac OS X * OSバージョン: Mac OS X (64bit) * メインメモリー: 2048MB以上で適宜設定 * 仮想ハードドライブファイルサイズ: 80GB (適宜設定) * ネットワークアダプタタイプ: Intel PRO/1000 MT Server (82545EM) * デフォルトで選択されているIntel PRO/1000 T Server (82453GC)だとうまく動かないそうです。 * CD/DVDドライブイメージ: 上で作成したOutput.dmgを選択 ## インストーラを実行 ### まずインストーラを中断して仮想ハードドライブをフォーマットしてから続行 * VMを起動してインストーラを実行します。 * OS X インストーラが起動したら、画面上部のメニューバーの[ユーティリティ]/[ディスクユーテリィティ...]を起動します。 * 左のリストでディスクを選択→右で[消去]タブを選択→フォーマットを[Mac OS 拡張 (ジャーナリング)]にして[消去]ボタンを押し、ディスクをフォーマットします。 * フォーマットが終わったらディスクユーティリティを終了し、インストーラを続行します。 * インストーラの指示に従い適宜設定してインストールを進めます。 ### 再起動したらDVD仮想ドライブからディスクを除去して続行 * 一度再起動したらVMのウィンドウの左上の赤い×アイコンを押しVMを停止させます。 * VirtualBoxのストレージの設定を開き、ストレージツリーでDVDイメージを選択し、[属性]/[CD/DVDドライブ]のディスクアイコンをクリックして[仮想ドライブからディスクを削除]メニューを選びます。 * [Oracle VM VirtualBox マネージャー]に戻り、VMを起動します。 * インストーラの指示に従い適宜設定してインストールを進めます。 |
|
| 566位 |
|
|||
|
22:23:50 |
(シナプス株式会社 所属) |
|
**PHP言語レベルでのエスケープシーケンス**
=============================== **[マニュアル](http://php.net/manual/ja/language.types.string.php)** も併せてお読みください。 シングルクオート内で処理されるシーケンス -------------------------------------------------- | 記述 | 実際の表示 | 意味 | 備考 | |:------:|:--------------:|:--------------------:|:-------------:| | **`\\`** | **\\** | バックスラッシュ | **【※1】** 直後にシングルクオートがない場合は省略可能 | | **`\'`** | **'** | シングルクオート | | ```php:【※1】 echo '[a]\[b]'; # => [a]\[b] echo '[a]\\[b]'; # => [a]\[b] echo '[a]\\\[b]'; # => [a]\\[b] echo '[a]\\\\[b]'; # => [a]\\[b] echo '\\'; # => \ echo '\'; # パースエラー ``` 慣れないうちは省略せずに全てエスケープすることをおすすめします。ちなみにこれはPHPに限っての挙動であり、 **C** や **Java** ではこのような挙動にはならず、 **必ずエスケープしなければなりません** 。 ダブルクオート内で処理されるシーケンス ----------------------------------------------- 一部マニュアルに記載がないものもあります。 | 記述 | 実際の表示 | 意味 | 備考 | |:------:|:--------------:|:--------------------:|:-------------:| | **`\\`** | **\\** | バックスラッシュ | **【※1】** 直後に他のエスケープ可能な文字がない場合は省略可能 | | **`\"`** | " | ダブルクオート | | | **`\$`** | $ | ドル記号 | **【※2】** 直後に変数名に使える文字がなければ省略可能 | | **`\0`** | | NULL文字 | (実際は下で紹介している8進数表記のものに属する) | | **`\n`** | | ラインフィード | | | **`\r`** | | キャリッジリターン | | | **`\t`** | | 水平タブ | | | **`\v`** | | 垂直タブ | PHP5.2.5以降のみ | | **`\e`** | | エスケープ | PHP5.4.0以降のみ | | `\123` | S | 8進数表記でのアスキーコード指定 | 正規表現は **`\[0-7]{1,3}`** | | `\x53` | S | 16進数表記でのアスキーコード指定 | 正規表現は **`\x[0-9A-Fa-f]{1,2}`** | | `{$var}` | $var の値 | 変数展開 | | | `$var` | $var の値 | 変数展開 | | | `{$var[0]}` | $var[0] の値 | 変数展開 | | | `$var[0]` | $var[0] の値 | 変数展開 | | | `{$var['hoge']}` | $var['hoge'] の値 | 変数展開 | ↓ との違いに注意 | | `$var[hoge]` | $var['hoge'] の値 | 変数展開 | ↑ との違いに注意 | **【※2】** ここでの「変数名に使える文字」とは、 `${"hoge\r\nhoge"}` などせずとも、直接PHPコードとして書けるものを指します。正規表現 **`[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*`** にマッチする文字列です。 ヒアドキュメント内で処理されるシーケンス ----------------------------------- | 記述 | 実際の表示 | 意味 | 備考 | |:------:|:--------------:|:--------------------:|:-------------:| | **`\$`** | $ | ドル記号 | **【※2】** 直後に変数名に使える文字がなければ省略可能 | | `{$var}` | $var の値 | 変数展開 | | | `$var` | $var の値 | 変数展開 | | | `{$var[0]}` | $var[0] の値 | 変数展開 | | | `$var[0]` | $var[0] の値 | 変数展開 | | | `{$var['hoge']}` | $var['hoge'] の値 | 変数展開 | ↓ との違いに注意 | | `$var[hoge]` | $var['hoge'] の値 | 変数展開 | ↑ との違いに注意 | Nowdoc内で処理されるシーケンス ---------------------------------------- 無し **正規表現エンジンレベルでのエスケープシーケンス** ===================================== 大部分は **[マニュアル](http://www.php.net/manual/ja/regexp.reference.escape.php)** 通りなので私からの説明は割愛しますが、 `\ddd` の項目にかなりややこしいケースがあるので、ここだけ説明を入れておきます。 処理の順序 ---------- 1. **2桁以下** のとき、まず10進数として扱い、その番目のキャプチャサブパターンが存在するときはそれを意味する。 **1~99** であって、<ins>0は該当しない</ins>。 2. 2桁以下で1に該当しなかったとき、<ins>3桁に満たない分だけ頭から0埋めされる</ins>。 3. **0~7** の数字が続く分だけ、8進数コードとして処理する。それ以降はその文字自身を表す。  これホントにどうかと思うけど、上で述べたことには忠実に従ってるので… **エスケープ方法に関する注意** ======================= エスケープは2段構え ---------------- **PHP言語レベルでのエスケープ** と **正規表現エンジンレベルでのエスケープ** の2段構えとなっていることに十分に留意してください。 ### 例1: バックスラッシュのエスケープ **1. PHPコードレベルで文字列が読み取られる** 第1引数は文字列 **`/\\/`** として扱われます。 **2. 正規表現エンジンレベルで文字列が読み取られる** パターンは文字 **\\** にマッチするものとなります。 ```php:例1:バックスラッシュにマッチさせたい場合のコード preg_match('/\\\\/', $text); preg_match('/\\\/', $text); preg_match("/\\\\/", $text); preg_match("/\\\/", $text); ``` ### 例2: NULL文字のエスケープ **1. PHPコードレベルで文字列が読み取られる** 第1引数は文字列 **`/\000/`** または **`/\00/`** または **`/\0/`** として扱われます。 **2. 正規表現エンジンレベルで文字列が読み取られる** パターンは **NULL文字** にマッチするものとなります。 ```php:例2:NULL文字にマッチさせたい場合のコード preg_match('/\\000/', $text); preg_match('/\\00/', $text); preg_match('/\\0/', $text); preg_match('/\000/', $text); preg_match('/\00/', $text); preg_match('/\0/', $text); preg_match("/\\000/", $text); preg_match("/\\00/", $text); preg_match("/\\0/", $text); ``` <ins>PCRE正規表現関数は、パターンに関してはバイナリセーフでないので、NULL文字をそのまま渡すことが出来ません。</ins> | | **PCRE**<br />([preg_match](http://www.php.net/manual/ja/function.preg-match.php)) | **鬼車**<br />([mb_ereg](http://www.php.net/manual/ja/function.mb-ereg.php)) | **POSIX**<br /> ([ereg](http://www.php.net/manual/ja/function.ereg.php)) | |:--------------------------------:|:------------------------------:|:------------------------:|:--------------------:| |パターンはバイナリセーフか | × | ○ | × | |検索対象文字列はバイナリセーフか | ○ | ○ | × | 正規表現エンジンに対して無駄なエスケープは行わない ------------------------------------------------------------------ エスケープ不要な文字に関してエスケープをすることは可能な限り控えましょう。 PHP言語レベルでの「省略可能」については、 **「本来エスケープしなければならないものをエスケープしなくても動く」** というニュアンスでしたが、こちらは逆に **「本来エスケープしてはいけないものをエスケープしても動く」** という解釈になると思います。 特に **文字クラス** に関して冗長なエスケープをする人が数多く見られるので、エスケープが必要なものを列挙しておきます。 - **`]`** - **`\`** - **`:`** - **`^`** **(※先頭にくる場合のみ)** - `/` (デリミタに使った文字) 以上です。 **`[` や `-` の エスケープいらないの?** って思う人も多いかもしれませんが、本当にこれだけです。先頭以外の **`^`** はエスケープ不要です。 **`-`** にマッチさせたい場合は、エスケープするのではなく、文字クラスの **先頭** か **末尾** に入れるのが正解です。 **【重要な追記】** <ins>鬼車 (mb_ereg) の場合は</ins> **`[`** <ins>のエスケープが必要です。</ins> **正規表現関数の選択に関する注意** ============================= 正規表現関数を使わずに実現できるかどうかを最初に検討する --------------------------------------------- 無意味に処理速度の遅い正規表現関数を使うのはやめましょう。 ### 例1: 文字列の検索 正規表現の機能を利用していない単純な検索処理があります。 ```php:preg_match関数での実装 if (preg_match('/abc/', $str)) { echo '"abc" found.'; } ``` この処理は **[strpos](http://www.php.net/manual/ja/function.strpos.php)** 関数で実現できます。 ```php:preg_match関数での実装 if (strpos($str, 'abc') !== false) { echo '"abc" found.'; } ``` 比較演算子に `!=` ではなく `!==` を用いていることに注意してください。ドキュメント上に警告があります。  ### 例2: 文字列の削除 改行コードをすべて削除する処理があります。 ```php:preg_replace関数での実装 $str = preg_replace("/[\r\n]/", '', $str); ``` この処理は **[str_replace](http://www.php.net/manual/ja/function.str-replace.php)** 関数で実現できます。 **[strtr](http://www.php.net/manual/ja/function.strtr.php)** 関数でも実現できますが、処理速度は前者の方が高速です。 ```php:str_replace関数での実装 $str = str_replace(array("\r", "\n"), '', $str); ``` ```php:strtr関数での実装 $str = strtr($str, array_fill_keys(array("\r", "\n"), '')); ``` ### 例3: 文字列の置換 改行コードをすべてに `\r\n` に統一する処理があります。 ```php:preg_replace関数での実装 $str = preg_replace("/\r\n|\r|\n/", "\r\n", $str); ``` この処理は **[strtr](http://www.php.net/manual/ja/function.strtr.php)** 関数で実現できます。 ```php:strtr関数での実装 $str = strtr($str, array_fill_keys(array("\r\n", "\r", "\n"), "\r\n")); ``` **[str_replace](http://www.php.net/manual/ja/function.str-replace.php)** 関数では実現できません。その理由はドキュメントにも強調的に書かれています。  ### 例4: マルチバイト文字列の置換 **[str_replace](http://www.php.net/manual/ja/function.str-replace.php)** 関数はマルチバイト文字列の検知に対応していませんが、 `UTF-8` を採用している場合は問題なく使用することが出来ます。 ```php $str = str_replace("あいうえお", "かきくけこ", $str); ``` その理由は **[「マルチバイト非対応関数で文字化け発生 str_replace() と mb_ereg_replace」](http://www.softel.co.jp/blogs/tech/archives/525)** で解説されています。 `ASCII` の上位互換コードと言われる `UTF-8` ならではの特性です。 **[split](http://www.php.net/manual/ja/function.split.php)** 関数はPOSIX正規表現関数である ---------------------- どこにも `ereg` の文字が見当たらないので **「文字列を指定した文字列で分割する関数」** だと思い込む初心者さんが非常に多いのですが、これはPOSIX正規表現関数に該当します。本当に求められているのはこの関数ではなく **[explode](http://www.php.net/manual/ja/function.explode.php)** 関数です。 POSIX正規表現関数は全て非推奨関数である --------------------------------- POSIX正規表現関数は公式マニュアル上で **非推奨** であると警告されています。<ins>検索対象の文字列がバイナリセーフで無い</ins>という致命的な欠陥を抱えていることもあり、セキュリティ的に危険なのでこの関数は絶対に使わないでください。 `UTF-8` ならばPCRE正規表現関数を使う -------------------------------- PCRE正規表現関数を使うか鬼車正規表現関数を使うか迷うところですが、前者で比較的利用機会の多い **[preg_match_all](http://www.php.net/manual/ja/function.preg-match-all.php)** 関数に相当する **mb_ereg_all** のような関数が後者では実装されていません。`UTF-8` であればどちらでもOKなので、特に理由がなければ前者を選択しておいた方が無難でしょう。 **PCRE正規表現関数の修飾子やメタ文字に関する注意** ============================================= `u` 修飾子を使用する目的 --------------------- PCRE正規表現関数でマッチングの単位を **1バイト** から `UTF-8` **1文字** に変更したい場合、 `u` 修飾子を利用しますが、必ずしもマルチバイト文字列を含むパターンを扱う際に必要だとは限りません。 ### 不要な場合 ```php preg_match('/あ|い/u', $str); ``` - **`u` 修飾子がある**<br />→ 文字列 `あ` または `い` にマッチ - **`u` 修飾子がない**<br />→ バイト列 `E3` `81` `82` または `E3` `81` `84` にマッチ ### 必要な場合 ```php preg_match('/[あい]/u', $str); ``` - **`u` 修飾子がある**<br />→ 文字 `あ` `い` のいずれかにマッチ - **`u` 修飾子がない**<br />→ <ins>バイト `E3` `81` `82` `E3` `81` `84` のいずれかにマッチ</ins> `u` 修飾子の副作用的効果 --------------------- - **[preg_match](http://www.php.net/manual/ja/function.preg-match.php)** 関数または **[preg_match_all](http://www.php.net/manual/ja/function.preg-match-all.php)** 関数で **不正なUTF-8シーケンス** を検知したとき、返り値は **`FALSE`** になります。この性質を利用してエンコーディングバリデーションを行うことも出来ます。詳しくは **[「UTF-8を想定して、不正なバイト列が含まれていないかを1行でチェックする」](http://qiita.com/mpyw/items/f0628b35a368fa468775)** で解説しています。 - **[preg_replace](http://www.php.net/manual/ja/function.preg-match.php)** 関数または **[preg_replace_callback](http://www.php.net/manual/ja/function.preg-match-all.php)** 関数で **不正なUTF-8シーケンス** を検知したとき、返り値は **`NULL`** になります。 - **[preg_match](http://www.php.net/manual/ja/function.preg-match.php)** 関数または **[preg_match_all](http://www.php.net/manual/ja/function.preg-match-all.php)** 関数で **`PREG_OFFSET_CAPTURE`** フラグを指定したとき、オフセットの単位が **バイト** から **文字** に変化します。 `^` と `\A` 、 `$` と `\Z` と `\z` の違いを知る -------------------------------------------- | | `^` | `\A` | |:----:|:----:|:-----:| |通常時| 先頭にマッチ | 先頭にマッチ |マルチラインモード(m修飾子)| 先頭または **改行の直後** にマッチ | 先頭にマッチ | | | `$` | `\Z` | `\z` | |:----:|:----:|:-----:|:-----:| |通常時| 末尾または **末尾の改行の直前** にマッチ | 末尾または **末尾の改行の直前** にマッチ | 末尾にマッチ | |マルチラインモード(m修飾子)| 末尾または **改行の直前** にマッチ | 末尾または **末尾の改行の直前** にマッチ | 末尾にマッチ | シビアな正規表現では `$` や `\Z` は使うべきでないと思います。プログラミング言語によってこのあたりは若干変わってきますが、PHPではこのような実装となっています。 可能な限り最長マッチは独占的にする ------------------------------ 繰り返しパターンに関して、 **最短マッチ** , **最長マッチ** はご存じの人が多いと思いますが、 **独占的最長マッチ** の存在も忘れないでください。 | **最短マッチ** | **最長マッチ** | **独占的最長マッチ** | |:-----------------:|:-----------------:|:-------------------------:| | ?? | ? | ?+ | | *? | * | *+ | | +? | + | ++ | | {3, 8}? | {3,8} | {3,8}+ | | {5,}? | {5,} | {5,}+ | **【例】** `ABCDE123EFG45` のような文字列の末尾の数字のみを削除して `ABCDE123EFG` のようにしたい ```php:最長マッチで実行 $str = 'ABCDE123EFG45'; $str = preg_replace('/\\d+\\z/', '', $str); ``` 1. `1` にマッチし、次の文字を調べる。 2. `12` にマッチし、次の文字を調べる。 3. `123` にマッチし、ここで `\d+` のマッチはいったん終了する。 4. `123` のときこの場所は `\z` にはマッチしないので、バックトラックを行う。 5. `12` のときこの場所は `\z` にはマッチしないので、バックトラックを行う。 6. `1` のときこの場所は `\z` にはマッチしない。よってここはマッチ失敗とする。 7. `4` にマッチし、次の文字を調べる。 8. `45` にマッチし、ここで `\d+` のマッチはいったん終了する。 9. `45` のときこの場所は `\z` にはマッチする。よってここはマッチ成功とする。 このように最長マッチだと無駄にバックトラックが行われてパフォーマンスが低下してしまいますが、代わりに **独占的最長マッチ** を用いると・・・ ```php:独占的最長マッチで実行 $str = 'ABCDE123EFG45'; $str = preg_replace('/\\d++\\z/', '', $str); ``` 1. `1` にマッチし、次の文字を調べる。 2. `12` にマッチし、次の文字を調べる。 3. `123` にマッチし、ここで `\d+` のマッチは終了する。 4. この場所は `\z` にはマッチしない。よってここはマッチ失敗とする。 5. `4` にマッチし、次の文字を調べる。 6. `45` にマッチし、ここで `\d+` のマッチは終了する。 7. この場所は `\z` にはマッチする。よってここはマッチ成功とする。 このように無駄なバックトラックを防ぐことが出来ます。 |
|
| 567位 |
|
|||
|
01:27:57 |
(フリーランス 所属) |
|
[入門ChefSolo-InfrastructureasCode-1.0.0](http://tatsu-zine.com/books/chef-solo)を買ってvagrantにハマっています。 vagrantの設定について色々調べてみました。 ## 環境 * mac-osx ## 事前準備に必要なもの * virtualbox * ruby 1.9.3p327 (rbenv) ※入っていないかたは[rbenvで複数バージョンのRubyを管理するmac os lion](http://d.hatena.ne.jp/itog/20120223/1329995531)を読んでいれてみてください * vagrant(試したバージョンは1.1.5) ※入っていないかたは[vagrant](http://www.vagrantup.com/)からDownloadポチっと押してインストールしてきてください。 ## 参考にしたサイト * [VagrantをPluginで拡張する](http://www.ryuzee.com/contents/blog/4310) * [vagrant公式](http://docs.vagrantup.com/v2/getting-started/index.html) ## [vagrant](http://www.ryuzee.com/contents/blog/4292)とは? vm(virtualbox,vmware)をコマンドラインで作成、起動などができるもの chef-soloやるだけなら特に必要はないが、chef-soloをやる場合に何回も壊しては作るを繰り返すことになるので合ったほうが便利 ちなみにvagrantでもなくてもvirtualboxのコマンドでも頑張れば同様なことはできる。 chef-soloを使う為の箱の自動化処理部分を担ってくれる ## Vagrantfileとは Vagrantfileはvagrantの設定ファイルです 以下Vagrantfileの設定についての説明です。 またこれ以下の設定内容は全て下記のブロック内で設定してください。 ```ruby:Vagtanrfile Vagrant.configure("2") do |config| . . #このブロック内で定義してください。 end ``` ## config.vm.box [Command-Line Interface Box](http://docs.vagrantup.com/v2/cli/box.html) vagrant内部で持つvmの名前です。 これは ```shell vagrant box add {任意のvm名称} ``` の{任意のvm名称}の部分にあたります。 設定は下記にような形になります ```ruby:Vagrantfile config.vm.box = "{任意のvm名称}" ``` [公式](http://docs.vagrantup.com/v2/cli/box.html)より > The name argument of this command is a logical name, meaning you can effectively choose whatever you want. This is the name that Vagrant searches for to match with the config.vm.box setting in Vagrantfiles. ちょっと英語が読めませんがvagrant box add したものと同一でなければいけません(多分合ってる)。 また勘違いしやすいのですがこれとGUIの管理ツールから見えるbox名前は一致しません。 これは後で説明します。 ## config.vm.provider [Providers Configuration](http://docs.vagrantup.com/v2/providers/configuration.html) provider (virtualbox,vmware等)の基本設定を変更することができます。 ### boxの名称の変更 virtualboxのGUI上で見える名前を設定したい場合は ```ruby:Vagrantfile config.vm.provider :virtualbox do |vb| vb.name = "{任意の名前}" end ``` とすれば名前を変更できます。 vb.nameとvagrantで操作するbox名は一致しません。 あくまでvirtualbox上のGUIの名前になります。 ### メモリの設定 仮想boxのメモリの割り当てを変更したい場合は ```ruby:Vagrantfile config.vm.provider :virtualbox do |vb| vb.customize ["modifyvm", :id, "--memory", "変更したいメモリ(Mbyte単位)"] end ``` とすればメモリの割り当てを変更できます。 ## config.vm.network [Networking Basic Usage](http://docs.vagrantup.com/v2/networking/basic_usage.html) vmのネットワーク設定です。 [vagrantbox.es](http://www.vagrantbox.es/)のcentos6.4だとデフォルトはNATのポートフォワーディングのみの設定なので 自分はHostOnlyAdapterの設定を追加しています。 ```ruby:Vagrantfile config.vm.network :private_network, ip: "ゲストOS側の割り当てたい任意のIPアドレス" ``` 他にもportフォワーディングとかbrigeとか設定できるみたいですが試していないので割愛します。 ## config.vm.provision [Provisioning Basic Usage](http://docs.vagrantup.com/v2/provisioning/basic_usage.html) vmの起動後に実行してくれる設定を持たすことができます。 起動後に前処理をしてくれるイメージで問題有りません。 #### :shell 前処理をshellで実行出来ます。 設定は下記のようになります。 ```ruby:Vagrantfile config.vm.provision :shell, :path => "{任意の名前}.sh" ``` この例ですとvmの起動後カレントの{任意の名前}.shを実行するという処理になります。(起動時毎回) ちなみに自分は[vagrantbox.es](http://www.vagrantbox.es/)から取ってきたCentOS 6.4 x86_64 Minimal (VirtualBox Guest Additions 4.2.8, Chef 11.4.0, Puppet 3.1.0)のboxが最初rsyncが入ってなくて初回chef-soloを実行するとエラーになったので {任意の名前}.shを下記にように書きました。 ```shell:provisioning.sh #!/usr/bin/env bash # # provisioning shell # @see http://docs.vagrantup.com/v2/provisioning/shell.html # set -e # rsyncパッケージがデフォルトで入っていない場合はインストールする if ! yum list installed | grep rsync >/dev/null 2>&1; then yum -y install rsync fi ``` また:inlineというものもあって.shでなく直接Vagrantfileに書いて実行できるようです。試していないので説明は公式に譲ります。 ```ruby:Vagrantfile Vagrant.configure("2") do |config| # ... other configuration config.vm.provision :shell, :inline => "echo hello" end ``` 他にも * chef-soloの設定 * puppetの設定 * chef-cliantの設定 ができるみたいですが試していなので割愛します。 ## config.vm.synced_folder 共有フォルダの設定です。 デフォルトは * ホスト側はVagrantfileがおいてあるカレントディレクトリ * ゲスト側は/Vagrant が共有フォルダとして設定されていますが上記内容を変更出来ます。 ```ruby:Vagrantfile config.vm.synced_folder "ホスト側のディレクトリ", "ゲスト側のディレクトリ" ``` ## [おまけ]vagrant pluginコマンドについて vagrantはいろんなプラグインがgemで出まわってます。 http://rubygems.org/search?utf8=%E2%9C%93&query=vagrant このpluginをvagrantに食わせる場合は ```shell vagrant plugin install {gemの名称} ``` で入れられます。 有名どころだと[sahara](http://www.ryuzee.com/contents/blog/6555)とかもこれでinstallできます。 ちなみに[vagrantboxes](http://rubygems.org/gems/vagrantboxes)のpluginは自分の環境では上手く動作しませんでした。 ### vagrant-vbguest plugin Guest Additionsの自動更新をしてくれるもの ``` vagrant plugin install vagrant-vbguest ``` ## [余談]作ったvagrantのVMについて vagrantで作ったvmはGUI上で除去などすると 設定ファイルが残ったままになるので再度同じ名前のboxを作ろうするとエラーになります。 そんな時は~/.vagrant.dの削除をしてみてください。 今日はここまで調べたのでまた調べたらこの記事に追記していこうかと思ってます。 |
|
| 568位 |
|
|||
|
07:08:02 |
|
|
# 概要
一般的なListViewの高速化手法として`ViewHolder`というものがあります。 これはAdapter#getView()でアイテムの`findViewById()`の回数を減らす(生成済みのものを使いまわす)ことで速度を改善するというもの。 今回はこのViewHolderを使わないでListViewを高速化する方法を書きます。 その方法はズバリ、`Custom View`を使う方法です!! 順に説明していきます。 コードはGitHubに上げていますので、参考にしてみてください。 [mofumofu3n/NonViewHolder](https://github.com/mofumofu3n/NonViewHolder) # アイテムをカスタムViewにする ListViewのアイテムのレイアウトファイルと実装を書いていきます。 アイテムのルートレイアウトの名前は`ItemLayout.java`としています。 ## レイアウトファイル タイトル、概要、アイコンを持ったアイテムを想定しています。 ``` <?xml version="1.0" encoding="utf-8"?> <com.example.nonviewholder.ItemLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/titleView" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/descriptionView" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <ImageView android:id="@+id/iconView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:drawable/ic_dialog_alert" /> </com.example.nonviewholder.ItemLayout> ``` ## ItemLayoutの実装 アイテムのレイアウトはLinearLayoutを継承して実装しています。 ここでのポイントは`onFinishInflate()`が呼ばれた時に`findViewById()`することです。 こうすることでAdapterで新しくアイテムのレイアウトを生成するときにだけ`findViewById()`されます。 まさにViewHolderと同様の効果が得られます。 ``` public class ItemLayout extends LinearLayout { // タイトル TextView mTitleView; // 概要 TextView mDescriptionView; // アイコン ImageView mIconView; public ItemLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onFinishInflate() { super.onFinishInflate(); mTitleView = (TextView) findViewById(R.id.titleView); mDescriptionView = (TextView) findViewById(R.id.descriptionView); mIconView = (ImageView) findViewById(R.id.iconView); } public void bindView(Item item) { mTitleView.setText(item.title); mDescriptionView.setText(item.description); mIconView.setImageResource(item.icon); } } ``` # Adapterの処理 アダプターの処理はViewHolderを利用する時よりも簡単にかけます。 `getView()`の動きはほぼ一緒ですがViewHolderと同様の処理を Custom Viewの方で行っているため、レイアウトの生成(or 再利用)とアイテムをレイアウトに渡すだけで済みます。 ``` public class CustomItemAdapter extends ArrayAdapter<Item> { private LayoutInflater mFactory; private int mItemLayoutResource; public CustomItemAdapter(Context context, int resource, List<Item> objects) { super(context, resource, objects); mFactory = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mItemLayoutResource = resource; } @Override public View getView(int position, View convertView, ViewGroup parent) { final ItemLayout view; if (convertView == null) { // Viewがなかったら生成 view = (ItemLayout) mFactory.inflate(mItemLayoutResource, null); } else { view = (ItemLayout) convertView; } view.bindView(getItem(position)); return view; } } ``` # ViewHolderとの比較 ViewHolderを利用した時のAdapterはこちら。 Viewに対する処理がAdapterに依存するので、見通しが悪いです。(工夫の余地はあるかもしれません) AdapterにViewの処理を書くよりもCustom Viewにアイテムだけ渡しそちらでViewの処理を任せたほうが Adapterの肥大化が防げます。 ``` public class ItemAdapter extends ArrayAdapter<Item> { private LayoutInflater mFactory; private int mItemLayoutResource; public ItemAdapter(Context context, int resource, List<Item> objects) { super(context, resource, objects); mFactory = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mItemLayoutResource = resource; } @Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder holder; if (convertView == null) { convertView = mFactory.inflate(mItemLayoutResource, null); holder = new ViewHolder(); holder.title = (TextView) convertView.findViewById(R.id.titleView); holder.desc = (TextView) convertView.findViewById(R.id.descriptionView); holder.icon = (ImageView) convertView.findViewById(R.id.iconView); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } final Item item = getItem(position); holder.title.setText(item.title); holder.desc.setText(item.description); holder.icon.setImageResource(item.icon); return convertView; } class ViewHolder { TextView title; TextView desc; ImageView icon; } } ``` # まとめ ViewHolderを使わないでListViewを高速化する方法を書いてみました。 個人的にはTextViewに文字をセットする等のViewを操作する処理をCustom Viewに任せられ、 Adapterの見通しもよくなるため、ViewHolderよりもCustom Viewの方が好きです。 ViewHolderでもっと良い書き方あるよ!と言う方がいらっしゃいましたら、 ぜひコメントに書いて下さい。 あと、個人的なことですがQiitaに3日連続で投稿しています。 そろそろ投稿するネタが尽きてきました。。 |
|
| 569位 |
|
|||
|
00:12:42 |
(株式会社ソニックムーブ 所属) |
|
## 概要 `Keychain Services`というとパスワードを保存する仕組みだと思われる方が多いと思います。 実際にそうなのですが、Keychainに保存した情報は`NSUserDefaults`と違ってアプリを削除した後もデータを保持することができるという別のメリットがあります。 (Keychainの事を知らない人は[こちら](http://cocoadays.blogspot.jp/2011/02/ios-keychain-services.html)にとても分かりやすい説明が書いてあります) そういった要件があるときに`NSUserDefaults`のように簡単にデータを保存するライブラリがあったらいいなと思ったら既に作ってる人がいました。 [LUKeychainAccess](https://github.com/TheLevelUp/LUKeychainAccess)という名前のライブラリです。 `Keychain Services`は`NSUserDefaults`と違い、気軽に使うにはちょっと面倒な仕組みなのでとてもありがたいです。 #### 追記: iOSのバックアップを暗号化していない場合はキーチェーンの内容はバックアップされません。 機種変更の時などに初期化されてしまう環境もありますので、その辺も踏まえて利用しましょう。 > [takayamaさん](http://qiita.com/takayama)、ご指摘いただきありがとうございます。 ## 使い方 `Security.framework`をターゲットに追加してください。 次にソースをダウンロードしましょう。 https://github.com/TheLevelUp/LUKeychainAccess ダウンロードしたソースコード内の`LUKeychainAccess`ディレクトリごとプロジェクトにコピーして、 `LUKeychainAccess.h`ファイルをインポートしましょう。 使い方もとてもシンプルで`NSUserDefaults`とほぼ同様の使い方でデータを扱えます。 #### データの保存 ```objc // integer [[LUKeychainAccess standardKeychainAccess] setInteger:1 forKey:@"integerKey"]; // float [[LUKeychainAccess standardKeychainAccess] setFloat:1.0 forKey:@"floatKey"]; // double [[LUKeychainAccess standardKeychainAccess] setDouble:1.0 forKey:@"doubleKey"]; // BOOL [[LUKeychainAccess standardKeychainAccess] setBool:YES forKey:@"boolKey"]; // NSString [[LUKeychainAccess standardKeychainAccess] setString:@"string" forKey:@"stringKey"]; // NSObject [[LUKeychainAccess standardKeychainAccess] setObject:@"object" forKey:@"objectKey"]; ``` セットした値はすぐに保存されます。 #### 保存したデータの読み込み ```objc // integer NSInteger integerValue = [[LUKeychainAccess standardKeychainAccess] integerForKey:@"integerKey"]; // float float floatValue = [[LUKeychainAccess standardKeychainAccess] floatForKey:@"floatKey"]; // double float doubleValue = [[LUKeychainAccess standardKeychainAccess] doubleForKey:@"doubleKey"]; // BOOL BOOL boolValue = [[LUKeychainAccess standardKeychainAccess] boolForKey:@"boolKey"]; // NSString NSString *stringValue = [[LUKeychainAccess standardKeychainAccess] stringForKey:@"stringKey"]; // NSObject NSString *objectValue = [[LUKeychainAccess standardKeychainAccess] objectForKey:@"objectKey"]; ``` と正に`NSUserDefaults`と同じ使い勝手でした。 |
|
| 570位 |
|
|||
|
10:19:03 |
(Akatsuki Inc. 所属) |
|
Ansible を15分程度で「もう使えそう」と感じてもらうために書きました。
Ansible はプロビジョニング用アプリケーションです。 同じ目的のものでは Chef、Puppet などがあります。 Ansible の特徴だと感じたのは、とても簡単ですぐ実用できることです。 Chef で挫折してしまった私(不勉強ですみません)も、一日で仕事で必要なことができました。 簡単というと Fabric (やCapistrano) がありますが、環境構築やデプロイには Ansible が強いと感じます。 Python で書かれたものですがユーザーは YAML でタスクを書きます。 以下の作業で Python は出てきますが、Ansible をインストールする以外では本質的に不要なのでご安心(?)ください。 次を前提とします。 - Vagrant に Ubuntu 13.10 が用意されていること - Mac で pip (Pythonのパッケージ管理コマンド) を使えること Vagrant の準備は次を参考にしてください。 http://qiita.com/seizans@github/items/a0339682fcc70ae2d0f5 ## インストールする ``` $ pip install ansible ``` ## Ansible で Vagrant の Ubuntu にタスクを実行させる ### インベントリーファイル(hostリスト) を用意する Ansible では host をリストしたファイルをインベントリーファイルと呼びます。 必須なので用意します。 hosts というファイル名(ファイル名は任意)で、次の内容で作成します。(まだhostが1つだけ) ``` [vagrant] 192.168.111.222 ``` ### playbookファイル(処理スクリプト) を用意する 処理を記述したYAMLファイルが必要なので用意します。 provision_vagrant.yml というファイル名(ファイル名は任意)で、次の内容で作成します。 ``` - hosts: vagrant sudo: true user: vagrant tasks: - apt: pkg=git update_cache=yes state=latest - apt: pkg=vim update_cache=yes state=latest ``` あとは `ansible-playbook` コマンドで処理を実行できます。ただその前に・・・ ### Vagrantfile を用意する Vagrant 環境を ansible で楽に使えるように作成します。 Vagrantfile というファイル名(ファイル名はこれ指定)を次の内容で作成します。 ``` VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "ubuntu1310" # 自分のbox名と合わせる config.vm.network :private_network, ip: "192.168.111.222" # インベントリーファイルの内容と合わせる config.vm.provision :ansible do |ansible| ansible.playbook = "provision_vagrant.yml" # 作成したplaybookファイル名 ansible.inventory_path = "hosts" # 作成したインベントリーファイル名 end end ``` 準備ができました。起動させます。 ``` $ vagrant up ``` すると、VM を起動させるとそのまま playbook に書かれた処理が続けられます。 ``` $ vagrant ssh $ aptitude show git ``` などして、実際に `sudo aptitude install git` されたことを確認できます。 ## role を作る role は tasks を分割して管理するための単位です。 roles/python/tasks/main.yml というファイル名で、次の内容で作成します。(名前はrole名のpythonのみ任意) ``` - command: mkdir {{ virtualenvs_dir }} creates={{ virtualenvs_dir }} - pip: name=Django virtualenv={{ virtualenv_dir }} state=latest ``` また provision_vagrant.yml を次の内容に変更します。 ``` - hosts: vagrant sudo: true user: vagrant tasks: - apt: pkg={{ item }} update_cache=yes state=latest with_items: - git - python-virtualenv - hosts: vagrant user: vagrant roles: - python vars: work_dir: /home/vagrant virtualenvs_dir: "{{ work_dir }}/.virtualenvs" virtualenv_dir: "{{ virtualenvs_dir }}/env01" ``` 実行する単位(play) を1つ追加しました。 roles としては先に作った python という名前の role を指定しています。 vars には、処理内で使う変数を定義しています。 ここまでで python の virtualenv を用意して Django をインストールする処理が準備できました。 実行してみます。 なお先程は `vagrant up` で実行しましたが、今度は ansible のコマンドで実行してみましょう。 ``` $ ansible-playbook -i hosts provision_vagrant.yml --private-key=~/.vagrant.d/insecure_private_key (-iはインベントリーファイル指定、--private-keyはssh用のプライベート鍵指定) ``` 実際に virtualenv に Django がインストールされたことを確認しましょう。 ``` $ vagrant ssh $ source .virtualenvs/env01/bin/activate $ pip freeze ``` さて上記で role を使いましたが、ここまででは特に大きな恩恵は受けていませんが、 まずはいったん、準備する単位で分割するように作る慣習だ、などと考えておけばいいと思います。 role の中には tasks 以外にも、次のように機能毎のディレクトリを作成して使うことになります。 ``` - roles/ - role01/ - tasks/ - files/ - vars/ - templates/ - role02/ - ... ``` ## モジュールと次のステップ ここまでで雰囲気は把握できたのではないかと思います。 後は実際にタスクを書いていけば大丈夫だと思います。 その際に必要になるのが、モジュールの使い方を調べることです。 モジュールというのは、ここまでの `tasks` 内に出てきた `command`、`apt`、`pip` のことです。 適切なモジュールを選んで使っていくことで冪等性などの恩恵を受けられます。 各モジュールについては公式ドキュメントを確認してください。 http://docs.ansible.com/modules_by_category.html ## 参考 - 解説した内容のコード: https://github.com/seizans/ansible-tut - Vagrant で Ansible: http://docs.vagrantup.com/v2/provisioning/ansible.html - Ansible 公式: http://docs.ansible.com/index.html - http://www.slideshare.net/takushimizu/ansible-26200860 - http://apatheia.info/blog/2013/04/06/about-ansible/ - http://yteraoka.github.io/ansible-tutorial/ - https://gist.github.com/voluntas/7844901 |
|
| 571位 |
|
|||
|
19:18:55 |
|
|
CentOS6.5の標準ではPython2.6が、CentOS7.2ではPython2.7が入っており色々と不都合があるので、新バージョンかつよく使うバージョンをインストールする。
なおインストール先は `/opt/local` とする。 ## Developer Tools をインストール ```Shell # yum groupinstall "Development tools" # yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel ``` ## Python 2.7, 3.3 install まずダウンロードをしてきて、それらをインストールする。 ```shell curl -O https://www.python.org/ftp/python/3.3.3/Python-3.3.3.tgz curl -O https://www.python.org/ftp/python/2.7.6/Python-2.7.6.tgz tar zxf Python-3.3.3.tgz tar zxf Python-2.7.6.tgz ``` Python2.7 のインストール ```shell cd Python-2.7.6 ./configure --prefix=/opt/local make && make altinstall ``` Python3.3 のインストール ```shell cd Python-3.3.3 ./configure --prefix=/opt/local make && make altinstall ``` ## Easy_install をインストールさせる (2015/07/19: distribute のリンク先変更。 Thanks @mtomoaki_96kg さん :+1: ) ```shell curl -O https://pypi.python.org/packages/source/d/distribute/distribute-0.6.49.tar.gz tar xzf distribute-0.6.49.tar.gz cd distribute-0.6.49 /opt/local/bin/python2.7 ./distribute_setup.py /opt/local/bin/python3.3 ./distribute_setup.py ``` 2015/07/19 追記:distribute/setuptools をインストールしなくてもPIPはインストールできるようになっていますので、このステップは省略しても問題無いです。というより、pipで`pip install distribute`とコマンドを実行すればインストールできます。 see: https://pypi.python.org/pypi/distribute/0.6.49 ## PIP をインストール (2014/09/27 追記) (2015/04/22 raw.github.com から raw.githubusercontent.com へ変更) (2015/07/19 githubから pypa のリンクへ変更 see: https://pip.pypa.io/en/latest/installing.html#install-pip ) ```shell > curl -kL https://bootstrap.pypa.io/get-pip.py | /opt/local/bin/python2.7 > curl -kL https://bootstrap.pypa.io/get-pip.py | /opt/local/bin/python3.3 ``` ## VirtualEnv をインストールさせる ```shell /opt/local/bin/pip2.7 install virtualenv /opt/local/bin/pip3.3 install virtualenv ``` thanks sahiruhaさん :+1: ## 参考 - http://www.petitec.com/2013/04/centos6にpython2-7を入れる/ - http://toomuchdata.com/2012/06/25/how-to-install-python-2-7-3-on-centos-6-2/ - http://ymotongpoo.hatenablog.com/entry/2012/10/18/144352 - http://qiita.com/who_you_me/items/831d62f396e6d66dda66 - https://pip.pypa.io/en/latest/installing.html#install-pip |
|
| 572位 |
|
|||
|
09:04:42 |
|
|
これは[http://rtorruellas.com/vim-cheat-sheet/](http://rtorruellas.com/vim-cheat-sheet/)を和訳、一部加筆したものです。 ##カーソル移動 **h** -左移動 **j** -下に移動 **k** -上に移動 **l** -右に移動 **w** -単語の先頭へジャンプ(区切り文字まで) **W** -単語の先頭へジャンプ(区切り文字を含めない) **e** -単語の最後にジャンプ(区切り文字まで) **E** -単語の最後にジャンプ(区切り文字を含めない) **b** -単語の先頭へ戻る(区切り文字まで) **B** -単語の先頭へ戻る(区切り文字を含めない) **0** -(ゼロ)先頭に移動 **^** -行の最初の文字へ移動 **$** -行の終わりへ移動 **G** -最終行へ移動(番号を付けるとその行へ移動- 5Gは 5行目へ) >ヒント これらのコマンドの前に番号を付けるとその回数分繰り返します。 例えば、 **4j**は 4行下に移動します。 ##insertモード - テキストを追加/挿入 **i** -カーソル位置で挿入モードを開始 **I** -行の先頭で挿入モードを開始 **a** -カーソル位置の直後で挿入モードを開始 **A** -行の末尾で挿入モードを開始 **o** -現在の行の下に空白行を追加して挿入モードを開始 **O** -現在の行の上に空白行を追加して挿入モードを開始 **ea** -単語の末尾で挿入モードを開始 **ESC** -挿入モードを終了 ##編集 **r** -単一の文字を置き換える(挿入モードを使用しません) **J** -現在の行と次の行を統合する **cc** -現在の行を削除して挿入モードを開始 **cw** -カーソル位置の単語を削除して挿入モードを開始 **c$** -カーソル位置から行末までを削除して挿入モードを開始 **s** -カーソル位置の文字を削除して挿入モードを開始 **S** - 現在の行を削除して挿入モードを開始(CCと同じ) **xp** -カーソル位置と次の文字を入れ替える(技術的にはカットしてペースト) **u** -元に戻す(アンドゥ) **CTRL+r** -やり直し(リドゥ) **.** - 最後に使ったコマンドを行う ##テキストの選択(ビジュアルモード) **v** - ビジュアルモード開始。移動すると選択してその後にコマンドを実行できる(例えばY-ヤンク) **V** -行単位のビジュアルモードを開始 **o** -選択範囲の反対側に移動する **Ctrl+v** -ビジュアルブロックモード(矩形選択)を開始 **O** -ブロックの他のコーナーに移動する **aw** -単語をマーク **ab** -()ブロックを選択(括弧ごと) **aB** - {}ブロックを選択(括弧ごと) **ib** -()ブロックを選択(括弧を除く) **iB** - {}ブロックを選択(括弧を除く) **ESC** -ビジュアルモード終了 ##ビジュアルモードでのコマンド **>** - 選択範囲を右シフト **<** - 選択範囲を左シフト **y** - 選択範囲をヤンク(コピー) **D** - 選択範囲を削除する **~** - 大文字と小文字を入れ替える >ヒント ビジュアルブロックモード(矩形選択)で選択してシフトすると、選択位置からシフトできます。 ##カット&ペースト **yy** - 現在の行をヤンク(コピー) **2yy** - 2行をヤンク **yw** - カーソル位置の単語をヤンク **y$** - 現在行の行末までヤンク **p** - カーソル位置の後にペースト **P** - カーソル位置の前にペースト **dd** - 現在の行をカット(コピーして削除) **2dd** - 2行をカット **dw** - カーソル位置の単語をカット **D** - カーソル位置から行末までカット **d$** - カーソル位置から行末までカット(Dと同じ) **X** - 現在の文字をカットする ##終了 **:w** - 終了せずに保存 **:wq** - 保存して終了 **:x** - 保存して終了 **:q** - 保存せずに終了(変更点がある場合は終了できない) **:q!** - 保存せずに終了(変更点がある場合は破棄される) ##検索/置換 **/文字列** - 文字列を検索 **?文字列** - 文字列を逆方向に検索 **n** -同じ方向に再検索 **N** -逆方向に再検索 **:%s/old/new/g** - ファイル全体でoldをnewに置き換える **:%s/old/new/gc** - ファイル全体でoldをnewに置き換える(確認あり) ##複数のファイルでの作業 **:e** ファイル名 - 新しいバッファでファイルを編集 **:bname** または**:bn** - 次のバッファに移動 **:bprev** または**:bp** - 前のバッファへ移動 **:bd** -バッファを削除(ファイルを閉じる) **:sp** ファイル名 - 新しいバッファおよび分割ウィンドウでファイルを編集 **:vsp** ファイル名 - 新しいバッファおよび垂直分割ウィンドウでファイルを開きます **Ctrl+ws** - スプリットウィンドウ **Ctrl+ww** - ウィンドウを切り替える **Ctrl+wq** - ウィンドウを終了します **Ctrl+wv** - 垂直分割ウィンドウ >補足 当方環境ではCtrl+ws,Ctrl+wqが機能しませんでした。 ##タブ **:tabnew ファイル名** - 新しいタブで(ファイル名)を開く **:tabn ファイル名** - 同上 **Ctrl+wt** -独自のタブに現在の分割ウィンドウを移動 **gt** - 次のタブに移動する **:tabnext** - 同上 **:tabn** - 同上 **gT** - 前のタブに移動する **:tabprev** - 同上 **:tabp** - 同上 **(n)gt** - タブ番号(n)への移動 **:tabmove(n)** - 位置(n)番目に移動(左端が0) **:tabclose** - 現在のタブを閉じる **:tabc** - 同上 **:tabonly** - 現在の1を除く他のすべてのタブを閉じる **:tabo** - 同上 >補足 当方環境ではCtrl+wtが機能しませんでした。 |
|
| 573位 |
|
|||
|
09:49:17 |
(Akatsuki Inc. 所属) |
|
## 概要 [パーフェクトRuby](http://www.amazon.co.jp/gp/product/4774158798/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4774158798&linkCode=as2&tag=kidachi-22)を読んでいて便利だと思ったもの一覧です。 Array編 http://qiita.com/kidachi_/items/e9cb26c4e6cb36b70a1c Hash編 http://qiita.com/kidachi_/items/651b5b5580be40ad047e String編 http://qiita.com/kidachi_/items/7b355eb355b2d1390cf5 ## Enumerable ArrayやHash、Rangeなど、「オブジェクトの集まり」を表現するクラスには Enumerableがincludeされており、それら全てのクラスで活用できる。 ## 繰り返し処理 ### each_with_index 配列のkeyとvalueを両方繰り返す。 ```ruby > %w(Ruby Python Java).each_with_index do |value, key| > puts "#{key}: #{value}" > end 0: Ruby 1: Python 2: Java ``` ### reverse 末尾から順に繰り返す。 ```ruby > %w(Ruby Python Java).reverse { |val| puts val } => ["Java", "Python", "Ruby"] ``` ### each_slice 要素をn個ずつ区切って繰り返す。 ```ruby > %w(Ruby Python Java C Smalltalk Brainfuck).each_slice 2 do |a, b| > p [a, b] > end ["Ruby", "Python"] ["Java", "C"] ["Smalltalk", "Brainfuck"] > %w(Ruby Python Java C Smalltalk Brainfuck).each_slice 4 do |a, b, c, d| > p [a, b, c, d] > end ["Ruby", "Python", "Java", "C"] ["Smalltalk", "Brainfuck", nil, nil] # 帳尻が合わなかった部分はnil ``` ### each_cons n個の連続した要素を1つずつずらしながら繰り返す ```ruby > %w(Ruby Python Java C Smalltalk Brainfuck).each_cons 2 do |a, b| > p [a, b] > end ["Ruby", "Python"] ["Python", "Java"] ["Java", "C"] ["C", "Smalltalk"] ["Smalltalk", "Brainfuck"] ``` ## 評価結果を配列で返却 ### map 元の配列の各要素を変換して、新しい配列をつくる ```ruby > %w(Ruby Python Java).map { |s| s.upcase } => ["RUBY", "PYTHON", "JAVA"] ``` ※eachとの比較 ```ruby > %w(Ruby Python Java).each {|s| p s.upcase} "RUBY" "PYTHON" "JAVA" => ["Ruby", "Python", "Java"] ``` eachは、戻り値には変更はない。 ## 条件確認 ### all? 配列の要素が全て真であればtrueを返す。 ```ruby > [true, true, true].all? => true > [true, true, false].all? => false ``` ### none? 配列の要素が全て偽であればtrueを返す。 ```ruby > [false, false, false].none? => true > [true, true, false].none? => false ``` ### any? 配列の要素のうち一つでも真であればtrueを返す。 ```ruby > [true, true, false].any? => true > [false, false, false].any? => false ``` ### one? 配列の要素のうち一つだけ真であればtrueを返す。 ```ruby > [false, false, true].one? => true > [false, false, false].one? => false > [false, true, true].one? => false ``` ### 条件確認*ブロック all?, none?, any?, one?はそれぞれブロックを渡す事が出来る。 ```ruby # 要素が全て整数ならtrue > [1, 10, 100].all? {|v| v.is_a?(Integer)} => true # 要素が全て文字列ならtrue > [1, 10, 100].all? {|v| v.is_a?(String)} => false > ["Ruby", "Python", "Java"].all? {|v| v.is_a?(String)} => true ``` ## 部分的な要素の取得 ### grep 各要素に対してgrepをかけ、マッチする要素を取得する。 ```ruby # 正規表現にマッチする要素を取得 > %w(Ruby Python Java).grep(/y/) => ["Ruby", "Python"] # 指定の型にマッチする要素を取得 > ["Ruby", "Python", "Java", 1, 10, 100].grep(String) => ["Ruby", "Python", "Java"] > ["Ruby", "Python", "Java", 1, 10, 100].grep(Integer) => [1, 10, 100] ``` ### detect 戻り値が最初に真になった要素を取得 ```ruby > %w(Ruby Python Java C Smalltalk Brainfuck).detect { |s| s.include?("y") } => "Ruby" ``` ### select 戻り値が真になった要素を全て取得。 ```ruby > %w(Ruby Python Java C Smalltalk Brainfuck).select { |s| s.include?("y") } => ["Ruby", "Python"] ``` ### reject 戻り値が真になった要素以外を全て取得。 ```ruby > %w(Ruby Python Java C Smalltalk Brainfuck).reject { |s| s.include?("y") } => ["Java", "C", "Smalltalk", "Brainfuck"] ``` ### take 先頭から任意の数の要素を取得。 ```ruby > %w(Ruby Python Java C Smalltalk Brainfuck).take(3) => ["Ruby", "Python", "Java"] ``` ### drop 先頭から任意の数の要素をスキップしたものを取得。 ```ruby > %w(Ruby Python Java C Smalltalk Brainfuck).drop(3) => ["C", "Smalltalk", "Brainfuck"] ``` ### take_while ブロックが最初に偽を返すまでの要素を取得。 ```ruby > %w(Ruby Python Java C Smalltalk Brainfuck).take_while { |s| s.include?("y") } => ["Ruby", "Python"] ``` ### drop_while ブロックが最初に偽を返してからそれより後の要素を取得。 ```ruby > %w(Ruby Python Java C Smalltalk Brainfuck).drop_while { |s| s.include?("y") } => ["Java", "C", "Smalltalk", "Brainfuck"] ``` ## 畳み込み演算 ### inject 直前のブロックの戻り値が入った変数と、現在の要素が入った変数二つを利用して演算できる。 ```ruby > [1, 2, 3, 4].inject(0) { |result, num| result + num } => 10 # 0 +1 + 2 + 3 + 4 ``` 第一引数には初期値(上記では0)を設定可能。 初期値を省略すると、先頭の要素が初期値として用いられる。 ```ruby > [1, 2, 3, 4].inject { |result, num| result + num } => 10 # 1 + 2 + 3 + 4 ``` 繰り返しの度に呼び出すメソッドをシンボルで受け取ることも可能。 ```ruby > [1, 2, 3, 4].inject(:+) => 10 > [1, 2, 3, 4].inject(:*) => 24 ``` ## 繰り返しとオブジェクトの更新 ### each_with_object 要素を繰り返しながら、オブジェクトを更新していき、最終的に行き着いた値が戻り値となる。 引数には、初期値となるオブジェクトを渡す。 ```ruby > %w(Ruby Python Java).each_with_object({}) do |name, result| > p result > result[name] = name.length > end {} {"Ruby"=>4} {"Ruby"=>4, "Python"=>6} => {"Ruby"=>4, "Python"=>6, "Java"=>4} ``` ## 要素のグルーピング ### group_by 特定の条件に基づいて要素をグルーピングする。 ブロックを評価した結果がkeyとなるハッシュが返却される。 ```ruby > ["Ruby", 1.0, "Python", 1000, 2.5, "Java"].group_by { |v| v.class } => {String=>["Ruby", "Python", "Java"], Float=>[1.0, 2.5], Fixnum=>[1000]} ``` ### partition ブロックの戻り値が真か偽かによって二つのグループに分けた、新しい配列を返却する。 ```ruby > ["Ruby", 1.0, "Python", 1000, 2.5, "Java"].partition { |v| v.is_a?(String) } => [["Ruby", "Python", "Java"], [1.0, 1000, 2.5]] ``` ## 最小値と最大値 ### min, max, minmax ```ruby > range = (1..5) > range.max => 5 > range.min => 1 > range.minmax => [1, 5] ``` ### min_by, max_by, minmax_by ブロックで評価した結果の値で比較する。 (「何の最小値/最大値か」を決める事が出来る) ```ruby > lang = %w(Ruby Python Java C) > lang.min_by { |s| s.length } => "C" > lang.max_by { |s| s.length } => "Python" > lang.minmax_by { |s| s.length } => ["C", "Python"] ``` ## ソート ### sort, sort_by ```ruby > lang = %w(Ruby Python Java C) # アルファベット順 > lang.sort => ["C", "Java", "Python", "Ruby"] # 要素の長さ(1) > lang.sort { |a, b| a.length <=> b.length } => ["C", "Java", "Ruby", "Python"] # 要素の長さ(2) > lang.sort_by { |str| str.length } => ["C", "Java", "Ruby", "Python"] ``` sort_byは、各要素に対して1度しかメソッド呼び出し(今回は.length)を行わないため、sortより高速。 |
|
| 574位 |
|
|||
|
18:41:21 |
(ヤフー株式会社 所属) |
|
初日っぽく主に環境構築など導入部分について取り上げていきます。
Scalaに少し興味はあるけどまだやったことない!といった方に読んでいただけたら嬉しいです。 ## Scalaを使える状態にしてみる ### インストール 2つの方法を挙げてみます。 筆者は以下をMac OS X Mavericksで試しています。環境に応じて適宜読み替えてください。 2013.12.1現在の最新Scalaのバージョンは2.10.3です。 #### Homebrew Macの方はこれが一番楽かと思います。 ```bash $ brew install scala ``` その他のパッケージ管理ソフト(port、apt-get、yum)でも同じようにinstallできます。 但し最新のScalaではない可能性があるのでその点は注意してください。 #### 公式サイトからダウンロード パッケージ管理ソフトに頼らない方、確実に最新バージョンを使いたい方はこちらの方法です。 まずは<a href="http://www.scala-lang.org/">Scalaの公式サイト</a>から圧縮ファイルをダウンロードします。 ファイルを展開し、環境変数の設定を行ってください。 設定例は以下の通りです。コマンドとして実行するか、.bash_profileなどに追加してください。 ```bash export SCALA_HOME=/path/to/scala_dir export PATH=$PATH:$SCALA_HOME/bin ``` ### 設定完了の確認 Scala関係のコマンドが使えるかどうかを確認してください。 例として以下でバージョン情報の確認をしています。 問題なくインストールしたパッケージのバージョンが表示されれば完了です。 ```bash $ scala -version Scala code runner version 2.10.2 -- Copyright 2002-2013, LAMP/EPFL $ scalac -version Scala compiler version 2.10.2 -- Copyright 2002-2013, LAMP/EPFL $ fsc -version Fast Scala compiler version 2.10.2 -- Copyright 2002-2013, LAMP/EPFL ``` ## Hello World + α とりあえずはScalaを使ってみましょう。 ### Hello World! in REPL では早速Hello Worldしてみましょう。 まずはREPLを使った例です。 ```bash $ scala Welcome to Scala version 2.10.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_65). Type in expressions to have them evaluated. Type :help for more information. scala> println("Hello World") Hello World scala> :quit ``` REPLとはRead-eval-print loop(対話型実行環境)でコードを一行ずつ実行していける環境のことです。 Scalaのように本来はコンパイルをして実行する、といった言語において導入されていることは少ないものです。 ちょっとした計算処理や言語仕様の確認などができて大変重宝します。 ### Hello World! in ファイル 次の方法としてファイルを作成してHello Worldしてみます。 まずは以下な記述のファイルを作成してください。 ファイル名はHelloWorld.scalaとしてください。 ※ファイル名とクラス名は異なっていても構いませんが便宜上の理由から。 ```scala object HelloWorld extends App { println("Hello World!") } ``` これができたらコンパイル、実行してみます。 ```bash $ scalac HelloWorld.scala #コンパイル $ scala HelloWorld #実行 Hello World! ``` ### コンパイル遅くない? ここまでで気づかれた方も多いかもしれませんがコンパイルコマンドscalacはかなり遅いです。 何回か連続してコンパイルしてみるとより強く感じられるでしょう。 (これでもScala2.9.1あたりからかなりの改善が続いています) 単純な解決方法としてはscalacではなくfscコマンドを使用することです。 fscコマンドもScalaのコードをコンパイルするためのコマンドです。 端的にscalacとの違いを説明するとコンパイル用のデーモンが起動するか否かです。 fscでは初回の起動時にデーモンが起動して2回目以降に再利用するため高速化が望めます。 デーモンの起動が気にならない環境であれば高速化するためにこちらを使う方が良いでしょう。 ```bash $ fsc HelloWorld.scala #2回目以降からはかなり早い $ scala HelloWorld #デーモンを終了させるときは... $ fsc -shutdown [Compile server exited] ``` ### Scalaならではのコードのさわり せっかくプログラムが書ける状態になったのでScalaっぽいコードをご紹介していきます。 それぞれの詳しい説明はここでは避けますが興味を持っていただければぜひとも調べてみてください。 #### 関数オブジェクト Scalaはファーストクラスファンクションという性質を持っています。 この性質もあり、Scalaはオブジェクト指向+関数型というのがよく知られる特徴となっています。 ファーストクラスファンクションとは「関数を普通のオブジェクトとして扱える」ということです。 以下に簡単な例を示します。 引数として1つの文字列を受け取り、画面出力する関数リテラルを作成して呼び出しています。 ```scala val helloFunction = { string:String => println(string) } //短く書くと val helloFuction = println(_:String) helloFunction("hello") ``` 標準APIの中で実際にコードを書いていくと特にリスト系クラスの引数として関数オブジェクトを使うことが多くなります。 以下の例では全てメソッドの引数として関数オブジェクトを渡しています。 Scala(関数型言語)ではこのように関数やリストの組み合わせで多くの問題を解決していきます。 ```scala //リストの中身をすべて表示↲ (1 to 10).foreach(println) //リストの中身を全て2倍して表示 (1 to 10).map(_ * 2).foreach(println) //リストの中身から偶数のみを抽出して表示 (1 to 10).filter(_ % 2 == 0).foreach(println) ``` #### パターンマッチ パターンマッチとは平たく言えば条件分岐です。 記述方法はJavaのswitch文に似ていますが、条件の表現がかなり柔軟です。 パターンマッチは特に<strong>caseクラス</strong>と組み合わせるとより強力さを増します。 以下の例では性別によって出力の内容を変えています。 以下の例でもわかるようにパターンマッチでは変数の束縛もできます。(パターンマッチ内のnameやage) ```scala case class Person(name:String, age:Int, gender:String) val person = Person("Matsuda", 26, "man") person match { case Person(name, age, "man" ) => println("Hello, Mr.%s! Are you %d years old?".format(name, age)); case Person(name, _ , "woman") => println("Hello, Mrs.%s!".format(name)) //女性には失礼なので年齢は聞かない case Person(name, _ , _ ) => println("Hello, %s!".format(name)) //複雑な性別の方は名前だけを呼ぶ } ``` #### お手軽な並列処理 Scala2.9以降、標準で並列化APIがサポートされました。 このAPIを使うとマシンのコア数に応じた数のスレッドが並列実行されます。 いくつかのコレクションリストでサポートされており使い方は非常に簡単です。 ```scala (1 to 1000).toList.par.foreach(println) //1000までの表示を並列実行する ``` #### Maybeモナド プログラマなら誰でもNullPonterに陥ったことがあると思います。 ScalaではMaybeモナドの実装となるOptionというクラスが用意されており、これによってもうNullPointerに悩まされることはなくなります。 Maybeモナドは「値があるかもしれないし、ないかもしれない」ということを表した概念だと思っていただければ良いでしょう。 実装の説明としてはOptionクラスは抽象クラスです。 OptionクラスのサブクラスとしてSomeとNoneがあります。 Some=値がある、None=値が無い、です。 今回はListのメソッドの中で返却値としてOptionオブジェクトを返却するものを使用してみます。 Optionオブジェクトを取得したあとはforeachメソッドで処理を続けたり、match文で処理を分けることが出来ます。 ```scala val list = List("a", "b", "c") val optionValue = list.find(_ == "d") //値があれば表示する optionValue.foreach(println) //値の有無で処理を分ける optionValue match { case Some(value) => println("value is %s".format(value)) case None => println("no value") } ``` これまでの多くの言語ではnullを返して使う側でnullチェックをする、という実装方法を取ってきました。 こうした実装方法とMaybeモナドを使った実装との違いは<strong>「コンパイラがnullがあるかもしれないということを検知できるかどうか」</strong>というところにあります。 Scalaの場合はOptionクラスを使用せざるをえないのでコンパイルの時点でNullに対する対応ができていなければコンパイルが通りません。 <h2>実践的な開発環境</h2> 実践的な開発環境について取り上げていきます。 ここでは簡単な紹介に留めておきますので、実際に使い込もう!という時の参考になればと思います。 <h3>CUI</h3> CUIだけで開発する場合は sbt を使うことが多いです。 sbt(Simple Build Tool)は名前の通りScalaのビルドツールです。 <a href="http://www.scala-sbt.org/">公式サイトからのダウンロード</a>やbrew、portなどでインストールできます。 実際の開発時は以下のようなコマンドを使っていきます。 ソースコードの変更ごとに自動的にコマンド実行もできるのでかなり便利です。 ```bash $ mkdir scala_sample_project $ cd scala_sample_project $ sbt #プロジェクトの初期設定がされます。 #ここで開発作業。 #scala_sample_project や src/main/scala、src/test/scala 内のファイルがデフォルトで読み込み対象になります。 #変更したい場合は scala_sample_project/build.sbt にビルド設定を書いていきます。 #開発しながら適宜以下を実行していく $ sbt compile #コンパイル $ sbt test #テスト実行 $ sbt run #実行 #プロセスを維持し、ソースコードに変更がある場合に自動的に実行する $ sbt ~compile $ sbt ~test $ sbt ~run ``` <h3>GUI</h3> 最近はGUI(IDE)を使う場合にはEclipseやIntelliJの方が多いのではないでしょうか。 Scalaはそのどちらにもプラグインが存在しています。 それぞれの詳細は<a href=""http://scala-ide.org/>Scala IDE for Eclipse</a>や<a href="http://plugins.jetbrains.com/plugin/1347">JetBrains Plugin Repository</a>をご覧ください。 上記であげたsbtと連携することも可能です。 <h2>さいごに</h2> Scalaに初めて触れられるように環境構築+αについて触れました。 私の周りにはScalaを使っている人はほとんどいないので、 2014年は更にScalaユーザが増えコミュニティが活発化していると嬉しいです。 それでは良いお年を! [shoma2daをフォロー](https://twitter.com/intent/user?screen_name=shoma2da) > [QiitaのAndroidクライアントアプリ](https://play.google.com/store/apps/details?id=com.tech_tec.qiitarian&hl=ja)をリリースしました! > 是非ともお試しください! |
|
| 575位 |
|
|||
|
11:57:47 |
|
|
ドットインストールの CoffeeScript 入門より
http://dotinstall.com/lessons/basic_coffeescript ## CoffeeScript とは? * JavaScript に変換可能な言語 * JavaScript に比べて… * 使いやすい * 書きやすい * 高機能 * 最近人気 * hello.coffee -> hello.js といった感じでコンパイル ## 基本 * 行末までのコメントには `#` を使う * 複数行のコメントは `###` で挟む * varは不要 * 行末のセミコロンは不要 * ブロックはインデントで表現 * 丸括弧は曖昧性がない場合は省略可能 CoffeeScript: message = "Hello, world!" if message.length > 1 alert message JavaScript: var message; message = "Hello, world!"; if (message.length > 1) alert(message); ## 文字列 * ダブルクオーテーション `"` で囲む * 複数行文字列もOK * ヒアドキュメントは `"""` で囲む CoffeeScript: s = "Hello, world!" s = "Hello, world!" s = """Hello, world!""" JavaScript: var s; s = "Hello, world!"; s = "Hello, world!"; s = "Hello,\nworld!"; `#{var}` のように書くことで文字列中の変数展開ができる. CoffeeScript: name = "questbeat" alert "Hello, #{name}!" JavaScript: var name; name = "questbeat"; alert("Hello, " + name + "!"); ## 配列 CoffeeScript ではカンマではなく改行で区切ることができる. CoffeeScript: m = [ 1, 5, 8 2, 4, 2 ] JavaScript: var m; m = [ 1, 5, 8, 2, 4, 2, ]; `[a..b]` のように書くと, 範囲 `a` から `b` の配列を作成できる. また, `[a…b]` のように書くと `b` を含まなくなる. CoffeeScript: m = [1..5] m = [1...5] JavaScript: var m; m = [1, 2, 3, 4, 5]; m = [1, 2, 3, 4]; ## 連想配列 ブロック(波括弧)がインデントで表現できるので, 複雑な連想配列が簡単に書ける. CoffeeScript: h = "questbeat": "sales": 200 "cost": 80 "kuebi": "sales": 250 "cost": 40 JavaScript: var h; h = { "questbeat": { "sales": 200, "cost": 80 }, "kuebi": { "sales": 250, "cost": 40 } }; ## 条件分岐 ### if インデントでスッキリ書ける. CoffeeScript: signal = "red" if signal == "red" alert "Stop!" else if signal == "green" alert "Go!" else alert "Caution!" JavaScript: var signal; signal = "red"; if (signal === "red") { alert("Stop!"); } else if (signal === "green") { alert("Go!"); } else { alert("Caution!"); } 後置の `if` も可能. CoffeeScript: x = 20 alert "OK!" if x is 20 JavaScript: var x; x = 20; if (x === 20) { alert("OK!"); } また, 比較演算子がいくつか追加されている. * is (===) * isnt (!==) * not (!) * and (&&) * or (||) * ? (存在チェック) 連結比較式も書ける. CoffeeScript: x = 20 if 10 < x < 30 alert "OK!" JavaScript: var x; x = 20; if ((10 < x && x < 30)) { alert("OK!"); } ### switch `case` ではなく `when`, `default` ではなく `else`. CoffeeScript: signal = "red" switch signal when "red" alert "Stop!" when "green" alert "Go!" else alert "Caution!" JavaScript: var signal; signal = "red"; switch (signal) { case "red": alert("Stop!"); break; case "green": alert("Go!"); break; default: alert("Caution!"); } ## ループ ### for CoffeeScript: for i in [0..3] alert i JavaScript: var i, _i; for (i = _i = 0; _i <= 3; i = ++_i) { alert(i); } ### 配列のループ `for-in` を使って列挙. 要素のインデックスも取得できる. CoffeeScript: names = ["questbeat", "kuebi", "qb"] for name, index in names alert "Hello, #{name} (#{index})!" JavaScript: var index, name, names, _i, _len; names = ["questbeat", "kuebi", "qb"]; for (index = _i = 0, _len = names.length; _i < _len; index = ++_i) { name = names[index]; alert("Hello, " + name + " (" + index + ")!"); } ### 連想配列のループ `for-of` を使う. キーと値を取り出せる. CoffeeScript: sales = "questbeat": 100 "kuebi": 200 "qb": 300 for key, value of sales alert "#{key}: #{value}" JavaScript: var key, sales, value; sales = { "questbeat": 100, "kuebi": 200, "qb": 300 }; for (key in sales) { value = sales[key]; alert("" + key + ": " + value); } ## 関数 ### 基本形 関数には `->` を使う. CoffeeScript: hello = -> alert "Hello!" JavaScript: function hello() { alert("Hello!"); }; ### 引数 引数アリの関数は `(args, …) ->` と書く. CoffeeScript: hello = (name) -> alert "Hello, #(name)!" hello("questbeat") JavaScript: var hello; hello = function(name) { return alert("Hello, " + name + "!"); }; hello("questbeat"); ### 引数のデフォルト値 `label = default` のように書く. CoffeeScript: hello = (name = "questbeat") -> alert "Hello, #(name)!" hello() JavaScript: var hello; hello = function(name) { if (name == null) name = "questbeat"; return alert("Hello, " + name + "!"); }; hello()); ### 返り値のある関数 明示的に `return` する必要はなく, 最後に計算された値が自動的に返り値になる. CoffeeScript: sum = (a, b) -> a + b JavaScript: var sum; sum = function(a, b) { return a + b; }; ## CoffeeScript の導入 * Web上で試せるインタラクティブツール http://coffeescript.org/ * node.jsの管理をhomebrewからnodebrewに変えて, npmをインストール http://qiita.com/somtd@github/items/bd413e89d2db22ab795e * 正しいcoffee-scriptの環境づくり vim http://qiita.com/alpaca_taichou/items/fb442cfa78f91634cfaa |
|
| 576位 |
|
|||
|
20:49:44 |
(無職 所属) |
|
`man git-push` して `-u` オプションについて調べた。
``` -u, --set-upstream For every branch that is up to date or successfully pushed, add upstream (tracking) reference, used by argument-less git-pull(1) and other commands. For more information, see branch.<name>.merge in git-config(1). ``` *全く何を言ってるのかわからない!* ## 答え ネットで検索してみたら答えがわかった。 `git push -u origin master` とすると次回から `git push` だけで勝手に origin master で push してくれる。 ちなみに `git push` とかの マニュアルは全て `man git-push` とか `man git-pull` のようにダッシュで繋げて書けば見れます。`man git push` だと git と push それぞれのマニュアルを探そうとするので気をつけましょう。 ## 参考 ブランチ http://marina.sys.wakayama-u.ac.jp/vmlab/git/node6.html |
|
| 577位 |
|
|||
|
11:24:16 |
|
|
pythonを使ってマルチスレッド処理を行ないたい. pythonでスレッドを扱うにはthreadingモジュールを利用する. threadingモジュールでスレッドを扱うには二つの方法がある. 1. threading.Threadのサブクラスを作る. 2. threading.Threadのインスタンスを作る. それぞれについてサンプルを示す. ## サブクラスを作る サブクラスを作り,runにオーバーライドしてスレッド処理を実装する. 以下の例ではt秒おきにn回,時刻を表示するメソッドをrunとして実装した. ```python import threading import time import datetime class TestThread(threading.Thread): """docstring for TestThread""" def __init__(self, n, t): super(TestThread, self).__init__() self.n = n self.t = t def run(self): print " === start sub thread (sub class) === " for i in range(self.n): time.sleep(self.t) print "sub thread (sub class) : " + str(datetime.datetime.today()) print " === end sub thread (sub class) === " ``` このクラスのインスタンスを生成して実行するには次のようにすればいい. ``` python def hoge(n, t): print " === start sub thread (method) === " for i in range(n): time.sleep(t) print "[%s] sub thread (method) : " % threading.currentThread().getName() + str(datetime.datetime.today()) print " === end sub thread (method) === " if __name__ == '__main__': th_cl = TestThread(5, 5) th_cl.start() time.sleep(1) print " === start main thread (main) === " for i in range(5): time.sleep(10) print "main thread : " + str(datetime.datetime.today()) print " == end main thread === " ``` 結果,次のような出力を得る. ``` === start sub thread (sub class) === === start main thread (main) === sub thread (sub class) : 2014-01-23 11:11:59.124198 sub thread (sub class) : 2014-01-23 11:12:04.124809 main thread : 2014-01-23 11:12:05.124595 sub thread (sub class) : 2014-01-23 11:12:09.125251 sub thread (sub class) : 2014-01-23 11:12:14.125697 main thread : 2014-01-23 11:12:15.124054 sub thread (sub class) : 2014-01-23 11:12:19.126135 === end sub thread (sub class) === main thread : 2014-01-23 11:12:25.124051 main thread : 2014-01-23 11:12:35.124069 main thread : 2014-01-23 11:12:45.123824 == end main thread === ``` サブクラスに実装したメソッドが5秒おきに,メインが10秒おきに出力していることがわかる. ## インスタンスを作る まず処理内容を記述した関数を実装する. ```python def hoge(n, t): print " === start sub thread (method) === " for i in range(n): time.sleep(t) print "[%s] sub thread (method) : " % threading.currentThread().getName() + str(datetime.datetime.today()) print " === end sub thread (method) === " ``` これをスレッド処理にわたすにはthreading.Threadのインスタンスを生成する. 実行するには先ほどと同じくstartメソッドを使う. ```python th_me = threading.Thread(target=hoge, name="th_me", args=(5, 5,)) th_me.start() ``` 前のサブクラスとあわせたコードを以下のとおり. ```python import threading import time import datetime class TestThread(threading.Thread): """docstring for TestThread""" def __init__(self, n, t): super(TestThread, self).__init__() self.n = n self.t = t def run(self): print " === start sub thread (sub class) === " for i in range(self.n): time.sleep(self.t) print "sub thread (sub class) : " + str(datetime.datetime.today()) print " === end sub thread (sub class) === " def hoge(n, t): print " === start sub thread (method) === " for i in range(n): time.sleep(t) print "[%s] sub thread (method) : " % threading.currentThread().getName() + str(datetime.datetime.today()) print " === end sub thread (method) === " if __name__ == '__main__': th_cl = TestThread(5, 5) th_cl.start() time.sleep(1) th_me = threading.Thread(target=hoge, name="th_me", args=(5, 5,)) th_me.start() time.sleep(1) print " === start main thread (main) === " for i in range(5): time.sleep(10) print "main thread : " + str(datetime.datetime.today()) print " == end main thread === " ``` このスクリプトを実行すると出力は ``` === start sub thread (sub class) === === start sub thread (method) === === start main thread (main) === sub thread (sub class) : 2014-01-23 11:20:26.223721 [th_me] sub thread (method) : 2014-01-23 11:20:27.224347 sub thread (sub class) : 2014-01-23 11:20:31.223980 [th_me] sub thread (method) : 2014-01-23 11:20:32.224861 main thread : 2014-01-23 11:20:33.224297 sub thread (sub class) : 2014-01-23 11:20:36.224479 [th_me] sub thread (method) : 2014-01-23 11:20:37.225207 sub thread (sub class) : 2014-01-23 11:20:41.224737 [th_me] sub thread (method) : 2014-01-23 11:20:42.225643 main thread : 2014-01-23 11:20:43.224285 sub thread (sub class) : 2014-01-23 11:20:46.225189 === end sub thread (sub class) === [th_me] sub thread (method) : 2014-01-23 11:20:47.226193 === end sub thread (method) === main thread : 2014-01-23 11:20:53.223297 main thread : 2014-01-23 11:21:03.223190 main thread : 2014-01-23 11:21:13.222211 == end main thread === ``` 3つの処理が並列してはしり,処理のひとつひとつが思ったとおりにコントロールできていることがわかります. |
|
| 578位 |
|
|||
|
11:25:30 |
(株式会社trippiece 所属) |
|
メモ開放。InnoDBの行ロック関連について、それぞれの項目が必ずしも並列関係にあるわけではないが、以下のようにまとめていく。
* 排他ロックと共有ロック * SELECT ~ FOR UPDATE * SELECT ~ LOCK IN SHARE MODE ### 排他ロックと共有ロック 読み取りを許すかどうかの違い。排他ロックは対象行を全てのクエリからロックするため、UPDATEやDELETEなどの更新クエリはもちろん、SELECTなどの読み取りクエリも通さない。共有ロックは更新クエリを通さないが、読み取りクエリは通す。 (追記:排他ロックは分離レベルによってはSELECTを通すとのこと。 [公式](https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_exclusive_lock) ) 排他ロックは全てのクエリを通さず、共有ロックは排他ロックを伴うクエリを通さない、と言い換えたほうがいいかもしれない。 [公式](http://dev.mysql.com/doc/refman/5.0/en/innodb-lock-modes.html)では共有ロックは同トランザクション内のselectを許し、排他ロックは同トランザクション内のupdateとdeleteを許すとあるが、共有ロックが同トランザクションのupdateやdeleteを許さないわけではないし、排他ロックが同トランザクションのselectを許さないわけではない。 --- INSERTは対象行に排他ロックをかけるが、重複キーエラーが起きた時は対象行に共有ロックをかける。それはINSERT IGNOREの場合も同じ。 ``` mysql> insert ignore into products values (15, 'ipos', 23000); #Tx1 Query OK, 0 rows affected (0.00 sec) ``` ``` mysql> select * from products where product_id = 15 lock in share mode; #Tx1 +------------+--------------+-------+ | product_id | product_name | price | +------------+--------------+-------+ | 15 | ipod | 900 | +------------+--------------+-------+ 1 row in set (0.00 sec) mysql> select * from products where product_id = 15 for update; #待機 ``` --- 既存レコードに共有ロックをかけ、別トランザクションがその行に排他ロックをかけようとすると待機状態になる。この時、最初のトランザクション内で同じレコードに排他ロックをかけようとするとデッドロックになる。これは、別トランザクションでロック待機が存在していると、既に取得している共有ロックを排他ロックにアップグレードできず、待機状態になってしまうためである。これが共有ロックであるならばアップグレードは必要ないので、デッドロックは起きない。 ``` mysql> select * from products where product_id = 15 lock in share mode; #Tx1 +------------+--------------+-------+ | product_id | product_name | price | +------------+--------------+-------+ | 15 | ipod | 900 | +------------+--------------+-------+ 1 row in set (0.00 sec) ``` ``` mysql> select * from products where product_id = 15 for update; #Tx2 ``` ``` mysql> select * from products where product_id = 15 for update; #Tx1 ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction ``` ### SELECT ~ FOR UPDATE 排他ロックの一種。取得した値を利用して更新をかける際に使われることを想定しており、全てのクエリを通さない。 既存レコードに排他ロックをかけ合うことはできないが、固有値検索条件として存在しないレコードを指定するとギャップロックになる。ギャップロック同士は競合しないのでselect for updateをかけ合うことができるが、insertの挿入インテンションギャップロックと競合するので、その箇所にinsertをすることは許さない。 [参考1](http://dev.mysql.com/doc/refman/5.1-olh/ja/innodb-locks-set.html) [参考2](http://dev.mysql.com/doc/refman/5.1-olh/ja/innodb-record-level-locks.html) ``` mysql> select * from products where product_id = 17 for update; #Tx1 Empty set (0.00 sec) ``` ``` mysql> select * from products where product_id = 17 for update; #Tx2 Empty set (0.00 sec) ``` ``` mysql> select * from products where product_id = 17 lock in share mode; #Tx1 Empty set (0.00 sec) ``` ``` mysql> select * from products where product_id = 17 lock in share mode; #Tx2 Empty set (0.00 sec) ``` ``` mysql> insert into products values (17, 'name', 773463); #Tx1 # 待機状態 ``` ### SELECT ~ LOCK IN SHARE MODE 共有ロックの一種。排他ロックを伴うクエリを通さないため、UPDATEやDELETEはもちろん、SELECT ~ FOR UPDATEも通さない。SELECT ~ LOCK IN SHARE MODE同士のような、共有ロックを伴うクエリは通す。 ``` mysql> select * from products where product_id = 8 lock in share mode; #Tx1 +------------+--------------+-------+ | product_id | product_name | price | +------------+--------------+-------+ | 8 | thinkpad | 150 | +------------+--------------+-------+ 1 row in set (0.00 sec) ``` ``` mysql> select * from products where product_id = 8 for update; #Tx2 ``` --- 共有ロックしたレコードに別トランザクションが共有ロックする場合、ロックは共存する。結果、そのトランザクション外からの排他ロックを伴うクエリは弾かれる ``` mysql> select * from products where product_id = 8 lock in share mode; #Tx1 +------------+--------------+-------+ | product_id | product_name | price | +------------+--------------+-------+ | 8 | thinkpad | 150 | +------------+--------------+-------+ 1 row in set (0.00 sec) ``` ``` mysql> select * from products where product_id = 8 lock in share mode; #Tx2 +------------+--------------+-------+ | product_id | product_name | price | +------------+--------------+-------+ | 8 | thinkpad | 150 | +------------+--------------+-------+ 1 row in set (0.00 sec) ``` ``` mysql> select * from products where product_id = 8 for update; #Tx1 ``` ここでTx2が排他ロックをかけた場合、デッドロックになる。 対象レコードが存在しない場合はギャップロックがかかり、ギャップロック同士は競合しないが、insertの挿入インテンションギャップロックと競合するので、insertは通らない。 ``` mysql> select * from products where product_id = 17 for update; #Tx1 Empty set (0.00 sec) ``` ``` mysql> select * from products where product_id = 17 for update; #Tx2 Empty set (0.00 sec) ``` ``` mysql> select * from products where product_id = 17 lock in share mode; #Tx1 Empty set (0.00 sec) ``` ``` mysql> select * from products where product_id = 17 lock in share mode; #Tx2 Empty set (0.00 sec) ``` ``` mysql> insert into products values (17, 'name', 773463); #Tx1 # 待機状態 ``` |
|
| 579位 |
|
|||
|
03:00:25 |
|
|
最近扱ってるデータの可視化を模索してます。
その中でのオススメなJavaScript Packageを以下に紹介していきます。 ## amcharts - http://www.amcharts.com/ - MicroSoftやAmazonも使用していライブラリ。 - ここで紹介する中でも一番多機能だと思います。デザインもフラットできれいなのでチャート単体でも映えるので、色々なところに使えそうです。 ## Chart.js - http://www.chartjs.org - HTML5 canvasベースのJSライブラリ。フラットなグラフをかける。 - MIT License [2016/05/03追記] - @nishidemasami さんからの指摘で修正 ## HighCarts.js - http://www.highcharts.com/ - ベクターデータ形式でグラフ描画できる。 とにかく綺麗。オプションしだいで細部までいじれるので好き。 Gemもあるのでとにかく便利です。 [lazy_high_charts](https://github.com/michelson/lazy_high_charts) このGemを使う際はController上に直接書くとControllerをかなり汚すのでHelper化してしまいましょう。 [追記]商用に限り有料 ## Raphaël—JavaScript Library - http://raphaeljs.com/ - Qiitaで使用されているらしいJSライブラリ。可愛いグラフをかける。 ## D3.js - http://d3js.org/ - グラフ描画ももちろんできるが、専門は情報可視化。インフォグラフィクスとかもできちゃいますよ。 ## C3.js [2016/05/03追記] - http://c3js.org/ - D3.jsを元にした扱いやすいチャートパッケージ |
|
| 580位 |
|
|||
|
01:41:15 |
(pixiv Inc. 所属) |
|
今まで(恥ずかしながら)適当だった Emacs の設定を改革したらとても快適だったので紹介します
今回紹介したいのは * `init-loader.el` * `package.el` です # init-loader.el 今まで `.emacs` に適当に設定を書いていたので以下の様な問題で悩んでいました * 環境依存(具体的には Mac と Ubuntu)な設定を分離出来ないので環境構築に手作業が発生する * 設定を追加しても上の事情で各々の環境に手作業で導入する必要がある ということで `init-loader.el` の出番です https://github.com/emacs-jp/init-loader `~/.emacs.d/site-lisp/` 以下に `init-loader.el` を置いて `~/.emacs.d/init.el` に ```cl:init.el ;; ~/.emacs.d/site-lisp 以下全部読み込み (let ((default-directory (expand-file-name "~/.emacs.d/site-lisp"))) (add-to-list 'load-path default-directory) (if (fboundp 'normal-top-level-add-subdirs-to-load-path) (normal-top-level-add-subdirs-to-load-path))) (require 'init-loader) (setq init-loader-show-log-after-init nil) (init-loader-load "~/.emacs.d/inits") ``` と書きます 最初に `~/.emacs.d/site-lisp` 以下のものを全部読み込んでしまいます するとそれ以下に置いた `init-loader.el` が読み込まれますので単純に require するだけで実行できます これで `~/.emacs.d/inits` ディレクトリ以下に置かれた設定ファイルを読み込ませることができます ちなみにデフォルトもこの値なので上の設定は冗長ですが,今回は明示的に書いてあります `~/.emacs.d/inits` ディレクトリ以下のファイルは以下の設定に従って作成します * 環境に依存しない設定はファイル名の最初に 2 桁の数字をつける * 番号は優先度で 00 が最初に読み込まれて, 99 が最後に読み込まれる * 同じ数字を付けても良い * 環境依存な設定はそれぞれの環境のプレフィックスをファイル名の最初につける プレフィックスは以下のようになります | 環境 | prefix | |:----------------|------------------:| | Meadow | meadow | | Carbon Emacs | carbon-emacs | | Cocoa Emacs | cocoa-emacs | | emacs -nw | nw | | Windows | windows | | Linux | linux | なので `00-keybind.el` とか `cocoa-emacs-appearance.el` のように `prefix` - `機能名` `.el` とファイル名をつけます ファイルを機能ごとに分割するとメンテナンス性が格段に向上するので機能ごとにファイルを分割して作成してください 番号に特に決まりはないと思いますが,次に紹介する `package.el` 周りの設定は 20 番台に書くことが多いようです # package.el Emacs でも `apt-get` とか `gem` とか `cpanm` 的なあれを使いたくなります Emacs24 から標準で入った `package.el` を使えば簡単に使えるようになるので使います ただデフォルトのままでは入れられる種類が少ないので少し設定を追加します `~/.emacs.d/inits` 以下に `20-package.el` を置いて ```cl:20-package.el (require 'package) (add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/")) (add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/")) (package-initialize) ``` と書きます これで `M-x package-list-packages` とやるとインストールできるパッケージのリストが見れて,この上からインストールができます * インストールしたいパッケージの上で `i` キーを押す * 選択し終わったら `x` を押す これでインストールができます リストの取得でインターネットに接続しないでキャッシュから見たい時は `M-x package-list-packages-no-fetch` とします また入れるパッケージの名前がわかっているなら `M-x package-install` としてもインストールできます ## 自動インストール設定 最初に必ずインストールするものがあるなら自動設定しておきたいので設定します 先ほどの設定の後に(私は `20-package.el` の中に書いてしまっています) ```cl: (require 'cl) (defvar installing-package-list '( ;; ここに使っているパッケージを書く。 php-mode scala-mode markdown-mode scss-mode haskell-mode google-c-style yaml-mode open-junk-file )) (let ((not-installed (loop for x in installing-package-list when (not (package-installed-p x)) collect x))) (when not-installed (package-refresh-contents) (dolist (pkg not-installed) (package-install pkg)))) ``` こんな感じのことを書いておきます こうするとインストールされていなければ自動でインストールされるようになります # サンプルファイル 以上の設定をしたものを [catatsuy/dot.emacs.d · GitHub](https://github.com/catatsuy/dot.emacs.d) に置きました 参考程度に御覧ください |
|
| 581位 |
|
|||
|
21:08:30 |
|
|
## はじめに
みなさん、スマフォのゲームは好きですか? 僕は、 * 片手で操作できる * 画面は縦向き * 2D * 短時間でプレイできる * 音なしでも楽しめる * 側近的な奴がスタミナとかガチャとか言い出さない ような、電車やトイレでサクッと遊べるゲームが好きです。 今日は、そんなお手軽ゲームの代表的存在**ブロック崩し**をSprite Kitで作ってみます。 こちらが完成イメージです。  ## Sprite Kitとは Sprite Kitとは、iOS・Mac OS X向けの2Dゲームを作るための、Apple純正フレームワークです。 ### メリット * OS標準機能 * UIKit・AppKitと連携しやすい * 物理演算やパーティクルが簡単 ### デメリット * iOS 7, Max OS X 10.9以降が必要 * Andoroid対応不可([Cocos2d-x](http://www.cocos2d-x.org/)で作りましょう) * 3D未対応([Unity](http://unity3d.com/)で作りましょう) * 機能不足(結局[Kobold Kit](http://koboldkit.com/)などの助けが欲しくなるかも) ### メリット?デメリット?? * 大きなアップデートは年1回(変更に振り回されずには済むけど、他のフレームワークの進化に置いていかれる) という感じでしょうか。 ## 主な登場人物 以下が、Sprite Kitに登場する主な要素です。 他にもありますが、ひとまずこのあたりを知っていれば、あとはリファレンスを見ながら開発できます。  \# | 要素 | 説明 --- | --- | --- 1 | SKView | UIViewのサブクラス。Sprite Kitの描画を担当します。 2 | SKScene | ゲームの1画面に相当。この中にいろいろな子ノードを追加してゲーム画面を作っていきます。 3 | SKTransition | シーン遷移時のアニメーションを指定します。フェードイン・アウトやドアなど様々な種類があります。SKViewの`presentScene:transition:`メソッドで指定します。 4 | NodeCount, DrawCount, FPS | 開発用の情報表示です。SKViewの`showsDrawCount`, `showsNodeCount`, `showsFPS`をYESにすることで描画されます。 5 | SKSpriteNode | テクスチャ画像(SKTexture)を表示するためのノードです。色を指定して矩形の表示もできます。 6 | SKLabelNode | 1行のテキストを表示するノードです。 7 | SKShapeNode | CGPathを使った図形を表示するノードです。 8 | SKEmitterNode | パーティクルを表示するノードです。 9 | SKAction | ノードをアニメーションさせる時に利用します。SKNodeの`runAction:`で実行します。 なお、SKScene及び全てのノードはSKNodeのサブクラスです。`addChild:`や`runAction:`などの基本的な操作はそこから継承されています。 それでは、これらの要素を使ってゲームを作っていきます。 ## プロジェクトの作成 まずはプロジェクトを作ります。 **SpriteKit Game**テンプレートも用意されていますが、今回はStoryBoardsを使わないため、**Empty Application**から作ります。 **SpriteKit.frameWork**はXcode 5が自動でリンクしてくれるので、Build Phasesから追加する必要はありません。 まずは、ViewControllerを作ります。 `loadView`でself.viewをSKViewに差し替えて、`viewDidLoad`で情報表示の設定をした後、`SKScene`をインスタンス化して表示します。 また、ステータスバーも非表示にします。 ```objc:SJViewController.m @import SpriteKit; - (void)loadView { SKView *skView = [[SKView alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.view = skView; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. SKView *skView = (SKView *)self.view; skView.showsDrawCount = YES; skView.showsNodeCount = YES; skView.showsFPS = YES; SKScene *scene = [SKScene sceneWithSize:self.view.bounds.size]; [skView presentScene:scene]; } - (BOOL)prefersStatusBarHidden { return YES; } @end ``` これをAppDelegateで`rootViewController`に設定します。 ```objc:SJAppDelegate.m #import "SJViewController.h" - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. SJViewController *viewController = SJViewController.new; _window.rootViewController = viewController; [self.window makeKeyAndVisible]; return YES; } ``` この状態で実行すると、黒い背景にFPS等の情報だけがある画面が表示されます。  これでSprite Kitでの開発準備が整いました。 ## 初めてのシーン 最初の画面として、タイトルを表示するだけの単純な画面を作ります。 SKSceneのサブクラスとしてTitleSceneを追加して、SKLabelNodeを1つ追加します。 ```objc:SJTitleScene.m - (id)initWithSize:(CGSize)size { self = [super initWithSize:size]; if (self) { SKLabelNode *titleLabel = [SKLabelNode labelNodeWithFontNamed:@"HelveticaNeue"]; titleLabel.text = @"BREAKOUT!"; titleLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)); titleLabel.fontSize = 50.0f; [self addChild:titleLabel]; } return self; } ``` ViewControllerでこのシーンをSKViewに表示します。 ```objc:SJViewController.m import "SJTitleScene.h" - (void)viewDidLoad { /* 省略 */ //SKScene *scene = [SKScene sceneWithSize:self.view.bounds.size]; SKScene *scene = [SJTitleScene sceneWithSize:self.view.bounds.size]; /* 省略 */ } ``` これで中央に**BREAKOUT!**と表示される画面ができました。  ## ブロックの表示 それでは、ブロックを表示しましょう。 まず、設定をJSON(pistを記事に貼ると読みづらいため)で書きます。 ```js:config.json { "block" : { "margin" : 16.0, "width" : 34.0, "height" : 16.0, "rows" : 5, "max_life" : 5 }, } ``` 次にSKSceneのサブクラスとしてPlaySceneを追加し、設定を元にブロックを配置していきます。 設定は`initialize`で読み込んで、static変数`config`に保持しています。 `addBlocks`では、幅・高さ・マージン・rowsなどから表示できるブロック数を計算して表示しています。 また、`newBlock`の中では、ブロックの耐久力(`life`)をランダムに設定し`userData`に持たせて、それに応じて`updateBlockAplha:`メソッドで透明度を変化させています。 本来はBlockNodeをSKSpriteNodeのサブクラスとして作るべきところですが、簡略化のためSceneの中で処理しています。 また、今回は画像を使わないため`spriteNodeWithColor`で矩形にしていますが、通常は`spriteNodeWithImageNamed`や`spriteNodeWithTexture`でテクスチャ画像を指定して使います。 なお、`SKColor`はiOSならUIColor、MacならNSColorを返してくれるマクロです。 ```objc:SJPlayScene.m - (id)initWithSize:(CGSize)size { self = [super initWithSize:size]; if (self) { [self addBlocks]; } return self; } static NSDictionary *config = nil; + (void)initialize { NSString *path = [[NSBundle mainBundle] pathForResource:@"config" ofType:@"json"]; NSData *data = [NSData dataWithContentsOfFile:path]; if (!config) { config = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil]; } } # pragma mark - Block - (void)addBlocks { int rows = [config[@"block"][@"rows"] intValue]; CGFloat margin = [config[@"block"][@"margin"] floatValue]; CGFloat width = [config[@"block"][@"width"] floatValue]; CGFloat height = [config[@"block"][@"height"] floatValue]; int cols = floor(CGRectGetWidth(self.frame) - margin) / (width + margin); CGFloat y = CGRectGetHeight(self.frame) - margin - height / 2; for (int i = 0; i < rows; i++) { CGFloat x = margin + width / 2; for (int j = 0; j < cols; j++) { SKNode *block = [self newBlock]; block.position = CGPointMake(x, y); x += width + margin; } y -= height + margin; } } - (SKNode *)newBlock { CGFloat width = [config[@"block"][@"width"] floatValue]; CGFloat height = [config[@"block"][@"height"] floatValue]; int maxLife = [config[@"block"][@"max_life"] floatValue]; SKSpriteNode *block = [SKSpriteNode spriteNodeWithColor:[SKColor cyanColor] size:CGSizeMake(width, height)]; block.name = @"block"; int life = (arc4random() % maxLife) + 1; block.userData = @{ @"life" : @(life) }.mutableCopy; [self updateBlockAlpha:block]; [self addChild:block]; return block; } - (void)updateBlockAlpha:(SKNode *)block { int life = [block.userData[@"life"] intValue]; block.alpha = life * 0.2f; } ``` そして、TitleSceneをタップするとPlaySceneに遷移するようにします。 Transitionは上にスライドするものを指定しています。 なお、今回はSKSceneでタップをハンドルするため明示的に設定していませんが、ノードでタップを受け付ける時は、`userInteractionEnabled`をYESにするのを忘れないようにしましょう。 ```objc:SJTitleScene.m #import "SJPlayScene.h" - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { SKScene *scene = [SJPlayScene sceneWithSize:self.size]; SKTransition *transition = [SKTransition pushWithDirection:SKTransitionDirectionUp duration:1.0f]; [self.view presentScene:scene transition:transition]; } ``` これでブロックが表示されました。  ## パドルとボール 次はパドルとボールを表示して、タップでパドルを移動するところまで実装します。 パドルとボール用の設定を追加します。 ```js:config.json "paddle" : { "width" : 70.0, "height" : 14.0, "y" : 40.0, }, "ball" : { "radius" : 6.0, }, ``` `addPaddle`で設定を元にパドルを表示します。 画面がタップされたときにボールがなければ、`addBall`を呼び出してボールを追加します。 ボールがあればプレイ中と判断し、パドルを等速度で動かします。 パドルやボールは`name`に設定した値を元に`childNodeWithName:`で探索しています。(インスタンス変数に保持しても良い) パドルの移動はSKActionで行なっています。 ```objc:SJPlayScene.m - (id)initWithSize:(CGSize)size { /* 省略 */ [self addPaddle]; /* 省略 */ } # pragma mark - Paddle - (void)addPaddle { CGFloat width = [config[@"paddle"][@"width"] floatValue]; CGFloat height = [config[@"paddle"][@"height"] floatValue]; CGFloat y = [config[@"paddle"][@"y"] floatValue]; SKSpriteNode *paddle = [SKSpriteNode spriteNodeWithColor:[SKColor brownColor] size:CGSizeMake(width, height)]; paddle.name = @"paddle"; paddle.position = CGPointMake(CGRectGetMidX(self.frame), y); [self addChild:paddle]; } - (SKNode *)paddleNode { return [self childNodeWithName:@"paddle"]; } # pragma mark - Ball - (void)addBall { CGFloat radius = [config[@"ball"][@"radius"] floatValue]; SKShapeNode *ball = [SKShapeNode node]; ball.name = @"ball"; ball.position = CGPointMake(CGRectGetMidX([self paddleNode].frame), CGRectGetMaxY([self paddleNode].frame) + radius); CGMutablePathRef path = CGPathCreateMutable(); CGPathAddArc(path, NULL, 0, 0, radius, 0, M_PI * 2, YES); ball.path = path; ball.fillColor = [SKColor yellowColor]; ball.strokeColor = [SKColor clearColor]; CGPathRelease(path); [self addChild:ball]; } - (SKNode *)ballNode { return [self childNodeWithName:@"ball"]; } # pragma mark - Touch - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if (![self ballNode]) { [self addBall]; return; } UITouch *touch = [touches anyObject]; CGPoint locaiton = [touch locationInNode:self]; CGFloat speed = [config[@"paddle"][@"speed"] floatValue]; CGFloat x = locaiton.x; CGFloat diff = abs(x - [self paddleNode].position.x); CGFloat duration = speed * diff; SKAction *move = [SKAction moveToX:x duration:duration]; [[self paddleNode] runAction:move]; } ``` これでパドルが表示され、 * 1度目のタップでボールを表示 * 2度目以降のタップでパドルが移動 できるようになりました。  ## 物理演算と当たり判定 Sprite Kitには物理エンジンが内蔵されています。 ノードに`physicsBody`を設定すれば物理エンジンで管理されるようになるため、それだけで物理法則にしたがって動いてくれます。この簡単さが物理エンジンがビルトインされているメリットです。 まずは、設定に`speed`と`velocity`を追加します。 ```js:config.json "paddle" : { "width" : 70.0, "height" : 14.0, "y" : 40.0, "speed" : 0.005 }, "ball" : { "radius" : 6.0, "velocity" : { "x" : 50.0, "y" : 120.0 } }, ``` 次にシーン、ブロック、パドル、ボールそれぞれに`physicsBody`を設定します。これだけでノードが重力の影響を受けるようになり、ノード同士が衝突(collision)するようになります。 以下の画像の赤枠が、`physicsBody`の設定されている箇所です。  なお、この枠は[PhysicsDebugger](https://github.com/ymc-thzi/PhysicsDebugger)で描画しています。 ```ruby:Podfile pod 'PhysicsDebugger', git: 'https://github.com/ymc-thzi/PhysicsDebugger.git' ``` ```objc:YourScene.m #import "YMCPhysicsDebugger.h" - (id)initWithSize:(CGSize)size { self = [super initWithSize:size]; if (self) { [YMCPhysicsDebugger init]; /* Create scene contens */ [self drawPhysicsBodies]; } } ``` とすれば利用できます。 `physicsBody`をただ設定するだけだと、全てのノードが重力によって落下していくだけです。ゲームとして成立するように設定を行ないましょう。 ブロック・パドルは固定するため`dynamic`をNOにしています。 ボールは、**固定はしないが重力は無視**するため、`affectedByGravity`をNOにして、`velocity`で力を加えて動かしています。 また、`restitution`を1.0fにすることで、跳ね返る時に力が減衰しないように、`linearDamping`、`friction`はそれぞれ0にして空気抵抗をなくしています。 `usesPreciseCollisionDetection`をYESにすることで、衝突判定が正確になります。 `categoryBitMask`はノードのタイプを判別するためのビットマスクです。`contactTestBitMask`に設定したものと接触(contact)した場合、`didBeginContact:`が呼ばれます。 delegateはシーンの`physicsWorld.contactDelegate`で設定します。 今回はselfを指定し、その中でブロックの耐久力を減らしています。 なお、delegateメソッドに渡されるオブジェクトは順不同のため、categoryBitMaskで並び替えてから処理する必要があります。 ```objc:SJPlayScene.m static const uint32_t blockCategory = 0x1 << 0; static const uint32_t ballCategory = 0x1 << 1; @interface SJPlayScene () <SKPhysicsContactDelegate> @end - (id)initWithSize:(CGSize)size { /* 省略 */ self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame]; self.physicsWorld.contactDelegate = self; /* 省略 */ } # pragma mark - Block - (SKNode *)newBlock { /* 省略 */ block.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:block.size]; block.physicsBody.dynamic = NO; block.physicsBody.categoryBitMask = blockCategory; /* 省略 */ } - (void)decreaseBlockLife:(SKNode *)block { int life = [block.userData[@"life"] intValue] - 1; block.userData[@"life"] = @(life); [self updateBlockAlpha:block]; } # pragma mark - Paddle - (void)addPaddle { /* 省略 */ paddle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:paddle.size]; paddle.physicsBody.dynamic = NO; /* 省略 */ } # pragma mark - Ball - (void)addBall { /* 省略 */ CGFloat velocityX = [config[@"ball"][@"velocity"][@"x"] floatValue]; CGFloat velocityY = [config[@"ball"][@"velocity"][@"y"] floatValue]; /* 省略 */ ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:radius]; ball.physicsBody.affectedByGravity = NO; ball.physicsBody.velocity = CGVectorMake(velocityX, velocityY); ball.physicsBody.restitution = 1.0f; ball.physicsBody.linearDamping = 0; ball.physicsBody.friction = 0; ball.physicsBody.usesPreciseCollisionDetection = YES; ball.physicsBody.categoryBitMask = ballCategory; ball.physicsBody.contactTestBitMask = blockCategory; /* 省略 */ } # pragma mark - SKPhysicsContactDelegate - (void)didBeginContact:(SKPhysicsContact *)contact { SKPhysicsBody *firstBody, *secondBody; if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) { firstBody = contact.bodyA; secondBody = contact.bodyB; } else { firstBody = contact.bodyB; secondBody = contact.bodyA; } if (firstBody.categoryBitMask & blockCategory) { if (secondBody.categoryBitMask & ballCategory) { [self decreaseBlockLife:firstBody.node]; } } } ``` ようやくゲームらしくなってきました。  ### collisionとcontact collision(衝突)はその物体に触れた時に跳ね返るか、contact(接触)はdelegateが呼ばれるか、です。 ちょっと日本語のイメージと逆な気もします。いわゆる**当たり判定**はcontactの方ですね。 それぞれ`collisionTestBitMask`と`contactTestBitMask`で設定します。 デフォルトではcollisionは全てのノードと、contactは無しになっているようです。 例えばボールの`collisionTestBitMask`でブロックを指定しないようにすると、以下のGIFのように貫通する表現ができます。(`contactTestBitMask`は設定したままなのでブロックは破壊されます)  ## パーティクルで爆発 終盤にさしかかってきました。 ここで少し演出を加えてみましょう。 パーティクルは、細かい粒子によって、炎や水といった自然界の曖昧なものを表現する技術です。Sprite Kitでは、これを簡単に作るためのノード(SKEmitterNode)やエディタ([Particle Emitter Editor](https://developer.apple.com/LIBRARY/IOS/documentation/IDEs/Conceptual/xcode_guide-particle_emitter/Introduction/Introduction.html))が用意されています。 New File…からファイルを追加します。 Resourceから**SpriteKit Particle File**を選択し、Particle templateは**Spark**、ファイル名は`spark.sks`とします。 すると以下のような**sks**ファイルが作られます。  これを、ブロックの破壊時にブロックの座標に表示して、すぐフェードアウトさせます。 ```objc:SJPlayScene.m # pragma mark - Block - (void)decreaseBlockLife:(SKNode *)block { /* 省略 */ if (life < 1) { [self removeNodeWithSpark:block]; } /* 省略 */ } # pragma mark - Utilities - (void)removeNodeWithSpark:(SKNode *)node { NSString *sparkPath = [[NSBundle mainBundle] pathForResource:@"spark" ofType:@"sks"]; SKEmitterNode *spark = [NSKeyedUnarchiver unarchiveObjectWithFile:sparkPath]; spark.position = node.position; spark.xScale = spark.yScale = 0.3f; [self addChild:spark]; SKAction *fadeOut = [SKAction fadeOutWithDuration:0.3f]; SKAction *remove = [SKAction removeFromParent]; SKAction *sequence = [SKAction sequence:@[fadeOut, remove]]; [spark runAction:sequence]; [node removeFromParent]; } ``` これでブロックの耐久力が0になると、爆発と共に消滅するようになりました。  ## 仕上げ 最後に、もう少しゲームらしくなるように調整しましょう。 ラベルとライフ(残機)の設定を追加します。 ```js:config.json "label" : { "margin" : 5.0, "font_size" : 14.0 }, "max_life" : 5 ``` ライフと何ステージ目かを保持するプロパティと、イニシャライザを追加します。 ```objc:SJPlayScene.h @property (nonatomic) int life; @property (nonatomic) int stage; - (id)initWithSize:(CGSize)size life:(int)life stage:(int)stage; ``` `initWithSize:`は`initWithSize:life:stage:`を呼び出すように変更します。 イニシャライザの中で、ライフやステージを表示するSKLabelNodeも追加しています。 なお、ノードは`addChild:`されたのが遅いほど前面に表示されるため、何も設定しないと下記のようにラベルが隠れてしまいます。  今回ラベルには`zPosition`を設定して、あとから追加されるボールよりも手前に表示されるようにしています。(zPositionの小さい順に描画され、デフォルトは0.0のため) また、ボールの`velocity`に`self.stage`をプラスしてステージが進むごとに難易度をあげています。 `blockNodes`が0になればステージクリアとして、PlaySceneをもう一度表示します。 ```objc:SJPlayScene.m #import "SJGameOverScene.h" - (id)initWithSize:(CGSize)size life:(int)life stage:(int)stage { self = [super initWithSize:size]; if (self) { self.life = life; self.stage = stage; [self addBlocks]; [self addPaddle]; [self addStageLabel]; [self addLifeLabel]; [self updateLifeLabel]; self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame]; self.physicsWorld.contactDelegate = self; } return self; } - (id)initWithSize:(CGSize)size { return [self initWithSize:size life:[config[@"max_life"] intValue] stage:1]; } # pragma mark - Block - (void)decreaseBlockLife:(SKNode *)block { /* 省略 */ if ([self blockNodes].count < 1) { [self nextLevel]; } /* 省略 */ } - (NSArray *)blockNodes { NSMutableArray *nodes = @[].mutableCopy; [self enumerateChildNodesWithName:@"block" usingBlock:^(SKNode *node, BOOL *stop) { [nodes addObject:node]; }]; return nodes; } # pragma mark - Ball - (void)addBall { /* 省略 */ ball.physicsBody.velocity = CGVectorMake(velocityX + self.stage, velocityY + self.stage); /* 省略 */ } # pragma mark - Label - (void)addStageLabel { CGFloat margin = [config[@"label"][@"margin"] floatValue]; CGFloat fontSize = [config[@"label"][@"font_size"] floatValue]; SKLabelNode *label = [SKLabelNode labelNodeWithFontNamed:@"HelveticaNeue-Bold"]; label.text = [NSString stringWithFormat:@"STAGE %d", _stage]; label.verticalAlignmentMode = SKLabelVerticalAlignmentModeTop; label.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeRight; label.position = CGPointMake(CGRectGetMaxX(self.frame) - margin, CGRectGetMaxY(self.frame) - margin); label.fontSize = fontSize; label.zPosition = 1.0f; [self addChild:label]; } - (void)addLifeLabel { CGFloat margin = [config[@"label"][@"margin"] floatValue]; CGFloat fontSize = [config[@"label"][@"font_size"] floatValue]; SKLabelNode *label = [SKLabelNode labelNodeWithFontNamed:@"HiraKakuProN-W3"]; label.verticalAlignmentMode = SKLabelVerticalAlignmentModeTop; label.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft; label.position = CGPointMake(margin, CGRectGetMaxY(self.frame) - margin); label.fontSize = fontSize; label.zPosition = 1.0f; label.color = [SKColor magentaColor]; label.colorBlendFactor = 1.0f; label.name = @"lifeLabel"; [self addChild:label]; } - (void)updateLifeLabel { NSMutableString *s = @"".mutableCopy; for (int i = 0; i < _life; i++) { [s appendString:@"♥"]; } [self lifeLabel].text = s; } - (SKLabelNode *)lifeLabel { return (SKLabelNode *)[self childNodeWithName:@"lifeLabel"]; } # pragma mark - Callbacks - (void)update:(NSTimeInterval)currentTime { if((int)currentTime % 5 == 0) { CGVector velocity = [self ballNode].physicsBody.velocity; velocity.dx *= 1.001f; velocity.dy *= 1.001f; [self ballNode].physicsBody.velocity = velocity; } } - (void)didEvaluateActions { CGFloat width = [config[@"paddle"][@"width"] floatValue]; CGPoint paddlePosition = [self paddleNode].position; if (paddlePosition.x < width / 2) { paddlePosition.x = width / 2; } else if (paddlePosition.x > CGRectGetWidth(self.frame) - width / 2) { paddlePosition.x = CGRectGetWidth(self.frame) - width / 2; } [self paddleNode].position = paddlePosition; } - (void)didSimulatePhysics { if ([self ballNode] && [self ballNode].position.y < [config[@"ball"][@"radius"] floatValue] * 2) { [self removeNodeWithSpark:[self ballNode]]; _life--; [self updateLifeLabel]; if (_life < 1) { [self gameOver]; } } } # pragma mark - Utilities - (void)gameOver { SKScene *scene = [SJGameOverScene sceneWithSize:self.size]; SKTransition *transition = [SKTransition pushWithDirection:SKTransitionDirectionDown duration:1.0f]; [self.view presentScene:scene transition:transition]; } - (void)nextLevel { SJPlayScene *scene = [[SJPlayScene alloc] initWithSize:self.size life:self.life stage:self.stage + 1]; SKTransition *transition = [SKTransition doorwayWithDuration:1.0f]; [self.view presentScene:scene transition:transition]; } ``` `Callbacks`の部分では、Sprite Kitによるフレーム毎の処理に後に微調整を加えています。 Sprite Kitのゲームループは、以下の画像のようになっており、緑色の部分で独自処理をはさむことができます。  今回は、`update:`で5秒ごとにボールの速度をはやめています。 `didEvaluateActions`では、アクションにより画面外に行ってしまうパドルを画面内に収めています。 `didSimulatePhysics`では、ボールが画面最下部に行った時に破壊して、ライフを減らす処理をしています。0になればゲームオーバーを表示します。 ゲームオーバー用のシーンは、ほぼタイトル画面と同じです。 ```objc:SJGameOverScene.m #import "SJPlayScene.h" - (id)initWithSize:(CGSize)size { self = [super initWithSize:size]; if (self) { SKLabelNode *titleLabel = [SKLabelNode labelNodeWithFontNamed:@"HelveticaNeue"]; titleLabel.text = @"GAVE OVER..."; titleLabel.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)); titleLabel.fontSize = 40.0f; [self addChild:titleLabel]; } return self; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { SKScene *scene = [SJPlayScene sceneWithSize:self.size]; SKTransition *transition = [SKTransition pushWithDirection:SKTransitionDirectionUp duration:1.0f]; [self.view presentScene:scene transition:transition]; } ``` これで、ライフが0になると**GAME OVER…**と表示されます。タップでリトライします。  ## できあがり これで完成です。 PlaySceneが約300行、その他のファイルは数十行です。 とても短い、というわけではないですが、十分お手軽ではないでしょうか。 ちなみに僕は今、[RPGのリリースに向け悪戦苦闘中](http://spritekit.jp/)です。皆さんもSprite Kitでゲーム作成してみませんか。 ## ソースコード [tnantoka/hello-spritekit](https://github.com/tnantoka/hello-spritekit)にて公開しています。 ライセンスはThe MIT Licenseですので、ご自由にどうぞ。 スターしてくれたら喜びます! ## 参考 * [Sprite Kit Programming Guide](https://developer.apple.com/library/IOs/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Introduction/Introduction.html) 残念ながら、[日本語訳](https://developer.apple.com/jp/devcenter/ios/library/japanese.html)はまだ提供されていません。 * [Sprite Kit Framework Reference](https://developer.apple.com/Library/ios/documentation/SpriteKit/Reference/SpriteKitFramework_Ref/_index.html) |
|
| 582位 |
|
|||
|
00:41:00 |
(JUBILEE WORKS, Inc. 所属) |
|
Swiftの`DateFormatter`(Objective-Cでは`NSDateFormatter`)の使い方をまとめました。
## 日付から文字列を取得する ### 規定のStyleを使う 一番簡単なのは`dateStyle`と`timeStyle`を利用する方法。日付、時刻それぞれについて長め、標準、短め、なしの4つのスタイルを選択すると、localeに応じた適切な文字列を生成できる。 ```swift:Swift let dateFormatter = DateFormatter() dateFormatter.dateStyle = .medium dateFormatter.timeStyle = .none let dateString = dateFormatter.string(from: Date()) print(dateString) ``` ```objc:Objective-C NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateStyle = NSDateFormatterMediumStyle; dateFormatter.timeStyle = NSDateFormatterNoStyle; NSString *dateString = [dateFormatter stringFromDate:[NSDate date]]; NSLog(@"%@", dateString); ``` * ja_JPのとき: 2013/11/26 * en_USのとき: Nov 26, 2013 ### 固定のカスタムフォーマット 自由にフォーマットを指定したい場合、`dateFormat`で指定可能。書式は[Unicode Technical Standard #35](http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns)に準拠する。 ```swift:Swift let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd 'at' HH:mm" let date = Date() let dateString = dateFormatter.string(from: date) print(dateString) ``` ```objc:Objective-C NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"yyyy-MM-dd 'at' HH:mm"; NSDate *date = [NSDate date]; NSString *dateString = [dateFormatter stringFromDate:date]; NSLog(@"%@", dateString); ``` 注意点 * ユーザーが選択したカレンダー設定が反映される * 時刻の24時間表記のユーザー設定によって指定したフォーマットが上書きされる 上記コードでlocaleがja_JPの場合、期待される結果はおそらく「2013-11-26 at 23:21」であるが、カレンダーを「和暦」、24時間表示をオフに設定していると、結果は「0025-11-26 at 午後11:21」となってしまう。 これを防ぎたければ、以下のようにlocaleを`en_US_POSIX`に指定する。これは書式が変わらないことが保証されている特別なlocale。 ```swift:Swift dateFormatter.locale = Locale(identifier: "en_US_POSIX") ``` ```objc:Objective-C dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; ``` ### Localeに応じたカスタムフォーマット 表示する要素を自由に選択しつつ、ユーザーのlocaleにあわせた適切なフォーマットで表示したい場合は、`dateFormat(fromTemplate:options:locale:)`を使って適切なフォーマット文字列を取得する。 ```swift:Swift guard let formatString = DateFormatter.dateFormat(fromTemplate: "MMMdd", options: 0, locale: Locale.current) else { fatalError() } print(formatString) let dateFormatter = DateFormatter() dateFormatter.dateFormat = formatString let date = Date() let dateString = dateFormatter.string(from: date) print(dateString) ``` ```objc:Objective-C NSString *formatString = [NSDateFormatter dateFormatFromTemplate:@"MMMdd" options:0 locale:[NSLocale currentLocale]]; NSLog(@"%@", formatString); NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = formatString; NSDate *date = [NSDate date]; NSString *dateString = [dateFormatter stringFromDate:date]; NSLog(@"%@", dateString); ``` * ja_JPのとき * フォーマット: M月dd日 * 結果: 11月26日 * en_USのとき * フォーマット: MMM dd * 結果: Nov 26 * en_GBのとき * フォーマット: dd MMM * 結果: 26 Nov ## 文字列から日付を取得する RFC 3339な日付文字列をパースする例 ```swift:Swift let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "en_US_POSIX") dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'" dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) let string = "1985-04-12T23:20:50Z" let date = dateFormatter.date(from: string) ``` ```objc:Objective-C NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; dateFormatter.dateFormat = @"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"; dateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; NSString *string = @"1985-04-12T23:20:50Z"; NSDate *date = [dateFormatter dateFromString:string]; ``` ここでもlocaleを`en_US_POSIX`にしてフォーマットが固定であることを担保している。 ## 参考リンク * [Data Formatting Guide: Date Formatters](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html#//apple_ref/doc/uid/TP40002369-SW1) * [Date and Time Programming Guide](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/DatesAndTimes/DatesAndTimes.html#//apple_ref/doc/uid/10000039-SW1) |
|
| 583位 |
|
|||
|
05:04:27 |
|
|
ウェブサイトの会員登録なんかでメール送信をプログラムで書く機会は結構あると思います。
基本的に他所に書かれてある物を集めただけですが紹介したいと思います。主に自分参照用に。 Mailライブラリーを使う ---------------------- [TMail][tmail]には伝統がありますが、使い方が結構難しかった記憶があるので、今のRailsで使っているという[Mail][mail]というライブラリーを使ってみます。 `gem install mail` でインストールできます。 [tmail]: http://tmail.rubyforge.org/ [mail]: https://rubygems.org/gems/mail ### 1. メールを作る ### メール本文のオブジェクトを作ります。 ```ruby:mail.rb require 'mail' mail = Mail.new do from 'KitaitiMakoto@example.net' to 'KitaitiMakoto@example.net' subject 'Mail from Mail' body 'There is a body.' end ``` ### 2. メールを送る ### SMTPの設定をした後で、```#deliver!```を呼び出します。 ```ruby:mail.rb mail.delivery_method :smtp, { address: 'smtp.example.net', port: 587, domain: 'example', user_name: 'KitaitiMakoto', password: ($stderr.print 'password> '; gets.chomp) } mail.deliver! ``` これを実行すると、メールが送れます。 ruby ./mail.rb パスワードをエコーバックしないとか、環境変数や設定ファイルで設定するとか、そういう処理くらいは入れたほうがいいですかね? ### 3. クラスメソッドを使う ### クラスメソッドだけで送信処理をすることもできます。 ```ruby:mail.rb require 'mail' Mail.defaults do delivery_method :smtp, { address: 'smtp.example.net', port: 587, domain: 'example', user_name: 'KitaitiMakoto', password: ($stderr.print 'password> '; gets.chomp) } end Mail.deliver do from 'KitaitiMakoto@example.net' to 'KitaitiMakoto@example.net' subject 'Mail from Mail' body 'There is a body.' end ``` ### 4. オフラインで確認する ### 僕はオフラインで開発をすることが多いので、また、そうではなくても一々メールサーバーに接続したくないことも多いでしょう。テストの時など。 そういう時には、メールメッセージの送り先や内容だけを確認できます。 ```ruby:mail.rb puts mail.to puts mail.body ``` 変数を使ってtoやbodyを指定している場合に便利でしょう。 ActionMailerを使う ------------------ RailsではMail gemを直接触ることはなくて、実際にはActionMailerを触ることになると思います。 これは、勿論Railsを使っていなくても便利な物です。 gem install actionmailer でインストールします。 ### 1. 環境の設定 ### 設定は```ActionMailer::Base```のクラスメソッドを通して行います。 ```ruby:actionmailer.rb require 'action_mailer' ActionMailer::Base.delivery_method = :smtp ActionMailer::Base.smtp_settings = { address: 'smtp.example.net', port: 587, domain: 'example', user_name: 'KitaitiMakoto', password: ($stderr.print 'password> '; gets.chomp) } ``` ### 2. メール送信用クラスの作成 ### メール送信内容は```ActionMailer::Base```の子クラスを通じて組み立てます。 ```ruby:actionmailer.rb class SampleMailer < ActionMailer::Base def first_example(body) mail( to: 'KitaitiMakoto@example.net', from: 'KitaitiMakoto@example.net', subject: 'Mail from SampleMailer', body: body.to_s ) end end ``` ### 3. メールを送る ### (```mail```メソッドを呼んでいるだけですが)メール送信処理はインスタンスメソッドとして定義していますが、呼び出す時にはクラスメソッドを使います。 ```ruby:actionmailer.rb require 'English' if File.basename($PROGRAM_NAME) == File.basename(__FILE__) SampleMailer.first_example('There is a body.').deliver end ``` ruby ./actionmailer.rb でメールを送ります。 テンプレートファイルを使う -------------------------- メール本文をプログラムにハードコードするのはあり得ないので、テンプレートを使いたいですよね(送信先等の管理は特別なことが無いので省略)。 ### 1. テンプレートファイルの場所を指定 ### ActionMailer::Baseのクラスメソッドを使って、テンプレートファイル(を探す起点)の場所を指定します。 ```ruby:template.rb require 'action_mailer' ActionMailer::Base.prepend_view_path File.expand_path('../templates', __FILE__) # (その他設定) ``` ### 2. メール送信メソッドをテンプレート版に対応させる ### RailsのActionControllerの```respond_to```のようにして「テンプレートを使う」ということを指定できます。 これもRailsのビューテンプレートと同じように、インスタンス変数をテンプレート側でも使えます。 ```ruby:template.rb class TemplateMailer < ActionMailer::Base def template_example(name) @name = name mail( to: 'KitaitiMakoto@example.net', from: 'KitaitiMakoto@example.net', subject: 'Mail from TemplateMailer', ) do |format| format.text end end end ``` ```ruby format.html ``` と書くとHTMLメールを送ってくれるようです。 ### 3. テンプレートファイルを作る ### 1.で指定したように、テンプレートファイルは(ここでは)```templates```ディレクトリー以下に作ります。 ディレクトリー名にはクラス名をスネークケースにした物、ファイル名にはメソッド名を使います。テキストメールには```.text.erb```を、HTMLメールには```.html.erb```を付けます。 上の例だと templates/template_mailer/template_example.text.erb というファイル名になります。 ```erb:templates/template_mailer/template_example.text.erb There is <%= @name %>'s body. ``` ### 4. メールを送る ### メールの送信方法は変わりません。ので、送信せずに内容だけ確認するようにする、ということに挑戦してみましょう。 こうすればテストもしやすくなります。 ```ruby:template.rb require 'English' if File.basename($PROGRAM_NAME) == File.basename(__FILE__) mail = TemplateMailer.template_example('北市真') puts mail.body end ``` ```deliver``` を呼ばずに ```body``` 属性を確認します。 ruby ./template.rb で実行できます。 ``` password> There is 北市真's body. ``` あ、SMTP関係の設定はいりませんね。 ネストしたモジュールでのテンプレートファイル -------------------------------------------- さて、バッチ処理でメールを送る場合など、クラスがモジュールの中にあることも多いと思います。 ```ruby:nested.rb # ActionMailerの設定色々 # ActionMailer::Base... module Batch class Aggregate # ... # 色々のバッチ処理 # ... def report_done Mailer.done.deliver end def report_error(exception) # 例外情報からエラーメッセージを作る # error_message = ... Mailer.error(error_message).deliver end class Mailer < ActionMailer::Base def done # 正常終了を報告するメールを送る end def error(error_message) # エラーを報告するメールを送る end end end end ``` こういう時のテンプレートの場所は、モジュールのネスト分だけディレクトリーを掘った物になります。上の例だと、 * templates/batch/aggregate/mailer/done.text.erb * templates/batch/aggregate/mailer/error.text.erb の二つのファイルを使うことになります。 参考 ---- ### Mail ### Mailライブラリーについてはライブラリーその物の[README][mailreadme]が充分わかり易い物です。 ### ActionMailer ### ActionMailerについては、日記「[発狂する近況][hakkyo]」の記事(「[RubyからGMailにメールを送る テキスト編][actionmailer-text]」と「[html編][actionmailer-html]」)がとても参考になりました。 テンプレートファイルを置く場所については、ActionMailerのエラーメッセージを見て理解しました。 [mailreadme]: https://github.com/mikel/mail#readme [hakkyo]: http://bookmeter.que.jp/hakkyo/ [actionmailer-text]: http://bookmeter.que.jp/hakkyo/?date=20110830 [actionmailer-html]: http://bookmeter.que.jp/hakkyo/?date=20110905 |
|
| 584位 |
|
|||
|
20:15:18 |
(mixi 所属) |
|
 ## はじめに Rubyは人気の言語です。2014年にもなり今更かもですが、読み書き出来るようになりたいなぁと思って色々調べました。 まずは本を読むほうが体系的に分かって良いのかもしれません。例えば下記が良いのかなと思います。 - [はじめてのRuby](http://www.oreilly.co.jp/books/9784873113678/) - 電子版少し安い。1,848円 - [たのしいRuby](http://www.amazon.co.jp/dp/4797372273) - Matz氏が監修してる。2,730円 - [プログラミング言語 Ruby](http://www.oreilly.co.jp/books/9784873113944/) これも。3,192円 - [パーフェクトRuby](http://www.amazon.co.jp/dp/4774158798) - パーフェクトxxxシリーズ。3,360円 しかし、ネット上にも学習コンテンツやドキュメントが充実してるので、利用できるものは利用しちゃいましょう。 そこで、Rubyの初学者である私がたどり着いたオンラインドキュメント等をまとめました。 ### まず心構え [最初の一歩は始めること](http://www.aoky.net/articles/jason_zimdars/the_first_step_is_to_start.htm)を一読する - 原文: [The first step is to start](http://signalvnoise.com/posts/2538-the-first-step-is-to-start) - ([Basecamp](https://basecamp.com/)のWebデザイナー[Jason Zimdars氏](http://stream.jasonzimdars.com/)が書いた文章) あとは - [Matz氏](https://twitter.com/yukihiro_matz)という名を心に刻む - https://github.com/ruby/ruby に star つけとく その他 - [リーダブルコード](http://www.oreilly.co.jp/books/9784873115658/)は、Rubyに限らず読んだ方がいいですね。電子版少し安い。2,016円。 ## 基本 Rubyってどんなもんなのか、まずはオフィシャルな情報や基本的なことなどを抑えておきたいですね。 - https://www.ruby-lang.org/ #### リファレンスマニュアル - [Ruby リファレンスマニュアル](https://www.ruby-lang.org/ja/documentation/) - [Ruby 2.1.0](http://docs.ruby-lang.org/ja/2.1.0/doc/index.html) - [るりまサーチ](http://docs.ruby-lang.org/ja/search/) - [ruby-doc.org](http://ruby-doc.org/) ※ この辺は後述の[Dash](https://itunes.apple.com/jp/app/dash-docs-snippets/id458034879)を利用しちゃうほうが楽かもしれません。 #### Ruby Style Guide コーディングスタイルとか規約とか、一読しとくのが良いかも。 - https://github.com/bbatsov/ruby-style-guide - http://www.ruby.or.jp/ja/tech/development/ruby/050_coding_rule.html ## ドキュメントまとめ ### オンラインチュートリアル系 調べたら意外とたくさんあった。好きなのを見るのが良いと思いました。便利な世の中。 #### ミニツク [NaCl](http://www.netlab.jp/)さんが作ったこともあって、 **Matz氏が解説する動画** もある。日本語。 - http://www.minituku.net/ #### ドットインストール おなじみドットインストール。日本語。 - [Ruby入門 (全22回)](http://dotinstall.com/lessons/basic_ruby_v2) - 上記の方があたらしい。 - [【旧版】Ruby入門 (全32回)](http://dotinstall.com/lessons/basic_ruby) #### Codeacademy 最初はかなり易しいけど、オブジェクト指向的なところまでやってくれる。全19回。英語。 - http://www.codecademy.com/ja/tracks/ruby #### Code School tryruby.orgのコンテンツがここにある。RubyだけじゃなくてRailsとかRSpecとかもある。英語。 - https://www.codeschool.com/paths/ruby #### RubyMonk [Matz氏もオススメ](https://twitter.com/yukihiro_matz/status/126447696478347265)らしい。 英語。 - https://rubymonk.com/ ### オンラインドキュメント #### ホワイの(感動的)rubyガイド why the lucky stiffという[正体不明の人気者](http://www.oreilly.co.jp/community/blog/2009/04/unidentified-popular-figure.html)によるRubyガイドの日本語訳。挿絵が楽しい。 - http://www.aoky.net/articles/why_poignant_guide_to_ruby/chapter-1.html #### Rubyソースコード完全解説 『Rubyソースコード完全解説』のHTML版。著者の青木氏により2004年から公開されている。 - http://i.loveruby.net/ja/rhg/book/ #### RubyLife ここのサイト、一度はググって引っかかたことがあるんじゃないでしょうか。 - http://www.rubylife.jp/ #### 逆引きRuby やりたいことから調べたいときはここ。 - http://www.namaraii.com/rubytips/ #### Codeacademy glossary 基本的なことが網羅されている用語集的なもの。英語。 - http://www.codecademy.com/ja/glossary/ruby #### Ruby 正規表現入門ドリル 正規表現の入門。 - http://engineerflies.blogspot.jp/2010/06/ruby_25.html #### お気楽 Ruby プログラミング入門 [広井誠氏](http://www.geocities.jp/m_hiroi/profile.html)によって2008年頃に書かれたドキュメント。全14回と番外編が用意されている。日本語。 - http://www.geocities.jp/m_hiroi/light/ruby.html ## その他 ### Rails #### Ruby on Rails チュートリアル Ruby初学者がいきなりRailsってのはアレかもしれないけど、一応リストしとく。 - http://railstutorial.jp/ - [Ruby on Rails Tutorial](http://ruby.railstutorial.org/)の日本語版。 ### RSpec テストも大事ってことでリストしとく。 #### Testing with RSpec 上記で紹介したCode Schoolのうちの1つ - https://www.codeschool.com/courses/testing-with-rspec #### RSpec の入門とその一歩先へ - http://d.hatena.ne.jp/t-wada/20100228/p1 - http://d.hatena.ne.jp/t-wada/20100306/p1 - http://d.hatena.ne.jp/t-wada/20100801/rspec_3rd_iter #### RSpecによるユニットテストの書き方 - http://tech.recompile.net/post/21340599029/rspec #### Better Specs Good / Bad 分かりやすい。 - http://betterspecs.org/ ### オフラインドキュメントブラウザ #### Dash - Rubyにかぎらず使える。オフラインでクラスリファレンスとか見たい場合は便利。 - http://kapeli.com/dash - Mac App Storeにもある - https://itunes.apple.com/jp/app/dash-docs-snippets/id458034879 ### 実行環境 #### rbenv + ruby-build Ruby環境はこいつで整えておく。最新verのruby使いたいですし。 - repo - https://github.com/sstephenson/rbenv - https://github.com/sstephenson/ruby-build - 参考 - 他にも**envつかうなら → [anyenvで開発環境を整える](http://qiita.com/luckypool/items/f1e756e9d3e9786ad9ea) #### irb / pry / tapp リファレンスマニュアルにも説明がある[irb (Interactive Ruby)](http://docs.ruby-lang.org/ja/2.1.0/library/irb.html)はちょっと動かすのには最適。 - しかし、その代替となる [pry](https://github.com/pry/pry) はおすすめらしいので、使いましょう - ついでに、プリントdebugするなら [tapp](https://github.com/esminc/tapp) もチェックしましょう - 参考 - [ググるよりもまずはpry](http://qiita.com/Kokudori/items/2b36068cdf2e40e75c2d) - [tappでプリントデバッグを便利に行う](http://qiita.com/kenchan@github/items/c53f435483f287e70240) #### エディタ - IDEが好きなら[RubyMine](http://www.jetbrains.com/ruby/)とかですかね - 私はvimつかいます - [vim使っているrubyistで、これ入れていないのはヤバいプラグインまとめ 9個 (2013-10-04更新)](http://qiita.com/alpaca_taichou/items/ab2ad83ddbaf2f6ce7fb) ## 参考 このドキュメントは、グーグル先生に聞きつつ、下記のサイト内容を参考にまとめたチラシの裏です。 - [Ruby/Rails Study Giide](http://qwik.jp/okinawarb/3.html) - 沖縄Rubyユーザグループ - [Rubyの歩き方](http://magazine.rubyist.net/?FirstStepRuby) - 日本Rubyの会 - [free-programming-books-ja.md#ruby](https://github.com/vhf/free-programming-books/blob/master/free-programming-books-ja.md#ruby) - [List of Free Programming Books](http://resrc.io/list/10/list-of-free-programming-books/) |
|
| 585位 |
|
|||
|
17:57:23 |
(kayac.com 所属) |
|
gitで自鯖のレポジトリにpushがあったら、 同じく自鯖にあるhtmlを更新したい。 (github-hookはちょっと前にやったけど、こっちは初めてだった) ## git hook git hookは、gitが持っている「コミットされたらなんかする」系の仕組み。 - [Git - Git フック](http://git-scm.com/book/ja/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%83%95%E3%83%83%E3%82%AF) 開発環境側で「コミットする直前」とか、 サーバーの側で「プッシュされた直後」とか、 いろんなタイミングで、決まったshell scriptを実行できる。 shell scriptは、```.git/hooks```に置く仕組みになっている模様。 今回は、この **サーバーサイドフック**の、 **post-receive**を使ってみる。 ## 設定する クライアントサイドでやる作業はなし。 サーバー側でやる作業はだいたいこんな感じ。 ```bash # gitレポジトリへ移動 cd git/my-website.git # post-receiveのスクリプトを設置 vi .git/hooks/post-receive # 実行権限付ける chmod +x .git/hooks/post-receive ``` スクリプトは、今回はこのくらいシンプルに。 ```--git-dir=.git```は付けないと怒られました。 ```.git/hooks/post-receive #!/bin/sh cd /home/fnobi/sites/my-website git --git-dir=.git pull ``` これで完了。 ```git/my-website.git```へpushがあったら、 ```/home/fnobi/sites/my-website```に設置してあるhtmlが自動更新されるようになりました。わーい! ## 成功したかどうかの確認 hookのスクリプトが失敗したときのログとか、どこに出るのかなぁと思っていたら、 pushしたとき手元に出てた。 "remote:"っていう行。 ```bash % git push Counting objects: 11, done. Delta compression using up to 4 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 631 bytes, done. Total 6 (delta 3), reused 0 (delta 0) remote: fatal: Not a git repository: '.' To sasyugo:git/bukko-music-children.git f225eab..f7932b2 master -> master ``` うまく行ったときは、ちゃんとそういうログが出てますよ。 ```bash remote: From /home/fnobi/git/my-website remote: bbec307..8becb96 master -> origin/master remote: Updating bbec307..8becb96 remote: Fast-forward remote: index.html | 4 ++++ remote: src/ejs/index.html.ejs | 4 ++++ remote: 2 files changed, 8 insertions(+), 0 deletions(-) ``` べんりだなぁ。 ## まとめ クライアントサイドフックで、コミットメッセージに「バルス」が含まれてるかどうかを検知して、 含まれてたらレポジトリを破壊するやつとかあって笑った。 - ['git-バルス'をgit-hookにした - 這い寄るゆろよろ・アンド・ライジングフォース日記](http://yuroyoro-blog.tumblr.com/post/28319112466/git-git-hook) そういえば[コミットした瞬間の顔を撮影するやつ](http://www.ideaxidea.com/archives/2012/05/lolcommits.html)とかもあったし、 いろんなことできそう。 ## 参考 - [gitのhookを使ってみた。 - 画竜点睛を衝く](http://mapyo.hatenablog.com/entry/2013/04/14/git%E3%81%AEhook%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F%E3%80%82) シンプルに書いてあってよかった。 - [Git hooks まとめ - Qiita [キータ]](http://qiita.com/khlizard/items/dfe1ec9d82c0ed5da7c6) それぞれのhookのタイミングが細かく載ってる - [Lightweight git hook management tool その名も git-hook を作りました - 鳩舎](http://rosylilly.hatenablog.com/entry/2012/07/30/082748) 設置の作業を簡単にやるための拡張あった。いい。 |
|
| 586位 |
|
|||
|
03:21:41 |
(Wantedly, Inc. 所属) |
|
HTTPリクエストでJSONが返ってくるAPIを処理するとかよくあるケースだけど、意外と書き方忘れるのでメモ ```ruby require 'net/http' require 'uri' require 'json' uri = URI.parse('http://www.example.com/sample.json') json = Net::HTTP.get(uri) result = JSON.parse(json) puts result ``` ## RedirectとかTimeoutとかエラー処理とかちゃんとやりたい時用 ```ruby require 'net/http' require 'uri' require 'json' def get_json(location, limit = 10) raise ArgumentError, 'too many HTTP redirects' if limit == 0 uri = URI.parse(location) begin response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http| http.open_timeout = 5 http.read_timeout = 10 http.get(uri.request_uri) end case response when Net::HTTPSuccess json = response.body JSON.parse(json) when Net::HTTPRedirection location = response['location'] warn "redirected to #{location}" get_json(location, limit - 1) else puts [uri.to_s, response.value].join(" : ") # handle error end rescue => e puts [uri.to_s, e.class, e].join(" : ") # handle error end end puts get_json('http://www.example.com/sample.json') ``` ## 公式ドキュメント - http://ruby-doc.org/stdlib-2.0/libdoc/net/http/rdoc/Net/HTTP.html - http://ruby-doc.org/stdlib-2.0/libdoc/uri/rdoc/URI.html - http://ruby-doc.org/stdlib-2.0/libdoc/json/rdoc/JSON.html ## 日本語版ドキュメント - http://doc.ruby-lang.org/ja/2.0.0/library/net=2fhttp.html - http://doc.ruby-lang.org/ja/2.0.0/class/URI.html - http://doc.ruby-lang.org/ja/2.0.0/class/JSON.html |
|
| 587位 |
|
|||
|
23:07:51 |
|
|
[Ruby on Rails Advent Calendar](http://qiita.com/advent-calendar/2013/ruby-on-rails) 21日目の記事です。
DeviseとOmniauthを使って認証管理をしてみます。 ## Deviseとは何か > Devise is a flexible authentication solution for Rails based on Warden. It: > - Is Rack based; > - Is a complete MVC solution based on Rails engines; > - Allows you to have multiple models signed in at the same time; > - Is based on a modularity concept: use just what you really need. と[Github](https://github.com/plataformatec/devise)にあるように、 WardenをベースとしたRails向け`フレキシブル` `オーセンティケーション` `ソリューション`です。 - Rackベース - とはいってもRails専用。 - Rails上で完全なMVCソリューションベース - 完全なMVCらしいです。 - 同時に複数のモデルでのサインインが可能 - 例えばUserモデルとAdminモデルを作った時、同時に2つのロールでログインできます。 - モジュール方式のコンセプトであり、必要な物だけを使うことができる - 10個のモジュールを`devise`というDSLで付けたり外したりして、`Devise`が提供する機能の必要なものだけ使うことができます。 ## ここでは何をやるか Deviseを使って認証管理をします。 認証にはfacebookを使用します。 ### 要件 - ランディングページのようなページがある。 - ログインしなければ見れないページがある。 - facebookのOAuthを利用してログインする。 ## やってみる ### 1. ランディングページとなるページの作成 `welcome`コントローラーをジェネレーターで作ります。 ```bash $ rails g controller Welcome index ``` ### 2. / をwelcome#indexを設定 `config/routes.rb`の`get "welcome/index`を以下のように変更します。 ```ruby:config/routes.rb root "welcome#index" ``` ### 3. ログイン後のページ /home を作成 ログイン後のページとなる`home`コントローラーを生成します。 ```bash $ rails g controller Home index ``` ### 4. Devise, Omniauthを使うための記述 `Gemfile`に下記を追記してgemをインストールします。 ```ruby:Gemfile gem 'devise' gem 'omniauth' gem 'omniauth-facebook' ``` ```bash $ bundle install ``` ### 5. Deviseを使うための準備 作っているアプリケーションがdeviseを使えるようにします。 ```bash $ rails g devise:install create config/initializers/devise.rb create config/locales/devise.en.yml =============================================================================== Some setup you must do manually if you haven't yet: 1. Ensure you have defined default url options in your environments files. Here is an example of default_url_options appropriate for a development environment in config/environments/development.rb: config.action_mailer.default_url_options = { :host => 'localhost:3000' } In production, :host should be set to the actual host of your application. 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root :to => "home#index" 3. Ensure you have flash messages in app/views/layouts/application.html.erb. For example: <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> 4. If you are deploying on Heroku with Rails 3.2 only, you may want to set: config.assets.initialize_on_precompile = false On config/application.rb forcing your application to not access the DB or load models when precompiling your assets. 5. You can copy Devise views (for customization) to your app by running: rails g devise:views =============================================================================== ``` いろいろ表示されているので、実行していきます。 ### 6. ActionMailerのデフォルトURLオプションを変更 production環境では別途`config/environments/production.rb`に設定が必要です。 ```ruby:config/environments/development.rb config.action_mailer.default_url_options = { :host => 'localhost:3000' } ``` ### 7. Userモデルを生成 deviseのジェネレーターを使ってUserモデルを作ります。 自動生成する他に以下のカラムを追加します。 - 複数のサービスログインを可能にするための `provider` - facebookなどでのユーザーidである `uid` - ログイン中に表示させるための名前 `name` - サービスへのアクセストークン `token` ```bash $ rails g devise User provider:string uid:string name:string token:string ``` ### 8. FacebookのApp IDとApp Secretをセット https://developers.facebook.com/apps/ へアクセスして`App ID`と`App Secret`を取得します。 ```ruby:config/initializers/devise.rb require "omniauth-facebook" Devise.setup do |config| … config.omniauth( :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET'], {:scope => 'email'} ) end ``` 環境変数に`App ID`と`App Secret`を入れてサーバを再起動します。 ([dotenv](https://github.com/bkeepers/dotenv)を使うと便利!) ```bash $ export FACEBOOK_APP_ID="123456789012345" $ export FACEBOOK_APP_SECRET="1234567890abcdefghijklmnopqrstuvwxyz" $ rails s ``` ### 9. UserモデルをOmniauthで認証可能にする `:omniauthable`をUserモデルに追加します。 ```ruby:app/models/user.rb class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable end ``` ### 10. Facebookログインリンクを設置 `user_omniauth_authorize_path`というヘルパーメソッド使ってログインリンクを生成します。 ```html:app/views/welcome/index.html.erb <%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %> ``` ### 11. facebook認証後のコールバックメソッドを定義する `app/controllers/users`ディレクトリを作成し、`omniauth_callbacks_controller.rb`を作ります。 `request.env['omniauth.auth']`というリクエストパラメータにコールバックされた値が入っています。 値を確認するために例外を発生させてその変数の中身を表示してみます。 ```ruby:app/controllers/users/omniauth_callbacks_controller.rb class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def facebook raise request.env['omniauth.auth'].to_yaml end end ``` ### 12. facebookからのコールバックをログイン処理 まず、Userモデルに`find_for_facebook_oauth`というクラスメソッドを追加します。 このメソッドはコールバックされた値の`provider`と`uid`からDBを検索します。 存在しなかったら新規ユーザーを生成して返却します。 ```ruby:app/models/user.rb def self.find_for_facebook_oauth(auth) user = User.where(provider: auth.provider, uid: auth.uid).first unless user user = User.create( name: auth.extra.raw_info.name, provider: auth.provider, uid: auth.uid, email: auth.info.email, token: auth.credentials.token, password: Devise.friendly_token[0,20] ) end return user end ``` コールバックメソッドでは先ほど生成したメソッドにパラメータを渡してユーザーインスタンスを生成し、 ログイン処理をしてリダイレクトします。 ```ruby:app/controllers/users/omniauth_callbacks_controller.rb class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def facebook @user = User.find_for_facebook_oauth(request.env["omniauth.auth"]) if @user.persisted? sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format? else session["devise.facebook_data"] = request.env["omniauth.auth"] redirect_to new_user_registration_url end end end ``` ### 13. ログイン後のリダイレクト先を変更 `user_root`という別名をつけるとそこへリダイレクトしてくれます。 同時に`/home/index`から`/home`にルーティングを変更します。 ```ruby:config/routes.rb get "home", to: "home#index", as: "user_root" ``` ### 14. ログイン・ログアウトのリンクを整理 下のコードではログイン中(`user_signed_in?`で確認できます)ならば「ユーザー名・ログアウトリンク」を、 そうでなければ「ログインリンク」を表示します。 ```html:app/views/layouts/application.html.erb <div id="user-info"> <% if user_signed_in? %> <%= current_user.name %> [<%= link_to 'Sign out', destroy_user_session_path, method: :delete %>] <% else %> <%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %> <% end %> </div> ``` ### 15. homeコントローラーへのアクセスをログイン中のみに制限 `before_filter`に`authenticate_user!`を追加することによってログインしているかどうかを判別し、アクセス制御をします。 ```ruby:app/controllers/home_controller.rb class HomeController < ApplicationController before_filter :authenticate_user! def index end end ``` ### 16. 15のリダイレクト先をfacebook認証のページに変更 `authenticate_user!`をオーバーライドして、facebook認証URLに書き換えます。 ```ruby:app/controllers/application_controller.rb def authenticate_user! session[:user_return_to] = env['PATH_INFO'] redirect_to user_omniauth_authorize_path(:facebook) unless user_signed_in? end ``` ## さいごに DeviseとOmniauthを使えばログイン管理がとても簡単に実装することができます。(ちょっと長いですが) 今回はfacebookを対象としましたが、サービスへのApp登録、コールバックメソッド定義をすればTwitterでもGoogleでもログイン認証の選択肢を増やすことができます。 Porvider一覧はOmniauthのWikiで確認できます。[List of Strategies · intridea/omniauth Wiki](https://github.com/intridea/omniauth/wiki/List-of-Strategies) omniauth用のscaffoldを作るGemを作っている方も[shu0115/minimum-omniauth-scaffold](https://github.com/shu0115/minimum-omniauth-scaffold) 今回はアクセストークンを保存しているので、Facebookだったら[koala](https://github.com/arsduo/koala)や[fb_graph](https://github.com/nov/fb_graph)を使ってAPIを叩くことも可能です。 使う際は`scope`の指定をお忘れなく。 Githubに一連の流れを記しておきました。[Commits · ytkt/TryDeviseAndOmniauth](https://github.com/ytkt/TryDeviseAndOmniauth/commits/master) (途中で気づいたんですが、ブランチを切らずに進めてしまったためとても見にくいです) 間違っているところ、マズイ所があれば教えていただけると幸いです。 ## Tips ### Facebook permissions `config/initializers/devise.rb`に設定するscopeの一覧です。 [Permissions : Facebook開発者向けドキュメントの日本語訳とTips] (http://facebook-docs.oklahome.net/archives/51927681.html) ### dotenv `App Secret`などをハードコーディングしているのはよろしくないので、環境変数に入れてそれをRailsから読み込みます。 この際、[dotenv](https://github.com/bkeepers/dotenv)を使うと便利です。 |
|
| 588位 |
|
|||
|
13:53:47 |
(Nepula,Inc. 所属) |
|
## 2013.11.03 追記
電子国土Web.NEXT という名称で試験公開されていたものが、「地理院地図」「地理院タイル」として正式公開されました。 * [地理院地図の公開について | 国土地理院](http://www.gsi.go.jp/johofukyu/johofukyu40032.html) * [地理院タイルを用いた開発 | 地理院地図](http://portal.cyberjapan.jp/help/development.html#siyou) 地理院タイルは [従来版タイル](http://portal.cyberjapan.jp/help/development/oldScheme.html) と仕様が異なります。従来版タイルは **2013年度中に提供終了予定** との事ですし、新しい地理院タイルの方が大幅に仕様が簡略化されたので、こちらを使った方が良いです。 このエントリも「電子国土」を「地理院地図(地理院タイル)」に修正しました。 ---- ## 2013.10.17 追記 [コメント](http://qiita.com/amay077/items/979dfc858a21c8bbb7a9#comment-6d0be86c2ba90df50808)で頂いていますが、現在は、 **「日本向けにローカライズされた」** Googleマップ側でも表記が修正されたようで、当初「問題がある」と言われていた地域は問題が解消されているように見えます。 **ただし、下記のように Google Maps API で「region=JP」が指定されている場合に限ります。** * [Google Maps API v3 で 日本海(東海)と「東海」が併記されるのを避けるには - わからん](http://d.hatena.ne.jp/kitokitoki/20130415/p1) これによって「Googleマップ禁止令はもはや無用である」と考えることもできますし、「依然として国の制御下には無い」と考えることもできます。 以下の情報は必要なくなるかも知れませんが、今後「Googleマップ以外の選択肢」の一つとして参考になれば幸いです。 -- 追記ここまで -- ---- 「領土等が正しく表示されていない」として、国や自治体、国公立の機関に、「Google マップ禁止令」が出ているそうです。 * [グーグルマップの「利用禁止令」 竹島や北方領土が「日本名でない表記」 政府が自治体などに要請](http://www.huffingtonpost.jp/2013/09/29/google-map-forbidden_n_4011180.html) * [Googleマップ禁止令?! 制作会社の苦悩と対応を考える。 - NAVER まとめ](http://matome.naver.jp/odai/2138082171982287301) * [大学公式ページでのGoogleMap禁止令と彩雲 : 5号館のつぶやき](http://shinka3.exblog.jp/20690859/) * [Twitter - うちの大学でも例の「グーグルマップ禁止令」によるサイトのチェックが始まった…](https://twitter.com/nissyyu/status/387870282570272768) これ自体の是非はいろいろあるようですが、公的機関の情報表示が、国によって「under control」じゃないのはイカン、というのはまあ分かりますよ。 で、対策としてはいくつかあります。。。 #### 画像の地図や PDF に変える * どんな時代錯誤ですか #### 他の地図APIを使う * Yahoo Japan、Mapion、Bing、OSM とか。 * Google Map API からこれらに変更する手間は結構かかるんではないかと。特に地図サイトを作りこんでいるならなおさら。 * Yahoo Japan、Mapion は、規約的に公的機関で無償で使用OK でしたっけ? * Bing, OSM は「制御下には無い」でしょう。 #### GoogleMapの問題のある表記を「正しい表記」に上書きする * [Googleマップ禁止令?! 制作会社の苦悩と対応を考える。](http://matome.naver.jp/odai/2138082171982287301) で実践されていた手法、ある意味驚きました(^_^;) * これ→[日本政府に「禁止されない」 Google Maps - jsdo.it - Share JavaScript, HTML5 and CSS](http://jsdo.it/toaSoku/gmapforjapan) * スクロール時にチラッと元表記が見えちゃうのと、元地図画像の変化に追従するのが難しいんですよね。 いずれも対応コストや規約、ユーザビリティを考えると決め手に欠けます。 ## ではどうするか? [地理院地図](http://portal.cyberjapan.jp/)という、国土交通省国土地理院が作って提供している地図データ、および地図システムがあります。 身内ならこれ使えよ、という話です。実際、大学などに送られた通知には、こちらを利用するための「相談窓口」が記載されているようです。 ただし、Google Map API から地理院地図の地図APIに移行することは、前述の通り、それなりのコストが発生します。 ので、 **「Google Map API を使って地理院地図を利用する方法」** を紹介します。 Google Map API には他の地図データを Overlay(重ねあわせ)する機能が備わっており、これを利用します。 既に実現されてる方々がいらっしゃいます。 * [Googleマップを使って国土地理院の地図を見る](http://user.numazu-ct.ac.jp/~tsato/webmap/map/gmap2.html?data=djws) * [y2blog » Google Maps APIを用いて電子国土V4背景地図を表示する](http://y2web.net/blog/computer/webmap/show_cj4_tiles_on_google_map_system-3355/) これらのサイトを参考にしてもよいですが、ここでは、Googleマップで地理院地図(地理院タイル)を使う、 最もシンプルな実装例を紹介します。 ### 1. Googleマップを表示するページを用意する ここでは、仮の「運用中のサイト」として、Google マップを使うシンプルなページを用意しました。 * [Google Maps Javascript API v3 の使用例](http://jsdo.it/amay077/tM0q) HTML+Javascript のソースコードはこんな感じ([Geekなぺーじ:Google MAPS JavaScript APIの単純な例](http://www.geekpage.jp/web/google-maps-api/v3/helloworld.php) を参考にさせて頂きました) ```html:google.html <!DOCTYPE html> <html> <head> <style type="text/css"> html { height: 100% } body { height: 100%; margin: 0px; padding: 0px } #map { height: 100% } </style> <script src="http://maps.google.com/maps/api/js?v=3&sensor=false" type="text/javascript" charset="UTF-8"></script> <script type="text/javascript"> function init() { var opts = { zoom: 5, mapTypeId: google.maps.MapTypeId.ROADMAP, center: new google.maps.LatLng(39, 135) }; var map = new google.maps.Map(document.getElementById("map"), opts); } </script> </head> <body onload="init()"> <div id="map"></div> </body> </html> ``` ### 2. 地理院地図(地理院タイル)を表示するように置き換える * [Google Maps Javascript API v3 での地理院地図の表示例](http://jsdo.it/amay077/wFtJ)  表示が地理院地図に置き換わっているのが確認できると思います。右上の地図タイプ切り替えは要らないので消してます。あと、ロゴの表示が必要なので、左下に置いています。(地理院地図の正式なロゴが公開されたら置き換えてください) という処理を追加したのが、下のコード。 変更が1行、追加行が30行くらいです。 変更箇所は「←」で、追加箇所は「↓↓」「↑↑」で示しています。 [gist](https://gist.github.com/amay077/6928205/revisions) でも diff を見られます。 ```html:gsi_map.html <!DOCTYPE html> <html> <head> <style type="text/css"> html { height: 100% } body { height: 100%; margin: 0px; padding: 0px } #map { height: 100% } </style> <script src="http://maps.google.com/maps/api/js?v=3&sensor=false" type="text/javascript" charset="UTF-8"></script> <script type="text/javascript"> function init() { var opts = { zoom: 5, mapTypeId: "GsiMaps", // 地理院地図の英語表記は「GIS Maps」 center: new google.maps.LatLng(39, 135) }; var map = new google.maps.Map(document.getElementById("map"), opts); map.setOptions({ mapTypeControl: false // 右上の地図タイプ選択を消す }); // 地理院タイルを Overlay する // via http://portal.cyberjapan.jp/help/development.html map.mapTypes.set("GsiMaps", { name:"地理院地図(GSI Maps)", tileSize:new google.maps.Size(256,256), minZoom:5, maxZoom:18, getTile:function(tileCoord, zoom, ownerDocument) { var img = ownerDocument.createElement("img"); img.style.width = "256px"; img.style.height = "256px"; var x = (tileCoord.x % Math.pow(2, zoom)).toString(); var y = tileCoord.y.toString(); img.src = "http://cyberjapandata.gsi.go.jp/xyz/std/" + zoom + "/" + x + "/" + y + ".png"; return img; } }); // 左下に電子国土ロゴを表示(TODO: 地理院地図の正式なロゴが公開されたら置き換える) var logo = document.createElement('DIV'); logo.style.padding = '3px'; map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(logo); logo.innerHTML = '<a href="http://portal.cyberjapan.jp/portalsite/kiyaku/index.html" target="_blank"><img style="width:32px, height:32px" src="http://cyberjapan.jp/images/icon01.gif" alt="電子国土" /></a>'; } </script> </head> <body onload="init()"> <div id="map"></div> </body> </html> ``` ### 不安とか懸念とか… #### 地図の表示が Google に比べて… まあ、そうですね。国土地理院にどんどんフィードバックしていきましょう。 #### 「試験公開」ってなってるんですけど… 地理院地図は「正式公開」になったので安心ですね。 旧Verについては、2013年度中に運用停止とのことです。 * [地理院地図|旧版情報](http://portal.cyberjapan.jp/help/oldver.html) #### 利用規約とかどうなってるの? [地理院地図|利用規約](http://portal.cyberjapan.jp/help/termsofuse.html) を見てください。 ## まとめ いかがでしょうか? 現在の Google マップを使ったページに、2箇所の修正を加えるだけで、「問題のない」地理院地図に切り替える事ができます。これは今回の問題の一つの解決方法になるのではないでしょうか? 個人的には、今回の「Googleマップ禁止令」は、国が所有する膨大な地図データを広く使ってもらえるチャンスだと思っています。 地理院地図の表示例を見てもらえれば分かりますが、地図としての見た目はともかく、データの量・精度については、Googleマップ(というかゼンリン)と肩を並べる(あるいは上回るところもある)と思っています。 せっかく税金で作られている地図なんですから、上手に活用していけば日本全体の利益になるんじゃないかと思います。 最後にお約束で、本件のご利用は自己責任でお願いします。 |
|
| 589位 |
|
|||
|
12:11:28 |
(株式会社PLEASURE 所属) |
|
`http://www.example.com/`にアクセスして、ドキュメントルートにある`index.php`を実行できるようにするまでの手順。 ドキュメントルートは、`/var/www`とする。 index.phpは例のやつ。 ```php:/var/www/index.php <?php phpinfo(); ?> ``` ## 1. php-fpmをインストール なんかremiリポジトリがどうとか色々あったけど、 ``` # yum list | grep php-fpm php-fpm.x86_64 5.3.3-27.el6_5 updates ``` あれ?あるぞ?ということで、そのままyumでインストールできた。 ``` # yum -y install php-fpm ``` ## 2. php-fpmの設定を変更 apacheってなってるところをnginxに変更。 ```diff:/etc/php-fpm.d/www.conf - user = apache + user = nginx - group = apache + group = nginx ``` ## 3. nginxの設定を変更 ドキュメントルートの設定と、PHPを実行できるように修正。 ```diff:/etc/nginx/conf.d/default.conf location / { - root /usr/share/nginx/html; + root /var/www; - index index.html index.htm; + index index.php; } - #location ~ \.php$ { - # root html; - # fastcgi_pass 127.0.0.1:9000; - # fastcgi_index index.php; - # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; - # include fastcgi_params; - #} + location ~ \.php$ { + root /var/www; + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME /var/www$fastcgi_script_name; + include fastcgi_params; + } ``` ## 4. php-fpmの起動 php-fpmの起動。ついでに自動起動の設定をしておく。 ``` # /etc/init.d/php-fpm start # chkconfig php-fpm on ``` ## 5. nginxの再起動 default.confを変更したので再起動。 ``` # /etc/init.d/nginx restart ``` ## 6. index.phpの実行 ブラウザから`http://www.example.com`にアクセス。 phpinfo()の結果が表示されればOK。 - - - > Blog URL : http://www.utano.jp/ (Syntax Error.) |
|
| 590位 |
|
|||
|
19:35:49 |
(株式会社PLEASURE 所属) |
|
## nginx公式ページからyumリポジトリのURLをコピー 公式ページはここ。 http://nginx.org/en/linux_packages.html#stable 「CentOS 6」ってリンクがあるので、URLをコピー。現時点でのURLはこれ(2014/01/30)。まぁ変わらないと思うけど。 http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm ## CentOSにてリポジトリを登録 先ほど取得したURLを使って、yumリポジトリをCentOSに登録。 ``` # rpm -ivh http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm ``` 下記ファイルが`/etc/yum.repos.d/`配下につくられる。 ```nginx.repo # nginx.repo [nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/6/$basearch/ gpgcheck=0 enabled=1 ``` ## nginxをyumでインストール あとは普通に。。。 ``` # yum -y install nginx ``` バージョン確認。 ``` # nginx -v nginx version: nginx/1.4.4 ``` ## nginxを起動 あ、これだけなんだ。 ``` # nginx ``` - - - > Blog URL : http://www.utano.jp/ (Syntax Error.) |
|
| 591位 |
|
|||
|
19:53:15 |
|
|
# 背景
railsアプリからメールを送信することは簡単にできますが、開発中に送信メールを確認したいときはどうするでしょうか。 今回はrailsアプリ開発中にメールを確認する方法を紹介します。 # 準備 今回はメール確認の例として、[devise](https://github.com/plataformatec/devise)を用いてメール認証が必要なユーザー登録の仕組みを用意します。 ## deviseインストール railsアプリケーションがすでに初期化されていたとして、 Gemfileにdeviseを追加し、インストールを行います。 ``` bundle exec rails generate devise:install bundle exec rails generate devise user bundle exec rails generate devise:views users ``` 生成したviewを読むように設定します。 ```ruby Devise.setup do |config| # 中略 config.scoped_views = true end ``` モデルでメール認証を有効化します。 ```ruby class User < ActiveRecord::Base devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable end ``` メール認証に関する部分のmigrationも変更しておきます。 ```ruby class DeviseCreateUsers < ActiveRecord::Migration def change create_table(:users) do |t| ## Database authenticatable t.string :email, :null => false, :default => "" t.string :encrypted_password, :null => false, :default => "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ## Rememberable t.datetime :remember_created_at ## Trackable t.integer :sign_in_count, :default => 0, :null => false t.datetime :current_sign_in_at t.datetime :last_sign_in_at t.string :current_sign_in_ip t.string :last_sign_in_ip ## Confirmable t.string :confirmation_token t.datetime :confirmed_at t.datetime :confirmation_sent_at t.string :unconfirmed_email # Only if using reconfirmable ## Lockable # t.integer :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at t.timestamps end add_index :users, :email, :unique => true add_index :users, :reset_password_token, :unique => true # add_index :users, :confirmation_token, :unique => true # add_index :users, :unlock_token, :unique => true end end ``` `config/environments/development.rb`の設定も忘れずに。 ```ruby config.action_mailer.default_url_options = { host: 'localhost:3000' } ``` これでrailsサーバーを起動して、`http://localhost:3000/users/sign_up`にアクセスすればユーザー登録が出来るので、メールが送信されます。  # コンソールでの確認 railsサーバーを起動しているコンソールを見てみると、メール送信のログが出力されています。 ``` Sent mail to hoge@email.com (12.1ms) Date: Wed, 06 Nov 2013 10:05:01 +0900 From: please-change-me-at-config-initializers-devise@example.com Reply-To: please-change-me-at-config-initializers-devise@example.com To: hoge@email.com Message-ID: <527995bddd516_2a4d3fe5d64135c4906e9@MBP.local.mail> Subject: Confirmation instructions Mime-Version: 1.0 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 7bit <p>Welcome hoge@email.com!</p> <p>You can confirm your account email through the link below:</p> <p><a href="http://localhost:3000/users/confirmation?confirmation_token=Ewx4qYxPR4Ex9yqySYEJ">Confirm my account</a></p> (0.7ms) commit transaction Redirected to http://localhost:3000/ Completed 302 Found in 927ms (ActiveRecord: 3.9ms) ``` 少しメール送信の文面を変更してみましょう。 `app/views/users/mailer/confirmaion_instructions.html.erb` ```erb <p>ようこそ <%= @email %> さん!</p> <p>下のリンクからユーザー登録を完了してください</p> <p><%= link_to 'ユーザー登録を完了する', confirmation_url(@resource, :confirmation_token => @token) %></p> ``` もう一度メール送信して、コンソールから確認してみると・・・ ``` Sent mail to fuga@email.com (40.4ms) Date: Wed, 06 Nov 2013 10:09:10 +0900 From: please-change-me-at-config-initializers-devise@example.com Reply-To: please-change-me-at-config-initializers-devise@example.com To: fuga@email.com Message-ID: <527996b6b39fd_2a4d3fe5d7dd27e49078@MBP.local.mail> Subject: Confirmation instructions Mime-Version: 1.0 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: base64 PHA+44KI44GG44GT44GdIGZ1Z2FAZW1haWwuY29tIOOBleOCkyE8L3A+Cgo8 cD7kuIvjga7jg6rjg7Pjgq/jgYvjgonjg6bjg7zjgrbjg7znmbvpjLLjgpLl rozkuobjgZfjgabjgY/jgaDjgZXjgYQ8L3A+Cgo8cD48YSBocmVmPSJodHRw Oi8vbG9jYWxob3N0OjMwMDAvdXNlcnMvY29uZmlybWF0aW9uP2NvbmZpcm1h dGlvbl90b2tlbj14b2EzeTR2OTlpOExZQ3hycWRvTSI+44Om44O844K244O8 55m76Yyy44KS5a6M5LqG44GZ44KLPC9hPjwvcD4K (7.2ms) commit transaction Redirected to http://localhost:3000/ Completed 302 Found in 148ms (ActiveRecord: 8.1ms) ``` エンコードされてしまっているので、confirmation_token付きのリンクを確認できなくなってしまいました。 そもそもメールの内容をコンソールから確認するのは面倒ですね。 # letter_opener 一つ目のメール確認方法は[letter_opener](https://github.com/ryanb/letter_opener)です。 Gemfileにletter_openerを追加し、インストールします。 ```ruby group :development do gem 'letter_opener' end ``` `config/environments/development.rb`に`config.action_mailer.delivery_method = :letter_opener`の一行を追加します。 ```ruby config.action_mailer.default_url_options = { host: 'localhost:3000' } config.action_mailer.delivery_method = :letter_opener ``` これだけでメールが送信されたとき(今回の場合はユーザー登録を行ったとき)にブラウザでメールを確認することが出来ます。  さらに[letter_opener_web](https://github.com/fgrehm/letter_opener_web)というgemもあります。 こちらもGemfileに追加し ```ruby group :development do gem 'letter_opener_web' end ``` `config/environments/development.rb`の`config.action_mailer.delivery_method`を`:letter_opener_web`に変更 ```ruby config.action_mailer.default_url_options = { host: 'localhost:3000' } config.action_mailer.delivery_method = :letter_opener_web ``` `config/routes.rb`に`/letter_opener`で`LetterOpenerWeb::Engine`をマウントするだけです。 ```ruby Your::Application.routes.draw do devise_for :users if Rails.env.development? mount LetterOpenerWeb::Engine, at: "/letter_opener" end end ``` これで`http://localhost:3000/letter_opener`にアクセスすることで送信されたメールを確認することが出来るようになります。  # mailcatcher もう一つの方法は[mailcatcher](http://mailcatcher.me)です。 先ほどのletter_openerはrailsアプリケーションに組み込みましたが、mailcatcherは単独で動作する開発用のSMTPサーバーです。 gemで配布されており、インストールは簡単です。 ``` gem install mailcatcher ``` `mailcatcher`コマンドで起動し、デフォルトではバックグラウンドで起動し、`smtp://localhost:1025`が使用されます。 起動オプションで適宜変更することも可能です。 ``` Usage: mailcatcher [options] --ip IP Set the ip address of both servers --smtp-ip IP Set the ip address of the smtp server --smtp-port PORT Set the port of the smtp server --http-ip IP Set the ip address of the http server --http-port PORT Set the port address of the http server --[no-]growl Growl to the local machine when a message arrives -f, --foreground Run in the foreground -b, --browse Open web browser -v, --verbose Be more verbose -h, --help Display this help information ``` railsアプリケーションと連携する場合は`config/environments/development.rb`の`config.action_mailer.smtp_settings`で設定を行います。 ```ruby config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: 'localhost', port: 1025 } ``` あとは`http://localhost:1080`にアクセスすればメールを確認することが出来ます。  ### まとめ どちらの方法も少しの設定で開発環境でメールを確認出来るようになるので、railsアプリからメールを送信する機能を確認する場合に活用できそうです。 |
|
| 592位 |
|
|||
|
16:50:24 |
(Increments inc. 所属) |
|
# TL;DR
- `go get` は Ruby でいう `gem` みたいなもん - `$GOPATH` は自分の環境に合わせて好きに指定してよい - 例えば `$HOME/.go` とか `$HOME/go` とか - 好きに設定してもいいけど、一度設定したらそれをずっと使い続けた方がたぶんいい # ことの始まり homebrewでGoをインストールしたらのっけから > Go 1.1 から `go get` コマンドは `$GOROOT` をパッケージダウンロード先として使わなくなりなりました。 > `go get` 使うには `$GOPATH` が必要です。 と言われて、間違った設定をしてあとで苦労したくなかったので色々と調べた。 ## go get コマンド ``` % go help get ``` > 使い方: go get [-d] [-fix] [-u] [build flags] [packages] > > パッケージ(依存するものも)をダウンロードしてインストールする。 > > -d フラグがあるとダウンロードだけでインストールしない。 > -fix フラグは fix tool をダウンロードしたパッケージの依存関係を解消する前に実行する。(詳しくは `godoc fix` を見れば分かるらしい。) > -u フラグがあるとパッケージとその依存パッケージをアップデートする。 > > build flagsは `go build` と `go install` のフラグを指定する。 > > パッケージを確認するとき `get` はローカルにインストールされている Go のバージョンと適合するブランチやタグを探す。最も重要なルールは、ローカルで走っているのが "go1" だったとすると、 `get` は "go1" という名前のブランチとタグを探すということ。もしそういうバージョンが無かったら、最新のものを取ってくる。 > > パッケージの指定の仕方については `go help packages` を見る。 > > どうやってソースコードを見つけるかは `go help remote` を見る。 ## GOPATH ``` % go help gopath ``` > Goパスは import 文の解決に使われる。go/build パッケージで実装されている。 > > 環境変数 GOPATH は Go コードを探す場所を表す。(PATH みたいな感じ) > Unix の場合はコロンで区切る。 > Windows の場合はセミコロンで区切る。 > Plan 9だとリスト。 > > GOPATH は標準の Go の外でパッケージをビルドしたりインストールする際に設定されていなければならない。 > > GOPATH に入っているディレクトリはそれぞれ決められた構造を持っていなければならない: > > (詳しくは割愛) > > 例: > > ``` > GOPATH=/home/user/gocode > > /home/user/gocode/ > src/ > foo/ > bar/ (go code in package bar) > x.go > quux/ (go code in package main) > y.go > bin/ > quux (installed command) > pkg/ > linux_amd64/ > foo/ > bar.a (installed package object) > ``` > > Go は GOPATH の各ディレクトリからソースコードを探すが、ダウンロードしたファイルは常に GOPATH の中で一番最初のディレクトリにインストールされる。 ## 結局どうすればいいのか GOPATH は決まった場所を指定するものではなく、自分の環境で好きな場所を勝手に指定すればよい、という性質のものらしい。つまり、正しい場所を指定しておかないと Go が使えないとかそういった性質のものではない(それを恐れていた)、と。 インターネットを観測した感じだと - `$HOME/go` - `$HOME/.go` あたりを設定している人が多い印象。設定は `.bashrc` や `.zshrc` で行う。 ```bash:~/.bashrc export GOPATH=$HOME/.go ``` |
|
| 593位 |
|
|||
|
17:01:05 |
|
|
EC2に対してcapistranoでデプロイするためにはSSH接続できるユーザーが必要になる。 今回はdeployユーザーを作成する。 ##ユーザー作成 既存ユーザー(ec2-user)EC2に接続する。 ``` ssh -i /home/vagrant/.ssh/XXX.pem ec2-user@XX.XXX.XXX.XXX ``` ユーザーを作成する。 ``` useradd deploy passwd deploy ``` sudoができるように/etc/sudoersファイルを変更する。 (sudo vi /etc/sudoersだと読み取り専用になってしまうので次のコマンドでファイルを開く) ``` sudo visudo ``` ファイルに1行追加する。 rootに同じ設定があるので、そのすぐ下に追加するといい。 ```etc/sudoers deploy ALL=(ALL) ALL ``` ##鍵の作成 作ったユーザーに変更して鍵を作成する。 ``` sudo su - deploy cd /home/deploy mkdir .ssh cd .ssh ssh-keygen -t rsa mv id_rsa.pub authorized_keys ``` ※ファイル名を書き換えるのはAMILinuxのSSHの初期設定がauthorized_keysとなっているため。 権限を変更する。 ``` chmod 600 authorized_keys cd ../ chmod 700 .ssh ``` ※権限が適切でないと後にSSH接続できなくなる。 ローカルに秘密鍵をコピーする。 catで中身を確認してそれをローカルに保存しておく。 ``` cat id_rsa ``` ※後でssh接続に必要なのでホームの.sshなどに入れておくといい。 ##SSHの設定 設定ファイル/etc/ssh/sshd_configを確認する。 ``` vi /etc/ssh/sshd_config ```次の記述があることを確認する。 なければ追加する。```/etc/ssh/sshd_configAuthorizedKeysFile .ssh/authorized_keys ``` ※AMILinuxは初期状態でこれが記述されている。 ##接続の確認 接続できれば無事終了。 ``` ssh -i /home/vagrant/.ssh/id_rsa deploy@XX.XXX.XXX.XXX ``` |
|
| 594位 |
|
|||
|
22:33:46 |
|
|
皆さん環境構築とか、システムの設定作業とかって、どのように作業していますか?
古きは環境構築手順書を使って行なっていましたが、昨今の自動化ブームに伴って、 chefやcapistrano、fabricなどのツールを検討されている方も多いと思います。 ただ、最近はやりの自動化ツールって、RubyとかPythonを多少知っている必要があったり、 独自DSLや特殊な用語を覚える必要があったりと、学習コストが高くてとっつきにくくないですか? まわりにススメても「あー便利そうだねけど難しそうだね」で終わってしまうパターンが多々あります。 #そこでPacifistaですよ Pacifistaは「環境構築をまるごとプログラミングする。それもシンプルに」を目的としたOSSの自動化ツールです。 http://pacifista.ukiuni.org/ Pacifistaには、以下の特徴があります。 * JavaScriptでコードを書く事が出来る。 * JavaScript以外にも、Ruby,Python,Groovyなど複数の言語に対応している。 * 対象となるサーバにSSHでコマンドを投げるので、エージェント不要。 * EC2やVirtualBox上での仮想マシン操作や、構築後のテストなど、機能が豊富。 * 仮想マシン起動→構築→テストを1ファイルでシンプルに書くことが出来る。 個人的には、社内の大抵の人が読み書き出来るJavaScriptで書けるというのがとても魅力でした。 #Pacifistaのセットアップ Javaがインストールされた環境に、公式サイトからダウンロードしたファイルを展開するだけです。簡単。 #使ってみよう! ではPacifistaを使って、サーバにApacheをインストールしてみます。 コードはJavaScriptで記述します。JavaScriptが何となく読める人だったら、容易に理解出来ると思います。 ```js:apacheinstall.js //対象のサーバにssh接続するための設定 var host = "192.168.10.100" var sshPort = "22" var user = "user" var password = "pass" //対象のサーバに接続 var remote = Remote.create(); remote.connect(host, sshPort, user, password); //rpm -q httpdを実行し、Apacheがインストールされていればエラー終了する if (remote.execute("rpm -q httpd").contains("httpd")) { throw "httpd is already installed" } //Apacheのインストールと起動 remote.call("sudo yum install -y httpd"); remote.call("sudo /etc/init.d/httpd start"); remote.call("sudo /sbin/chkconfig httpd on"); //80番ポートに接続できるかテスト var tester = Tester.create(remote); tester.assertPortOpen(80); ```` 作成したコードは、Scriptsフォルダに配置しましょう。JavaScriptで記述しているので、拡張子は.jsにします。 作成したコードを実行する際は、以下のようにコマンドを実行します。 ```` $ bin/pacifista #sudo yum install -y httpd Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * base: ftp.jaist.ac.jp * extras: ftp.jaist.ac.jp * updates: ftp.jaist.ac.jp base | 3.7 kB 00:00 extras | 3.5 kB 00:00 updates | 3.4 kB 00:00 Setting up Install Process Resolving Dependencies --> Running transaction check ---> Package httpd.i686 0:2.2.15-29.el6.centos will be installed --> Finished Dependency Resolution Dependencies Resolved ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: httpd i686 2.2.15-29.el6.centos updates 828 k Transaction Summary ================================================================================ Install 1 Package(s) Total download size: 828 k Installed size: 2.8 M Downloading Packages: httpd-2.2.15-29.el6.centos.i686.rpm | 828 kB 00:00 Running rpm_check_debug Running Transaction Test Transaction Test Succeeded Running Transaction Warning: RPMDB altered outside of yum. Installing : httpd-2.2.15-29.el6.centos.i686 1/1 Verifying : httpd-2.2.15-29.el6.centos.i686 1/1 Installed: httpd.i686 0:2.2.15-29.el6.centos Complete! #sudo /etc/init.d/httpd start httpd を起動中: httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName [ OK ] #sudo /sbin/chkconfig httpd on ```` はい、サックリとApacheがインストール出来ました!! コード書いてコマンド一発と、とてもシンプルですね。今回試してみて、ハマりどころゼロでした。 事例はとても少ないですが、公式サイトの情報だけで十分に使いこなせます。 というわけで、Pacifista、オススメです。興味ある方是非使ってみて下さい。 |
|
| 595位 |
|
|||
|
09:27:09 |
(WAmazing Inc 所属) |
|
さて、皆さん割と homebrew で openssl 入れて brew link しちゃってる人も多いと思います。そんな環境でその openssl を使って Ruby をコンパイルすると、OpenSSL 利用時に証明書エラーが発生します。 ```sh $ pry [1] pry(main)> require 'open-uri' => true [2] pry(main)> open('https://www.google.com/').read OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed from ~/.rbenv/versions/2.0.0-rc2/lib/ruby/2.0.0/net/http.rb:917:in `connect' ``` エラーをぐぐると山ほど出てきます。ただ大抵書かれてる対応方法が ```sh $ ruby -ropenssl -e "p OpenSSL::X509::DEFAULT_CERT_FILE" "/usr/local/etc/openssl/cert.pem" ``` して出てくる標準証明書のパスに ```sh $ wget -O /usr/local/etc/openssl/cert.pem http://curl.haxx.se/ca/cacert.pem ``` で対応してね、という方法でいささか気持ちが悪かったのでちょっと調べました。 ## curl.haxx.se って? cURL のサイトです。 - http://curl.haxx.se/docs/caextract.html に詳しく書いてありますが、curl のサイト上で配布してる cacert.pem は、Mozilla で利用している証明書です。 (しかしハッシュ値も表示されて無くて https でもないので、いささかこの証明書を突っ込むの不安ですね…) ## homebrew から証明書を入れる 同等の証明書は curl-ca-bundle という formula で提供されているので、 ```sh $ brew install curl-ca-bundle $ brew list curl-ca-bundle /usr/local/Cellar/curl-ca-bundle/1.87/share/ca-bundle.crt $ cp /usr/local/Cellar/curl-ca-bundle/1.87/share/ca-bundle.crt /usr/local/etc/openssl/cert.pem ``` することで、証明書を設置でき、SSL でのエラーは発生しません。 |
|
| 596位 |
|
|||
|
16:48:07 |
(コロプラ 所属) |
|
マイグレーションに関しては[別記事に移動](http://qiita.com/edo_m18/items/717fe32d744a10df7179)しました。 ------------- なんとなくCoreDataを理解してきたのでメモ。 CoreDataはO/Rマッピングフレームワークで、Objective-C内のオブジェクトをRDBなどのストアとマッピングしてデータを永続化できる仕組み(であってるかな?) ざっくり言ってしまえば、SQLiteをObjective-Cから扱うための便利クラスを集めたフレームワーク。 (もちろん、SQLite以外のストアもあるからあくまでイメージ) なので、様々なクラスが関わりあいながら、DBからデータを取得したり保存したり、といったことが可能となっている。 そもそもDBにはそれほど詳しくないので、間違っていたらバシバシコメントもらいたいんですが、ざっくり言うとCoreDataの用語とRDBの用語は以下の関連性になっているみたい。 | CoreData | RDB | |----------|--------| | エンティティ | テーブル | | 管理オブジェクト(NSManagedObject) | レコード | | 管理オブジェクトコンテキスト(NSManagedObjectContext) | クエリ? | | 管理オブジェクトモデル(NSManagedObjectModel) | DB | ##使う前の準備 まず、CoreDataを使うためにいくつかのインスタンスを生成しておく必要があります。 `NSManagedObjectModel`、`NSPersistentStoreCoordinator`、`NSManagedObjectContext`の3つです。 1. `NSManagedObjectModel`を定義を元に生成 2. `NSManagedObjectModel`のインスタンスを元に`NSPersistentStoreCoodinator`クラスを生成(ちなみに和訳は「永続化ストア」) 3. CoreDataで使うSQLiteのファイルを取得 4. 生成した`NSPersistentStoreCoodinator`インスタンスを`NSSQLiteStoreType`に指定し、(3)で取得したファイルのURLを指定してセットアップ 5. (4)までで生成した`NSPersistentStoreCoodinator`を元に、`NSManagedObjectContext`インスタンスを生成 use core dataのチェックを入れて自動生成されたコードを参考にすると、以下のようにアクセサメソッド内でインスタンスを生成している。 ```objc - (NSManagedObjectModel *)managedObjectModel { if (_managedObjectModel != nil) { return _managedObjectModel; } NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"HogeModel" withExtension:@"momd"]; _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; return _managedObjectModel; } ``` ```objc - (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if (_persistentStoreCoordinator) { return _persistentStoreCoordinator; } NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"HogeProj.splite"]; NSError *error = nil; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; if (![_persistentStoreCoordinator addPersistentStoreWitType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return _persistentStoreCoordinator; } ``` ```objc - (NSManagedObjectContext *)managedObjectContext { if (_managedObjectContext != nil) { return _managedObjectContext; } NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; if (coordinator != nil) { _managedObjectContext = [[NSManagedObjectContext alloc] init]; [_managedObjectContext setPersistentStoreCoordinator:coordinator]; } return _managedObjectContext; } ``` ##CoreDataを使う 前述までの処理で使う準備が整いました。 自分の理解では、 `NSManagedObjectModel`はモデルデータ全体の設定。つまりDBスキーマ。 `NSPersistentStoreCoodinator`は上記DB役との仲介役。コーディネータ。 `NSManagedObjectContext`は、上記とアプリケーションをつなぐ役。なので、基本的にはセットアップが終わったら、アプリケーション上では`NSManagedObjectContext`を介してデータのやりとりを行います。 実際の(いわゆるMVCのModelとしての)モデルデータについてはCoreDataのモデルエディタ(?)を使ってXcode上でさくっと設定が作れます。それを元にインスタンスを生成する、という感じです。 ###CoreDataを使う手順 1. NSFetchRequestインスタンスを生成 2. NSManagedObjectContextインスタンスを介してモデルデータを取得する。 3. (2)で`NSManagedObject`のインスタンスの配列が返される(モデルデータを操作するラッパー的なクラス?) 4. 取得した`NSManagedObject`を介してデータの更新などをする 5. 操作完了後に`[managedObjectContext save:&error];`を実行して、操作したモデルデータを永続化ストアに伝えて更新する ちょーざっくり書くとこんな感じ。 要は、`NSManagedObjectContext`を介してモデルデータを取得し、`NSManagedObject`を使ってデータの更新を行う、ということです。 実際のサンプルコードは以下。 ```objc //fetch設定を生成 NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([HogeData class])]; //sort条件を設定 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; [fetchRequest setSortDescriptors:sortDescriptors]; //ARCオフの場合はreleaseする //[sortDescriptor release]; //[sortDescriptors release]; //fetch設定を元に、managedObjectContextからデータを取得 //返り値は`NSManagedObject`の配列になる NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil]; for (NSManagedObject *data in results) { //do something. } ``` ###CoreDataで新規データを生成する ```objc HogeData *hogeData = (HogeData *)[NSEntityDescription insertNewObjectForEntityForName:@"HogeData" inManagedObjectContext:managedObjectContext]; ``` なんだかややこいですが、`HogeData`はXcodeのモデルエディタで操作するエンティティの名前です。 実体は`NSManagedObject`クラスを継承しているサブクラスになります。 これらの情報と、managedObjectContextから新規データを生成します。 生成には`NSEntityDescription`クラスの`insertNewObjectForEntity:inManagedObjectContext:`メソッドを使います。(`[[HogeData alloc] init]`はしない) 生成してすぐに、いわゆるテーブルに新規レコードが追加されます。 返された`NSManagedObject`サブクラスのインスタンスから各種情報を設定し、最後にcontextのsave:メソッドを実行して更新すればめでたく新規データの追加が完了します。 ###CoreDataのデータ削除 データの削除には、`NSManagedObjectContext`の`deleteObject:`メソッドを利用します。 実際のコード例は以下。 ```objc //削除対象のフェッチ情報を生成 NSFetchRequest *deleteRequest = [[NSFetchRequest alloc] init]; [deleteRequest setEntity:[NSEntityDescription entityForName:@"HogeData" inManagedObjectContext:managedObjectContext]]; [deleteRequest setIncludesPropertyValues:NO]; //managed object IDのみフェッチ NSError *error = nil; //生成したフェッチ情報からデータをフェッチ NSArray *results = [managedObjectContext executeFetchRequest:deleteRecest error:&error]; //[deleteRequest release]; //ARCオフの場合 //フェッチしたデータを削除処理 for (NSManagedObject *data in results) { [managedObjectContext deleteObject:data]; } NSError *saveError = nil; //削除を反映 [managedObjectContext save:&saveError]; ``` 参考にした記事 -> [Core Data: Quickest way to delete all instances of an entity - stackoverflow](http://stackoverflow.com/questions/1383598/core-data-quickest-way-to-delete-all-instances-of-an-entity) ----------------------- ##アプリで保存可能なディレクトリ情報を取得する アプリ単位で保存できる領域が決まっており、そのディレクトリ情報を取得する方法。 (ちょっと勘違いがあるかも。とりあえずのメモです) まず、CoreDataを使うようにするとAppDelegate.mに定義されているソース。 ```objc - (NSURL *)applicationDocumentsDirectory { NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; return url; } ``` NSFileManagerを使って、該当のディレクトリを`NSURL`型で取得して返しています。 ## CoreDataに保存されている件数を取得する 追加ロード時など、現在CoreDataに何件のデータが保持されているかを知りたいケースがあるかと思います。 その場合には`NSManagedObjectContext#countForFetchRequest`を利用します。 ```objc // Entityを指定してFetchRequestを作る NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClassName(anEntityClass.class)]; request.includesSubentities = NO; NSError *error = nil; AppDelegate *delegate = UIApplication.sharedApplication.deleate; // NSManagedObjectContextから、生成したリクエストを元に件数だけを取得 // (NSManagedObjectContextは適宜定義されているものに置き換えてください) NSInteger count = [delegate.managedObjectContext countForFetchRequest:request error:&error]; // もし見つからなければ0件 if (count == NSNotFound) { return 0; } return count; ``` |
|
| 597位 |
|
|||
|
00:57:23 |
|
|
[Getting Started: Building a Chrome Extension - Google Chrome](https://developer.chrome.com/extensions/getstarted.html) を見ながら作った.
## 流れ manifest と ポップアップ時の html を作ってアイコン作って Chrome ストアに上げる. ## どういうものを作るか **あとで読む Extension** アイコンを押すと, 現在見ているページが trello のあとで読むリストに追加される. を作りたかったが, 想定した時間ではできなかったので, とりあえずリンクを Markdown 形式とかで表示する Extension を作った. ## ベースを作る Extension 管理用ディレクトリを作る. ```bash mkdir linkmaker cd linkmaker # git init から Initial Commit まで ``` ディレクトリ直下に manifest.json を作成する. [Manifest File Format - Google Chrome](https://developer.chrome.com/extensions/manifest.html#icons) を参考にして作る. ```manifest.json { "manifest_version": 2, "name": "Link Maker", "version": "1.0", "description": "Make link of current tab quickly", "browser_action": { "default_icon": "icon.png" }, "permissions": [ "tabs" ], "icons" : { "128": "icon.png" } } ``` icon.png も作成する. 最大で 128 * 128 サイズなので, 500 四方で作っておいた. ここまでできたら, Chrome の chrome://extensions/ に移動し, 「パッケージ化されていない拡張機能を読み込む」から作業したディレクトリを選択し, インストール. 右上にアイコンが表示されれば OK. ## 現在見ているページを取得する JavaScript (jQuery) を使う. まず, 土台となる HTML を用意する. これは, 拡張ボタンを押した時に浮き上がってくるやつのこと. ```bash # JavaScript のライブラリは js/lib に入れる. mkdir -p js/lib # jQuery 2.1.0 圧縮版 をとりあえず落としておく wget -O js/lib/jquery.min.js http://code.jquery.com/jquery-2.1.0.min.js ``` popup.html を作る ```popup.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Link Maker</title> <link rel="stylesheet" href="css/popup.css" type="text/css"> </head> <body> <p id="title"></p> <p id="url"></p> <!-- Load JavaScript--> <script src="js/lib/jquery.min.js"></script> <script src="js/popup.js"></script> </body> </html> ``` CSS (popup.css) も用意しておく. ```css/popup.css body { width: 400px; margin: 0px; background-color: #fff; min-width: 357px; overflow-x: hidden; font-family: "Helvetica Neue",Helvetica,"Hiragino Kaku Gothic Pro","Lucida Grande",Verdana,Arial,Meiryo,"メイリオ",sans-serif; } img { margin: 5px; border: 2px solid black; vertical-align: middle; width: 75px; height: 75px; } ``` JavaScript ファイルを作る. ```js/popup.js $(function () { chrome.tabs.getSelected(null, function(tab) { $('#title').text(tab.title); $('#url').text(tab.url); }); }); ``` 最後に, manifest の default_popup に popup.html を指定しておく. ```manifest.json { "manifest_version": 2, "name": "Link Maker", "version": "1.0", "description": "Make link of current tab quickly", "browser_action": { "default_popup": "popup.html", "default_icon": "icon.png" }, "permissions": [ "tabs" ], "icons" : { "128": "icon.png" } } ``` ## ここまでのディレクトリ構造 - linkmaker - manifest.json - popup.html - css - popup.css - js - popup.js - lib - jquery.min.js - icon.png これで, 拡張機能ページからリロードすると, 現在見ているページのタイトル, URL を表示するはず. ## 公開する Bootstrap とか使ったりして, 見た目をある程度整えたら公開する. 公開する前に, 登録料として $5 払う必要がある. 手順は以下を参考にした. [Publishing Your App - Chrome Web Store — Google Developers](https://developers.google.com/chrome/web-store/docs/publish) 1. Extension を zip で固めておく - とりあえず [Dashboard](https://chrome.google.com/webstore/developer/dashboard) へ - ``$5`` 課金 - 新しいアイテムを追加から, zip をアップロード - 拡張のスクリーンショットなどを追加し, 公開 [Link Maker](https://chrome.google.com/webstore/detail/link-maker/ifflbbedjddjmfkcpjflbjbngojooojc) [sqrtxx/linkmaker](https://github.com/sqrtxx/linkmaker)  できてよかった, よかったですね. ### 同系統の Extension あまり調べてないが, Copy Fixer を愛用している. [Copy Fixer](https://chrome.google.com/webstore/detail/copy-fixer/gkpoieconkiifjafemnnohlegmfpipaa) ## References - [Getting Started: Building a Chrome Extension - Google Chrome](https://developer.chrome.com/extensions/getstarted.html) - [Qiitaの通知を表示できるChrome拡張を作った - Qiita [キータ]](http://qiita.com/yuku_t/items/cc30710256da2427c974) - [Publishing Your App - Chrome Web Store — Google Developers](https://developers.google.com/chrome/web-store/docs/publish) |
|
| 598位 |
|
|||
|
21:32:43 |
(株式会社ソニックムーブ 所属) |
|
## 概要 ちょっと大げさなタイトルですが、仕事で円グラフを描画したいという要望があったので調査してみたときのメモです。 グラフを描写するライブラリで無料かつ良さそうなものをいくつかをピックアップしてみました。 ・[XYPieChart for iOS](https://www.cocoacontrols.com/controls/xypiechart) 円グラフ専用のライブラリです。 円グラフのみ使用するのであればデザイン、アニメーションなど含めて一番よいライブラリでしょう。 ・[PNChart for iOS](https://www.cocoacontrols.com/controls/pnchart) 円グラフ、折れ線グラフ、棒グラフの3つをサポートしてます。 サポートしているグラフも一番多くデザインもシンプルで気に入っているのですが、円グラフは通常のものと表現方法が違い一つのデータしか扱えないようです。 ここを改善できればこの3つの中では一番よいライブラリだと思います。 ・[iOSPlot for iOS](https://github.com/honcheng/iOSPlot) 円グラフと折れ線グラフの両方をサポートしています。 機能的にも中々よいのですが、ここ半年以上更新されていないので今後のアップデートは期待できなさそうです。 この3つを比較した結果、折れ線グラフ、棒グラフなら`PNChart`、円グラフオンリーなら`XYPieChart`と使い分けるのが良さそうです。 「別のグラフ使いたいよ」という方は自力で作るか、別のライブラリ探してみてください。 [追記] ・[Core Plot](https://github.com/core-plot/core-plot) この[サンプル](https://github.com/core-plot/core-plot/wiki/Example-Graphs)をご覧になれば分かると思いますが、かなり高機能なライブラリです。 その代わり高機能だけにそれだけ複雑な仕様にもなっているので、業務などでがっつりグラフを描写する必要がある場合などはCore Plotが一番柔軟に対応できるでしょう。 ここでは`XYPieChart`で円グラフを表示する方法を紹介します。 ## XYPieChartの使い方 Cocoa Podsで導入するのが一番楽です。 下記のPodfileをプロジェクトのルードディレクトリ配下に作成して、コンソールから`pod install`コマンドを実行すれば導入できます。 ``` platform :ios, '6.0' pod 'XYPieChart', '~> 0.2' ``` 次にUIViewControllerを作成します。 ここでは便宜上`ASChartViewController`という名前にします。 後は下記ソースコードを作成したViewControllerのソースに貼付けてもらえば円グラフを画面に表示できます。 ソースコード内のコメントを見ていただければ必要最低限の使い方は把握できると思います。 ```objc #import "ASChartViewController.h" #import "XYPieChart.h" @interface ASChartViewController () <XYPieChartDelegate, XYPieChartDataSource> // 各スライスの色 @property(nonatomic, strong) NSArray *sliceColors; // 各スライスのデータ @property(nonatomic, strong) NSMutableArray *slices; @end @implementation ASChartViewController - (void)viewDidLoad { [super viewDidLoad]; // スライスに表示するデータの定義 self.slices = [NSMutableArray arrayWithCapacity:5]; for (int i = 0; i < 5; i ++) { NSNumber *one = [NSNumber numberWithInt:rand() % 60 + 20]; [self.slices addObject:one]; } // 各項目の背景色を定義 self.sliceColors = @[[UIColor colorWithRed:246/255.0 green:155/255.0 blue:0/255.0 alpha:1], [UIColor colorWithRed:129/255.0 green:195/255.0 blue:29/255.0 alpha:1], [UIColor colorWithRed:62/255.0 green:173/255.0 blue:219/255.0 alpha:1], [UIColor colorWithRed:229/255.0 green:66/255.0 blue:115/255.0 alpha:1], [UIColor colorWithRed:148/255.0 green:141/255.0 blue:139/255.0 alpha:1]]; // パイチャートの初期化 XYPieChart *pieChart = [[XYPieChart alloc] initWithFrame:CGRectMake(10, 10, 220.0, 220.0)]; // デリゲートの設定 pieChart.delegate = self; // データソースの設定 pieChart.dataSource = self; // パイチャートの中心位置 pieChart.pieCenter = self.view.center; // YESの場合、パーセンテージで数字を表示します。 pieChart.showPercentage = NO; // 値を表示するラベルのフォント pieChart.labelFont = [UIFont systemFontOfSize:12.0]; // 値を表示するラベルの色 pieChart.labelColor = [UIColor blackColor]; // 値を表示するラベル背景のシャドウカラー pieChart.labelShadowColor = [UIColor darkGrayColor]; // 表示アニメーションのスピード pieChart.animationSpeed = 1.0; // パイチャートの背景色 [pieChart setPieBackgroundColor:[UIColor darkGrayColor]]; [self.view addSubview:pieChart]; // パイチャートを描画します。 [pieChart reloadData]; } #pragma mark - XYPieChart Data Source // スライスの件数 - (NSUInteger)numberOfSlicesInPieChart:(XYPieChart *)pieChart { return self.slices.count; } // 各スライスの値 - (CGFloat)pieChart:(XYPieChart *)pieChart valueForSliceAtIndex:(NSUInteger)index { return [self.slices[index] floatValue]; } // 各スライスの色を設定(Optional) - (UIColor *)pieChart:(XYPieChart *)pieChart colorForSliceAtIndex:(NSUInteger)index { return self.sliceColors[(index % self.sliceColors.count)]; } // 各スライスに表示する文字列の設定(Optional) - (NSString *)pieChart:(XYPieChart *)pieChart textForSliceAtIndex:(NSUInteger)index { return [NSString stringWithFormat:@"%@ 件", self.slices[index]]; } @end ``` 実行結果のスクリーンショットです。  使い勝手もよく必要最低限の機能は揃っているのでオススメです。 |
|
| 599位 |
|
|||
|
22:47:51 |
|
|
# CentOS 上で Vagrant を導入するまでのメモ
GNOME 等 GUI が動作する環境での Vagrant 導入までの手順については, 様々なサイトで説明されていた. しかしながら, CUI での Vagrant 導入までの日本語の手順は少なかったのでメモする. というのも, まず, CUI での VirtualBox 導入までのドキュメントが少なかった. (もともと, GUI で Linux を扱うことがなかったので, これから X Window 入れて, っつーことが面倒くさかった.) やってみてわかったことは, VirtualBox をインストールできればよいということ. インストールしてしまえさえすれば, 自分で VM を起動できなくても vagrant が VM を起動してくれるし, vagrant のインストールはとても簡単である. ``` % cat /etc/redhat-release CentOS release 6.4 (Final) % uname -a Linux centos_63 2.6.32-358.23.2.el6.x86_64 #1 SMP Wed Oct 16 18:37:12 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux ``` まず, VirtualBox をインストールする. 日本語だと情報が少なかったんで, いろいろ読んでみる. 特に参考になったのは以下のドキュメントだった. http://wiki.centos.org/HowTos/Virtualization/VirtualBox https://www.virtualbox.org/manual/ch02.html#install-linux-host 基本的に, 上記2つのドキュメント以外は参考にしていない. (というのも, わりとすんなり入ってくれたからです.) さて, インストールメモを書いていこうっと. ## DKMS のインストール まず, DKMS をインストールします. これをインストールする理由は, そうすることが推奨されていたからです. とは言え, インストールする理由もメモっておきます. VirtualBox は vboxdrv と呼ばれる特殊なカーネルモジュールを用いています. カーネルモジュールとは, カーネル空間で動作するプログラムのことであり, 動的に(カーネルが動作しているときに)カーネルに組み込んで, 動作させられるプログラムのことです. http://www.oidon.net/linux/build-arm-linux-kernel-module http://itpro.nikkeibp.co.jp/article/Keyword/20081107/318745/ http://homepage3.nifty.com/rio_i/lab/driver24/001module.html http://www.tldp.org/LDP/lkmpg/2.6/html/index.html カーネルモジュールは将来的なカーネルアップデートがあった際にはインストールし直さなければなりません. それは非常に面倒くさいです. そのために, dkms のインストールします. DKMS はカーネルモジュールのビルドとアップデートを助けるフレームワークだそうです. DKMS が インストールされていれば, VirtualBox カーネルモジュールは常に動作し続けることが可能となります. もし, ホスト OS のカーネルがアップデートされたとしても心配いりません. VirtualBox のカーネルモジュール(vboxdrv)も自動的にビルド、アップデートされます. DKMS のインストール方法は以下 http://wiki.centos.org/HowTos/Virtualization/VirtualBox 上のドキュメントをそのまま実行していけば問題なくできました. yum からインストールするためには、DKMS があるリポジトリを追加してやればオッケーです. 僕は, なんとなく yum からインストールするようにしたかったので, そうします. ``` sudo rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm cat /etc/yum.repos.d/epel.repo [epel] name=Extra Packages for Enterprise Linux 6 - $basearch #baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch failovermethod=priority enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6 [epel-debuginfo] name=Extra Packages for Enterprise Linux 6 - $basearch - Debug #baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch/debug mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-debug-6&arch=$basearch failovermethod=priority enabled=0 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6 gpgcheck=1 [epel-source] name=Extra Packages for Enterprise Linux 6 - $basearch - Source #baseurl=http://download.fedoraproject.org/pub/epel/6/SRPMS mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-source-6&arch=$basearch failovermethod=priority enabled=0 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6 gpgcheck=1 ``` yum からの DKMS のインストールが可能となったので, インストールします. ``` % yum info dkms Name : dkms Arch : noarch Version : 2.2.0.3 Release : 17.el6 Size : 207 k Repo : installed From repo : epel Summary : Dynamic Kernel Module Support Framework URL : http://linux.dell.com/dkms License : GPLv2+ Description : This package contains the framework for the Dynamic Kernel Module Support (DKMS) : method for installing module RPMS as originally developed by Dell. ``` ``` yum install dkms ``` ## VirtualBox のインストール つづいて, VirtualBox をインストールします. ここでも, 気分的に yum 使いたかったので, yum からのインストールをメモります. yum からインストールしたい場合に参考になるページ http://www.if-not-true-then-false.com/2010/install-virtualbox-with-yum-on-fedora-centos-red-hat-rhel まず, リポジトリを追加します. ``` cd /etc/yum.repos.d/ sudo wget http://download.virtualbox.org/virtualbox/rpm/rhel/virtualbox.repo ``` そして, yum からのインストールが可能になったことを確認します. ``` % yum list | grep VirtualBox VirtualBox-3.2.x86_64 3.2.18_89859_el6-1 virtualbox VirtualBox-4.0.x86_64 4.0.20_89853_el6-1 virtualbox VirtualBox-4.1.x86_64 4.1.28_89849_el6-1 virtualbox VirtualBox-4.2.x86_64 4.2.18_88780_el6-1 virtualbox VirtualBox-4.3.x86_64 4.3.0_89960_el6-1 virtualbox ``` yum からインストールします. ``` yum install VirtualBox-4.3 ``` つづいて, setup をおこないます. yum install を行うと, yum installer は, vboxusers グループと必要なカーネルモジュール(vboxdrv)を構築します. virtual box のユーザは, vboxusers グループに所属していなければなりません. 次のコマンドでユーザをグループに所属させます. ``` sudo usermod -a -G vboxusers username ``` ここまでで, インストールは完了となります. この後, VirtualBox の起動をしなければならないような気がしたのですが, それは vagrant に任せることにします. なぜなら, CUI からの VirtualBox の起動を手作業でやることは, 中々大変な予感がしたからです. (大変な作業だから Vagrant があるんです) ## Vagrant の導入 Vagrant の導入は本当に簡単です. 以下を読めばすぐにできるでしょう. http://docs.vagrantup.com/v2/installation/index.html Vagrant とは何でしょう? とりあえず, そこから調べます. とりあえず, ドキュメントを読みます. http://docs.vagrantup.com/v2/getting-started/index.html Vagrant は仮想マシンを肩代わりするツールだそうです. これらの仮想マシンは, VirtualBox, VMware, AWS, で提供されます. 仮想マシンの提供者(VirtualBox, VMWare, AWS)を provider と呼びます. provider には様々なものを選ぶことができるそうです. VirtualBox, VMWare, AWS, etc... 用途によって, provider を選択することもできるそうです.(すなわち, provider と仮想マシンの設定が独立しているということ?) Getting Started では, Virtual Box を利用しています.(だから今回 VirtualBox をインストールしました.) CentOS 6.4 64bit に vagrant をインストールするには以下のように, rpm からインストールすればいいだけです. http://docs.vagrantup.com/v2/installation/ ``` sudo rpm -Uvh http://files.vagrantup.com/packages/a40522f5fabccb9ddabad03d836e120ff5d14093/vagrant_1.3.5_x86_64.rpm ``` インストールするホストに応じて, rpm を選択しましょう. http://downloads.vagrantup.com/ ## UP AND RUNNING インストールは完了しました. 続いてセットアップして起動します. http://docs.vagrantup.com/v2/getting-started/index.html ドキュメントに書かれている通りコマンドを叩いてみます. ``` % vagrant init precise32 http://files.vagrantup.com/precise32.box % vagrant up ``` 叩くと次のようなメッセージが表示されます. ``` % vagrant init precise32 http://files.vagrantup.com/precise32.box A `Vagrantfile` has been placed in this directory. You are now ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on `vagrantup.com` for more information on using Vagrant. ``` Vagrantfile なるものがあるらしいです. よく分からんが, とりあえず, vagrant up をしてみます. ``` % vagrant up Bringing machine 'default' up with 'virtualbox' provider... [default] Box 'precise32' was not found. Fetching box from specified URL for the provider 'virtualbox'. Note that if the URL does not have a box for this provider, you should interrupt Vagrant now and add the box yourself. Otherwise Vagrant will attempt to download the full box prior to discovering this error. Downloading or copying the box... Progress: 23% (Rate: 992k/s, Estimated time remaining: 0:06:13) ``` すると, なにやらダウンロードっぽいことが始まります. マシン 'default' を 'virtualbox' provider へ bring up with しているらしいです. vagrant up が完了すると以下のようになります. ``` % vagrant up Bringing machine 'default' up with 'virtualbox' provider... [default] Box 'precise32' was not found. Fetching box from specified URL for the provider 'virtualbox'. Note that if the URL does not have a box for this provider, you should interrupt Vagrant now and add the box yourself. Otherwise Vagrant will attempt to download the full box prior to discovering this error. Downloading or copying the box... Extracting box...te: 422k/s, Estimated time remaining: 0:00:01)0 Successfully added box 'precise32' with provider 'virtualbox'! [default] Importing base box 'precise32'... [default] Matching MAC address for NAT networking... [default] Setting the name of the VM... [default] Clearing any previously set forwarded ports... [default] Creating shared folders metadata... [default] Clearing any previously set network interfaces... [default] Preparing network interfaces based on configuration... [default] Forwarding ports... [default] -- 22 => 2222 (adapter 1) [default] Booting VM... [default] Waiting for machine to boot. This may take a few minutes... [default] Machine booted and ready! [default] The guest additions on this VM do not match the installed version of VirtualBox! In most cases this is fine, but in rare cases it can cause things such as shared folders to not work properly. If you see shared folder errors, please update the guest additions within the virtual machine and reload your VM. Guest Additions Version: 4.2.0 VirtualBox Version: 4.3 [default] Mounting shared folders... [default] -- /vagrant ``` コマンドはステータスコード 0 で終わったので成功したらしいです. いろいろ不明点はありますが, とりあえず, Getting Started のフローに従って, vagrant ssh コマンドを叩いてみます. ``` % vagrant ssh Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic-pae i686) * Documentation: https://help.ubuntu.com/ Welcome to your Vagrant-built virtual machine. Last login: Fri Sep 14 06:22:31 2012 from 10.0.2.2 vagrant@precise32:~$ ``` すると, ubuntu にログインっぽいことができました. 一応うまくいったってことかな? どういう仕組みなんだろう?すごい. これから調査していく予定です. |
|
| 600位 |
|
|||
|
00:42:47 |
(Cyber Agent, Inc. 所属) |
|
例えば、個人ではGithub、会社ではGHEのアカウントを持っているなどの場合、同じマシンでアカウント(コミッター名)を使い分ける方法。
単純に、`--global`を付けなければリポジトリ単位で設定できるので、使用頻度が高い方を`--global`で設定し、もう一方はリポジトリ単位で設定する。 ファイルでいうと、`--global`をつけるとhomeの設定`~/.gitconfig`に、つけないとそのリポジトリ内の設定`./.git/config`に書かれるということになる。 #####メインのアカウント(~/.gitconfig) ``` git config --global user.name "メインアカウントのユーザ名" git config --global user.email "メインアカウントのメールアドレス" ``` #####サブのアカウント(./.git/config) ``` cd [ローカルリポジトリのディレクトリ] git config user.name "サブアカウントのユーザ名" git config user.email "サブアカウントのメールアドレス" ``` |
|
| 601位 |
|
|||
|
19:48:17 |
|
|
objcにARCが導入されてから久しいですが、開発者がメモリ管理から解放されたかというと、そうでもないです。strongとweakの導入によって、少し抽象化がなされましたが、オブジェクトのallocate/releaseは意識してプログラムする必要があります。この記事は、ARC環境下での強参照/弱参照/循環参照についてイマイチ理解が及ばないという人のための記事であると同時に、私の個人的なまとめでもあります。 ## 循環参照とは? 循環参照という現象は、私も最初はなかなか理解できませんでした。まず、文字的な定義から言うと、循環参照(retain cycle)とは **「オブジェクト同士がお互いに強い参照を持っているからどちらも解放されない」**という現象のことです。 iOSにはObjective-C 2.0のランタイムが実装されていますが、ガベージコレクションは存在しません。理由はよく知らないですがシステム的な余裕がないのかもしれません。(というか、OSX10.8からもXcodeはGCを公式でDeprecatedにしており、ARCがスタンダードになっていくのは間違いありません) ## 強参照/弱参照、メモリリーク 私はちょうどARCが導入された頃(二年くらい前?)にobjcを書き始め、とくにメモリ管理に気を使わずに長いことプログラミングを楽しんできました。いまでも実際の場面でほとんどメモリ管理を気にすることはありません。(非同期Blocksの中にstrong selfをキャプチャしないようにするのに注意するくらい) 強参照/弱参照の分かりにくい点は、その名前にひとつの原因があると思っています。 そもそも、 **オブジェクトへの参照(reference)とは、ヒープ領域に動的にallocateされたobjcオブジェクトへのポインティングのことを差し** ているのですが、強参照/弱参照については、より具体的に言うと **「オブジェクトのオーナーシップ(所有権)」** のことを意味しています。 オブジェクトがヒープ領域に存在するとき、それはそのオブジェクトがどこからか参照されている(インスタンス変数、静的変数、局所変数、...)ことを意味しますが、これを言い換えると、 **そのオブジェクトは何者かによって所有されている** ということを意味します。所有権は、必ず親子関係が必要になり、親が子を所有し(強参照し)、そのメモリ開放のタイミングの責任を持つようになります。 ```objectivec @interface SomeObject () @property NSString *hoge; @end @implementation - (void)someMethod { NSString *s = [[NSString alloc] initWithString:@"hogehoge"]; self.hoge = s; } @end ``` 上記のコードは、あるオブジェクトが自身のインスタンスメソッドの中で、インスタンス変数へ文字列を代入した部分です。プロパティhogeは、省略されていますが、デフォルトでstrong,copyの属性を持っているので、sの所有者はself、つまりSomeObjectのインスタンスになります。所有されたオブジェクトは、所有者(SomeObject)が消えた(メモリが解放された)ときに、芋づる式に開放されるという特性を持ちます。 ```objectivec @interface SomeObject () @property NSString *hoge; @property NSMutableArray *array; @property NSMutableDictionary *dictionary; @end @implementation - (void)someMethod { NSString *s = [[NSString alloc] initWithString:@"hogehoge"]; NSString *key = @"fugafuga"; self.hoge = hoge; [self.array addObject:s]; [self.dictionary setObject:s forKey:key]; } @end ``` 上記のようなコードも同様です。が、ちょっと考え方を変える必要があります。それは、コレクションクラスの参照の特徴です。Cocoaには、代表的なコレクションクラスとして、NSArray, NSDictionary, NSSet, NSOrderedSetが存在しますが、これらのクラスは、型を制限せず、C プリミティブ型以外のすべてのobjcオブジェクトを格納することができます。 **通常、コレクションクラスはその内部のオブジェクトに対して強参照を持つ** のですが、これはコレクションクラスが存在しているときにコンテンツに対する参照をロストすることを防ぐためです。配列に放り込んだはずがいつの間にかオブジェクトが解放されていて"解放済み領域の不正なアクセス"になったら困りますよね。 ちなみにNSSet、NSDictionaryは、valueだけではなく、keyに対しても強参照を持っています。NSDictionaryはNSCopyingプロトコルに準拠したオブジェクトをkeyに設定できますが、それはそのコピーをkeyとして用いているからです。上記のコードでは、このオブジェクトが開放されるまで、文字列@"hogehoge"と@"fugafuga"は解放されません。 ## 1つのオブジェクト、複数のオーナー さっき私は **オブジェクトの所有権には親子関係がある** と言いました。ですが、前述の例では親子関係は単一のものしかありませんでいたが、この例では文字列sに対して、少なくともself, self.array, self.dictionaryの三つの異なるオブジェクトが強参照を持っています。 **これでこのオブジェクトが解放されたとき、きちんとsは消えてくれるのでしょうか?** それを現したのが下のgif画像です。  これから分かるように、sに対する所有者は複数存在しますが、そのすべてがリニアな親子関係にあなので、最上位の所有者であるSomeObjectが解放されない限り、子供たちは解放されませんし、、SomeObjectが解放された場合、上位から順に所有者が消えた強参照が消滅して結果的に最下位のsもきちんと消えてくれます。これがARC下でのオブジェクトのオーナーシップの基本です。 では弱参照とはなんでしょうか。循環参照と合わせて説明します。 ## 弱参照と循環参照 ```objectivec @interface ParentObject () @property SharedObject *shared; @property SomeObject *some; @property OtherObject *other; @end @interface SomeObject () @property SharedObject *shared; @property ParentObject *parent; @end @interface OtherObject () @property SharedObject *shared; @property ParentObject *parent; @end ``` ```objectivec:ParentObject.m @implementation - (id)init { if (self = [super init] ) { self.shared = [SharedObject alloc] init]; self.some = [SomeObject alloc] init]; self.other = [OtherObject alloc] init]; self.some.shared = self.shared; self.other.shared = self.shared; self.shared.some = self.some; self.shared.other = self.other; self.shared.parent = self; } return self ? self : nil; } @end ``` **なんだか色々と間違っている気がするこんな三つのオブジェクトがあったとします。** これらは親子関係があるにもかかわらず、親子の間で頻繁にデータをやり取りしたりメソッドを呼び出したりする必要があるようです。そこで横着してこれら3つをすべて双方向に参照するようなプロパティを作ってしまいました。するとどうなるか。  こんなことが起きます。実装者としては、ParentObjectが消えるタイミングでSome,Other,Sahredの三つのオブジェクトも消えてほしいのですが、実際はParentが消えることはありません。なぜなら、Parentに対してSome,Other,Sharedが所有権を主張しているからです。 あるオブジェクトが別のオブジェクトに対して強参照を持つとき、強参照されている(所有されている)オブジェクトは、所有者が消えるまで、解放されません。これがオーナーシップの基本です。通常は、先の例で上げたように所有者と所有物の間に単方向の強参照があり、芋づる式に消えて行くのですが、もしその参照が双方向だった場合どうなるのでしょう? 答えは、お互いがお互いの所有権を主張しており、Aが本来消えるタイミングではBが所有権を主張して解放させず、Bが本来消えるタイミングではAが所有権を主張して解放されませんA,Bへの他のあらゆるオブジェクトからの参照が消えたとしても、AとBはお互いがお互いを所有しあっているので消えません。 **これが循環参照です。** ## 循環参照を防ぎつつ、双方向の参照を残しておきたいような場合に使う属性が、弱参照(weak reference) 強参照が所有権だとすれば、弱参照は使用権です。所有権を持つオブジェクトは、その所有物に対して自由に参照が可能で、かつ、その開放権を持っているといえ、使用権を持っているオブジェクトは、対象のオブジェクトを好きなときに参照し、使用することができるものの、所有者がそのオブジェクトを開放してしまったら、使用することができません。 なぜなら、オブジェクトの所有権を持っていないからです(ちなみに、オブジェクトの参照が消えた場合、weak propertyには自動的にnilが代入されるので解放済み領域への不正なアクセスにはなりません)。 所有者をコピー屋さん、所有物をコピー機、使用者を自分と考えると分かりやすいかもしれません。自分は好きなときにコピー屋にいってコピーができますが、コピー屋が潰れたらコピー機は処分されて、もう使うことはできない。イメージとしてはそういうことです。 先の循環参照のモデル図を改良すると以下のようになります。強参照が単方向になり、開放が正しく行われていることが分かります。  これから分かることは、 **強参照は単方向でさえあれば、いくつ所有者があっても構わない** ということです。 # まとめ ARCに慣れ親しんだプログラマにとっては少し複雑なstrong/weakリファレンスですが、きちんと理解しないとdelegateやnotification,KVOなどで思わぬバグを産むのできちんと理解しておいたほうが良さそうです。 # ※2014/03/02 9:59追記NSStringのpropertyの属性値について @tomohisaota さんからご指摘いただいたNSStringのpropertyのデフォルトの属性値ですが、調査の結果copyはないことが判明しました。strongだけのようです。 |
|
| 602位 |
|
|||
|
19:13:04 |
|
|
## Vimの最低限の操作
#### ファイルを開く `$ vim path/to/file` bashでTabを押すと補完が効く。 補完が効かないときは大抵タイプミスしている。 #### 上書き保存して終了 `Esc`を押してから`:wq` #### 保存しないで終了 `Esc`を押してから`:q!` #### Vimのチュートリアルで勉強 `$ vimtutor` #### 画面移動 `Ctrl+F`/`Ctrl+B`でForward/Backに1画面分移動できる。 ## 少しだけ応用編 ### 文字の検索 `/searchword`で検索、`n`で次へ、`N`で前へ ### 文字の置換 `:%s/from/to/g`でfromに一致する文字をtoに全部置換。 色々オプションがあるらしいけどまだ全部覚えていないのと、エスケープが必要な文字が良くわかっていない。 ### 行の挿入系 `O`でカーソル行に新しい行を挿入して編集開始、`o`だとカーソル行の1つ下に挿入して編集開始。 ### 単語を消して編集開始 `ciw` 会社で教えてもらった神コマンド。 追記: `ci"`は""で囲まれた中身を削除、`ci'`は''で囲まれた中身を削除など、 最後の文字を変えるといろいろ応用がきく。 ### 行を削除 `dd`でカーソルのある行自体を削除。 ### 行でカーソル位置より後ろを全部削除 `D`で同じ行のカーソル位置より後ろを全部削除。 ### 行番号 `:set number`で行番号を表示する。 `:set nonumber`で行番号を非表示にする。 ### ファイルを開く `:e path/to/file`でファイルを開く。 bashよりも優秀な補完がきく。 ### 数行のインデントやコメントアウトを一気に削除 Control(ctrl)キーを押しながらvを押すことで矩形選択モードになる。 そのまま`x`を押すと選択された範囲の文字が一気に消える。 ### 数行に同じ文字を一気に挿入 Control(ctrl)キーを押しながらvを押すことで矩形選択モードになる。 そのまま`I`を押してから好きな文字を入力する。最期にEscで一気に挿入。 ## ついに画面分割 ### コントロールキーの表記 Vim界で`<C-w>`はControl(Ctrl)キーを押しながらwを押すことを示す。 ### 画面を分割する `<C-w>v` で画面を垂直分割、`<C-w>s` で画面を水平分割。 ### 画面を移動する `<C-w>h`, `<C-w>j`, `<C-w>k`, `<C-w>l` で分割した画面を移動できる。 分割先でファイルを開くといい感じに2つ以上のファイルを同時編集できる。 ### タブなど タブ機能も`:tab〜`などのコマンドでできるらしいがタイピングが面倒くさい。 ## .vimrcを少し弄り出す ### カラースキームとの出会い [molokai](https://github.com/tomasr/molokai)をダウンロードして `~/.vim/colors/molokai.vim` に配置する。 `vim ~/.vimrc` で.vimrcを開き(無い場合は新規作成)`colorscheme molokai`と記述する。 入れてみたら綺麗になって満足、いい感じ。 ### スワップファイルを無効に .vimrcに`set noswapfile`を足すとgitによく引っかかって邪魔なスワップファイルを無効に。 ### 自動でインデントするように .vimrcに`set autoindent`を足すと自動でインデントするように。 ### タブの下げ幅をカスタマイズ .vimrcに`set tabstop=2`でタブの下げ幅をスペース2個分の表示にする。 ### タブ入力時にスペースを使うように .vimrcに`set expandtab`でタブ入力時にスペースが自動で入力されるようになる。 ### Ctrl+Vで貼付けても階段状にならないように .vimrcに`set pastetoggle=<C-E>` で貼付け時に<C-E>にすると、通常のコピペで貼付けても階段状にならない!!!神! ### vimの設定をgithubに保存 細かい設定をいちいち書くのが面倒なので、 `~/.vim`や`~/.vimrc`をgithubに保存しておくとcloneするだけでよい。楽ちん。 ## 改めてタブの導入 ### 面倒なタブコマンドではなく新しいバインドで導入 ```vim " Tab pages "{{{2 " Fallback "{{{3 " the prefix key. noremap <C-t> <Nop> " Basic "{{{3 " Move new tabpage at the last. nnoremap <silent> <C-t>n :<C-u>tabnew \| :tabmove<CR> nnoremap <silent> <C-t>c :<C-u>tabclose<CR> nnoremap <silent> <C-t>o :<C-u>tabonly<CR> nnoremap <silent> <C-t>i :<C-u>tabs<CR> nmap <C-t><C-n> <C-t>n nmap <C-t><C-c> <C-t>c nmap <C-t><C-o> <C-t>o nmap <C-t><C-i> <C-t>i nnoremap <silent> <C-t><Space> :<C-u>TabpageTitle<CR> nmap <silent> <C-t><C-@> <C-t><Space> nmap <silent> <C-t><C-Space> <C-t><Space> " Moving around tabpages. "{{{3 nnoremap <silent> <C-t>k \ :<C-u>execute 'tabnext' 1 + (tabpagenr() + v:count1 - 1) % tabpagenr('$')<CR> nnoremap <silent> <C-t>j \ :<C-u>execute 'tabprevious' v:count1 % tabpagenr('$')<CR> nnoremap <silent> <C-t>J :<C-u>tabfirst<CR> nnoremap <silent> <C-t>K :<C-u>tablast<CR> nmap <C-t><C-j> <C-t>k nmap <C-t><C-k> <C-t>j nmap <C-t><C-t> <C-t>k nmap <C-t>h <C-t>j nmap <C-t>l <C-t>k nmap <C-t><C-h> <C-t>j nmap <C-t><C-l> <C-t>k ``` [emonkak](https://github.com/emonkak/config)氏のをパクってきた。 `<C-t>`から色々タブの操作をできるようにしたらほんとうに快適になった!! ### Vimを起動したまま.vimrcを反映 `:source ~/.vimrc` で起動したまま.vimrcを反映する。 ただし変更後にクリアされていないキーバインド等は想定通りに反映されないことも。 ## プラグインに手を出し始めた ### NeoBundleの導入 [Shougo/neobundle.vim](https://github.com/Shougo/neobundle.vim)のREADMEにならって導入。 `:NeoBundleInstall`はよくわからないけれど、とりあえずVimを再起動すればどうにかなるっぽい。 ### Unite.vimの導入 [Shougo/unite.vim](https://github.com/Shougo/unite.vim)を導入。 `NeoBundle 'Shougo/unite.vim'`と書くだけでOKだった。 使い方がわからなくて放置・・・。 ### ctrlpの導入 [kien/ctrp.vim](https://github.com/kien/ctrlp.vim)を導入。 使い方は[この辺](http://qiita.com/oahiroaki/items/d71337fb9d28303a54a8)で。 まだ使い始めたばかりだけれどかなり使いやすく気に入っている。 ### VimShellの導入 [Shougo/vimshell.vim](https://github.com/Shougo/vimshell.vim)を導入。 タブ操作と組み合わせてつかうととても便利だでした。 Vimを閉じる必要が無くなった。 ### agvimの導入 [ag(The Silver Searcher)で検索しpecoで更に絞り込みvimで該当行を開く](http://qiita.com/fmy/items/b92254d14049996f6ec3) キーバインドはjkにしてみた。 ## いまここ ## これから fugitive.vimがオススメと聞いているので使ってみたい。 vim-filer.vimやUniteをちゃんと使えるようになると、VimShellでcpとかしなくても済みそう・・。 |
|
| 603位 |
|
|||
|
23:01:42 |
(ヤフー株式会社 所属) |
|
tmux使ってますか? いまだに使いこなせていない方も多いのではないでしょうか。 もちろん私もその一人です。 今回は改めてtmuxの使い方を調べてみたのでまとめておきます。 ## 複数の画面や領域を使いこなす まずは単発のtmuxをうまいこと使うための方法です。 tmuxにはウインドウとペインという概念があります。 ウインドウは簡単に言うと **ある一つの画面** のことです。ペインは **ある一つの領域** といったところでしょうか。 ### ウインドウ #### ウインドウ追加 まずはtmuxを起動します。 ``` $ tmux ``` tmuxが起動したら`PREFIX c`と素早く押してみてください。 `PREFIX`はデフォルトでは`Ctrl-b`です。(変更の仕方は[こちらの記事](http://qiita.com/shoma2da/items/a82b0be38f10954a45ba)などをご覧ください) #### ウインドウ切り替え 複数のウインドウを作成したらウインドウを切り替えてみましょう。 これにはいくつかの方法があります。 * `PREFIX n`:次のウインドウに切り替え(next) * `PREFIX p`:前のウインドウに切り替え(previous) * `PREFIX 数字`:番号を指定してウインドウを切り替え * `PREFIX w`:ウインドウ一覧を表示。その後`Ctrl-p`や`Ctrl-n`でウインドウを選択して`Enter` #### ウインドウ名変更 それぞれのウインドウには名前が付けられます。デフォルトでは`bash`あたりになっているのではないでしょうか。 適当なウインドウ内で`PREFIX ,`(カンマ)を押してみましょう。 入力すると画面下のバーにカーソルが移動してウインドウ名の変更ができます。 #### ウインドウ終了 ウインドウの終了は基本的には`exit`コマンドを使います。 ``` $ exit ``` exitが使えず強制的に終了させたい場合があるかもしれません。 その場合は`PREFIX &`を使いましょう。 ### ペイン #### ペイン分割する ペインは縦横それぞれ、更に再帰的に何度でも分割できます。 どこかのウインドウ内で以下のコマンドを入力してみてください。 * 縦:`PREFIX %` * 横:`PREFIX "` #### ペイン切り替え ペイン分割したらペイン間を行き来してみましょう。 コマンドは`PREFIX o`です。 #### ペイン終了 終了についてはウインドウの終了とほぼ同じ操作です。 基本的には`exit`コマンドを使用しますが、それが出来ない場合は`PREFIX x`です。 ## セッションを使いこなす tmuxを一つ起動した単位=セッションといいます。 セッションは一時的にコンソールから切り離してバックグラウンドで残しておいて後から繋ぎ直す、といったことができます。 ### セッションの作成 普通に`tmux`すれば1つセッションを作成しています。 ``` $ tmux ``` セッションに名前を付けておくこともできます。 ``` $ tmux new -s my_session ``` ### デタッチ コンソールから切り離すことをデタッチといいます。デタッチされたセッションはバックグラウンドで生き続けます。 デタッチは`PREFIX d`でできます。 ### セッションの確認 存在しているセッション一覧を確認することが出来ます。 ``` $ tmux ls ``` ### アタッチ デタッチしたセッションを再度繋ぎ直すことをアタッチと言います。 直前のセッションは以下のようにアタッチ出来ます。 ``` $ tmux a ``` セッション名を指定してアタッチもできます。 ``` $ tmux a -t セッション名 ``` `-t`オプションにつづいてセッション名を指定します。 成功すればバックグラウンドにあったセッションの作業をデタッチ時そのままの状態で継続できます。 ### セッション名の変更 任意のタイミングでセッション名は変更できます。 ``` $ tmux rename -t 変更前セッション名 変更後セッション名 ``` すでにセッション内にいるときは`Prefix + $`でリネームできます。 ### セッションの削除 セッションを終了します。 ``` $ tmux kill-session -t セッション名 ``` 全て終了する場合はserverごと落としましょう。 ``` $ tmux kill-server ``` ## 最後に 今回はtmuxの使い方をまとめました。 慣れるととても便利で手放せないものになるでしょう。 プログラマたるもの常に効率良く作業していきたいものですね。 [shoma2daをフォロー](https://twitter.com/intent/user?screen_name=shoma2da) <a href="http://www.amazon.co.jp/gp/product/4894712741/ref=as_li_ss_il?ie=UTF8&camp=247&creative=7399&creativeASIN=4894712741&linkCode=as2&tag=shoma2da-22"><img border="0" src="http://ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=4894712741&Format=_SL160_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=shoma2da-22" ></a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=shoma2da-22&l=as2&o=9&a=4894712741" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> |
|
| 604位 |
|
|||
|
00:18:29 |
(株式会社Subot 所属) |
|
自分の場合は[これ](https://gist.github.com/thomd/956095)を利用させて頂いてます。
使い方は簡単で、どこでもいいから適当にリンク先のコードをファイルに書き出して置いておく。 例だと~/bin/ssh-host-color となっているので自分もそうしている。 特にPATHが通っている必要はない。 ポイントはコード中の ```bash if [[ "$@" =~ thomd ]]; then set_term_bgcolor 40 0 0 elif [[ "$@" =~ git ]]; then set_term_bgcolor 0 40 0 fi ``` で、ifの中のthomdやgitに当たる箇所を変更する。 実際にsshコマンドの中に含まれている文字列を書いておくことで、それに反応してset_term_bgcolorが実行される。 set_term_bgcolorの後の数字はRGBに当たる数字になっているので、自分でお好みの色を指定すればよい。 配置したらパーミッションを実行可能にしておく。 ```bash $ sudo chmod 744 ~/bin/ssh-host-color ``` 次に、シェルの設定にエイリアスを追加。 自分の場合はzshなので、例えば.zshrcに ```zsh alias ssh=~/bin/ssh-host-color ``` を追記しおく。 ```bash $ source .zshrc ``` やiTermを再起動すれば、設定が反映される。 自分は、本番サーバーでは背景を赤っぽくしている。 ひと目で識別できるし、開きっぱなしにしているとソワソワするので良い。 |
|
| 605位 |
|
|||
|
09:49:39 |
(Akatsuki Inc. 所属) |
|
## 概要 [パーフェクトRuby](http://www.amazon.co.jp/gp/product/4774158798/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4774158798&linkCode=as2&tag=kidachi-22)を読んでいて便利だと思ったもの一覧です。 Enumerable編 http://qiita.com/kidachi_/items/a00558cfb0a6a3e23f4b Array編 http://qiita.com/kidachi_/items/e9cb26c4e6cb36b70a1c String編 http://qiita.com/kidachi_/items/7b355eb355b2d1390cf5 ## 繰り返し処理 ### each ハッシュのキーと値を繰り返す。 ```ruby > hash = { Ruby: 1, Python: 2, Java: 3 } => {:Ruby=>1, :Python=>2, :Java=>3} > hash.each do |key, value| > p "#{key}: #{value}" > end "Ruby: 1" "Python: 2" "Java: 3" => {:Ruby=>1, :Python=>2, :Java=>3} ``` ### each_key ハッシュのキーだけ繰り返す。 ```ruby > hash.each_key do |key| > p "#{key}" > end "Ruby" "Python" "Java" => {:Ruby=>1, :Python=>2, :Java=>3} ``` ### each_value ハッシュの値だけ繰り返す。 ```ruby > hash.each_value do |value| > p "#{value}" > end "1" "2" "3" => {:Ruby=>1, :Python=>2, :Java=>3} ``` ## マージ ### merge 複数のハッシュを1つにまとめる。 キーが重複している場合は、引数として渡された方のハッシュの値で上書きされる。 ```ruby > hash1 = { Ruby: 1, Python: 2} > hash2 = { Java: 3, Python: 4} > hash1.merge(hash2) => {:Ruby=>1, :Python=>4, :Java=>3} ``` ## キーと値の入れ替え ### invert キーと値を入れ替えた新しいハッシュを生成する。 ```ruby {Ruby: 1, Python: 2}.invert => {1=>:Ruby, 2=>:Python} # 入れ替えの結果キーが重複した場合は、片方の値が消滅する。 > {Java: 3, Ruby: 3}.invert => {3=>:Ruby} ``` ## キーや値の存在確認 ### has_key?, has_value? キーや値が存在するかどうかチェックする。 ```ruby > hash = {Ruby: nil} => {:Ruby=>nil} # 存在するキーでも存在しないキーもどちらもnilを返す > hash[:Ruby] => nil > hash[:Python] => nil # has_key?で区別する。 > hash.has_key?(:Ruby) => true > hash.has_key?(:Python) => false # 値の存在チェックしたい場合はhas_value?を用いる。 > hash.has_value?(nil) => true > hash.has_value?(1) => false ``` ## キーや値の取得 ### keys, values キーや値を配列で取得する。 ```ruby > hash = {Ruby: 1, Python: 2, Java: 3} > hash.keys => [:Ruby, :Python, :Java] > hash.values => [1, 2, 3] # 値からキーを取得するにはHash#key > hash.key(1) => :Ruby # キーから値を取得するにはHash#values_at > hash.values_at(:Ruby, :Python) => [1, 2] ``` ## デフォルト値 ### デフォルト値の設定 存在しないキーを呼んだ時に返す値を設定。 ```ruby > has_default = Hash.new('We love Ruby!') => {} > has_default[:aaaa] => "We love Ruby!" ``` デフォルト値はブロックで定義することも可能。 ブロックの引数には、「ハッシュ自身」と「参照されたキー」が渡される。 ```ruby > has_default = Hash.new { |hash, key| "I (#{hash}) don't have #{key}!" } => {} > has_default[:Ruby] => "I ({}) don't have Ruby!" > has_default[:Python] => "I ({}) don't have Python!" > has_default[:Ruby] = 1 => 1 > has_default[:Python] => "I ({:Ruby=>1}) don't have Python!" ``` ### fetch 指定したキーに値が存在しなかった場合の戻り値を指定できる。 ```ruby > hash = {Ruby: 1, Python: 2} > hash.fetch(:Java, "No key!") => "No key!" # 値が存在するキーならば通常通り値を返却。 > hash.fetch(:Ruby, "No key!") => 1 # ブロックによる指定も可能 > hash.fetch(:Java) {|key| "I don't have #{key}!"} => "I don't have Java!" ``` ## ハッシュの変換 ### ハッシュから配列へ to_aで二次元配列へ変換する。 ```ruby > hash = {Ruby: 1, Python: 2} > arr = hash.to_a => [[:Ruby, 1], [:Python, 2]] # 二次元配列にはassocでアクセス可能 > arr.assoc(:Ruby) => [:Ruby, 1] ``` ### 配列からハッシュへ 偶数個の配列をハッシュに渡すパターン ```ruby > arr = ["Ruby", 1, "Python", 2] > Hash[*arr] => {"Ruby"=>1, "Python"=>2} # 奇数個の場合は不可 > arr = ["Ruby", 1, "Python", 2, "Java"] => ["Ruby", 1, "Python", 2, "Java"] > Hash[*arr] ArgumentError: odd number of arguments for Hash ``` 二次元配列をハッシュに渡すパターン ```ruby > arr = [[:Ruby, 1], [:Python, 2]] => [[:Ruby, 1], [:Python, 2]] > Hash[arr] => {:Ruby=>1, :Python=>2} ``` |
|
| 606位 |
|
|||
|
02:10:47 |
(Souzoh, Inc. (affiliated by Mercari, Inc.) 所属) |
|
## 構造体 Go言語には、クラスがない。そのため、代わりに構造体(struct)を使い、その型にメソッドを設けることで似たような振る舞いをさせる。 http://play.golang.org/p/WosVAwNGad ``` package main import "fmt" type Person struct { FirstName string LastName string } func (p *Person) Name() string { return p.FirstName + " " + p.LastName } func main() { person := &Person{"Taro", "Yamada"} fmt.Println(person.Name()) } ``` ## インタフェース Go言語にはインタフェースがある。明示的に実装しなくてもインタフェースが定義するメソッドをすべて実装していれば、そのインタフェースを実装していることになる(ダックタイピング)。 http://play.golang.org/p/hqHGJZek3a ``` package main import "fmt" type Person struct { FirstName string LastName string } func (p *Person) Name() string { return p.FirstName + " " + p.LastName } type Named interface { Name() string } func printName(named Named) { fmt.Println(named.Name()) } func main() { person := &Person{"Tarou", "Yamada"} printName(person) } ``` ## 構造体の埋め込み Go言語の構造体はクラスではないため、継承ができない。しかし、埋め込みを使うことで他の構造体をラップした構造体を作ることができる。 中に埋め込んだ構造体のフィールドやメソッドはあたかも外の構造体のもののように振る舞うことができる。 http://play.golang.org/p/VyKJCRdEFB ``` package main import "fmt" type Hoge struct { N int } type Piyo struct { Hoge M int } func main() { piyo := &Piyo{Hoge{1}, 2} fmt.Println(piyo.N, piyo.M) fmt.Println(piyo.Hoge.N, piyo.M) } ``` ## 埋め込みを使ったインタフェースの部分実装 埋め込む構造体にあるインタフェース定義するメソッドの一部を実装させ、残りのメソッドは外側の構造体で実装するという手法が使える。下の例だと、Nameメソッドはperson構造体で共通の実装をして、Titleメソッドはfemaleとmaleで別の実装をしている。また、Personインタフェース以外はパッケージ外にエクスポートしないことで、使用者に詳細の実装を見せないようにしている。 http://play.golang.org/p/fbARybwPOd ``` package main import "fmt" type Gender int const ( Female = iota Male ) type Person interface { Name() string Title() string } func New(gender Gender, firstName, lastName string) Person { p := &person{firstName, lastName} if gender == Male { return &male{p} } else { return &female{p} } } type person struct { firstName string lastName string } func (p *person) Name() string { return p.firstName + " " + p.lastName } type female struct { *person } func (f *female) Title() string { return "Ms." } type male struct { *person } func (m *male) Title() string { return "Mr." } func printFullName(p Person) { fmt.Println(p.Title(), p.Name()) } func main() { taro := New(Male, "Taro", "Yamada") printFullName(taro) hanako := New(Female, "Hanako", "Yamada") printFullName(hanako) } ``` |
|
| 607位 |
|
|||
|
16:27:28 |
(KAYAC Inc. 所属) |
|
意外とまとまっていないかったので作業しながらまとめたメモ。 ##CocoaPodsとは 公式:[http://cocoapods.org](http://cocoapods.org) CocoaPodsをプロジェクトに追加する方法はこのまとめが丁寧でわかり易かった。 [CocoaPodsを使ったXcodeプロジェクトの作り方(1)](http://www.shigekitakeguchi.com/archives/973) [CocoaPodsを使ったXcodeプロジェクトの作り方(2)]( http://www.shigekitakeguchi.com/archives/1016) [CocoaPodsを使ったXcodeプロジェクトの作り方(3)](http://www.shigekitakeguchi.com/archives/1026) [CocoaPodsを使ったXcodeプロジェクトの作り方(4)] (http://www.shigekitakeguchi.com/archives/1040) ##1.自前のライブラリをCocoaPodsに対応させる まずはライブラリを作成しているプロジェクトのファイル構成を推奨されている形にまとめる。 ###公式が推奨するファイル構成 ``` . ├── Classes └── ios └── osx ├── Resources ├── Project └── Podfile ├── LICENSE ├── Readme.markdown └── NAME.podspec//これは今からつくるファイル。 ``` 実際の構成はこんな感じ。  ##2.Pod(PodSpecファイル)の作成 自前ライブラリを作成しているプロジェクトのディレクトリにて下記のpodコマンドを実行する。 ``` pod spec create MyLibrary ``` これで先ほどの`MyLibrary.podspec`ファイルが作成される。 ##3.PodSpecファイルの編集 自動作成されたPodSpecにはコメントアウトされたテンプレートが出力されるが、最終的には不必要なコメントは全て削除する。 PodSpecファイル内は必要に応じて書き換える必要がある。 今回はGithub上に作成したソースを参照する場合のPodSpecファイルの例。 ``` Pod::Spec.new do |s| s.name = "SWScrollView" s.version = "0.0.1" s.summary = "Scroll view like Star Wars opening crawl." s.license = { :type => 'MIT', :file => 'LICENSE.txt' } s.homepage = "https://github.com/somtd/SWScrollView" s.author = { "SOMTD" => "matsuda-so[at]kayac.com" } s.source = { :git => "https://github.com/somtd/SWScrollView.git", :tag => "0.0.1" } s.platform = :ios, '5.1' s.requires_arc = true s.source_files = 'SWScrollView/**/*.{h,m}' s.resources = 'SWScrollView/**/*.xib' s.frameworks = 'QuartzCore' end ``` 今回の例では以下の2点が特殊だったので注意。 - 自前ライブラリが`QuartzCore.framework`に依存している。 - 自前ライブラリがxibファイルを含んでいる。 そのため、上記のPodSpecファイル上で ``` s.resources = 'SWScrollView/**/*.xib' s.frameworks = 'QuartzCore' ``` の記述が必要となっている。 ##4.PodSpecファイルのチェック PodSpecファイルを作成した後に正しく書かれているかどうかを以下のpodコマンドを実行して確認することができる。 ``` pod spec lint MyLibrary.podspec ``` ただし、この場合はGithub上のPodSpecファイルに対して評価を行なっているため、まだpushしていないときなどは以下のようなエラーが返ってくる。 ``` [!] Pod::Executable fetch origin tags/0.0.1 2>&1 fatal: Couldn't find remote ref tags/0.0.1 fatal: The remote end hung up unexpectedly ``` いきなりpushしたもので試せない場合は、 ``` pod lib lint ``` というコマンドをプロジェクトのディレクトリで実行するとローカルのPodSpecファイルを評価してくれるので、先に試すほうがいいかもしれない。 [【参考】:pod spec lint: "--local" option no longer available](https://github.com/CocoaPods/CocoaPods/issues/1176) うまくいくと、 ``` MyLibrary passed validation. ``` と表示される。 ##5.DemoProjectにつくったPodをイントールしてみる。 つくったPodを自分のDemoプロジェクトにインストールして確認してみる。といっても手順は簡単。  同じライブラリプロジェクトのDemoプロジェクトに移動し、下記のpodコマンドを実行。 ``` pod setup ``` そして直下に`Podfile`を作成する。 ``` touch Podfile ``` そして`Podfile`を下記のように書き換える。 ``` platform :ios, '5.1' pod 'MyLibrary', :path => '..' ``` `Podfile`の編集が完了したら下記のpodコマンドを実行。 ``` pod install ``` 成功すると同じディレクトリ配下に`MyLibraryDemo.xcworkspace`が作成されるので、それを起動し、ライブラリ自体が正しく反映されていることを確認できる。 これでDemoプロジェクトからは自分のローカルのライブラリを見に行くことになるので開発を進めながらもライブラリをPodで管理することができる。 ##6.CocoaPods/Specsのフォーク 自前のライブラリが整ったら、いよいよGithubを使ってCocoaPods本体に取り込んでもらう準備をする。 (CocoaPods本体に取り込まなくてももちろん公開することはできるが、今回はこの手順で説明を進める) まずはCocoaPodsの大本からSpecsというリポジトリを自分のGithubリポジトリ上にforkする必要がある。 [CocoaPods/Specs](https://github.com/CocoaPods/Specs)  おそらく下記のようなGithubリポジトリが作成されるはず `https://github.com/<USER_NAME>/Specs` ##7.自前のライブラリをCocoaPodsにpushする(ローカルで準備) Demoプロジェクトでも正しく動いていることを確認したら、まずは自分のプロジェクトのGithubリポジトリに変更をプッシュする。 そのときに、versionのタグをわすれずに付けておく。(付けないとpod lintしたときに怒られる) ``` git tag 0.0.1 git push --tags ``` pushしたあとに確認。 ``` pod spec lint MyLibrary.podspec ``` lintの確認で問題がなければディレクトリを`.cocoapods/master`に移動する。 (このディレクトリは一番最初にcocoapodsを導入したときに作られている?) ``` cd ~/.cocoapods/master ``` 移動した時点でのブランチ名は`master`になっているはずなので、そこから任意のブランチに切り替える。ここではブランチ名をforkとして進める。 ``` git checkout -b fork ``` このブランチに先ほどforkしたGithubリポジトリをremote addする。 ``` git remote add MyFork https://github.com/<USER_NAME>/Specs.git ``` ここに新たに追加したPodSpecファイルを追加する。 追加するときにルールがあるので以下の通りに作成し、追加するようにする。 - masterディレクトリの下にpodの名前でディレクトリをつくる。 - podの名前ディレクトリの下にバージョン番号のディレクトリを作る。 - そのディレクトリの下に`MyLibrary.podspec`ファイルを追加する ##8.自前のライブラリをCocoaPodsにpushする(pull requestを送る) ディレクトリに追加したことを確認し、先ほど作成したremoteのブランチにプッシュする。 ``` git push MyFork fork ``` 再びGithubのforkしたレポジトリ`https://github.com/<USER_NAME>/Specs`を開く。 forkしたときのブランチを選択すると自分のpushした変更が確認できる。 これをもとにCocoaPods本体、つまりmasterブランチにpull requestを送る。  自分がforkしたときのブランチ名を選択。  早ければ1日以内にmergeされ取り込みが完了となる。 [【参考】:CocoaPodsでPodの利用&作成のメモ](http://qiita.com/makoto_kw/items/edf758a67bd4c2ba5b7a) [【参考】:Creating and maintaining a pod](https://github.com/CocoaPods/CocoaPods/wiki/Creating-and-maintaining-a-pod) [【参考】:Sharing pod specifications with yourself and the world](https://github.com/CocoaPods/CocoaPods/wiki/Sharing-pod-specifications-with-yourself-and-the-world) [【参考】:Contributing to the master repo](http://docs.cocoapods.org/guides/contributing_to_the_master_repo.html) |
|
| 608位 |
|
|||
|
13:15:10 |
|
|
## [RubyMine](http://www.jetbrains.com/ruby/) とは
Rails 用 IDE。 有料だが、自分の知る限りでは最強。 ちなみに姉妹製品 [WebStorm](http://www.jetbrains.com/webstorm/) の上位版という位置づけなので、HTML/CSS/JavaScript の開発にも使える。 ### どのへんが最強なの? 全般的に言えるのは「ちゃんと IDE になっている」ということ。 他の無料 IDE は少し特殊なことをやろうとするとターミナルでやるしかないとか、 プラグインをいろいろ入れて合わせ技で・・・ということが多々あるのだが、 RubyMine は自身でほぼ完結しており、IDE 上で実現されている。 (ターミナルでコマンド打った方が早い場合もあるが) * MVC 単位の階層表示が可能なナビゲーション。 * ジャンプ機能(メソッド定義部、アクションに対応するビューファイル等)。 * 補完が賢く、高速。 * まともなデバッガ。 * データベースの閲覧と編集。 * クイックヘルプ。 * VCS(Subversion, Git)内蔵。 * UI やキーバインドのカスタマイズ。 * プラグインによる機能拡張。 * REST Client 機能。 ## 使い方 ### ウィンドウの構成 #### Project パネル 現在開いているプロジェクトのディレクトリ構造、およびデータ構造を表示する。 ここでファイルやクラスをダブルクリックすると、これに対応する内容が Editor に展開される。 #### Structure パネル 現在編集中のファイルの論理構造を表示する。 一覧からメソッドを選択すると、定義部にジャンプできる。 #### Favorites パネル #### Run パネル 起動したアプリケーションのログを表示するコンソールを表示。 #### Debug パネル デバッガのコントロール機能、スタックフレーム、変数ウォッチなどを表示。 #### Database パネル データベースへの接続と、テーブル一覧を表示。 #### Changes パネル git 等の VCS でソースを管理している場合、変更ファイルと管理下にないファイルの一覧を表示。 変更したファイルに対して diff や revert を行うことができる。 ### クイックヘルプ デフォルトだとクイックヘルプのパネルが表示されていないが、View > Tool Windows > Documentation で表示できる。 別ウィンドウで表示されるのが嫌なら Floating Mode を OFF にすればよい。 カーソル下の単語を調べるには View > Quick Documentaion を選択する。 適当なショートカットキーを割り当てるか、Control キーを押しながら単語にマウスカーソル合わせることでもヘルプを閲覧できる。 ### モデルの E-R 図を閲覧する Project パネル上で右クリックし、Diagrams > Show Diagram を選択。 Select Diagram Type ダイアログが表示されるので Rails Model Dependency Diagram を選択する。 表示された E-R 図は上部にあるツールバーアイコンで詳細度を調整することができる。 * Migration Fields: エンティティの属性の表示を ON/OFF * System DB Fields: id や created_at などのシステム固有属性を ON/OFF ### JavaScript のデバッグ > あらかじめ Chrome に機能拡張 "JetBrains IDE Support" をインストールしておくこと。 * Run > Edit Configurations を選択。 * +ボタンをクリックし、JavaScript Debug > Local を選択。 * 以下を入力。 * Name: 適当に。 * Path: デバッグした JavaScript コードを利用する HTML ファイルを選択。 * Browser: Chrome を選択。 あとは Run > Debug で指定ブラウザが起動して HTML ファイルを表示するので、必要に応じて Editor にブレイクポイントを設定すればよい。 ### REST Client * Tools > Test RESTful Web Service を実行。 * REST Client パネルの Request Parameters タブに入力。 * HTTP method * Host/port * Path * Query string parameters * パネルの左上にある Submit request をクリック。 * Response タブにレスポンス内容が表示される。 * View as XML や View as JSON で整形されたレスポンスが閲覧できる。 ## プラグイン プラグインのインストールは環境設定から行えるようになっている。 * Preferences/Settings > Plugins にある Browse Repositories をクリック。 * インストールしたいプラグインを選択し、Download and Install する(要再起動)。 ### LiveEdit Rails の erb ファイルや HTML ファイルの編集をブラウザ(Chrome)でリアルタイムに確認できるプラグイン。 #### 準備 * LiveEdit プラグインを Download and Install. * Chrome を開き、Preferences > 機能拡張を選択。 * デベロッパーモードを ON にする。 * "他の機能拡張を見る" をクリックし、"JetBrains IDE Support" をインストールし、有効にする。 #### 使用方法 * Rails の場合はアプリケーションを起動。 * 編集対象ファイルを開き、View > Open in Browser でこれを表示する。 * URL のドメインは localhost でないと動作しないため、0.0.0.0 等になっている場合は修正する。 * View > Live Edit を ON にする。 ### Markdown Markdown 記法をサポートするプラグイン。 .md ファイルを開くと Editor 下部にタブ Preview が現れるので、どのように見えるか確認できる。 ## Tips ### Editor のフリーカーソルを無効にする * 環境設定 Preferences/Settings > Editor を開き、Allow placement of caret after end of line を OFF にする。 ### Editor で等幅フォントを使用する(Mac) デフォルトの設定だと日本語フォントが等幅にならない。 また Osaka-mono がなぜかフォント一覧に出てこないため、以下のようにする。 * [ここ](http://mix-mplus-ipa.sourceforge.jp)から Migu 1M フォントをダウンロードし、インストール。 * 環境設定 Preferences/Settings > Editor > Color & Fonts > Font を開き、Scheme name を Save As で複製。 * フォントセレクタから Migu 1M を選択。 ### ファイルを素早く開く Navigate メニュー下にある * Class... * File... * Symbol... のショートカットを覚えておけば、キーボード操作だけでファイルを開くことができる。 ### デバッガ起動中に再度 Debug を実行しても、デバッガが再起動しない デフォルトの設定だと元のデバッグセッションを残したまま新たなデバッガを起動するため、 ポートが押さえられずエラーになってしまう。 以下のように設定すればデバッグセッション 1 つを共有して再利用するようにできる。 * Run > Edit Configurations でデバッグ用のコンフィグを選択。 * 右上にある Share と Single instance only を ON にする。 ### CoffeeScript のデバッグ CoffeeScript で書いたコードをリアルタイムで JavaScript に変換し、 変換時に生成したソースマップを利用して CoffeeScript コード上でのデバッグを実現する。 [ScreenCast](http://www.screenr.com/3xu7) * RubyMine 5.4 以降。 * File Watcher プラグインのインストール。 * Preferences/Settings > File Watcher で CoffeeScript を追加。 * sudo npm install -g coffee-script * Program: /usr/local/bin/coffee * Arguments: --compile --bare --map $FileName$ * --bare: クラス、メソッドなどをグローバルに定義。 * --map: ソースマップを生成。 * Output paths: $FileNameWithoutExtension$.js:$FileNameWithoutExtension$.map ブレイクは body onload 後じゃないと捕捉されないっぽいので、jQuery を使い $ -> 以降に処理を書くとよい。 ### Dash と連携する [Dash](https://itunes.apple.com/us/app/dash/id458034879?ls=1&mt=12) は有料だけどいろいろなリファレンスマニュアルを閲覧できて便利。 [ここ](https://github.com/Kapeli/AppCodeDashSearch)にある Dash.jar をダウンロードし、プラグインとしてインストールする。 お好みのショートカットキーを設定すれば、Editor のカーソル下にあるキーワードを Dash で検索できる。 |
|
| 609位 |
|
|||
|
00:14:13 |
(COMPASS, inc 所属) |
|
今までどういうふうに実装されているのか知らなかったので、
jquery_bottomを読み解いて理解したのでメモ。 jquery_bottomはこちら。 https://github.com/jimyi/jquery_bottom ## 必要な情報 * 要素の表示領域 * 要素のスクロール分を含めた高さ * 要素のスクロール位置 この情報がわかれば、下記の情報を導き出せる。 「要素の表示領域」+「要素のスクロール位置」=「現在の表示位置の高さ」 「現在の表示位置の高さ」さえわかれば、後はその値が「要素のスクロール分を含めた高さ」にどれだけ近づいているかで、スクロール位置を判断することができる。 ## 実装してみる ```scroll.js // スクロールさせる要素を取得 var elm = document.getElementById('scrollElement'); // 要素の表示領域を取得 var height = elm.offsetHeight; // スクロールイベントを定義 elm.onscroll = function() { // 要素のスクロール分を含めた高さを取得 var scrollHeight = elm.scrollHeight; // 要素のスクロール位置を取得 var scrollTop = elm.scrollTop; // 現在の表示位置の高さ var scrollPosition = height + scrollTop; // どれだけ近づいたかを判断する値 // 0に近いほど、スクロールの最終が近い var proximity = 0; if ((scrollHeight - scrollPosition) / scrollHeight <= proximity) { alert('Scroll finish!!'); } } ``` サンプルはこちら。 http://jsdo.it/tanihiro/scroll-finish-event ## 注意点 document.Element.scrollHeightは、要素のpaddingも含めるので、 paddingを設定している場合は、その値を考慮しないといけない。 参考 https://developer.mozilla.org/en-US/docs/DOM/element.scrollHeight |
|
| 610位 |
|
|||
|
01:01:36 |
|
|
##文字列から日付(datetime)
```python from datetime import datetime as dt tstr = '2012-12-29 13:49:37' tdatetime = dt.strptime(tstr, '%Y-%m-%d %H:%M:%S') ``` strptimeの第二引数は第一引数のフォーマットを渡す。 例えば、 tstr = `'2012/12/29 13:49:37'`だった場合、 dt.strptime(tstr, `'%Y/%m/%d %H:%M:%S'`) ##文字列から日付(date) ```python import datetime tstr = '2012-12-29 13:49:37' tdatetime = datetime.datetime.strptime(tstr, '%Y-%m-%d %H:%M:%S') tdate = datetime.date(tdatetime.year, tdatetime.month, tdatetime.day) ``` ##日付から文字列 ```python from datetime import datetime as dt tdatetime = dt.now() tstr = tdatetime.strftime('%Y/%m/%d') ``` **参考** 週など他の指定子(%Yみたいなやつ)が知りたい場合は、下記リンクの一番下から確認してください [基本的な日付型および時間型](http://docs.python.jp/2/library/datetime.html) |
|
| 611位 |
|
|||
|
00:47:29 |
(アルクテラス株式会社 所属) |
|
Macで漫然とRubyをインストールすると、rails consoleやirbで日本語を使えないことがある。(どうなるかというと、日本語の変換を確定した途端に「\U+FFE3\U+FFAB」みたいにUnicodeのエスケープっぽいものが表示される。)そうなった時の直し方を説明する。 ## なぜ日本語を使えないのか 大雑把に言うと、Macに付属しているコマンドライン処理のライブラリ(libedit)が日本語に対応していないから。これを使わず、libreadlineを使うようにすれば直る。 ## 誰がlibeditを使っているのか Rubyをインストールしたディレクトリのどこかにあるreadline.bundleが使っている。findコマンドを使えば見つけられる。例えば、rbenvを使ってRubyをインストールしたなら、下のコマンドで見つかるだろう。 ``` $ find ~/.rbenv/versions -name readline.bundle ``` 例として、ここでは下のパスにreadline.bundleがあったものとする。 ``` ~/.rbenv/versions/1.9.3-p194/lib/ruby/1.9.1/x86_64-darwin11.3.0/readline.bundle ``` ## readlineをインストールする homebrewやmacportでインストールすればいい。例えば、homebrewなら下のようにする。 ``` $ brew install readline ``` ## どうやってreadlineを使わせるのか install_name_toolというコマンドを使う。readline.bundleというのはライブラリのエイリアスみたいなもので、別の場所にある実体を参照しているだけである。よって、install_name_toolでreadline.bundleにreadlineを使わせればいい。同時に、libeditから切り離す必要もある。 install_name_toolを実行するには、readlineとlibeditの正確なパスを知る必要がある。readlineのライブラリファイルは下のようなパスにインストールされたはずだ。 ``` /usr/local/Cellar/readline/(readlineのバージョン)/lib/libreadline.dylib ``` 例えばreadline-6.2.4をインストールしたなら、下のようになる。 ``` /usr/local/Cellar/readline/6.2.4/lib/libreadline.dylib ``` ## libeditはどこにあるのか otoolというコマンドで知ることができる。otoolはLinuxにおけるlddの役割を果たす。つまり、readline.bundleが参照している実体を調べられる。今回の例なら、下のように実行すればいい。 ``` $ otool -L ~/.rbenv/versions/1.9.3-p194/lib/ruby/1.9.1/x86_64-darwin11.3.0/readline.bundle ``` するとlibedit.dylibのパスが表示される。ここでは以下のように表示されたとする。 ``` /usr/lib/libedit.3.dylib ``` このlibeditとreadline.bundle別れさせ、libreadlineとくっつければいい。 ## install_name_tool いよいよ大詰め。install_name_toolではダイナミックライブラリに関する様々な操作が可能だが、今回は-changeというオプションで参照先の変更を行う。文法は下の通り。 ``` $ install_name_tool -change 削除するパス 新しいパス bundleファイル ``` 今回の例なら下のようになる。 ``` $ install_name_tool -change /usr/lib/libedit.3.dylib /usr/local/Cellar/readline/6.2.4/lib/libreadline.dylib ~/.rbenv/versions/1.9.3-p194/lib/ruby/1.9.1/x86_64-darwin11.3.0/readline.bundle ``` これでrails cなりirbなり起動してみればいい。日本語を入力すればその通りに表示されるはずである。 |
|
| 612位 |
|
|||
|
23:30:47 |
|
|
# enum の値を対応する任意の文字列に変換する
画面に表示や印刷するときなど、enum値を文字列に変換したいときがあります。 そういうとき、C#ではenumに対しても拡張メソッドを定義することが可能なので、enum値を表示用の文字列に 変換する拡張メソッドを定義しておくと便利です。 例えば、 ```csharp // enum定義 enum Gender { Unknown, Male, Female }; ``` というenum定義に対して、 ```csharp // enum定義のヘルパクラス static class GenderExt { // Gender に対する拡張メソッドの定義 public static string DisplayName(this Gender gender) { string[] names = { "不明", "男性", "女性" }; return names[(int)gender]; } } ``` という静的クラスを定義すると、 ```csharp Console.WriteLine(Gender.Male.DisplayName()); // => '男性' と出力される。 ``` という感じで使うことができます。 # 数値がenumで定義済みかどうかチェックする 外部からの入力値などをenum値に変換するときなど、値がキャスト可能かどうか事前に確認したい時があります。 そういう場合、Enumクラスの静的メソッド`IsDefined`で値が定義済かどうかチェックすることができます。 [MSDN:Enum.IsDefined メソッド ](http://msdn.microsoft.com/ja-jp/library/system.enum.isdefined\(v=vs.110\).aspx) > public static bool IsDefined( Type enumType, Object value ) 使い方は以下のような感じで。 ```csharp int n = 1 // 入力値 if (Enum.IsDefined(typeof(Gender), n)) Console.WriteLine(Gender); else Console.WriteLine("Undefined"); ``` 実行すると、 > Male と表示される筈です。 # enumの値を列挙する enumで定義された値を列挙したいしたい場合、Enumクラスの静的メソッド `GetValues` を用いることで、定義済みの値を配列で受け取ることができます。 [MSDN:Enum.GetValues メソッド](http://msdn.microsoft.com/ja-jp/library/system.enum.getvalues\(v=vs.110\).aspx) > public static Array GetValues( Type enumType ) 戻り値は Array なので、そのまま foreach に渡すことができます。 ```csharp foreach (var gender in Enum.GetValues(typeof(Gender))) { Console.WriteLine(gender.DisplayName()); } ``` 実行すると、 > 不明 男性 女性 と表示される筈です。 # サンプルプログラム 上記内容を簡単なヘルパクラスにまとめてみました。 ```csharp // enum定義 enum Gender { Unknown, Male, Female }; // enum定義のヘルパクラス static class GenderExt { // Gender に対する拡張メソッドの定義 public static string DisplayName(this Gender gender) { string[] names = { "不明", "男性", "女性" }; return names[(int)gender]; } // 整数値が enum で定義済みかどうか? public static bool IsDefined(int n) { return Enum.IsDefined(typeof(Gender), n); } // Foreach用のGetEnumeratorを持つヘルパクラス public class EnumerateGenders { public IEnumerator<Gender> GetEnumerator() { foreach (var gender in Enum.GetValues(typeof(Gender))) yield return gender; } } // enum定義をforeachに渡すためのヘルパクラスを返す public static EnumerateGenders Enumerate() { return new EnumerateGenders(); } } class Program { static void Main(string[] args) { // enumを文字列に変換する。 Console.WriteLine("{0} => {1}", Gender.Unknown, Gender.Unknown.DisplayName()); // 整数値がenumの値として定義済みかチェックする int n = 1; if (GenderExt.IsDefined(n)) Console.WriteLine("整数値:{0}は{1}として定義されています。", n, ((Gender)n).ToString()); else Console.WriteLine("整数値:{0}は未定義です。", n); // 定義済みのenumを列挙する foreach (var gender in GenderExt.Enumerate()) { Console.WriteLine("{0}:{1}", gender.DisplayName(), (int)gender); } } } ``` 実行すると > Unknown => 不明 整数値:1はMaleとして定義されています。 不明:0 男性:1 女性:2 と表示される筈です。 実際のプログラムで利用する場合、拡張メソッドとそれ以外のユーティリティ的なメソッドでクラスを分割し、ユーティリティ的なメソッドは汎用的に使えるようにジェネリッククラスとして記述するほうが良いでしょう。 ```csharp // ジェネリックを利用した汎用ヘルパクラス static class EnumUtil<T> { // 整数値が enum で定義済みかどうか? public static bool IsDefined(int n) { return Enum.IsDefined(typeof(T), n); } // Foreach用のGetEnumeratorを持つヘルパクラス public class EnumerateEnum { public IEnumerator<T> GetEnumerator() { foreach (T e in Enum.GetValues(typeof(T))) yield return e; } } // enum定義をforeachに渡すためのヘルパクラスを返す public static EnumerateEnum Enumerate() { return new EnumerateEnum(); } } ``` 使い方は ```csharp foreach (var gender in EnumUtil<Gender>.Enumerate()) { Console.WriteLine(gender) } ``` という感じ。 さらに、`using`を利用して ```csharp using GenderUtil = EnumUtil<Gender>; ``` という感じに別名を定義するとより使いやすくなります。 |
|
| 613位 |
|
|||
|
19:36:17 |
|
|
Qiitaはマークダウンの少し独自のやつで記述出来る。
 #エスケープ バックスラッシュ \\を前に置いてエスケープ \\#エスケープ で下のような表示になる。 \#エスケープ #引用 \>で引用 下に空行が必要 >引用 #コードの挿入 上下に空行をいれ、バッククオート3つ ```で囲む。 JIS配列なら shift + @ で ' がでる > (空行) > \`\`\`ruby:qiita.rb > puts 'おかしいと思いませんか? あなた' > \`\`\` > (空行) これでこうなる。 ```ruby:qiita.rb puts 'おかしいと思いませんか? あなた' ``` 文字列を囲むにはバッククオート \` 1つで囲む \`囲むのです\` `囲むのです` #見出し \#の数で表す。 #これはH1タグです(#) ##これはH2タグです(##) ###これはH3タグです(###) ####これはH4タグです(####) #文字強調 前後に 半角スペース か 改行文字 が必要 ##太字にする > \<b>test\</b> > \**太字にする** > \__太字にする__ <b>test</b> **太字にする** __太字にする__ ##イタリックにする。 \*か、\_で囲む。 > \*イタリック* > \_イタリック_ *イタリック* _イタリック_ #リンク []と()で囲む [文のリンク](リンク先アドレス) ここを[こうyahoo!](http://www.yahoo.jp) ここを\[こうyahoo!]\(http://www.yahoo.jp) #リスト ##Disc型 上下に空間が必要。 * + - の後に半角が必要。 どのマークでも同じ・。 > \+ 1 > \* 2 > \- 3 + 1 * 2 - 3 #水平線 >\* \* \* >\*\*\* >\*\*\*\*\* >\- \- \- >\--------------------------------------- * * * *** ***** - - - --------------------------------------- #テーブル ``` | Left align | Right align | Center align | |:-----------|------------:|:------------:| | This | This | This | | column | column | column | | will | will | will | | be | be | be | | left | right | center | | aligned | aligned | aligned | ``` | Left align | Right align | Center align | |:-----------|------------:|:------------:| | This | This | This | | column | column | column | | will | will | will | | be | be | be | | left | right | center | | aligned | aligned | aligned | これを適当に書き換えれば良い ``` | レフト | センター | 右 | |:-----------|------------:|:------------:| | aaa | This | This | | ななな | column | あああ | | ぬぬぬ | will | ううう | ``` | レフト | センター | 右 | |:-----------|------------:|:------------:| | aaa | This | This | | ななな | column | あああ | | ぬぬぬ | will | ううう | #ページをマークダウンで見る アドレスの最後に .md を付ける。 Qiita書き方メモ.md http://qiita.com/hiroyuki_hon/items/f2a779bb295fd12646ab.md #参照元 Markdown記法 チートシート http://qiita.com/Qiita/items/c686397e4a0f4f11683d http://qiita.com/Qiita/items/c686397e4a0f4f11683d.md |
|
| 614位 |
|
|||
|
00:55:12 |
|
|
※この記事は、2013年12月20日に書きました #■まえがき 最新のzshをHomebrewでインストールするついでに、 brew updateをした際にエラーがでたのでメモ。 ※尚、MacOSX 10.9.1です。 #■Homebrewがどのくらい古いか確認 ```Bash:ターミナル $ cd $(brew --prefix) $ git log ``` ```cd /usr/local```でもおk⊂(^ω^)⊃ 私の場合、 2013年8月14日までしかコミットがなかった。 ちょっと古い。 #■brew updateを叩いてみる ```Bash:ターミナル $ brew update error: Your local changes to the following files would be overwritten by merge: Library/Formula/mysql.rb Please, commit your changes or stash them before you can merge. error: Your local changes to the following files would be overwritten by merge: README.md Please, commit your changes or stash them before you can merge. Aborting Error: Failure while executing: git pull -q origin refs/heads/master:refs/remotes/origin/master ``` はい、怒られたった。 ログ見る限り、mergeで上書きできない、pullできないとかgit関連のエラーっぽい。 ```refs/heads/master:refs/remotes/origin/master``` ここあたり怪しい予感! #■brew updateできるようにした ```Bash:ターミナル $ cd $(brew --prefix) $ git fetch origin $ git reset --hard origin/master ``` homebrewのある場所に移動して、 fetchして、ローカルリポジトリをリモートの最新で上書きした。 ##参考にしたサイトはこちら + [【Homebrew】updateの時にgitのエラーがでた 2013-08-13](http://junko.hatenablog.com/entry/2013/08/13/000223 "【Homebrew】updateの時にgitのエラーがでた 2013-08-13") + [homebrew - Brew update failed - Stack Overflow](http://stackoverflow.com/questions/14113427/brew-update-failed "homebrew - Brew update failed - Stack Overflow") #■再度、brew update ```brew update```したら、がーーーっといっぱいログが流れます。 ```git log```したら 2013年12月19日のものが最新コミットでした。 Homebrewが最新になりました!(∩´∀`)∩バンザ──イ #◎チラシの裏 ##チラシの裏1 恐らく```cd $(brew --prefix)```で```/usr/local```に移動すると思う。 [Homebrew公式サイト](http://brew.sh/ “Homebrew公式サイト”) >Homebrewは独自のディレクトリーにパッケージをインストールし、それらを/usr/localにシンボリックリンクを張ります。 >``` $ cd /usr/local $ find Cellar Cellar/wget/1.12 Cellar/wget/1.12/bin/wget Cellar/wget/1.12/share/man/man1/wget.1 >$ ls -l bin bin/wget -> ../Cellar/wget/1.12/bin/wget ``` ##チラシの裏2 参考にしたサイトでは、 ```/usr/local/Library/Formula```に移動して ```git fetch```してたけど、 ```cd /usr/local````でおk⊂(^ω^)⊃ gitのローカルリポジトリは```/usr/local```にあるのだし、 下位ディレクトリ(/usr/local/Library/Formula)に移動して、 gitコマンドを叩いても```/usr/local```以下が ```$git reset --hard origin/master```されると思うのです。 なので、長いパス書かなくてもいいよとの事 |
|
| 615位 |
|
|||
|
23:07:12 |
(bexide 所属) |
|
[実践Vim](http://tatsu-zine.com/books/practical-vim)読みました。
達人出版会さんからpdf版が発売されてるのでそちらがおすすめです。何やらAmazonと比べて600円ぐらい安いようですね。 その中の > 第6部 ツール > 第16章 ctagsを使ってソースコードのインデックスを作成し、ナビゲーションを行う > TIP102: ctagsと連携するようにVimを構成する の所を掘り下げて見ようと。元々は「Vimを構成する」ですがすでにvimを使いこなす時の環境構築になってしまうのかなと思い少しタイトルを広くとらえる形にしました。 # ctagsってなに まずctagsの説明ですが、実践vimの言葉を引用すると > ctagsは、コードベースを走査し、キーワードのインデックスを作成する外部プログラムだ。 てやつで、そのctagsを使って生成されたtagsファイルを使う事によってキーワードの宣言元へ移動したりキーワードの補完をする事が出来ます。IDEで標準的に実装されてる機能が当たり前に使えるようになります。 # ctagsのインストール debian系 sudo apt-get install exuberant-ctags mac brew install ctags macでは標準でctagsが入っているけどBSD版なので対応言語が少なかったりオプションが違ったりするのでインストールしておきましょう。 # ctagsの実行方法 ctags -R でカレントディレクトリにtagsファイルが作成されます。 これをvimで読み込む設定にしておけばジャンプできたり補完できたりと便利になるしろもの。 # 今回考察すること [実践Vim](http://tatsu-zine.com/books/practical-vim)の方ではプチまとめとしての`考察`の項では以下のように書かれている。 > コミットするたびにコードベースのインデックスを再作成するのがちょうどよいところを突いている。確かに、作業中のローカルコピーと、tagsファイルが分離されるが、エラーはなんとかなる。アクティブに作業しているコードは、タグを使ってナビゲーションを行いたくなることはあまりないコードだ。 僕もそう思う。 しかしそれはバージョン管理を使っていて、なおかつ分散バージョン管理、例えばGitを使っているプロジェクトにしか適応されないという問題を抱えている。 バージョン管理使うようにしたりgit使うようにすればいいじゃんって、そう思うけどもそうじゃないじゃん?やりたいこととやることが離れすぎちゃうじゃん?やる気無くなっちゃうじゃん?技術ってそうじゃないじゃん?もっと簡単に出来るようにしてインターネットでいい気分したいじゃん?いい気分してる人と仲良くしたいじゃん?なかよしインターネットしたいじゃん?もっと世界良くして行きたいじゃん? という訳でsvn使ってる弊社でも使えるように環境構築をしてきて培ったノウハウを出して行こうかなというのが本記事の目的になります(前置き長過ぎ..) svn使うとコミットしてもhookがホストリポジトリでしか実行されないのでローカルではctags実行できないのでそれ対応です。 # 目次 - gitのhookを利用してctags実行 - vimでファイル保存した時にctagsをバックグラウンド実行 とりあえずgitのhookを利用してのctags実行と、vimでファイル保存時にctags実行の二段構えで行くという考え。git使ってるならhook使えば良いしvimで保存時に作るようにしちゃえばどの環境でも大抵使えるからいいっすよねって感じで〜感じで〜 ## gitのhookを利用してctags実行 vim強であるtpope氏のブログ記事が参考になる。 [Effortless Ctags with Git](http://tbaggery.com/2011/08/08/effortless-ctags-with-git.html) というかこれだけだと解りづらいので自分が過去に書いた記事を引用 [gitのhookでtags作成](http://qiita.com/soramugi/items/9a3687bb81ce35e3f99a) `git checkout`したり`git clone`したり`git merge`したり`git commit`した時にctagsのコマンド実行をさせる まずはhookで実行させるスクリプトの作成 mkdir -p .git_tmp/hooks touch .git_tmp/hooks/post-commit # git commit hook touch .git_tmp/hooks/post-merge # git merge, git pull hook touch .git_tmp/hooks/post-checkout # git checkout hook touch .git_tmp/hooks/ctags chmod +x .git_tmp/hooks/* ```post-系 #!/bin/sh .git/hooks/ctags >/dev/null 2>&1 & ``` ```ctags rm tags /usr/local/bin/ctags --tag-relative --recurse --sort=yes --append=no ``` とする。ctagsのパスとかは別途設定してくだしあ。 ctagsのオプションを軽く説明すると - `--tag-relative` 作成されるtagsファイル内のキーワードのパスが相対パスに - `--recurse` ディレクトリを再起的に - `--sort=yes` 作成されたキーワードをsortする - `--append=no` 既存のタグファイルに追加しない あとはgitを使う時に作成したhookを共通して使うように設定してあげる git config --global init.templatedir '.git_tmp' これで、次からcloneするgitリポジトリはhookが反映される。 vimを開けばタグジャンプだったり補完だったりが「vimの標準機能のみで」実行することができる。 ここからは好みだが`.git`ディレクトリ下にtagsファイルを作成する方法もある。 そうすればいちいち`.gitignore`の指定をしなくてすんだり、tagsの変更分をコミットしたりしなくてすむ。 `.git`以下に作ってしまうともし閲覧する必要が出てきた時に別のファイルを削除してしまった場合等を考えて設定してみてほしい。考え過ぎかもしれないが。 その場合は以下になる ```ctags rm .git/tags /usr/local/bin/ctags --tag-relative --recurse --sort=yes --append=no -f .git/tags ``` あとは`.vimrc`に以下のようにtagsファイルを指定してあげる ```.vimrc set tags+=.git/tags ``` [fugitive.vim](https://github.com/tpope/vim-fugitive)を使っていれば標準で`.git/tags`を見るように設定されている。 ## ファイル保存時にctags実行 `.vimrc`にファイル保存時に作成する設定をしても良いが、あまりにもソースコードの行数が多かったりするとctagsプロセスが終了する時間がかかってしまう。 なのでバックグラウンドで実行する設定にする必要がある、なおかつバックグラウンドで実行したとしても、頻繁に保存した時にctagsプロセスが多重起動してしまった時が厄介で作成されるtagsファイルのフォーマットが崩れてしまうので作成しなおす必要が出てくる。ああ厄介だーどうしようー と、そんなこともあろうかとctagsを保存時に実行する[auto-ctags.vim](https://github.com/soramugi/auto-ctags.vim)というプラグインを作成しておいたので問題はなかった。(宣伝) インストールをして ```.vimrc NeoBundle 'soramugi/auto-ctags.vim' ``` vimを開いて以下のコマンドを打ち込めばtagsファイルが作成される :Ctags `.vimrc`に以下の設定をしてやれば保存時に勝手にtagsファイルが作成されます。 ```.vimrc let g:auto_ctags = 1 ``` 格納ディレクトリも指定できます ```.vimrc let g:auto_ctags_directory_list = ['.git', '.svn'] ``` tagsファイルを読み込む設定はしていないので、自分が設定したディレクトリパスを指定してあげてください。こんな感じで ```.vimrc set tags+=.svn/tags ``` 使い方はここら辺とかに乗ってるんじゃないかな(ほじ(宣伝)) [tagsファイルを作成する「auto-ctags.vim」作った](http://soramugi.hateblo.jp/entry/2013/12/01/150433) # まとめ 結構長くなった。。 以上でgitを使っていてコミットした時にtagsファイルを作成したり、ファイルを保存した時にtagsファイルを作成する環境を構築する方法でした。 元々は自分が過去に書いた記事 [ctagsをちゃんと使う](http://qiita.com/soramugi/items/7014c866b705e2cd0b95)が結構見られてるみたいで、そういう需要があるのではないかと思いまとめてみた次第です。書いた時から色々得た知識もありますしね。 でもあんまりctagsの記事を見ないのですが他のvim強の方達は他の方法を取っているのかも?あんまりタグジャンプしないのかな。 タグジャンプはすげー便利な機能だと思っているのでもっと簡単に使えればいいなと思いプラグイン作った次第です。よければ使ってやって下せえ。 あと実践vim面白いよ! enjoy the vim! |
|
| 616位 |
|
|||
|
00:39:03 |
(Bitjourney, inc 所属) |
|
職場の先輩にtmuxをおすすめされたので導入してみました! これからはうっかりTerminalを閉じてしまって泣くことがなくなりそうです! tmuxとは 引用 : [https://bytebucket.org/ns9tks/tmux-ja/wiki/tmux-ja.html](https://bytebucket.org/ns9tks/tmux-ja/wiki/tmux-ja.html) > tmux は端末を多重化し、 1 つのスクリーンから複数の端末を作成、アクセス、制御することを可能にします。 tmux をスクリーンからデタッチしバックグランドで動作させておいて、 その後再度アタッチすることができます。 > tmux は起動されると 1 つのウィンドウを持つ新しい セッションを作成しスクリーンに表示します。 スクリーンの一番下にあるステータスラインはカレントセッションの情報を表示し、 対話型コマンドの入力に使用されます。 > セッションとは tmux に管理される疑似端末の集合の 1 つです。 各セッションはリンクする 1 つ以上のウィンドウを持ちます。 ウィンドウはスクリーン全体を占有し、 各々が独立した疑似端末である複数の矩形ペインへ分割することができます (疑似端末の技術的な詳細は pty(4) マニュアルページドキュメント)。 tmux のインスタンスはいくつでも同じセッションに接続することができ、 ウィンドウはいくつでも同じセッションに作成しておくことができます。 全セッションが kill されたときに tmux は終了します。 ようするに * 1つの画面でたくさんの環境を開いておける * Terminalを閉じてもまたtmuxを実行すれば閉じた時の状態で復元できる ということです。 便利です。 では早速入れてみましょう。 おなじみのHomebrewでさくっとインストールします。 ``` % brew install tmux ==> Installing dependencies for tmux: pkg-config, libevent ==> Installing tmux dependency: pkg-config ==> Downloading http://pkgconfig.freedesktop.org/releases/pkg-config-0.28.tar.gz ######################################################################## 100.0% ==> ./configure --prefix=/usr/local/Cellar/pkg-config/0.28 --disable-host-tool --with-internal-glib - ==> make ==> make check ==> make install /usr/local/Cellar/pkg-config/0.28: 10 files, 604K, built in 64 seconds ==> Installing tmux dependency: libevent ==> Downloading https://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz ######################################################################## 100.0% ==> ./configure --disable-debug-mode --prefix=/usr/local/Cellar/libevent/2.0.21 ==> make ==> make install /usr/local/Cellar/libevent/2.0.21: 48 files, 1.8M, built in 30 seconds ==> Installing tmux ==> Downloading http://downloads.sourceforge.net/project/tmux/tmux/tmux-1.8/tmux-1.8.tar.gz ######################################################################## 100.0% ==> Patching patching file osdep-darwin.c patching file utf8.c ==> ./configure --prefix=/usr/local/Cellar/tmux/1.8 --sysconfdir=/usr/local/etc ==> make install ==> Caveats Example configurations have been installed to: /usr/local/Cellar/tmux/1.8/share/tmux/examples Bash completion has been installed to: /usr/local/etc/bash_completion.d ==> Summary /usr/local/Cellar/tmux/1.8: 14 files, 624K, built in 94 seconds ``` これでtmuxとTerminal上で入力すればtmuxが立ち上がるようになります。 tmux上での基本的な操作は下記を参照ください。 bind-keyはデフォルトではC-b(Ctrl + b)になっています。 * `bind-key d` : デタッチ(tmuxの終了) * `bind-key c` : 新しいウィンドウを作る * `bind-key n` : 次のウィンドウへ移動する * `bind-key p` : 前のウィンドウへ移動する * `bind-key 数字` : 指定したウィンドウへ移動する * `bind-key w` : ウィンドウを一覧表示する * `bind-key %` : 画面を縦に分割する * `bind-key ”` : 画面を横に分割する * `bind-key !` : 画面の分割を解除する * `bind-key o` : 分割した画面間を移動する * `bind-key ,` : windowに名前をつける * `bind-key ?` : コマンド一覧を表示 よく使うtmuxコマンドは下記です。 * `% tmux ls` : セッション一覧を確認 * `% tmux a` : 最後に使用したセッションにアタッチ * `% tmux a -t "セッション名"` : 指定したセッションにアタッチ * `% tmux kill-session -t "セッション名"` : セッション終了 ちなみに私の.tmux.confはこのようになっています。 まだ導入したばかりであまりカスタマイズしていませんがご参考までに。 ``` # Prefix変更 C-b -> C-t set-option -g prefix C-t bind-key C-t send-prefix unbind-key C-b # key bind (windowの移動) # カーソルキーで移動 bind -n left previous-window bind -n right next-window # key bind (paneの移動) # Shift + カーソルキーで移動 bind -n S-left select-pane -L bind -n S-down select-pane -D bind -n S-up select-pane -U bind -n S-right select-pane -R # 256色端末を使用する set-option -g default-terminal "screen-256color" # viのキーバインドを使用する set-window-option -g mode-keys vi ``` |
|
| 617位 |
|
|||
|
18:34:03 |
|
|
* クラスの落とし穴1 - プロパティの初期化 (この投稿) * [クラスの落とし穴2 - メソッドとクロージャ](http://qiita.com/cocottejs/items/0c1756ac7e7ec8bae7cb) * [クラスの落とし穴3 - 継承](http://qiita.com/cocottejs/items/e75f751c7aa8a7361aab) * [クラスの落とし穴4 - プライベート変数の実装](http://qiita.com/cocottejs/items/35e0edef71d8c0fc3348) # はじめに javascriptでもクラスを作成する事が多くなってきました。 しかし、「javascriptにはクラスがない」とよく言われ、実装側が擬似的にクラスを定義しています。 クライアントサイドで大規模開発が増えてきたため、オブジェクト指向の概念で実装したいと思っているのでしょう。 javascriptでは安易な疑似クラスの作成によって見事落とし穴にはまる事があります。 ここでは、本来javascriptにはないクラスをうまく実装する方法を順に追って説明します。 "落とし穴とは、 **うまく動いているけど気がついていないだけで実はマズい実装の事** とします ## 簡単なクラスの実装 クラスの定義には幾つかの方法がありますが、多数派であるプロトタイプを使用を採用します。 ```javascript: // クラスを定義 function Klass () {}; // プロパティ Klass.prototype.name = 'foo'; // メソッド Klass.prototype.setName = function setName (value) { this.name = value; }; ``` 使用するには以下のようにします。 ```javascript: var instance = new Klass(); instance.setName('bar'); console.log(instance.name); ``` 基本的にはこの様な記述でクラスのようなものが出来ました。 動作もうまく行っているようです。 ## 間違った実装 次の例は先ほどとほとんど同じようなコードに見えて、クラスの機能としては確実に間違っているところがあります。 それがどのような理由かすぐにわかるでしょうか? ```javascript: // クラスを定義 function Klass () {}; // プロパティ Klass.prototype.hobbies = []; // メソッド Klass.prototype.addHobby = function addHobby (value) { this.hobbies.push(value); }; ``` 最初の例と変わらないように見えますので、うまく動作しそうです。 実際に使用してみても問題はないように見えます。 ```javascript: var instance = new Klass(); instance.addHobby('guitar'); console.log(instance.hobbies); // ['guitar'] ``` しかし、次のコードを動作させるとよくわかります。 ```javascript: var inst1 = new Klass(); inst1.addHobby('guitar'); console.log(inst1.hobbies); // ['guitar'] var inst2 = new Klass(); inst2.addHobby('jogging'); console.log(inst2.hobbies); // ['guitar', 'jogging'] ``` `inst2`の`hobbies`には、`jogging`だけではなく`guitar`も追加されています。 じつは、prototypeに追加されたプロパティは`new`で作成されたオブジェクトから参照する事ができるだけでなく、どの(OOでいう)インスタンスからも変更できます。 そのため、 **`inst1`と`inst2`が参照している`hobbies`は同じ実体です。** これはプロトタイプチェーンによる参照による正常な動作です。 理解すると「なんだ単純なミスだ」と思うかもしれません。 しかし、この不具合の厄介なところは、 **一つのインスタンスを作成しテストしただけでは全く間違いに気がつかない事**です。 では最初のクラス定義はなぜうまく行ったのでしょうか? それは、文字列は参照しているプロパティを変更したのはなく、 **新たに値を設定した事** により参照がnewで作成されたオブジェクトのプロパティとして上書きされたため、たまたまうまく動作したにすぎません。 代入と`array.push`にプロトタイプチェーンでの参照時に大きな違いがあることがポイントです。 この事は、`console.log(inst1)`でオブジェクトを出力するとよくわかります。 最初の`setName`を実行したインスタンスは`{name: 'bar'}`と出力され、`addHobby`を実行したインスタンスは`{}`と出力されます。`hobbies`プロパティが存在しません。 ## 正しい実装方法は? クラスを定義するのに定石となる実装方法はあるのでしょうか? おそらく一番簡単な方法はコンストラクタで、すべてのプロパティに初期値を設定する事です。 最初の例と2番目の例のプロパティ・メソッドを一緒に定義して書き直すと次のようになります。 ```javascript: // クラスを定義 function Klass () { // プロパティ this.name = 'foo'; this.hobbies = []; }; // メソッド Klass.prototype.setName = function setName (value) { this.name = value; }; Klass.prototype.addHobby = function addHobby (value) { this.hobbies.push(value); }; ``` しかし、メソッドや定数までコンストラクタで設定する必要はありません。prototypeがメモリ節約をする利点が生かされなくなるからです。 特に共通の処理であるメソッドをコンストラクタで設定することは、また[別のある落とし穴](http://qiita.com/cocottejs/items/0c1756ac7e7ec8bae7cb)が存在します。 # さいごに **プロパティの実装はコンストラクタで行いましょう** これだけ覚えておけば今回は大丈夫です まだまだ落とし穴がたくさんありますので、他の投稿も確認していただければと思います。 |
|
| 618位 |
|
|||
|
14:23:17 |
|
|
##環境設定
`config/environments/development.rb`を以下のように設定 ```rb:/config/environments/develop.rb config.action_mailer.delivery_method = :smtp config.action_mailer.raise_delivery_errors = true config.action_mailer.smtp_settings = { :enable_starttls_auto => true, :address => 'smtp.gmail.com', :port => '587', :domain => 'smtp.gmail.com', :authentication => 'plain', :user_name => 'ユーザー名@gmail.com', :password => 'gmailパスワード' } ``` ##メーラークラスを自動生成 ``` #rails generate mailer クラス名 メソッド名 $ rails generate mailer NoticeMailer sendmail_confirm create app/mailers/notice_mailer.rb invoke erb create app/views/notice_mailer create app/views/notice_mailer/sendmail_confirm.text.erb invoke test_unit create test/functional/notice_mailer_test.rb ``` ##メーラークラスの編集 ```rb:/app/mailers/notice_mailer class NoticeMailer < ActionMailer::Base #デフォルトのヘッダ情報 default from: "user@gmail.com" # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # # en.notice_mailer.sendmail_confirm.subject # def sendmail_confirm @greeting = "Hi" mail to: "user@sample.com", subject: "ActionMailer test" end end ``` ##メールテンプレートの編集 テンプレートの拡張子を`html.erb`にすればHTMLメール、`text.erb`にすればテキストメールとなる。 ```erb:/views/notice_mailer/sendmail_confirm.html.erb NoticeMailer#sendmail_confirm <%= @greeting %>, find me in app/views/app/views/notice_mailer/sendmail_confirm.html.erb <h2>テスト</h2> <p>あいうえおかきくけこさしすせそたちつてとなにぬねのあいうえおかきくけこさしすせそたちつてとなにぬねのあいうえおかきくけこさしすせそたちつてとなにぬねのあいうえおかきくけこさしすせそたちつてとなにぬねのあいうえおかきくけこさしすせそたちつてとなにぬねのあいうえおかきくけこさしすせそたちつてとなにぬねの</p> ``` 以上でActionMailerからGmailを経由して送信できるようになった。 ``` $ rails c NoticeMailer.sendmail_confirm.deliver ``` メールが送信されたかを確認。 ##viewから送信 ```rb:items_controller.rb def mail_send @mail = NoticeMailer.sendmail_confirm.deliver render :text => 'send finish' end ``` ```erb:index.html.erb <%= link_to 'Send Mail', action: "mail_send" %> ``` 以上でviewから送信できる。 |
|
| 619位 |
|
|||
|
14:08:07 |
(Zalando SE 所属) |
|
RSpec 2.14.0 からは `allow`, `expect_any_instance_of`, `allow_any_instance_of ` も使えるようになりました。 ```should_to_expect.rb obj.should ... expect(obj).to ... obj.should_not ... expect(obj).not_to ... obj.should =~ // expect(obj).to match(//) [1, 2, 3].should =~ [3, 2, 1] expect([1, 2, 3]).to match_array([3, 2, 1]) obj.should > 3 expect(obj).to be > 3 lambda { ... }.should raise_error expect { ... }.to raise_error # RSpec 2.14.0 or later obj.stub(:foo) allow(obj).to receive(:foo) SomeClass.any_instance.should_receive(:foo) expect_any_instance_of(SomeClass).to receive(:foo) SomeClass.any_instance.stub(:foo) allow_any_instance_of(SomeClass).to receive(:foo) # RSpec 2.99.0.beta1 or later it { should eq(something) } it { is_expected.to eq(something) } # RSpec 3.0.0.beta1 or later obj.stub_chain(:foo, :bar).and_return(something) allow(obj).to receive_message_chain(:foo, :bar).and_return(something) ``` |
|
| 620位 |
|
|||
|
01:38:14 |
(株式会社キュリオシティソフトウェア 所属) |
|
Storyboardを使ってUITableViewのある画面を作り、そのCellを複数画面で使いまわすためにxibとして作成するときのTipsです。
UITableViewにiOS5から追加されたregisterNib:forCellReuseIdentifier:メソッドを使うことで、作成したxibをUITableViewに登録することができ、nilチェックとloadNibを必要としないシンプルなコードにすることができます。 コードで例を示すために使いまわしたいCellを"SampleTableViewCell"とします。作成するのは3つ * SampleTableViewCell.xib * SampleTableViewCell.h * SampleTableViewCell.m ここで説明するregisterNib:forCellReuseIdentifier:メソッドは、UITableViewを保持するUITableViewControllerのviewDidLoadなどで呼び出します。これによってCell用に作成したxibファイルがUITableViewに登録されます。 ```objective-c: - (void)viewDidLoad { [super viewDidLoad]; UINib *nib = [UINib nibWithNibName:@"SampleTableViewCell" bundle:nil]; [self.tableView registerNib:nib forCellReuseIdentifier:@"Cell"]; } ``` あとはStoryboardでStaticやDynamicなCellを呼び出す時と同じように次のようにします。 ```objective-c: - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; SampleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; // Configure the cell... return cell; } ``` これで下記のようにcellの再利用をnilチェックし生成する必要はありません。 ```objective-c: - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; SampleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (!cell) { // こういう処理は必要なくなるよと cell = [SampleTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } // Configure the cell... return cell; } ``` なぜ再利用のチェックと生成を行う必要がないかというと、先ほどの説明通りにnibを登録してればnilを返すことがないからです。これについては[AppleのUITableViewのリファレンス](http://developer.apple.com/library/ios/#documentation/uikit/reference/UITableView_Class/Reference/Reference.html)でdequeueReusableCellWithIdentifier:がnilを返す場合について下記のように言及されていましたので参考になります * 再利用されるcellが存在しない時 * nibが登録されていない時(iOS5から) * Classが登録されていない時(iOS5から) つまり、nilが返るという状態は実装ミスです。実装ミスがすぐに発覚するので良いつくりといえるでしょう。 したがって、先述のメソッドによりCellが登録されることで内部キューになければ自動で生成を行なってくれるため、Cellの再利用チェックと生成のコードが必要ないわけです。余談ですがStoryboardのCellを使うようになってから再利用チェックを書く必要がなくなっていたのはStoryboardが登録の手続きを行なっていたからなんですね。 これらは当然、Storyboard+xibの時にかぎらず、UITableViewでさえあれば使えます。また、registerClass:forCellReuseIdentifier:によりクラスを指定できるためxibである必要も有りません。 |
|
| 621位 |
|
|||
|
14:38:39 |
(SonicGarden 所属) |
|
# まず読むべき書籍 * [プロを目指す人のためのRuby入門](http://amzn.to/2zXKrTt) * [RailsによるアジャイルWebアプリケーション開発](http://goo.gl/Ju59R) * [Rails3レシピブック 190の技](http://goo.gl/F2HNM) * [Ruby on Rails 3 アプリケーションプログラミング](http://goo.gl/SGYue) * [初めてのRuby](http://goo.gl/naHsZ) * [プログラミング言語Ruby](http://goo.gl/Plt3X) * [Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp) # Web * [Ruby on Rails Tutorial](https://www.railstutorial.org/) * [RailsGuides](http://guides.rubyonrails.org/) * [Better Specs](http://betterspecs.org/) * [Ruby on Rails: Core](https://groups.google.com/forum/#!forum/rubyonrails-core) * [rails/rails](https://github.com/rails/rails) * [The Ruby Toolbox](https://www.ruby-toolbox.com/) # Podcast * [Railscasts](http://railscasts.com/) * [Ruby5](http://ruby5.envylabs.com/) # レベルが上がったら読む書籍 * [メタプログラミングRuby](http://goo.gl/550vp) |
|
| 622位 |
|
|||
|
17:41:54 |
|
|
クラスというかコンストラクタですが。
```javascript var Point = (function() { // コンストラクタ var ctor = function Point(x, y) { // new なしで呼び出すとエラー if (!(this instanceof ctor)) throw new TypeError('Constructor cannot be called as a function.'); this.x = x; this.y = y; }; var proto = ctor.prototype = {}; proto.constructor = ctor; // 別名コンストラクタ ctor.from = (function(c) { c.prototype = proto; return c; }(function from(object) { if (!(this instanceof ctor)) throw new TypeError('Constructor cannot be called as a function'); this.x = object.x; this.y = object.y; })); // メソッド // Point.prototype.moveっていちいち書きたくない。 proto.move = function move(dx, dy) { this.x += dx; this.y += dy; }; return ctor; }()); ``` 以下の点に気をつけていたらこんなかんじに。 - `Point.name`は`"Point"`であるべき。 - デバッグしやすい。大変重要。 - `Point()`はエラーを投げるべき。 - 勝手に`new Point()`だと解釈するのは良くないと思う。 - `new Point(0, 0)`の他に`new Point.from({x: 0, y: 0})`のような別の名前のコンストラクタもあると便利 - `(new Point.from({x: 0, y: 0}) instanceof Point) === true` - Dartからパクった - 引数の型とか数を見て処理を分けるよりも、名前自体が変わっている方が作り易い - コードの意味もわかりやすいと思うし - `Point.prototype.move.name`は`"move"`であるべき。 継承する場合はもうちょっといろいろ。 ```javascript // Object.createがない環境のための用意。 var create = Object.create || function create(proto) { var ctor = function() {}; ctor.prototype = proto; return new ctor(); }; // Pointクラスを継承するPoint3Dクラス。 var Point3D = (function() { // プロトタイプチェーンに組み込みたいコンストラクタはここに書く。 // もし他のコンストラクタを継承したくなっても、ここだけ書き換えればいい。 var superCtor = Point; var superProto = superCtor.prototype; var ctor = function Point3D(x, y, z) { if (!(this instanceof ctor)) throw new TypeError('Constructor cannot be called as a function.'); superCtor.call(this, x, y); this.z = z; }; var proto = ctor.prototype = create(superProto); proto.constructor = ctor; ctor.from = (function(c) { c.prototype = proto; return c; }(function from(object) { if (!(this instanceof ctor)) throw new TypeError('Constructor cannot be called as a function'); // ファクトリメソッドじゃなくて別名コンストラクタにすると // こんな感じに再利用出来る。 superCtor.from.call(this, object); this.z = object.z; })); proto.move3D = function move3D(dx, dy, dz) { // Point.prototype.move.call(...)とか書きたくないからsuperProto変数を用意する。 superProto.move.call(this, dx, dy); this.z += dz; }; return ctor; }()); ``` |
|
| 623位 |
|
|||
|
13:17:25 |
|
|
普段は C++ をメインに使っているが最近 JavaScript を使うことになったので、 JavaScript の便利ツールを調べた結果をメモとして残すことにした。
他にも便利なツールなどありましたら教えていただけると嬉しいです。 # JSLint / JSHint JavaScript のコードを分析してjs ファイルに潜む悪いパーツを検出してくれるツール。 下記コマンドにてインストール可能。 ``` $ npm install -g jshint ``` ### 使い方 例えば fizzbuzz.js に対して構文解析を行う場合は下記コマンドで ok。 ``` $ jshint fizzbuzz.js ``` ### JSLint と JSHint は何が違うのか? 厳しすぎる (というかプログラムをダグラス・クロックフォード氏の流儀で書くよう強制する) 判定を行う JSLint に問題を感じたアントン氏が JSLint からフォークしたものが JSHint である。 厳しい判定を好む人は JSLint 、 **致命的なエラーとならない箇所** の警告は出さないで欲しい人は JSHint を使うと良いのだろうか。 ちなみに、2013年には英語圏の JavaScript コミュニティにおいて、 JSHint 支持者が JSLint 支持者を上回るだろうとのこと。 参考記事 : http://blog.node.ws/?p=1379 # jscheckstyle 各関数 / メソッドの循環的複雑度を算出するツール。 下記コマンドにてインストール可能。 ``` $ npm install -g jscheckstyle ``` ### 使い方 例えば fizzbuzz.js に対して jscheckstyle を実行すると下記の様な出力が得られる。 ```fizzbuzz.js function isMultiple(src, base) { 'use strict'; return src % base === 0; } function fizzbuzz(n) { 'use strict'; if(isMultiple(n, 15)) { return 'fizzbuzz'; } else if(isMultiple(n, 3)) { return 'fizz'; } else if(isMultiple(n, 5)) { return 'buzz'; } else { return n.toString(); } } // サンプル console.log( fizzbuzz(1) ); // => 1 console.log( fizzbuzz(3) ); // => fizz console.log( fizzbuzz(5) ); // => buzz console.log( fizzbuzz(15) ); // => fizzbuzz ``` | Line | Function | Length | Args | Complex... | |:-----------|------------:|:------------|------------:|:------------:| | 1 | isMultiple | 4 | 2 | 2 | | 6 | fizzbuzz | 12 | 1 | 8 | **行数 ( Length ) は空行やコメントを含む** ため他の関数との相対的な比較に使用する。 # Jasmine JavaScript の単体テストフレームワーク。 [こちら](https://github.com/pivotal/jasmine/downloads) よりダウンロード可能。 ### 使い方 ダウンロード後解凍し、下記を実施する。 * src ディレクトリにテスト対象コードを配置 * spec ディレクトリにテストコードを配置 * テスト対象コードとテストコードを SpecRunner.html にインクルード 上記手順を実施後、SpecRunner.html を開くと結果がブラウザに表示される。 テストコードの書き方はダウンロードパッケージに含まれるサンプルコードを参照。 ### 補足 コマンドライン版 Jasmine が欲しい人は、下記コマンドにて入手可。 ``` $ npm install -g jasmine-node ``` # Sinon.js スタブやモック、フェイクオブジェクトの提供に特化した JavaScript 用ライブラリ。 下記コマンドで入手可。 ``` $ npm install -g sinon ``` ### 使い方 [こちら](http://sinonjs.org/docs/) のサイトを参考。 # PhantomJS js ファイルを実行できる Webkit ベースのコマンドラインツール。 DOM や Canvas などにネイティブにアクセスできるため、アプリのテストツールとしても利用可能。 mac を使っている場合は下記コマンドで入手可。 ``` $ brew install phantomjs ``` ### 使い方 [ここ](https://github.com/ariya/phantomjs/wiki/Examples) にサンプルコードがあるので、それを参考に使用する。 # grunt.js node.js を利用した CUI のビルドツール。タスクを設定し、それらを自動化してくれる。 インストールは下記コマンドで ok。 ``` $ sudo npm install -g grunt-cli ``` ### 使い方 grunt.js は様々なことができるが、今回は js フォルダ以下にある js ファイルに変更が加えられる度に自動で jshint を実行する例を紹介する。フォルダ構成は下記の通り。 ``` project/ js/sample.js ``` まず project フォルダへ移動し、下記コマンドを入力。入力後対話形式で name などを聞いてくるので必要事項を入力する。 ``` $ npm init ``` 入力完了後 project フォルダに package.json が生成される。 ( 入力内容により異なるが ) 例えば次の様な内容となる。 ```package.json { "name": "test", "version": "0.0.0", "description": "test", "main": "Gruntfile.js", "scripts": { "test": "" }, "repository": "", "author": "MasayaMizuhara", "license": "BSD" } ``` package.json 生成後、下記コマンドにより生成した package.json を有効にする。 ``` $ npm install grunt --save-dev ``` 続いて使用するプラグインをインストールする。 ``` $ npm install grunt-contrib-watch grunt-contrib-jshint --save-dev ``` タスクを使用する Gruntfile.js を project フォルダ直下に作る。 今回のサンプルでの内容は下記の通り。 ``` module.exports = function(grunt) { grunt.initConfig({ jshint: { files: ['js/*.js'], }, watch: { files: ['js/*.js'], tasks: ['jshint'] } }); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-watch'); }; ``` Gruntfile.js のあるフォルダにて下記コマンドを実行する。 これにより、js フォルダ以下にある js ファイルが変更される度に jshint が実行される様になる。 ``` $ grunt watch ``` ### 参考サイト 参考にしたサイト。 * http://kojika17.com/2013/03/grunt.js-memo.html * http://1000ch.net/2013/08/01/FrontendCodingEnvironment/ |
|
| 624位 |
|
|||
|
17:32:10 |
(フリープログラマ 所属) |
|
## 注意:この記事は古い情報です。
下記のページを代わりに読んでください。 [データサイエンティストを目指す人のpython環境構築 2016 - Qiita](http://qiita.com/y__sama/items/5b62d31cb7e6ed50f02c) 以下は、上記を読んで足りない場合のみ、読むようにしてください。 ------------------------ この記事は、他の言語でのプログラミング経験はあるけどPythonは初めての人向けに、Pythonを使ったデータ解析プログラミングを始めるまでのおすすめルートを示すものです。 対象者は、WindowsまたはMacユーザです。Linuxユーザは自分でできると思うので割愛。 ## データ解析に必要なもの・ライブラリ * Python本体 * NumPy: 配列データ(ベクトルや行列)を簡単に扱うためのライブラリ * SciPy: 科学計算用ライブラリ * matplotlib: グラフの作図ライブラリ * pandas: Rみたいなデータフレームが使える * (オプション)IPython: 便利な対話環境+レポート作成機能 * (オプション)pip: 便利なパッケージマネージャ * (オプション)scikit-learn: 機械学習用ライブラリ ## Pythonのインストール ### Windowsの場合 #### 1. Python, NumPy, SciPy, matplotlibのインストーラを使う この記事に従います。 * [windows7 - Python, SciPy, matplotlibのインストール(Windows) - Qiita](http://qiita.com/mojaie/items/995661f7467ffdb40331) 注意すべきは、**Windowsが64bitでも、Pythonのインストーラは32bit用を使うこと** です。 以下引用: > 1. Pythonをインストールします。Win用のインストーラがあります。 > <http://www.python.org/ftp/python/2.7.4/python-2.7.4.msi> > > 2. NumPyをインストールします。Win用のインストーラがあります。 > <http://sourceforge.net/projects/numpy/files/NumPy/1.7.0/numpy-1.7.0-win32-superpack-python2.7.exe/download> > > 3. SciPyをインストールします。Win用のインストーラがあります。 > <http://sourceforge.net/projects/scipy/files/scipy/0.12.0/scipy-0.12.0-win32-superpack-python2.7.exe/download> > > 4. matplotlibをインストールします。Win用のインストーラがあります。 > <https://github.com/downloads/matplotlib/matplotlib/matplotlib-1.2.0.win32-py2.7.exe> #### 2. pandasのインストール [Windows32bit で Python27 & Pandas/StatsModels - けいれん現象の幽玄美よ](http://elfcobe.hateblo.jp/entry/20130310/1362841572) に従います。 1. <http://pandas.pydata.org/getpandas.html> から 「pandas-0.10.0.win32-py2.7.exe」をダウンロード 2. exeをダブルクリックしてインストール。 ### Macの場合 SciPy Superpackを使ってインストールします。 #### 1. 準備 Xcodeを予めインストールしておきます。 Pythonは2.x系が予めインストールされているはずなので、これを使います。 #### 2. SciPy Superpackをインストール [PythonをMacで使う.主に研究用.: Scipy Superpackをインストール](http://pythonnumpy.blogspot.jp/2012/06/scipy-superpack.html) に従います。 1. <http://fonnesbeck.github.com/ScipySuperpack/>からinstall_superpack.shをダウンロード。 2. ターミナルでinstall_superpack.shのあるディレクトリに移り、実行する: sh install_superpack.sh 3. 以下のように聞かれるので、「n」と答える。 Are you installing from a repository cloned to this machine (if unsure, ansew no)?(y/n) n #### 3. pipを使って各種ライブラリをインストール 後述の「pip, IPythonのインストール(Win/Mac) 」に従ってpipをインストールした上で、pipを使って残りのライブラリをインストールします。 pip install pandas pip install scikit-learn ### pip, IPythonのインストール(Win/Mac) この項の方法はWindowsとMacで共通しています。 [pip と ipython インストール手引き (Windows編) - secretbase.log](http://cointoss.hatenablog.com/entry/20120531/1338474875) を参考にpipとIPythonをインストールしてみます。以下、引用。 > #### setuptoolsのインストール > setuptools をインストールします。この中に easy_install も含まれています。 > <http://pypi.python.org/pypi/setuptools> > > pythonのバージョンに合わせた ファイルをダウンロードします。(引用者注:Windowsの場合は)setuptools-0.6c11.win32-py2.6.exeを選択し実行します。 > > #### pip のインストール > pipは、easy_install を置き換えるものとして開発されていて、パッケージのuninstallもできて便利なのでこちらを入れます。 > easy_install から pip をインストールします。 > > コマンドプロンプトを起動します。 > > easy_install pip > > #### ipython のインストール > 環境が整いました。pip にて ipython をインストールします。 > > pip install ipython > pip install pyreadline ### 別の方法(Win/Mac):Enthought Canopyを使う方法 上記のライブラリ一式をインストールするには、別の方法があります。Enthought Canopy のパッケージであるCanopy Expressを使うと、データ解析に必要な色々なライブラリ(NumPy, SciPy, matplotlib, IPython, pandasなど)が一気にインストールできるそうです。インストール方法は [Windows7 IPython インストール - 蛇使いのブログ](http://oneshotlife-tom.hatenadiary.jp/entry/2013/10/06/Windows7_IPython_%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB) にも載っています。 インストールするには、<https://www.enthought.com/products/canopy/> にアクセスしてCanopy Express(無料)をインストールすれば、(Windowsの場合)スタートメニューにある「Canopy command prompt」からPythonの処理系一式を使うことが出来ます。 ただし、この記事を書いた時点(14.03.07)では、上記のURLはInternal Server Errorとなってアクセス出来ませんでした。もしアクセスできるなら、試してみてもいいかもしれません。 ## おすすめの開発環境 おすすめの開発環境(IDE)を紹介します。Emacs/Vimを使いたい方は、自分で調べて下さい。 ### PyCharm Community Edition (Win/Mac/Linux) 僕が愛用しているIDEです。無料で使えて、補充などもうまく効いてくれます。 マルチプラットフォームなのが最大の特徴です。プラグインでVimエミュレータもあります。 データ解析には、無料のCommunity Editionだけで十分間に合うのでこれを使います。 * [Python IDE & Django IDE for Web developers : JetBrains PyCharm](http://www.jetbrains.com/pycharm/) ### PyScripter(Windows) 僕は使ったことがないですが、Windowsの人はこれを入れてもいいかもしれません。 * 紹介記事: [Pythonの開発環境にはPyScripterがとってもおススメ:まあまあ元気になる話:ITmedia オルタナティブ・ブログ](http://blogs.itmedia.co.jp/murayama/2013/11/pythonpyscripte-c462.html) * 公式: [pyscripter - An open-source Python Integrated Development Environment (IDE) - Google Project Hosting](https://code.google.com/p/pyscripter/) |
|
| 625位 |
|
|||
|
17:03:17 |
|
|
Web APIのレスポンスを扱ったりしていると、ネストしたハッシュや配列に出くわすことがある。
```rb require 'json' require 'open-uri' json = open("http://iss.ndl.go.jp/books/R100000002-I000011125696-00.json").read api_response = JSON.parse(json) #{"link"=>"http://iss.ndl.go.jp/books/R100000002-I000011125696-00", # "identifier"=> # {"JPNO"=>["21976263"], # "NSMARCNO"=>["121144000"], # "ISBN"=>["978-4-87783-259-9"]}, # "title"=>[{"value"=>"セマンティック・ウェブ入門", "transcription"=>"セマンティック ウェブ ニュウモン"}], # "creator"=>[{"name"=>"赤間, 世紀, 1960-", "transcription"=>"アカマ, セイキ"}], ... } ``` `api_response`から必要な情報だけを抜き出したいが、一部のデータが存在しない可能性がある。 ```rb # このデータは必ず存在することが保証されているが、 title = api_response["title"][0]["value"] # ISBNは存在しないかもしれない。その場合、[0]の呼び出しでNoMethodErrorが発生してしまう。 isbn = api_response["identifier"]["ISBN"][0] ``` 条件分岐などを使ってみても冗長になる。 ```rb isbn = api_response["identifier"]["ISBN"] if api_response["identifier"] && api_response["identifier"]["ISBN"] isbn = api_response["identifier"] && api_response["identifier"]["ISBN"] && api_response["identifier"]["ISBN"][0] ``` どのように書けばいいだろうか。 ## digメソッドを使う Ruby 2.3.0からdigメソッドがArray, Hash, OpenStruct, Structに実装された。 ```rb isbn = api_response.dig("identifier", "ISBN", 0) #=> "978-4-87783-259-9" isbn = api_response.dig("identifier", "TRCMARCNO", 0) #=> nil ``` digメソッドは次のような動作をする。 1. 最初の引数をキーにして値を取り出そうとする - 引数の型がキーとして不正ならTypeError例外を発生させる(例:Arrayに対して文字列をキーにしたとき、OpenStructに対して数値をキーにしたとき) 2. キーに対応する値が存在しなければ`nil`を返す 3. 要素が見つかったとき 1. それが`nil`ならば`nil`を返す 2. 引数が残っていなければ、見つかった要素を返す 3. 引数が残っていれば、見つかった要素のdigメソッドに残った引数を渡して呼び出す - digメソッドが呼び出せなければTypeError例外を発生させる ### Polyfill - [dig_rb](https://rubygems.org/gems/dig_rb): Array, Hash, Struct, OpenStructに対応している。Pure Rubyで実装されている。 - [backport_dig](https://rubygems.org/gems/backport_dig): Array, Hash, OpenStructに対応している。Array#dig, Hash#digはC言語による拡張ライブラリとして実装されている。 - [ruby_dig](https://rubygems.org/gems/ruby_dig): Array, Hashに対応している。Pure Rubyで実装されている。 ## rescueを使う ```rb isbn = api_response["identifier"]["ISBN"][0] rescue nil # 見つからなかったら`nil` ``` NoMethodErrorはStandardErrorのサブクラスなのでrescue修飾子で捕捉できる。 rescue修飾子は本来捕捉すべきでない例外まで握りつぶしてしまうので使うべきでないという意見もあるが、HashやArrayへのアクセス程度ならほとんど問題にならないと思う。どうしてもNoMethodErrorだけを捕捉したい場合は`begin`〜`end`で囲んでrescue節を使う必要がある。(rescue修飾子の中で`$!`を参照するという手はあるが、あまりスマートではないように思う。) ## nil#[]を定義する ```rb class NilClass def [](*) nil end end ``` 何も考えずに`result["identifier"]["ISBN"][0]`などと書くことができる。変更が広い範囲に影響するので、予期しない結果を招く恐れがある。 ## deep_fetch系ライブラリを使う `{a: {b: [1]}}.deep_fetch(:a, :b, 0) == 1`のような機能を提供するライブラリがいくつか存在する。 要素へのアクセスに使うメソッド名(`[]`, `fetch`)、要素が見つからなかったときの挙動、HashとArray以外の要素の扱いなどに微妙な違いがあるので、使う前にソースを確認するのがよい。 ### Hashie::Extensions::DeepFetch * [rubygems.org](http://rubygems.org/gems/hashie) * [hashie/deep_fetch.rb · intridea/hashie](https://github.com/intridea/hashie/blob/master/lib/hashie/extensions/deep_fetch.rb) ```rb require 'hashie/extensions/deep_fetch' example = { identifier: { JPNO: ["21976263"], NSMARCNO: ["121144000"], } } # Hash#deep_fetchを定義する Hash.include(Hashie::Extensions::DeepFetch) example.deep_fetch(:identifier, :JPNO) # => ["21976263"] example.deep_fetch(:identifier, :JPNO, 0) # => "21976263" example.deep_fetch(:identifier, :ISBN) # => Hashie::Extensions::DeepFetch::UndefinedPathError example.deep_fetch(:identifier, :ISBN) { "not found" } # => "not found" ``` - 要素へのアクセスには`fetch`メソッドが使われる。 - Hash, Arrayやそれらのサブクラス以外でも、`fetch`メソッドがあれば要素にアクセス可能。 - Integerに変換できない値をキーとしてArrayにアクセスしようとするとTypeError例外が発生する。(`{a: [0, 1]}.deep_fetch(:a, :b) #=> TypeError`) #### モンキーパッチを避ける ```rb # 方法1: 特異メソッドとして追加 example.extend(Hashie::Extensions::DeepFetch) example.deep_fetch("identifier", "JPNO", 0) # 方法2: UnboundMethodをbindする deep_fetch = Hashie::Extensions::DeepFetch.instance_method(:deep_fetch) deep_fetch.bind(example).call("identifier", "JPNO", 0) ``` ### deep_fetch * [rubygems.org](http://rubygems.org/gems/deep_fetch) * [pewniak747/deep_fetch · GitHub](https://github.com/pewniak747/deep_fetch) ```rb # README.mdより抜粋 require 'deep_fetch' example = { :foo => { :bar => [ 'a', 'b', 'c' ], :baz => :boo } } example.deep_fetch(:foo, :baz) # => :boo example.deep_fetch(:foo, :boo) # => KeyError: key not found: :boo example.deep_fetch(:foo, :boo) { "not found" } # => "not found" example.deep_fetch(:foo, :bar, 1) # => 'b' ``` * Hash、Arrayとそれらのサブクラス以外の要素にはアクセスできない。 * Integerに変換できない値をキーとしてArrayにアクセスしようとするとTypeError例外が発生する。(`{a: [0, 1]}.deep_fetch(:a, :b) #=> TypeError`) * 現在の実装では、Arrayにアクセスして得られたオブジェクトが`deep_fetch`メソッドを持っていないとNoMethodErrorが発生する([Pull Request #3](https://github.com/pewniak747/deep_fetch/pull/3))。 * Hashから存在しないキーで値を取ろうとしたときにはKeyErrorが起きる。しかしArrayについては`{a: []}.deep_fetch(:a, 100)`のように存在しないArrayのインデックスにアクセスしたときには単に`nil`が返る。IndexErrorなどの例外は発生せず、ブロックも呼ばれない。 ### vine * [rubygems.org](http://rubygems.org/gems/vine) * [guangnan/vine · GitHub](https://github.com/guangnan/vine) ```rb {a: {b: [:foo, :bar]}}.access("a.b.0") #=> :foo {a: {b: [:foo, :bar]}}.access("a.b.c") #=> TypeError: no implicit conversion of String into Integer {a: {b: [:foo, :bar]}}.access("a.b.1.2") #=> "r" ``` ## hash_mapper Hashから別のHashを作りたいときには[hash_mapper](http://rubygems.org/gems/hash_mapper)が便利。[README](https://github.com/ismasan/hash_mapper#readme)を読めばすぐ使える。 ```rb require 'hash_mapper' require 'date' require 'pp' api_response = {"identifier"=> {"JPNO"=>["21976263"], "NSMARCNO"=>["121144000"], "ISBN"=>["978-4-87783-259-9"]}, "title"=>[{"value"=>"セマンティック・ウェブ入門", "transcription"=>"セマンティック ウェブ ニュウモン"}], "date"=>["2011.3"], "issued"=>["2011"]} # DSL風の記法で、どの要素をどの要素に移し、その際にどのような処理をするかを簡潔に指定できる class NDLBook extend HashMapper map from('/title[0]/value'), to('/title') map from('/identifier/ISBN[0]'), to('/isbn') {|isbn| isbn.gsub("-", "")} map from('/issued[0]'), to('/dates/issued', &:to_i) map from('/date[0]'), to('/dates/date') do |date| Date.parse(date.gsub(".", "/")) end map from('/non-existent/items'), to('/are/just/ignored') end pp NDLBook.normalize(api_response) #{:title=>"セマンティック・ウェブ入門", # :isbn=>"9784877832599", # :dates=> # {:issued=>2011, :date=>#<Date: 2011-03-01 ((2455622j,0s,0n),+0s,2299161j)>}} ``` hash_mapperはHashから値を取り出すとき、クラスを見て細かく場合分けをしたり、`respond_to?`などでチェックしたりせず、どんどん`[]`を適用して`nil`が出るまで掘り進んでいくので、思ったより階層が浅かった場合には例外が出たり、予期しない値が返されることがある(FixnumやStringも`[]`というメソッドを持つことに注意)。 ```rb require 'hash_mapper' class A extend HashMapper map from("/a/b/c"), to("/abc") end p A.normalize({a: 0}) # => .../hash_mapper-0.1.2/lib/hash_mapper.rb:134:in `[]': no implicit conversion of Symbol into Integer (TypeError) ``` ## 参考・その他の方法 * [ruby on rails - Equivalent of .try() for a hash? - Stack Overflow](http://stackoverflow.com/questions/6224875/equivalent-of-try-for-a-hash) * [Looking for a Good Way to Avoid Hash Conditionals in Ruby - Stack Overflow](http://stackoverflow.com/questions/4371716/looking-for-a-good-way-to-avoid-hash-conditionals-in-ruby/4380514) * [Rails - 扱いにくい階層の深いHashをフラット(1階層)にする - Qiita](http://qiita.com/okappy/items/72625a3e9437d70bc7c4) * [Hashie::Extensions::DeepFind](https://github.com/intridea/hashie#deepfind): ネストしたHashやArrayなどから、指定したキーに対応する値を再帰的に探す。 --- <p> This document is licensed under <a rel="license" href="http://creativecommons.org/publicdomain/zero/1.0/">CC0</a>. </p> |
|
| 626位 |
|
|||
|
17:57:19 |
|
|
## 長い前置き
今年(2013年)の3月に、「[使ってはいけない LaTeX のコマンド・パッケージ・作法]」という記事が公開され、LaTeX 使用者の間で少し話題になっているようです。記事の中でも述べられているように、その記事の内容は CTAN で「[l2tabu パッケージ]」として公開されている“An Essential Guide to LaTeX2e Usage”という文書[^l2tabu]からの抜粋となっています。この文書は TeX Live にも含まれていて、`texdoc l2tabuen` を実行すると PDF を読むことができます。 [使ってはいけない LaTeX のコマンド・パッケージ・作法]: http://ichiro-maruta.blogspot.jp/2013/03/latex.html [l2tabu パッケージ]: http://ctan.org/pkg/l2tabu [^l2tabu]: 原版はドイツ語で、英語等の 5 言語の版があります。 しかし、l2tabu は欧米のユーザの間でまとめ上げられた「タブー集」ですので、欧米とは異なる事情をもつ日本人に特有の事項については当然ながら触れられていません。そこで、このような「**日本人のためのタブー集**」を紹介しようと思います。初回となる本記事では画像の取り扱い、すなわち graphicx パッケージの使用に関するタブーを集めました。(なおここの内容は graphicx と graphics パッケージの両方に当てはまります。) 画像の取扱に関しては日本も海外も関係ないじゃないかというと、残念ながらそうはいえません。海外では pdfTeX で一発で TeX 文書を PDF に変換するのが一般的であるのに対し、日本では pTeX 系エンジンを使う関係で、TeX 処理系と DVI ドライバが別になっているので、これが原因のトラブルが起こりやすいのです。加えて「これが最善の方法」という決定打がないため、それぞれ一長一短がある複数の方式が併存していて、これが初級者の混乱の原因となっています。 この問題に対する究極的な解決法は、日本での TeX の使用が「pdfTeX の日本語版」といえる LuaTeX-ja に収斂されることでしょうが、それにはまだ長い時間がかかります。それまでは、従来の DVI ウェアに関するノウハウが必要な状態が続くでしょう。 ## タブー①: dvipdfm って何? dvipdfmx の間違いでしょ。 以上。 ### その dvipdfm は本当に dvipdfm ですか どうしても「いや私は dvipdfm を使いたい!」というならそれを止めはしません。だけど、そういう人には、ちょっと確かめてほしいことがあります。 `dvipdfm --help` を実行してみてください。 ``` This is dvipdfmx-20130405 by the DVIPDFMx project team, ...... ``` もし、こんな風に、`This is dvipdfmx` から始まるメッセージが出たなら、そう、 > あなたが dvipdfm だと思ってるソレは**実は dvipdfmx です**。 これは大事件です。なぜかというと……。 ### dvipdfm と dvipdfmx は互換じゃない dvipdfmx はほぼ dvipdfm の上位互換なのですが、画像の読込に関しては**決定的な非互換性**があります。`\includegraphics` で画像(EPS 以外)を読み込む場合に必要な事前作業(そう、アレです)が異なるのです。 * dvipdfm : **ebb を起動**して `.bb` ファイルを作成。 * dvipdfmx : **extractbb を起動**して `.xbb` ファイルを作成。 ただし、実際には非互換になるケースはレアなので、間違った方を使っていたとしても(ドライバがソレの方に合っていたなら)大抵の場合には問題は起こりません。だからといって、放っておくと、実際にそのレアケースに当たった場合に頭を抱え込むことになってしまいます。 というわけで、「自分が使ってる dvipdfm が実際には dvipdfmx であった」場合は、dvipdfm 用じゃなくて dvipdfmx 用の作業手順に従いましょう……。 ……じゃなくて、素直に、**dvipdfmx という名前の dvipdfmx を使いましょう**。当然、graphicx のドライバ指定も `dvipdfmx` にしましょう。(ドライバオプションについては後で触れます。) ```latex \usepackage[dvipdfmx]{graphicx} ``` ### (補足)dvipdfm 終了のお知らせ <ins>(2015-12-31補足)</ins> 前小節の説明では、「`dvipdfm` コマンドを実行しても dvipdfmx が起動するなら、それは dvipdfmx と全く同じ動作をする」ことを前提にしていましたが、それは自分の事実誤認による誤りでした(すみません)。正しくは以下のようになります。 * 2009年~2013年頃の TeX システムにおいて `dvipdfm` コマンドで起動するのは「dvipdfmx の“dvipdfm 互換モード”」である。 * これを用いる場合は、“dvipdfm 用の作業手順”に従う必要がある。 しかし、2013年頃にまた状況が変わって、現状は以下のようになっています。 * 以前と同じく、`dvipdfm` コマンドで「dvipdfmx の“dvipdfm 互換モード”」が起動する。 * しかし、**graphicx パッケージはもはや dvipdfm をサポートしない**。なので、“dvipdfm 用の作業手順”と“dvipdfmx 用の作業手順”のどちらを用いても正常に動作しない場合がある。 つまり、**dvipdfm は本当に葬り去られた**、ということです。今まで何か理由があって敢えて dvipdfm を使っていた人も、何とかして dvipdfmx に移行する必要があるのです。 詳しくは、以下の記事を参照ください。 * [dvipdfm 終了のお知らせ(ただし2年前)](http://d.hatena.ne.jp/zrbabbler/20150916/1442413624) ## タブー②: \\includegraphics の bb オプション dvipdfmx で(dvipdfm は既に葬り去られました)画像を読み込む場合、画像のサイズ(バウンディングボックス)を `.xbb` ファイルでなくて `\includegraphics` の `bb` オプションで指定する、という方法があります。 ```latex \includegraphics[bb=0 0 480 360,width=10cm]{banana.jpg} ``` わざわざ `.xbb` を作る必要がなくて便利……、確かにそうなのですが、実はこの方法には重大な落とし穴があります。 > bb に指定する数値の**単位は「ポイント」(bp)**である。 もっと端的にいうと。 > **ピクセル単位ではない。** つまり、例えば JPEG 画像 banana.jpg のサイズを調べて 480×360 ピクセルであるからといって、「`bb=0 0 480 360`」と指定するのは必ずしも**正しくない**、ということです。dvipdfm/dvipdfmx において、bb の指定がピクセル単位であったことは過去も含めて一度もありません[^pixel]。 [^pixel]: bbに指定する値は `112mm` のように単位を明示することもできますが、何れにしても、ピクセル単位での指定はできません。 1 bp というのは 1/72 in ≒ 0.3528 mm のこと[^bigpoint]で、要するに「物理的な長さ」です。ということは、正しい bb の値を書くには、その画像の「物理的な寸法」を知っている必要があります。PDF 画像はともかく[^pdf-size]として、PNG や JPEG 等のビットマップ画像の「物理的な寸法」って何でしょう? [^bigpoint]: EPS 時代の慣習を踏んでいるため、TeX のポイント(1 pt = 1/72.27 in)でなく Adobe のポイント(1 bp = 1/72 in)が使われます。 [^pdf-size]: PDF 画像のサイズは一般的に「物理的な寸法」で現れます。 PNG や JPEG の画像形式はビットマップのデータの他に「解像度」(dpi 値)の情報も保持しています(ただしこれはオプションなので無い場合もある)。例えば、480×360 ピクセルの banana.jpg の解像度が 96 dpi であったとします。すると、1 in = 96 px = 72 bp なので、1 px = 0.75 bp という換算になり、これに従うと、banana.jpg の寸法は 360 bp × 270 bp と決まります。 従って、`bb` オプションの正しい指定は次のようになります[^width]。 ```latex \includegraphics[bb=0 0 360 270,width=10cm]{banana.jpg} ``` [^width]: ちなみに、この横幅 360 bp は 12.7 cm に等しいので、上の指定の場合、画像は約 78.7 % に縮小されたことになります。 このように、「解像度」の情報を知れば `bb` に指定すべき正しい値がわかるのですが、残念ながら、多くの人はそれを調べる手段を知らないものと思います。幸いなことに、TeX を使っているならば、正しい値を調べる極めて簡単な方法があります。それは、「extractbb を実行して `.xbb` ファイルを得てその中の `HiResBoundingBox` の値を見る」ことです。 ``` %%Title: ./banana.jpg %%Creator: extractbb 20130405 %%BoundingBox: 0 0 360 270 %%HiResBoundingBox: 0.000000 0.000000 360.000000 270.000000 %%CreationDate: wed Dec 25 00:10:02 2013 ``` これを見ると、 `HiResBoundingBox` は `0.000000 0.000000 360.000000 270.000000` となっているので、これをそのまま `bb` オプションの値に書けば OK です。 ※一般的には、整数値に丸めた `BoundingBox` よりも小数の `HiResBoundingBox` の方が正確でより適切です。今の場合は同じ値ですが[^number]。 [^number]: `360` と `360.000000` に機能の違いはないので、値を書き写す時に無駄なゼロを省いてもかまいません。 いや、実はもっともっと便利な方法があります! **extractbb で生成した .xbb ファイルをそのまま置いておく** と、LaTeX が勝手にその中身を読んでくれます! `bb` の値を書き写す必要すらありません! おお、なんと便利な方法なのでしょう……。最初からこれをやっておけばよかったですね…… ;-) ### (補足) hiresbb オプション 先ほど、「`BoundingBox` よりも `HiResBoundingBox` が望ましい」と書きましたが、graphicx パッケージが `.xbb` ファイルを読む場合には既定では `BoundingBox` が使われます。graphicx のオプションに `hiresbb` を加えると `HiResBoundingBox` の方を使うようになります。 ```latex \usepackage[dvipdfmx,hiresbb]{graphicx} ``` ### (補足) 既定の解像度 PNG や JPEG 画像の解像度情報はオプションなので画像ファイルによっては持っていないこともあります。その場合は、dvipdfmx(および extractbb)は解像度として 72 dpi を仮定します[^dvipdfm-dpi]。ちなみに、この時は 1 px = 1 bp となるので、bb に書く値は(偶然)ピクセル数と一致します。 [^dvipdfm-dpi]: dvipdfm(および ebb)では画像ファイルの持つ情報は無視され、解像度は一律に 100 dpi が仮定されていました。 ### (補足)trim および viewport オプション `\includegraphics` のオプションには、「画像の一部分だけを切り出して出力する」ための trim と viewport というオプションがあり、これらの値は bbox と同じ形式の 4 つの数値で指定されます。これらの数値についても非明示時の単位はポイント(bp)と解釈されます(**ピクセルではない**)。 ## タブー③: ドライバオプションは dvips だが実際に使ってるのは dvipdfmx 「ドライバオプション」というのは、もちろん、graphicx のパッケージオプションに書くアレです。 ```latex \usepackage[dvipdfmx]{graphicx} ``` 大昔ならともかく、今では、graphicx や color の「ドライバオプション」は、用いる DVI ウェア[^dviware]の名をそのまま指定するのが「正解」です。 [^dviware]: pdfTeX などの PDF 出力のエンジンの場合は、便宜的にエンジンを「DVI ウェア」と見なします。 すなわち: * dvips を使うなら `dvips` * dvipdfmx を使うなら `dvipdfmx` * pdfTeX を使うなら `pdftex` * dviout を使うなら `dviout` * xdvi を使うなら `xdvi` (dvipdfm は既に葬り去られました。) 単純明快ですね。 そういうわけで、ドライバオプションと実際の DVI ウェアが一致しないという状態は、少なくとも**イレギュラーである**と認識すべきです。 残念ながら、そういうイレギュラーな指定がどうしても必要な場面(主にドライバのバグ回避のため)は存在しますが、それは要するに**バッドノウハウ**です。 ### (参考)他のパッケージの事情 graphicx 以外のドライバ依存なパッケージでも、最もメジャーな DVI ウェアである dvips/dvipdfmx/pdfTeX については、最近(5 年以内)では、DVI ウェアとドライバオプションは「一致している」のが正解になっています。つまり、もし `dvipdfmx` というドライバオプションが存在しないなら、それは「パッケージが dvipdfmx に正式には対応していない」ということです。この場合、「代わりに `dvips` を指定する」という回避策をとると取り敢えず一部の機能は使えるようになるかも知れません。しかし、その動作は保証されないのはもちろんですし、さらに、「そもそも“間違いの”ドライバが指定されている」ことは想定されていないため、そのせいで思わぬところで不可解な現象が発生するという可能性も**覚悟しておく**必要があります。 dviout に関しては、本体の開発は細々と続けられているものの、周辺の開発をやろうとする人が皆無であるため、最近のパッケージ(PGF/TikZ、animate、media9 等)についての dviout 対応は**全滅**という悲惨な状況です。基本的なパッケージである pict2e ですらアウトです。 私は Windows 屋なので、xdvi についてはよく知りません……。 ## タブー④: dviout(または xdvi)と dvipdfmx の「ドライバ二刀流」 ほとんどの人は、自分がメインに使う DVI ウェアを一つに決めていると思われますが、中には「dviout や xdvi を使って画面上で出力を確認した上で、dvips や dvipdfmx で最終出力(PostScript 文書か PDf 文書)を得る」というアクロバティックな方法を採用している人がいます。この場合、当然ながら、ドライバ指定をどうするかが問題になります。 ただし、dvips との「二刀流」はあまり問題がないようです。というのも graphicx パッケージに関しては dviout も xdvi も比較的 dvips と互換性があるため、ドライバ指定を `dvips` にすれば済むからです。 問題なのは dvipdfmx との「二刀流」です。昔の dvipdfm は比較的 dvips と互換であったようですが、傾向として dvipdfmx は pdfTeX の機能に近づける方向に発展していて、そのため dvips との互換性は悪くなる一方です。結果的に、dviout/xdvi と dvipdfmx の両方で「正しい出力」を得ようとすることを本当に望むなら、使う DVI ウェアを切り替えるたびに文書中のドライバ指定の方も切り替えるという面倒極まる方法を採るしかなくなっています。 実際に、「dvipdfmx との二刀流」を実践している人を見ると、次のうちのどれかを行っているようです。 * 実際に、面倒を惜しまず、ドライバ指定を切り替える。 * ドライバ指定は `dvipdfmx` として、dviout での完全な表示は諦める。 (それで「確認」になるのかは疑問だが。) * なんだかよく解らないバッドノウハウに依拠して苦行を回避する。 何れにしても、少しばかり**マゾな感じ**がします。 どうしてもマゾな道を進みたいというなら止めはしません。でも、他人もマゾであるとは限らないので、dviout/xdvi + dvipdfmx という**「マゾな二刀流」**を他人に勧めるのは差し控えるのが賢明でしょう。 ## タブー⑤: ドライバオプションがない ドライバオプションはオプションなので省略できます。 ```latex \usepackage{graphicx} ``` 特に PDF 出力の LaTeX(pdfLaTeX/XeLaTeX/LuaLaTeX)の場合は通常は自動判別されるので、ドライバオプションは省略するのが普通です。では、DVI 出力の LaTeX の場合、省略した場合の既定の値は何でしょう? 答えは: > **環境依存です。** つまり、TeX ディストリビューションによって、既定値は異なります。TeX Live を含めて多くの環境では `dvips` が既定ですが、`dvipdfmx` が既定になっている環境もあるようです。 DVI 出力で graphicx のドライバオプションを省略している人で、既定値が“何か特定のものである”と思い込んでいた人は、その使用が適切であったかを改めて確認してみてください。特に気を付けてほしいのが、「他人が読むための LaTeX の解説記事を書く」場合です。実際、Web にある解説記事において、「不適切にドライバオプションが省略されている」ケースをよく見かけます。ドライバオプションについて言われなくても解っている中級者を対象としている場合を除いて、たとえ省略が可能なケースであっても、ドライバオプションは明示する方が適切だと思います。 ## 番外編: EPS すべきか PDF すべきか 欧米では、pdfTeX の使用が一般的になり、EPS 形式の画像の使用は非推奨になっています。それに対して、日本では(正確にいえば pTeX 系エンジンを使う場合では)、dvipdfmx を使えば PDF/PNG/JPEG 形式を扱うことができますが、pdfTeX を使う場合と異なり、色々と「弱点」がります。それとは別に、dvips の使用が必須になる場面というのは今でも歴然と存在します。このため、「EPS 形式の使用」が直ちにタブーであるとはいえない状況です。 本記事ではこの話題にはこれ以上踏み込まないこととし、代わりに、次の「2 つの疑問」だけを記しておきます。 * Distiller を持っていないのに dvips を使っている人に: dvips を使う理由はなんですか? * dvipdfmx を使っているのに EPS 画像を使っている人に: EPS 画像を使う理由はなんですか? もちろん、これらの疑問に対する「正当な理由」はいくらでもあります。問題は、「あなたが正当な理由を持っているのか」ということです。「私は EPS 画像の取り扱いに慣れている」というのも立派な理由になるでしょう。しかし、「これから TeX を始める人は恐らくそうではない」ということも気に留めておくといいでしょう。 |
|
| 627位 |
|
|||
|
11:51:18 |
(garbs 所属) |
|
Gitはとても便利だけど、一歩踏み込むと人外魔境だったりしてちょっとこわい。
ので、俺がGitを勉強していて「これは超便利」と感じたものをTIPS的にまとめておくことにする。 ## git add -p ↓こんな感じで起動する。 ``` $ git add [ファイルパス] -p ``` で、何が出来るのかっていうと、 **これからコミットする変更内容を取捨選択したり、編集したりする** ことができる。 たとえば、 ```diff diff --git a/git-add.md b/git-add.md index 356ee59..dbf3428 100644 --- a/git-add.md +++ b/git-add.md @@ -1 +1,5 @@ # git-add + +- add line-a +- add line-b +- add line-c ``` っていう変更があったとして、通常なら全部コミットするか、vimで開いて編集して別のコンソールでコミットした後、編集をロールバックするとか、そういうアナログな感じのことをやらなきゃいけないし、実際俺も最初の頃はそうしていた。 ところがどっこい、Gitにはちゃんとそれを回避する方法がある。 試しに`git add . -p`してみよう。すると、以下のように表示されるはずだ。 ```diff diff --git a/git-add.md b/git-add.md index 356ee59..dbf3428 100644 --- a/git-add.md +++ b/git-add.md @@ -1 +1,5 @@ # git-add + +- add line-a +- add line-b +- add line-c Stage this hunk [y,n,q,a,d,/,e,?]? ``` この時`?`と入力してReturnすることで、簡易的なヘルプを見ることができる。今回は真ん中の`- add line-b`だけをコミットしたかったと仮定する。どうするかというと、おもむろに`e`と入力してReturnするのだ。 すると、突然エディタが立ち上がる。俺の環境だとVimだ。 ```vim 1 # Manual hunk edit mode -- see bottom for a quick guide 2 @@ -1 +1,5 @@ 3 # git-add 4 + 5 +- add line-a 6 +- add line-b 7 +- add line-c 8 # --- 9 # To remove '-' lines, make them ' ' lines (context). 10 # To remove '+' lines, delete them. 11 # Lines starting with # will be removed. 12 # 13 # If the patch applies cleanly, the edited hunk will immediately be 14 # marked for staging. If it does not apply cleanly, you will be given 15 # an opportunity to edit again. If all lines of the hunk are removed, 16 # then the edit is aborted and the hunk is left unchanged. ``` この状態で例えば5行目と7行目を削除して、保存してみる。すると、おもむろに`git-add`を抜けて、通常のコンソールに戻るはずだ。そして、改めて`git diff --cached`を覗いてみよう。 ```diff diff --git a/git-add.md b/git-add.md index 356ee59..577a4b4 100644 --- a/git-add.md +++ b/git-add.md @@ -1 +1,3 @@ # git-add + +- add line-b ``` なんと、さっき消した2つの行が消えているではないか! じゃぁどこに消えたのか?安心して欲しい、ちゃんと編集内容は残っている。`git diff`を見てみよう。 ```diff diff --git a/git-add.md b/git-add.md index 577a4b4..dbf3428 100644 --- a/git-add.md +++ b/git-add.md @@ -1,3 +1,5 @@ # git-add +- add line-a - add line-b +- add line-c ``` つまり、さきほど`e`でやったことは、 **これからコミットしたいものだけを残して保存することで、それらだけを `Changes to be committed` にする** ということだったのだ。 この業を上手く使いこなせば、コミットの効率が飛躍的に伸びるはずだ。 (後大事なのは、Gitを使いこなしてる感があって「俺SUGEEEEE」できるという自己満足に浸れるという点である) |
|
| 628位 |
|
|||
|
23:16:25 |
|
|
# やってみよう Scala!
## 第一部:やってみる前に!(導入編) ### [第1章:Hello Worldから始めるScala](http://qiita.com/items/8d67fb3f211fb3e149db) >プログラムは動かさないと始まらない!最初は動かなくて嵌るかもしれない。 >でも自分の手を動かして書いたソースが動き始めるともっと書きたくなってくるはず! > >手を動かすことに喜びを感じたら、プログラミングの才能があるね! >最初はうまく書けなくても良いんだよ。 >とにかくソースを書いて動かしてみようぜ!! ### [第2章:What is 関数型言語?](http://qiita.com/items/9f0b9cc5c0f28deb48e2) >Scalaの醍醐味は関数型言語とオブジェクト指向言語のハイブリッドだ! > >今回はオブジェクト指向をやっている人には馴染みが薄い、 >関数型言語について語るよ。 ### [第3章:クロージャに Challenge!](http://qiita.com/items/259d3459bcef3bb15c69) > 今回はクロージャです。 >JavaScriptではお馴染みですが、Javaプログラマーにとってはどうでしょうか。 > > 何でJavaプログラマーかって? > meがJavaプログラマーであるからであーる! ### [第4章:But I remember オブジェクト指向 ](http://qiita.com/items/5cbba25f1d4d7c8953e5) > 今回はオブジェクト指向を試してみるよ! > > Scalaはオブジェクト指向もできるんだ! > ソースでの話がメインだよ。 > だからオブジェクト指向プログラミングだ。 ## 第二部:やりながら修行開始!(基本編) ### [第5章:型について語る](http://qiita.com/items/f98e157210c0b83e5842) > 今回から第二部に突入です! > > Scalaを勉強していて気づいたのですが、型、型、かた、カタ、カタカタ・・・ > 型という言葉が登場する頻度が高いんです! > 語るのはそんなに得意では無いですがw、 > もしかして型が重要な要素の一つかも・・・と思い筆を持つ。。 ### [第6章:Scalaのプリミティブ型を語る](http://qiita.com/f81@github/items/c43803f4a8da94615c9c) > プリミティブ型について語ってみる。 > > データ型としては基本だけど、重要だからね! > プリミティブ型とは。。。。。 > 基本型 という表現もするけど、 プリミティブ型 の方が一般的かな? ### [第7章:タプルにチャレンジ!](http://qiita.com/f81@github/items/a8419532c316d190782d) > 第7章も相変わらず 型 について語るぞ! > 今回の主役は、Javaプログラマーには馴染みがない タプル ! > > (Javaプログラマでなくとも気軽に読んでいたきたく思います。。) ### [第8章:Scalaのコレクション(Seq, Set, Map)入門](http://qiita.com/f81@github/items/dc79819d23ce4889d552) > まだまだ型だ! > 今回は複数のインスタンスを束ねる コレクション型 。 > アプリケーションを作るには重要な型だ! > コレクションも奥が深いから、今回は入門と言う形にするよ! ### [第9章:Scalaのトレイト(trait)](http://qiita.com/f81@github/items/5b96af593812286eec49) > まずは トレイト の感じを掴んでみYO! > 色々な意見があるかもしれないけど、トレイトをこういうモノだと思っている。 > > 機能を切りだして、再利用可能な塊にしたモノ ... ### [第10章:Scalaの「Traversable」リファレンス](http://qiita.com/f81@github/items/62ad3c56ce6be271ef35) > 今回はCollection パッケージの「Traversable」ついて、 > リファレンス風に紹介するよ。 > > # scala.collection.Traversable > > このトレイトは、コレクションの中でも上位トレイトで重要なメソッドを持っているんだ。 ### [第11章:ScalaのSeqリファレンス](http://qiita.com/f81@github/items/75c616a527cf5c039676) > # scala.collection.immutable.Seq > > 要素の順序を意識する必要がある場合に、使う箱だ。 > JavaのListと役割は似ているね。 > ただ色んなことができるんだ。 ### [第12章:ScalaのSetリファレンス](http://qiita.com/f81@github/items/115392ec869da2a41530) > # scala.collection.immutable.Set > 集合を現す箱。 > 要素の重複を許さない箱だ。 > > よって、要素の重複をなくしたり、存在判定をするときに使えるね。 ### [第13章:ScalaのMapリファレンス](http://qiita.com/f81@github/items/d0556b20bb00e041a814) > # scala.collection.immutable.Map > キーで値(要素)を特定することができる箱だ。 > キーの重複はできないからね。 > > 代表的なメソッドを使ってみたよ。 ### [第14章:ScalaのOption型とnullを語る](http://qiita.com/f81@github/items/7bca48469d9aea65780d) > Option型って知っているかい?これマジ凄いよ! > > NullPointerException、通称 ヌルポ 。 > 少し込み入ったJavaアプリを作って動かすと、大抵発生するよね。 > ... > Option型を上手く使えれば、コンパイル時にnullチェックみたいなことができてしまうんだ。 > とても素敵だよね! > > では、Option型について語ってみます。 ### [第15章:Scalaの制御構造](http://qiita.com/f81@github/items/b64832468d112b4c9597) > 最近、型り(語り)まくっていたけど、今回は制御構造だよ。 > > Scalaではどんな制御構造があるのでしょうか? > > if、for、while、try、match ...全て語ります。 ### [第16章:Scalaの等価性](http://qiita.com/f81@github/items/a90c16419645be5d7008) > 今回は 等価性 だ。 > > 等価という考え方は非常に大事だから。是非マスターしようぜ! > > 等価を考える上で、大事なことが2つあるんだ。 > 等価性 と 同一性 だ。 ### [第17章:Scalaの演算子](http://qiita.com/f81@github/items/1e72244cdac7ea512bbb) > 今回は 演算子 だ。 > プログラミングの中では基本的なとこみたいだよ。 > サラッと見てみよう! ### [第18章:Scalaのパターンマッチ](http://qiita.com/f81@github/items/aa46c248a38a171ed955) > 今回は パターンマッチ だ!! > こいつ凄い奴なんだぜ。 > > 実は最初、全く使い道がわからなかった。 > ifで良いんじゃね?とか思ってけど、 > ifよりもやれることの幅が広すぎるんだ。 ### [第19章:Scalaでカリー化と部分適用](http://qiita.com/f81@github/items/e8bfab96b4be9e404840) > 突然だけど、実は僕はカリーが大好きなんだ! > だから、Scalaを勉強していて カリー の文字が見えた時、急に親近感が湧いたんだ。 > そう、今回は カリー化 を語ってみるよ。 > JavaScriptでも出てくる話だから、知っている人も多いんじゃないかな? ### [第20章:Scalaでアクター!](http://qiita.com/f81@github/items/55b83d7f4104b1a4dfac) > ついに アクター について語る時がきました。 > Scalaの醍醐味の一つです。そして難しいです。。。 > > でも、アクターを使うと並列処理を扱うことが簡単になります。 > ただアクターには色々な種類があるので、そこが簡単じゃないかも。。。 ### [第21章:Scalaの型パラメータ](http://qiita.com/f81@github/items/7a664df8f4b87d86e5d8) > 皆、元気してた?久々の投稿をさせてもらうね。 > 今回から 応用編 に突入するよ! > > 今回のテーマは、 型パラメータ だ! > こんな感じで語っていくからね。 ### [第22章:Scalaの抽出子](http://qiita.com/f81@github/items/109dbe953dac516bd71a) > 抽出子とは、unapplyメソッドが定義されているオブジェクト のことです。 > 定義としては簡単だね! > > このメソッドの役割は、受け取ったインスタンスを分解して、 何かを抽出することです。 > パターンマッチでこのメソッドが使われることが多いです。 ## 第三部:もっとやれる気がする(応用編) > ご意見募集中!(修行中なので柔軟に対応します!) ## 第四部:ホントに使える技が知りたい(実用編) > ご意見募集中!(修行中なので柔軟に対応します!) |
|
| 629位 |
|
|||
|
16:53:28 |
|
|
[Git Advent Calendar / Jun.](http://qiita.com/advent-calendar/git) 21日目の記事を書かせて頂きます。
今回の記事では、gitのfilter-branchを紹介します。 ## filter-branchとは これは、大量のコミットの書き換えを機械的に行うオプションです。 (filter-branch自体はシェルスクリプトで書かれています。) これを使うとレポジトリの歴史上からコミットされたファイルを完全に抹消することができます! 今回、ファイルを抹消するためにfilter-branchの--index-filterオプションを使います。 ## 使うシチュエーション こんな怖いオプションどこで使うのかというと、例えば下記のようなシチュエーションが考えられます。 * パスワードファイルを間違ってcommitしてしまった or やんごとなき事情により削除したい * 巨大なファイルを間違ってcommitしてしまった 1コミットだけなら良いのですが、何回もcommitされていたり、ブランチを作ってマージとかを繰り返しているとなると、もう手動では削除はできないですよね。 そこで、今回はfilter-branchの--index-filterオプションを使って、全てのcommitを精査して対象のファイルを消します。 ## 実際にやってみる まず、サンプルのgitのレポジトリを作りました。 ブランチ(test_a)も作り、password.txtというファイルに対してcommit & masterにmergeしています。 ~~~ [ master ]$ ls password.txt test.txt [ master ]$ git log --graph --pretty=format:"%h : %s" * bdb9b96 : password 2 * b000e66 : password 1 * e0c5788 : initial commit ~~~ で、後になって**password.txt**を消したいとなったとします。 今回はサンプルなので、そのままやってしまいますが、実際のサービスではまずは、テスト用にcloneしたレポジトリで試してからやってください。 ~~~ [ master ]$ ls password.txt test.txt [ master ]$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch password.txt" --prune-empty -- --all Rewrite b000e661b5267f00a0cd32f9b83514a8e2714f4c (2/3)rm 'password.txt' Rewrite bdb9b9650c5fc1fe3c398672c1f22f86881325e1 (3/3)rm 'password.txt' Ref 'refs/heads/master' was rewritten Ref 'refs/heads/test_a' was rewritten Ref 'refs/remotes/origin/master' was rewritten [ master ]$ ls test.txt [ master ]$ git checkout test_a Switched to branch 'test_a' [ test_a ]$ test.txt ~~~ password.txtは消えてますね! 他のブランチでも消えています。 git log -pしても差分はみれません! よーしこれで完了だ!!。。。。。とは、いきません。 例えば、 ~~~ [ master ]$ git show b000e661b5267f00a0cd32f9b83514a8e2714f4c commit b000e661b5267f00a0cd32f9b83514a8e2714f4c Author: Spring_MT <today.is.sky.blue.sky@gmail.com> Date: Thu Jun 21 15:28:50 2012 +0900 password 1 diff --git a/password.txt b/password.txt new file mode 100644 index 0000000..f3097ab --- /dev/null +++ b/password.txt @@ -0,0 +1 @@ +password ~~~ とかすると、差分が見れてしまいます。。。。 ローカルレポジトリにgitのオブジェクトがまだ残っていることに起因しています。 ~~~ [ master ]$ ls .git/objects/b0/ 00e661b5267f00a0cd32f9b83514a8e2714f4c (object_idの最初の二文字がdirを示す) ~~~ これを消さないと、まだpassword.txtの中身がわかってしまうので、git gcで消しましょう。 ~~~ [ master ]$ rm -rf .git/refs/original/ [ master ]$ git reflog expire --expire=now --all [ master ]$ git gc --aggressive --prune=now Counting objects: 6, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (6/6), done. Total 6 (delta 0), reused 0 (delta 0) [ master ]$ git show b000e661b5267f00a0cd32f9b83514a8e2714f4c fatal: bad object b000e661b5267f00a0cd32f9b83514a8e2714f4c ~~~ これで、レポジトリからpassword.txtとその内容が抹消されました! git gcしているので、オブジェクトもなくなっているので、復旧は**不可能**です。 filter-branchをする時以外でも、commitを消した際は、git gcしておくと良いかと思います。 filter-branchは用法、用量を守ってお使いください。 ### 補足 #### --no-ffでmergeしている場合 mergeコミットがあると、 "error: duplicate parent"となって、git gcしてもオブジェクトが残ってしまいす。 この場合ローカルのデータを消すためには、push(forceオプション付き) -> レポジトリ消す -> clone する必要があります。 |
|
| 630位 |
|
|||
|
19:58:33 |
|
|
#フロントエンド開発今昔物語
フロントエンドと言っても、アプリやゲーム制作などではなく、いわゆるWebサイト制作のクライアントサイド側の話になります。 ##時代背景 ###昔 おじいさんには、Photoshop、Illustrator、Dreamweaverという3種の神器がありました。当時のWebサイト制作は、これらのソフトを利用し、おじいさんが一人で行っていました。おじいさんのような仕事をする人は、Webデザイナーと呼ばれ、とても人気がありました。おじいさんは幸せでした。 ###今 いつしかWebデザインは分業化されるようになりました。おじいさんは久しくデザインをしていません。現在の彼の仕事は、デザイナから受け取ったPhotoshopデータを基にWebサイトを制作することです。それはHTML5サイトというとても都合の良いバズワードでまとめらてしまうこともあります。このことについて、おじいさんはやり場のない憤りを抱いています。 ##HTML/CSSコーディング ###昔 おじいさんはDreamweaverというソフトを使いHTMLやCSSを書いていました。当時はまだNetscapeという名の絶滅種ブラウザまで存在していました。`<table>`レイアウトが翳りを見せ始め、`XHTML + CSS`がにわかに注目され出した頃のお話です。 ###今 おじいさんは、昨今のHTMLやCSSのコーティングに、プログラミングの概念が適用されるようになっていることを知りました。そのようなコーディング開発環境では、いままでは不可能だった、変数の利用や条件分岐、繰り返しといった一般的なプログラミングの利点をコーディングに活用することにより、開発速度を向上させ、品質を維持することができるようになりました。またBoilerplateというテンプレートセットを利用するようになったことも、安定した品質の成果物を制作する手助けとなっています。 ##JavaScript開発 ###昔 おじいさんはJavaScriptを本格的なプログラミング言語とは思っていませんでした。おじいさんの時代からjQueryは人気があるライブラリーでしたので、それなりに動きのあるサイトは作っていましたが、グローバル空間の汚染は日常茶飯事でしたし、ましてやMVCの概念を持ち込もうなどという事は思いもよりませんでした。 ###今 おじいさんは相変わらずjQueryを中心に開発を行っていますが、昨今のJavaScriptの流行から学び、グローバルな領域に変数を列挙してしまうようなことはなくなりました。近い将来はTypeScriptを導入しようと思っています。Backbone.jsやAngular.jsというMVCフレームワークについても関心をもっていますが、これらが彼の手がける案件に必要であったことはいまのところありません。 ##自動化 ###昔 おじいさんには自動化という概念がありませんでした。強いて言えばPhotoshopのアクションやドロップレットを作っていた事くらいでしょうか。 ###今 おじいさんはWebサイト制作に非常に便利な自動化ツールが存在していることを知りました。それはGruntと呼ばれていました。ブラウザのライブリロードや、HTMLやJS、CSSファイルのリントやコンパイル、JS、CSSの結合/圧縮等、いまではこのツールが非常に役に立ってくれています。 ##最適化 ###昔 おじいさんには最適化という概念がありませんでした。すべてのJavaScriptとCSSは開発時と同じ状態で`<head>`タグ内に読み込んでいました。そうすることに何も疑問を抱かなかったのです。 ###今 おじいさんはレンダリングブロックを意識し、これをできるだけ短くするため、JavaScriptとCSSを結合/圧縮するようになりました。画像は適切な単位でスプライトシートとして結合し、さらにPngについてはPngMiniを利用し圧縮をかけています。 ##最後に おじいさんは一昔前までは、Flashと呼ばれるとても活気のあるコンテンツの開発を行っていたのですが、さまざまな理由からFlashというメディアはこの世から徐々に姿を消しつつあります。そのことについて想いを馳せると、おじいさんはとても複雑な気持ちになります。Flashが花形だった当時と比べると、Webのトレンドも随分と様変りをしたものです。それでもおじいさんは、今もなおフロントエンドの開発に携われている自分は幸せだなぁと思っています。 |
|
| 631位 |
|
|||
|
21:50:28 |
(classmethod, Inc. 所属) |
|
#はじめに
iOSシミュレータは、開発の初期やサンプルコードを動かす時によく使用しますが、知らない機能や使い方があったのでまとめてみました。 #iOSシミュレータ? * Xcodeに付属 * アプリケーションのテスト用として設計されている。 ##特徴 * 設計や初期のテストの間に、アプリケーションの主な問題点を発見できる。 * iOSシミュレータでしか使えない開発ツールでアプリケーションをテストできる。(位置のシミュレート、画面描画に関する問題点の発見などが可能) * iOSデベロッパプログラムのメンバーになる前に、Xcodeでの開発方法やiOSの開発環境について学ぶことができる。(AppStoreから無料でダウンロードし、シミュレータ上でアプリを動作させることが可能) * Macがインターネットに接続されていれば、ウェブサイトのレイアウトや動作の確認にも使用できる。(iOSアプリ開発者でなくても使い道はある) ##シミュレータを使う ###1.Xcodeプロジェクトから直接起動し、実行する。 「Xcode scheme」ポップアップメニューからiOSシミュレータ(iPhone Retina (4-inch)など)を選択し、「Run」をクリックします。(プロジェクトがビルドされ、シミュレータが起動し、アプリケーションが実行されます。) ###2.単独で起動する Xcodeを起動し、次のいずれかの方法で実行可能です。 * 「Xcode」>「Open Developer Tool」>「iOS Simulator」コマンドを実行する。 * Controlキーを押しながらDock内の「Xcode」アイコンをクリックし、ショートカットメニューから「Developer Tool」>「iOS Simulator」を実行する。 ##内蔵アプリ シミュレータ内にインストールされたアプリは実機と同じように扱うことができます。 (App Storeから入手したアプリケーションはインストール出来ません。) ###設定アプリ iOSシミュレータには実機と同様に「設定アプリ」が存在しますが、iOSシミュレータの設定画面はテストを意識したものになっています。キーボード、言語設定、アクセシビリティのみ設定を切り替えることができます。 ###その他の標準アプリ * Safari * 写真 * 連絡先 * Game Center * カレンダー (iOS 7のシミュレータのみ) * マップ * Newsstand * Passbook (iPhoneのみ) #シミュレータ上でのジェスチャ操作 実機のようにスムーズにはいきませんがマウスでジェスチャ操作をシミュレートできます。 >|(ジェスチャ)|(デスクトップアクション)| >|:--------------|:----------------------------| >|タップ |クリック。 | >|タッチアンドホールド|マウスボタンまたはトラックパッドを長押し。| >|ダブルタップ |ダブルクリック。 | >|ドラッグ |ドラッグ | >|スワイプ |ドラッグ | >|フリック(はじく |素早くドラッグ。 | >###2本指ドラッグ >1. 2本指ドラッグを発生させようとする位置にポインタを置きます。 >2. Optionキーを押したままにします。 >3. 指でタッチする位置を表す円を、開始位置に移動します。 >4. Shiftキーを押したまま、円を目的の中心位置まで移動してからShiftキー を放すことにより、ピンチターゲットの中心点を移動します。 >5. Shiftキーとマウスボタンを押したまま、ドラッグしたい方向に円を描 くように動かした後、Shiftキーとマウスボタンを離します。 >###ピンチ >1. ピンチを発生させようとする位置にポインタを置きます。 >2. Optionキーを押したままにします。 >3. 指でタッチする位置を表す円を、開始位置に移動します。 >4. Shiftキーを押したまま、円を目的の中心位置まで移動してからShiftキー を放すことにより、ピンチターゲットの中心点を移動します。 >5. マウスボタンを押したまま、その円内から終了位置まで移動した後、 Optionキーを離します。 >###回転 >1. 回転を発生させようとする位置にポインタを置きます。 >2. Optionキーを押したままにします。 >3. 指でタッチする位置を表す円を、開始位置に移動します。 >4. Shiftキーを押したまま、円を目的の中心位置まで移動してからShiftキー を放すことにより、ピンチターゲットの中心点を移動します。 >5. マウスボタンを押したまま、その円を終了位置まで回転した後、Option キーを離します。 #各メニューについて Xcode5に付属しているiOSシミュレータ(バージョン7.0)で増えた機能には(New)をつけてみました。 ##「ハードウェア」メニュー ### デバイス シミュレータ上で動作させるデバイス及びOSを選択できます。 ### 反時計回りに回転、時計回りに回転 デバイスの回転をシミュレートできます。デバイスの向きを変えた時の動作を確認できます。 ### シェイクジェスチャー デバイスを振る操作をシミュレートできます。 ### ホーム、ロック ホーム画面及びロック画面へ切り替えることができます。アプリがバックグラウンドに移行した時の動作をシミュレートしたい時に役に立つでしょう。 ### メモリ警告をシミュレート 最前面にあるアプリケーションにメモリ不足の警告を送信できます。 ### 着信ステータスバーを切り替える 通話中、ナビゲーション実行中、録音中などの状態ではステータスバーが2倍の高さになりますが、この状態をシミュレートできます。ステータスバーの高さが2倍になった際のユーザインターフェイスを確認できます。 ### ハードウェアキーボードをシミュレート 「ハードウェアキーボードをシミュレート」をオンにすることで、キーボードドックや無線キーボード等のハードウェアキーボードをシミュレートできます。(「ハードウェアキーボードをシミュレート」をオンになっている状態では、ソフトウェアキーボードが非表示になります。) ### iOSでOSXと同じキーボードレイアウトを使用 (New) iOSシミュレータ用のキーボードとして、Macのキーボード配列に最も近いものが自動的に選択されます。 ### テレビ出力 テレビ出力をシミュレートできます。 ##「デバッグ」メニュー ### 最前面のAppでスローアニメーションを切り替える アニメーションの速度を落とし、アニメーションに問題がないか確認できます。 ### ブレンドレイヤー (カラー) レイヤが重なり合っている部分を目立つように表示できます。(レイヤが重なっている部分は赤くなります。) 例えばUILabelの背景色を透明(clearColor)にしている場合も赤くなります。 赤い部分をできるだけ減らすことで、描画性能を向上させることができます。 ### コピーイメージ (カラー) 「Core Animation」がコピーした画像の上に青色のオーバーレイが表示されるようになります。 ### 不揃いのイメージ (カラー) ビューのピクセル位置が小数点以下の単位でずれている場合にカラーのオーバーレイを表示させることができます。 ### オフスクリーンレンダリング (カラー) オフスクリーンで描画した領域に、マゼンタをオーバーレイして表示します。 ### システムログを開く... (New) コンソール.appを開くことができます。 ### iCloud同期をトリガー (New) iCloud同期機能のテストが可能です。(シミュレータの設定アプリでiCloudにサインインしておく必要あり。)複数デバイスにまたがるテストも可能であり、実機でのテストを行う前にシミュレータでのテストを行うと良いようです。 なお、iCloudのテストが可能なのはiOS 7.0をシミュレート時のみのようです。 ### 位置 経度緯度を入力し、特定の場所にいるようシミュレートすることができます。 ##「ウィンドウ」メニュー ### 表示サイズ Macの画面上に表示するシミュレータ画面のサイズを変更できます。 ### モーションコントロール (New) (未確認です) #その他 * iOSシミュレータには各種のコピー/ペースト機能が組み込まれており、シミュレータ内、およびシミュレータとMacの間で実行できます。 * 「command+S」でスクリーンショットを保存できます。「control+command+c」でスクリーンショットをペーストボードにコピーできます。 ##シミュレータのバージョン Xcodeのバージョンによって付属するiOSシミュレータのバージョンも異なります。 |Xcodeのバージョン|付属するiOSシミュレータのバージョン| |:--------------|:----------------------------| |Version 4.6.3 |バージョン 6.0 | |Version 5.0.2 |バージョン 7.0 | #参考情報 iOSシミュレータ ユーザーガイド https://developer.apple.com/jp/devcenter/ios/library/documentation/iOSSimulatorUserGuide.pdf |
|
| 632位 |
|
|||
|
03:19:51 |
(シナプス株式会社 所属) |
|
# 基本編
簡略化のために `die` を用いて説明するが、例えば入力フォームでバリデーションを行うとき、 ```php try { if ($name === '') { throw new Exception('名前が未入力です'); } if ($email === '') { throw new Exception('メールアドレスが未入力です'); } } catch (Exception $e) { die($e->getMessage()); } ``` のようなロジックがあるとする。これでは<ins>名前が未入力のときにメールアドレスのチェックを行わない</ins>。それを避けて、どちらもチェックしたいようにするときは ```php $errors = array(); if ($name === '') { $errors[] = '名前が未入力です'; } if ($email === '') { $errors[] = 'メールアドレスが未入力です'; } if ($errors) { die(implode("<br />\n", $errors)); } ``` というように `Exception` を使った処理を諦めている人が多いのではないだろうか?しかしこれだとMVCフレームワークを使った開発では、モデルにバリデーション処理を吸収させづらくなり、渋々コントローラを肥大化させるはめになるのではないかと思う。そこでこれを解決する案を素人なりに考えてみた。 **PHP5.3以降** であれば、 **[Exception::getPrevious()](http://php.net/manual/ja/exception.getprevious.php)** メソッドにより、例外をスタックとして積んでいくことができる。今回はこれを利用する。 毎回 `Exception` のコンストラクタを呼ぶのは面倒なので、関数を作る。この関数ではインスタンスを作るだけで、<ins>スローは行っていない</ins>。 ```php function e($message, $previous = null) { return new Exception($message, 0, $previous); } ``` 更に、例外スタックから順次取り出して配列に変換する関数を作る。スタックの性質故に逆順になることを避ける為、最後に **[array_reverse](http://www.php.net/manual/ja/function.array-reverse.php)** 関数を通している。 ```php function exception_to_array(Exception $e) { do { $errors[] = $e->getMessage(); } while ($e = $e->getPrevious()); return array_reverse($errors); } ``` これらを使って最初のロジックはこう書き換えることが出来る。 ```php try { $e = null; if ($name === '') { $e = e('名前が未入力です', $e); } if ($email === '') { $e = e('メールアドレスが未入力です', $e); } if ($e) { throw $e; } } catch (Exception $e) { die(implode("<br />\n", exception_to_array($e))); } ``` どうだろうか?これだけでも随分MVCに基づいた設計がしやすくなった(と私は思う)。 これを利用して、初心者向けにドットインストールで開講されている講座 **[「ユーザー管理をするWebサービスを作ろう」](http://dotinstall.com/lessons/sns_php_v2)** を自分の納得の行くように書き換えてみた。今回は **「フレームワークを使わないけどフレームワークっぽいことを手軽に実装する」** ことを目標としてみた。 **[GitHub - sns_php](https://github.com/Certainist/sns_php)** # 応用編 ## `$e` の初期化を不要にする 参照渡しにすればNoticeを発生せずに自動的にNULLとして初期化される性質を利用する。 ```php function e($message, &$previous = null) { return new Exception($message, 0, $previous); } ``` ```php try { if ($name === '') { $e = e('名前が未入力です', $e); } if ($email === '') { $e = e('メールアドレスが未入力です', $e); } if (!empty($e)) { throw $e; } } catch (Exception $e) { die(implode("<br />\n", exception_to_array($e))); } ``` ## 自作例外クラスを **Traversable** にする このクラスを継承させると、 `exception_to_array` 関数を通さなくても直接 `$e` をforeachにかけて反復処理させることが出来る。 ```php class TraversableException extends RuntimeException implements IteratorAggregate { public function getIterator() { $e = $this; do { $array[] = $e; } while ($e = $e->getPrevious()); return new ArrayIterator(array_reverse($array)); } } ``` ## **RuntimeException** クラスを **Traversable** にする **[Runkit](http://www.php.net/manual/ja/book.runkit.php)** を用いた超大型反則技。あらゆる実行時例外をforeachを用いて処理できるようになる。これはやってみる価値あるんじゃないかと。 ```php abstract class TraversableException extends Exception implements IteratorAggregate { public function getIterator() { $e = $this; do { $array[] = $e; } while ($e = $e->getPrevious()); return new ArrayIterator(array_reverse($array)); } } runkit_class_adopt('RuntimeException', 'TraversableException'); ``` |
|
| 633位 |
|
|||
|
10:52:18 |
(Nepula,Inc. 所属) |
|
Java8 で ``filter`` や ``map`` が使えるようになったー!
というわけで .NET の LINQ to Objects との対応表を作ってみました。 * LINQ - [Enumerable クラス (System.Linq)](http://msdn.microsoft.com/ja-jp/library/system.linq.enumerable(v=vs.110).aspx) * Java8 - [Stream (Java Platform SE 8 )](http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html) の比較です。 Java の方は * [Collectors (Java Platform SE 8 )](http://download.java.net/jdk8/docs/api/java/util/stream/Collectors.html) も使います。 まだ試したものは少ないので間違ってるかもしれない & カテゴライズが適当 なので、編集リクエストしてもらえるとありがたいです。 | 機能 | LINQ | Java8 | |----------------------|-------------------|----| | **[【基本的なやつ】](#3-1)** ||| | 抽出 | Where | filter | | 射影 | Select | map | | 並べ替え | OrderBy / OrderByDescending | sorted | | 後続を並べ替え(2次ソート条件) | ThenBy / ThenByDescending | n/a | | [平坦化して射影](#3-2) | SelectMany | flatMap | | **[【抽出系】](#3-3)** | | | | n件飛ばす | Skip | skip | | 条件を満たすまで飛ばす | SkipWhile | n/a | | n件まで流す | Take | limit | | 条件を満たすまで流す | TakeWhile | n/a | | **【合成系】** | | | | [連結](#3-4) | Concat | concat | | [積集合](#3-5) | Intersect | n/a | | [和集合](#3-5) | Union | n/a | | [差集合](#3-5) | Except | n/a | | [内部結合](#3-6) | Join | n/a | | [外部結合](#3-7)| GroupJoin | n/a | | 並びを逆にする | Reverse | n/a | | [2つの値を揃えて流す](#3-8) | Zip | n/a | | **【グループ化、集計系】** | | | | [重複を無くす](#3-9) | Distinct | distinct | | [畳み込み](#3-10) | Aggregate | reduce | | [グループ化](#3-11) | GroupBy | Collectors.groupingBy | | [平均](#3-12) | Average | IntStream.average / Collectors.summarizingXXX | | [件数](#3-12) | Count / LongCount | count | | [最大](#3-12) | Max | max | | [最小](#3-12) | Min | min | | [合計](#3-12) | Sum | IntStream.sum / Collectors.summarizingXXX| | 先頭 | First / FirstOrDefault | findFirst | | 終端 | Last / LastOrDefault | n/a | | とりあえず値を得る | | findAny | | [集計用の汎用関数?](#3-11) | | collect | | 1件の値を得る | Single / SingleOrDefault | | | 空なら既定値を返す | DefaultIfEmpty | | | [全データが条件にマッチするか?](#3-5) | All | allMatch | | [いずれかのデータが条件にマッチするか?](#3-5) | Any | anyMatch | | [いずれかのデータも条件にマッチしないか?](#3-5) | | noneMatch | | **【生成系】** | | | | 空っぽ | Empty | empty | | [範囲を生成](#3-1) | Range | n/a | | 繰り返す | Repeat | n/a | | [無限リスト生成](#3-3) | | generate / iterate | | **【その他】** | | | | | SequenceEqual | | | 列挙 | ToList().ForEach | forEach | | なんか Action を挟む(デバッグ用?) | | peek | ううむ、合成系の機能はほとんどないようです…ので自力でやるしか。 以下、サンプル。 ## サンプル LINQ の方は Mac+Mono(Xamarin) で試しています(ぼそり ### 抽出(Where)、並べ替え(OrderBy)、射影(Select) 0〜9 を、偶数値だけ抽出して、降順にソートして、値を10倍して、出力。 ```csharp:C# Enumerable.Range(0, 10) .Where(x => x % 2 == 0) .OrderByDescending(x => x) .Select(x => x * 10) .ToList().ForEach(Console.WriteLine); ``` ```java:Java Arrays.asList(0,1,2,3,4,5,6,7,8,9).stream() .filter(x -> x % 2 == 0) .sorted((x, y) -> y - x) .map(x -> x * 10) .forEach(System.out::println); ``` ```result 80 60 40 20 0 ``` ### 平坦化して射影(SelectMany) 1〜5のリストから、「n×10から始まるn件」のリストを生成。(結果見たほうが分かりやすいな(^_^;) ```csharp:C# Enumerable.Range(1, 5) .SelectMany(x => Enumerable.Range(10 * x, x)) .ToList().ForEach(Console.WriteLine); ``` ```java:Java Arrays.asList(1,2,3,4,5).stream() .flatMap(x -> IntStream.range(x * 10, x * 10 + x).boxed()) .forEach(System.out::println); ``` ```result 10 20 21 30 31 32 40 41 42 43 50 51 52 53 54 ``` ### 抽出系(Take, Skip) 1〜10のリストの3件飛ばして、5件取得。 ```csharp:C# Enumerable.Range(1, 10) .Skip(3) .Take(5) .ToList().ForEach(Console.WriteLine); ``` ```java:Java // 無限リストでも limit あるから大丈夫 Stream.iterate(1, x-> x++) .skip(3) .limit(5) .forEach(System.out::println); ``` ```result 4 5 6 7 8 ``` LINQ には件数でなく条件を指定できる ``TakeWhile`` ``SkipWhile`` がありますが、Java にはなさそうなので ``filter`` で代用しないといけなさそう。 ```csharp:C# Enumerable.Range(1, 10) .SkipWhile(x => x < 4) .TakeWhile(x => x < 9) .ToList().ForEach(Console.WriteLine); ``` ### 連結(Concat) 2つのリストをつなげる ```csharp:C# new int[] { 1, 2, 3 }.Concat(new int[]{ 30, 20, 10 }) .ToList().ForEach(Console.WriteLine); ``` ```java:Java Stream.concat( Arrays.asList(1,2,3).stream(), Arrays.asList(30,20,10).stream()) .forEach(System.out::println); ``` なんで static メソッドやねん…。 ```result 1 2 3 30 20 10 ``` ### 積集合(Intersect)、和集合(Union)、差集合(Except) 積集合:2つのリストから重複をなくす。 和集合:2つのリストをマージする。 差集合:リスト1を基準にリスト2との差分を得る。 ```csharp:C# var list1 = new int[]{1,2,3,4,5,6}; var list2 = new int[]{8,7,6,5,4}; list1.Intersect(list2) .ToList().ForEach(Console.WriteLine); list1.Union(list2) .ToList().ForEach(Console.WriteLine); list1.Except(list2) .ToList().ForEach(Console.WriteLine); ``` ```java:Java // 自力で実現かよw list1.stream().filter(x -> list2.stream().anyMatch(y -> y == x)) .forEach(System.out::println); Stream.concat(list1.stream(), list2.stream().filter(x -> list1.stream().noneMatch(y -> y == x))) .forEach(System.out::println); list1.stream().filter(x -> list2.stream().noneMatch(y -> y == x)) .forEach(System.out::println); ``` ```result 4 5 6 // 積 1 2 3 4 5 6 8 7 // 和 1 2 3 // 差 ``` ### 内部結合(Join) 商品マスタと売上テーブルを INNER JOIN する的な。 ```csharp:C# var master = new [] { new { Id = 1, Name = "Apple" }, new { Id = 2, Name = "Grape" } }; var sales = new [] { new { Id = 1, Sales = 100 }, new { Id = 2, Sales = 200 }, new { Id = 2, Sales = 300 }, new { Id = 3, Sales = 400 }, }; master.Join(sales, outer=>outer.Id, inner=>inner.Id, (o, i) => new { o.Name, i.Sales }) .ToList().ForEach(Console.WriteLine); ``` ```java:Java // 自力 List<Pair<Integer, String>> master = Arrays.asList( new Pair<>(1, "Apple"), new Pair<>(2, "Grape") ); List<Pair<Integer, Integer>> sales = Arrays.asList( new Pair<>(1, 100), new Pair<>(2, 200), new Pair<>(2, 300), new Pair<>(3, 400) ); master.stream() .flatMap(outer -> sales.stream() .filter(inner -> outer.getKey() == inner.getKey()) .map(z-> new Pair<String, Integer>(outer.getValue(), z.getValue()))) .forEach(System.out::println); ``` ```result { Name = Apple, Sales = 100 } { Name = Grape, Sales = 200 } { Name = Grape, Sales = 300 } ``` ### 外部結合(GroupJoin) 商品マスタと売上テーブルを OUTER JOIN する的な。結合先のテーブルに行が見つからなかったものは null になる。 ```csharp:C# var master = new [] { new { Id = 1, Name = "Apple" }, new { Id = 2, Name = "Grape" }, new { Id = 5, Name = "Orange" }, }; var sales = new [] { // Orange は無い new { Id = 1, Sales = 100}, new { Id = 2, Sales = 200}, new { Id = 3, Sales = 400}, }; master.GroupJoin(sales, outer=>outer.Id, inner=>inner.Id, (o, i) => new { o.Name, FirstOfSales = i.Select( x=>(int?)x.Sales).FirstOrDefault() }) // 無かったら null にしたいので null許容型にしてから FirstOrDefault .ToList().ForEach(Console.WriteLine); ``` たぶん普通は First じゃなくて Sum とか使うんだろう。 ```java:Java // これも自力 List<Pair<Integer, String>> master = Arrays.asList( new Pair<>(1, "Apple"), new Pair<>(2, "Grape"), new Pair<>(5, "Orange") ); List<Pair<Integer, Integer>> sales = Arrays.asList( new Pair<>(1, 100), new Pair<>(2, 200), new Pair<>(2, 300), new Pair<>(3, 400) ); master.stream().map(outer->new Pair<String, Optional<Integer>>(outer.getValue(), sales.stream() .filter(inner->inner.getKey() == outer.getKey()) // Id でフィルタ .map(x->x.getValue()) // Sales だけに射影 .findFirst())) // 同一Id中の先頭 .forEach(System.out::println); ``` ```result [.NET] { Name = Apple, FirstOfSales = 100 } { Name = Grape, FirstOfSales = 200 } { Name = Orange, FirstOfSales = } // 相手が居ないやつは null になる [Java] Apple=Optional[100] Grape=Optional[200] Orange=Optional.empty // Option だから empty になるのは良い ``` ### 2つの値を揃えて流す(Zip) 2つのリストの値をひとつずつセットにして流す。 ```csharp:C# var arr1 = new int[] { 1, 2, 3, 4, 5 }; var arr2 = new string[] { "hoge", "fuga", "piyo" }; arr1.Zip(arr2, (x, y) => new {x, y}) .ToList() .ForEach(Console.WriteLine); ``` ```java:Java // FIXME どうやるの? Streams.zip はどこいった? ``` ```result { x = 1, y = hoge } { x = 2, y = fuga } { x = 3, y = piyo } ``` ### 重複を無くす(Distinct) 重複する数値リストから重複をなくす。 ```csharp:C# new int[]{1,3,4,3,2,4} .Distinct() .ToList().ForEach(Console.WriteLine); ``` ```java:Java Arrays.asList(1,3,4,3,2,4).stream() .distinct() .forEach(System.out::println); ``` ```result 1 3 4 2 ``` ### 畳み込み いろいろな集計の素、畳み込み。言語により fold とか reduce とか aggregate とか、いろいろな呼び名がありますね。 よい例が浮かなかったので Max を実装してみました。 ```csharp:C# var max = new int[]{1,5,3,7,2,4} .Aggregate(Int32.MinValue, (x, y) => Math.Max(x, y)); Console.WriteLine(max); ``` ```java:Java int max = Arrays.asList(1,5,3,7,2,4).stream() .reduce(Integer.MIN_VALUE, (x, y) -> Math.max(x, y)); System.out.println(max); ``` ```result 7 ``` ### グループ化 リストの要素をキーにしてグループ化する。Salesは合計を計算する。 ```csharp:C# var sales = new [] { new { Id = 1, Sales = 100 }, new { Id = 2, Sales = 200 }, new { Id = 2, Sales = 300 }, new { Id = 3, Sales = 400 }, }; sales.GroupBy(x=>x.Id, (Id, groupedSales) => new {Id, SumOfSales = groupedSales.Sum( element => element.Sales) // Sales は合計する }) .ToList().ForEach(Console.WriteLine); ``` (LINQ ではありませんが、 ``List.LookUp`` を使って実現することもできるようです → [コメント:2014/03/22 00:29](http://qiita.com/amay077/items/9d2941283c4a5f61f302#comment-82388821b902ad7999b0)) ```java:Java // javafx に Pair があったので Tuple 代わりに使っちゃった List<Pair<Integer, Integer>> list1 = Arrays.asList( new Pair<>(1, 100), new Pair<>(2, 200), new Pair<>(2, 300), new Pair<>(3, 400) ); list1.stream().collect(Collectors.groupingBy(x -> x.getKey())) .entrySet().stream() // group化の結果が Map なので、エントリを Stream 化 .map(x -> new Pair<Integer, Integer>( x.getKey(), // Key が Id に相当 x.getValue().stream().collect(Collectors.summingInt(y->y.getValue())))) // Value が List なのでまた Stream 化して合計を得る .forEach(System.out::println); // Collectors.groupingBy 使わずに Map.merge を使ったほうが分かりやすい気も。。。 list1.stream().collect( () -> new HashMap<Integer, Integer>(), (map, item) -> map.merge(item.getKey(), item.getValue(), (x, y) -> x + y), // 同じキーの値を加算してく (left, right) -> left.putAll(right)) .forEach((k, v) -> System.out.println(k + ":" + v)); ``` Java の方、カオスすぎる…。.NET の ``IGrouping`` を Map でやってるからだな。 ```result [.NET] { Id = 1, SumOfSales = 100 } { Id = 2, SumOfSales = 500 } // ID=2 の Sales が合計されている { Id = 3, SumOfSales = 400 } [Java] 1=100 2=500 3=400 ``` ### 合計(Sum)、最大(Max)、最小(Min)、平均(Average)、件数(Count)、先頭(First)、終端(Last) 集計いろいろ。 ```csharp:C# var list1 = Enumerable.Range(0, 10); Console.WriteLine("Sum={0}", list1.Sum()); Console.WriteLine("Max={0}", list1.Max()); Console.WriteLine("Min={0}", list1.Min()); Console.WriteLine("Count={0}", list1.Count()); Console.WriteLine("First={0}", list1.First()); Console.WriteLine("Last={0}", list1.Last()); Console.WriteLine("Average={0}", list1.Average()); ``` ```java:Java List<Integer> list1 = Arrays.asList(0,1,2,3,4,5,6,7,8,9); IntSummaryStatistics stats = list1.stream().collect(Collectors.summarizingInt(x -> x)); // Max,Min,Count,Average が取得できる System.out.println("Sum=" + stats.getSum()); System.out.println("Max=" + stats.getMax()); System.out.println("Min=" + stats.getMin()); System.out.println("Count=" + stats.getCount()); System.out.println("First=" + list1.stream().findFirst().orElse(-1)); // summarizing では取れない System.out.println("Last=" + list1.stream().sorted((x,y) -> y-x).findFirst().orElse(-1)); // 微妙 System.out.println("Average=" + stats.getAverage()); System.out.println("Average=" + IntStream.range(0, 10).average()); // 型指定 Stream なら average, sum がある(結果は Option に包まれる) ``` ```result Sum=45 Max=9 Min=0 Count=10 First=0 Last=9 Average=4.5 ``` …疲れた。。。 |
|
| 634位 |
|
|||
|
19:32:24 |
|
|
宗教上の理由で削除しました
|
|
| 635位 |
|
|||
|
07:23:25 |
(Gunosy, inc. 所属) |
|
ある日Redisのログを眺めるとこんな子が大量に現れてて焦った。 ```log:redis.log [15487] 04 Aug 21:02:37.523 * 1 changes in 900 seconds. Saving... [15487] 04 Aug 21:02:37.523 # Can't save in background: fork: Cannot allocate memory ``` redis-cli infoしても、使用してるメモリは全体の半分ちょっとだったし、空きは十分にある… 答えは Redis Administration http://redis.io/topics/admin に書いてありました。 ## 原因と解決策 redisのバックアップが走る際、おそらく現状使用している量と同じだけのallocateを要求しているために、redis自体はメモリ使用が50%強だとしても、バックアッププロセスが落ちてしまう模様。 解決は簡単で、Redis Administrationに書いてあるとおり、応急処置的に ```sh: sysctl vm.overcommit_memory=1 ``` を設定して、allocateを許可してあげればいい。 あとは、/etc/sysctl.confへ以下を追記。 ```config: vm.overcommit_memory = 1 ``` ## 見積の2倍は用意しよう 結論として、Redisを運用する際は見積もりして必要だと思われた容量の2倍は出来るだけ用意しといたほうが安定運用できますよ、という話。 |
|
| 636位 |
|
|||
|
11:56:38 |
|
|
## 参考
* [GitHub - fgrehm/vagrant-lxc: LXC provider for Vagrant](https://github.com/fgrehm/vagrant-lxc) ## インストール ```bash: sudo apt-get install -y vagrant-lxc ``` * [Discover Vagrant Boxes | Atlas by HashiCorp](https://atlas.hashicorp.com/boxes/search?provider=lxc) LXCのイメージ一覧 ```rb:Vagrantfile Vagrant.configure("2") do |config| config.vm.box = "debian/jessie64" config.vm.network :forwarded_port, host: 3000, guest: 80 end ``` > config.vm.network :public_network, ip: "192.168.100.10", bridge: 'enp3s0' LXCはpublic_network指定をサポートしていません。 * LXCのtemplateは `/usr/share/lxc/templates/`にあります ## 起動 ```bash: vagrant up --provider=lxc vagrant ssh ``` ### 停止 ```bash: vagrant halt ``` ## box化 ```bash: vagrant package ``` * package.box が生成される。 ## X転送 ```bash: vagrant ssh -- -X sudo apt-get update sudo apt-get install -y xhosts xterm ``` # provision ```rb:Vagrantfile # -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| config.vm.box = "debian/jessie64" config.vm.provision :shell, :path => "provision.sh" config.vm.network :forwarded_port, host: 3000, guest: 80 end ``` ```bash:provision.sh ## locales echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen locale-gen update-locale LANG=ja_JP.UTF-8 ## timezone echo "Asia/Tokyo" > /etc/timezone dpkg-reconfigure -f noninteractive tzdata #apt-get update ``` ```bash:provision実施 vagrant provision ``` |
|
| 637位 |
|
|||
|
15:21:32 |
(スタートアップ 所属) |
|
# Sinatraの使い方あれこれ
## 異なるルートで同じ対応をしたい DRYを実践したい ```ruby require 'sinatra' ['/one', '/two', '/three'].each do |route| get rounte do "Triggered #{route} via GET" end post route do "Triggered #{route} via POST" end end ``` ## RESTリクエストのパラメータを受け取りたい 例えばこんなリクエスト `http://my.server.address/function?key1=value1&key2=value2&key3=value3` こんなふうに受けることができる ```ruby require 'sinatra' get '/function' do params['key1'] params['key2'] params['key3'] end ``` ## 正規表現を用いたルート ```ruby require 'sinatra' get %r{/\A((?:sp|gr)eedy)\z} do |c| "You got cought in the #{c} route!" end ``` ## 別のルートへリダイレクトする ```ruby require 'sinatra' get '/redirect' do redirect 'http://www.google.com' end get '/redirect2' do redirect 'http://www.google.com', 301 end ``` ステータスコード`301`は「Moved Permanently」 デフォルトは`302`の「Temporary redirection」 ## 静的なファイルを見せる ```ruby require 'sinatra' get '/public.html' do end ``` `public`フォルダが使われる。例えば`public`フォルダの中に`javascrpts`フォルダがあるとすると、`http://localhost:4567/javascripts`でアクセスできる。 ## Erbファイルを見せる まず`views`フォルダを作る。 ```ruby require 'sinatra' get '/index' do erb :index end ``` ## Erbファイルに変数を渡す ```ruby require '/home' do @name = 'Random User' erb :home end ``` インスタンス変数にアクセスする。 ```html+erb:home.erb <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Using variables</title> </head> <body> <h1>Hello, <%= @name %>!</h1> </body> </html> ``` 評価したい文は`<%= %>`タグで囲む。ループによる制御構造などは`<% %>`で囲む。 ## 事前・事後に処理したいことがあるとき ```ruby require 'sinatra' before do @before_value = 'foo' end get '/' do "before_value has been set to #{@before_value}" end after do puts "After Filter called to perform some task" end ``` "After Filter called to perform some task" はターミナルに出力される ## 404 Not Foundのカスタマイズ ```ruby require 'sinatra' before do content_type :txt end not_found do "Whoops! You requested a route that wasn't available." end ``` ## 500 Internal Server Errorのカスタマイズ ```ruby require 'sinatra' ... error do "Y U NO WORK?" end ``` ## Session管理 ```ruby require 'sinatra' configure do enable :sessions end before do content_type :txt end get '/set' do session[:foo] = Time.now "Session value set." end get '/fetch' do "Session value: #{session[:foo]}" end ``` セッションをクリアするには ```ruby get '/logout' do session.clear redirect 'fetch' end ``` ## Cookieの利用 ```ruby require 'sinatra' get '/' do response.set_cookie "foo", "bar" "Cookie set. Would you like to <a href=\"/read\">read it</a>?" end get '/read' "Cookie has a value of: #{request.cookies['foo']}." end get '/delete' response.delete_cookie "foo" "Cookie has been deleted." end ``` ## ファイル添付 ```ruby require 'sinatra' before do content_type :txt end get '/attachment' do attachment 'name_your_attachment.txt' "Here's what will be sent downstream, in an attachment called 'name_your_attachment.txt'." end ``` ----- ブログやってます:[PAPA-tronix !](http://weed.cocolog-nifty.com/wzero3es/) |
|
| 638位 |
|
|||
|
03:43:39 |
|
|
※v7がリリースされたため、ちょっと情報が古いです。 [Butter Knife v7への移行](http://qiita.com/yyaammaa/items/408bc3fe429175fd74ff) もあわせて参照してください
Android用のView Injectionライブラリである [Butter Knife](http://jakewharton.github.io/butterknife/) について解説します (といいますか、サイトに書いてあることをほとんどそのまま日本語にしただけです) 。 ## 概要 Butter Knifeは [ActionBarSherlock](http://actionbarsherlock.com/) などでお馴染みの [Square](http://squareup.com) のJake WhartonさんによるAndroid用のView Injectionライブラリです。 ## 使い方 このライブラリの目的が、 * Activity, ViewのfindViewByIdを楽に書く * ViewのonClickListenerなどを楽に書く というシンプルなものなので、簡単に理解できると思います。以下は見慣れたActivityのコードです。 ```java public class MainActivity extends Activity { TextView mTextView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView) findViewById(R.id.activity_main_textview); } } ``` Viewが1つや2つであればよいのですが、増えていくとsetContentViewのあとにfindViewByIdがずらっとならんだ見辛いコードになります。 これを以下のように書くことができます。 ```java public class MainActivity extends Activity { @InjectView(R.id.activity_main_textview) TextView mTextView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.inject(this); // 以下にmTextViewを使ったコード (mTextView.setText()とか)を } } ``` ```@InjectView(int id)``` でViewのResource Idを指定すると ```ButterKnife.inject(Activity)``` で ```mTextView = (TextView) findViewById(R.id.activity_main_textview)``` と同じ処理が行われます。 また、 ```java public class MainActivity extends Activity { Button mButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton = (Button) findViewById(R.id.activity_main_button); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onClickButton(); } }); } void onClickButton() { // ボタンが押されたときの処理 } } ``` findViewByIdして、OnClickListenerをセットして…といういつものButtonの処理も、Buttonの数が増えてくるとたいへん見辛いコードになりがちですが、これも、 ```java public class MainActivity extends Activity { @OnClick(R.id.activity_main_button) void onClickButton() { // ボタンが押されたときの処理 } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.inject(this); } } ``` ButtonのonClick時に呼びたいメソッドに ```@OnClick(int id)``` でViewのResource Idを指定しておくだけでよくなります。 複数のButtonのonClickイベントで共通する処理を書きたい時にも、 ```java @OnClick({ R.id.activity_main_button1, R.id.activity_main_button2, R.id.activity_main_button3 }) void onClickButton(CustomButton button) { if (button.isHoge()) { // ... } } ``` このように書けるので便利です。```@InjectView``` も ```@OnClick``` もViewを継承したクラスであれば使うことができますのでカスタムViewでもOK。 また、Injectionの対象となるViewを指定することもできるので、Fragmentでも、 ```java public class MainFragment extends Fragment { @InjectView(R.id.fragment_main_textview) TextView mTextView; @Override View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_main, container, false); ButterKnife.inject(this, view); // 以下にmTextViewを使った処理を return view; } @Override void onDestroyView() { super.onDestroyView(); ButterKnife.reset(this); } } ``` のように使えます (Fragmentの場合はonDestroyViewでButterKnife.resetを呼んでください)。 同様に、ListViewなどで使うViewHolderパターンも以下のように簡潔に書けます。 ```java public class MyListAdapter extends BaseAdapter { @Override public View getView(int position, View view, ViewGroup parent) { ViewHolder holder; if (view != null) { holder = (ViewHolder) view.getTag(); } else { view = inflater.inflate(R.layout.list_item, parent, false); holder = new ViewHolder(view); view.setTag(holder); } // 以下にViewHolderの中身を使った処理を holder.mTitleView.setText("title"); holder.mNameView.setText("my name"); return convertView; } static class ViewHolder { @InjectView(R.id.title) TextView mTitleView; @InjectView(R.id.name) TextView mNameView; public ViewHolder(View view) { ButterKnife.inject(this, view); } } } ``` その他にも以下を扱うことが出来ます。 * ViewのOnLongClick * ListViewなどのOnItemClick, OnItemLongClick * TextViewのOnEditorAction * CompoundButtonのOnCheckedChanged ## おまけ 同じようなコードをBaseActvityの中やHogeUtilの中に書いたりしている方も多いと思われる ```View#findViewById(int id)``` や ```Activity#findViewById(int id)``` のヘルパーもあります。 ```java View childView = getLayoutInflater().inflate(R.layout.child, null); TextView childText = ButterKnife.findById(childView, R.id.child_text); ImageView childImage = ButterKnife.findById(childeView, R.id.child_image); ``` ## IDEのセットアップ - [Butter Knife](http://jakewharton.github.io/butterknife/) のサイトからjarを落としてきます - jarをlibs以下においてJavaのBuild pathを通して…といういつもの設定をします - Annotation Processingの設定を行います - IntelliJ IDEA / Android Studioの場合はそのまま動くらしい(使っていないので試してない)です。動かない場合は [こちら](http://jakewharton.github.io/butterknife/ide-idea.html) を参照 - Eclipseの場合は [こちら](http://jakewharton.github.io/butterknife/ide-eclipse.html)を参照 - .apt_generated/ に XXX$$ViewInjector.java のようなファイルが生成されていればOKです。生成されていない場合はprojectをcleanしたりIDEを再起動するとうまくいったりします ## ProGuard ProGuardを使う場合は proguard-project.txt などで以下の設定をしておきます。 ``` -dontwarn butterknife.internal.** -keep class **$$ViewInjector { *; } -keepnames class * { @butterknife.InjectView *;} ``` ## 開発の経緯など [Jake WhartonさんのGoogle+の投稿. Mar 6, 2013](https://plus.google.com/+JakeWharton/posts/T5uPgt9FWHz) ``` People really like the concept of view "injection" for Android and it often comes up when talking about Dagger. Last night, I wrote a view injection library which uses annotation processing as a little experiment and half-joke. ``` [Dagger](http://square.github.io/dagger/) について他の開発者と話をしているときにViewのInjectionの話題がよく出てくるので、Annotation Processingで試しにつくってみました、ぐらいの感じでスタートしたようです。 ``` RoboGuice uses an annotation in a different package and reflection at runtime whereas this uses code generation along with a static method call. It uses the same annotation name so people feel at home (similar to how @Inject is a standard). This is for people who use Dagger or Guice (or no dependency injection) but can't bring themselves to write findViewById calls. ``` で、Guice ([RoboGuice](https://github.com/roboguice/roboguice)) との違いは、GuiceはAnnotationのルックアップやフィールドへの値の代入にReflectionを使っている(ので、遅い)けど、Butter Knifeはそうじゃなくでビルド時にコードが生成されるよ、と (Reflectionしているのは `ButterKnife.inject` ぐらい)。 [Jake WhartonさんのGoogle+の投稿. Mar 26, 2013](https://plus.google.com/+JakeWharton/posts/bWawKt8jqyi) ``` Ondřej Kroupa: Seems nice, but what is main advantage to android-annotations library? Jake Wharton: This is a tiny, purpose-built library whereas AA is an application framework. ``` [AndroidAnnotations](http://androidannotations.org/) でも ```@ViewById``` や ```@Click``` を使って同じことができるけど違いは? という質問に対しては、AndroidAnnotationsはいろいろなことができるアプリケーションフレームワークだけど、Butter KnifeはViewのInjectionだけに特化したシンプルなライブラリだよ、と。 他の方がButter Knifeの紹介記事を書かれているのですが、 [5 Reasons You Should Use Butterknife For Android](http://www.thekeyconsultant.com/2013/09/5-reasons-you-should-use-butterknife.html) ``` 3. You find AndroidAnnotations to be overkill for your project. AndroidAnnotations is a cool idea. It has tons of features and more are being added all the time. Depending on which camp you are in, adding features can be both a blessing and curse. If you want to use annotations to inject everything imaginable then definitely give AndroidAnnotations a look. I am not 100% sold on the annotating and injecting of everything just yet. For now views, viewholders, and OnClickListeners is enough for me. ``` AndroidAnnotationsを使ってるならそれで良いし、もし、このプロジェクトはそこまでは必要ないなあと思ったらButter Knifeを使う、という使い分けをすればいいんじゃない、と。 AndroidAnnotationsを使うと、HogeActivityに対してInjectionされたHogeActivity_ というクラスが生成されるので、```startActivity(new Intent(this, HogeActivity_.class))``` と書いたり、AndroidManifest.xmlに ```<activity android:name=".HogeActivity_" />```と書いたりしないといけないのが個人的にはちょっと嫌だなあというのがあります (そういえばAndroidAnnotationsを作った [Pierre-Yves Ricau](https://github.com/pyricau) さんもSquareですね) 私もfindViewByIdをさぼるぐらいならあえて使う必要はないかなあと思ってたのですが、使い始めてみるとこれが大変楽なのでおすすめです。 最後に余談ですが、この "Butter Knife" というネーミングはとても気が利いていて良いなあと思いました。 * Dagger (短刀) はいろいろなことができる。切ったり刺したり削ったり * Butter knife (バターナイフ) はパンにバターを塗ることぐらいしかできない。が、パンにバターを塗るときにはとても便利 ということで、なんでもできる(Dependency Injectorである) [Dagger](http://square.github.io/dagger/) に対して、パンにバターを塗る(ViewをInjectする) ことしかできないけど、その用途であればとても便利なものだよ、という意味でButter Knifeという名前にしたんじゃないかなあと。 [Butter Knife - View "injection" library for Android](http://jakewharton.github.io/butterknife/) |
|
| 639位 |
|
|||
|
00:33:51 |
(Zonu.me 所属) |
|
はいこんにちはこんにちは。プログラミング初心者のZonu.EXEです。 今日は[Ruby Advent Calendar 2012](http://qiita.com/advent-calendar/2012/ruby)の20日めです…? ちょっと前まで自宅警備員で、一個月ほど前からアルバイトでるびーおんれーるずを書くお仕事に就いたのですが、Ruby基礎力が低すぎて困ることが多々あります。 そんなわけで、Rubyをきちんと学ぶために基礎の基礎から調べてみましょか、みたいなテンションで書いてました。 ほんとは字句解析だけでがっつりと書きたかったんですけれど、ねたを集める時間がなかったので枝葉末節がひどいです。 タイトルと内容が合ってない気もするけど気にするな! 全然毛色が違ってるねたが混ざってても気にするな! オムニバス! ##はじめに プログラミング言語で書かれたソースコードを読んで何らかの処理を行ふソフトウェアを「処理系」と呼びます。よくわかんないですけど、何らかの処理をしてくれるんですね。処理系はコンパイラだったり、インタプリタだったりします。 Rubyの文脈においては、プログラミング言語の名前はRuby、処理系は本家の**Ruby**、Javaで書かれた**JRuby**、.NET環境で動くIronRuby、Rubyで書かれた**Rubinius**があります。 まあ、ふつうにふつうの**Ruby**で全然問題ありませんね。 > 本家本元のRubyは言語名と区別するために(コマンド名と同じ)小文字で`ruby`、(Jと対比して)C言語で書かれてるから**CRuby**、Matzが作った処理系だから_Matz Ruby Implementation_ 、縮めて**MRI**と呼ばれたりします。 > いろんな呼びかたがありますね ヾ(〃><)ノ゙ ##コード解析とは 別にそんなに難しいことばを持ち出して警戒する必要もないのですが、要するに書いたコードがどんな意味を持つのかを調べるってだけの話です。 コード解析の種類には**動的解析**と**静的解析**がありますが、別にこれも難しく捉へる必要はないです。「コードだけを見ればわかる」のが**静的解析**、「実際に動かしてみればわかる」のが**動的解析**です。 ただし動的解析にはテストフレームワークなどを使ったテストを含みますが、今回はテストについては触れません。RSpecやTest::Unitを使ったテストのことは、いつかもっとえろいひとがしてくれるのでは。 この記事では静的とか動的って話はあまりしない気がします。 ##環境構築 いまどきMacに標準添付**のRuby1.8**(_賞味期限切れ_)はださいので、ちゃんとしたRubyを入れること。 2012年12月20日現在でいちばんナウい安定版は**Ruby 1.9.3-p327**です。**2.0 preview2**で痛い目に遭っても泣かない子はそっちでもたぶん可。 [OS Xでrbenvを使ってruby 1.9.3の環境を作る](http://qiita.com/items/9dd797f42e7bea674705)や[rbenv & ruby-buildの使い方メモ](http://qiita.com/items/b07beebca21ba7ed8e7f)を参考にすると、たいへん良いですね。 餘談ですが、そらはー師匠の記事で触れられてる**Xcode**の代りに[kennethreitz/osx-gcc-installer · GitHub](https://github.com/kennethreitz/osx-gcc-installer)のパッケージを使っても良さげらしいですね。僕自身は未検証なんですけど。 最近ナウい対話環境は**Pry**なので、`gem install rdoc pry-doc`とかやってください。依存でいろいろgemが入ります。 ##開発環境 **Pry**が入ったので使ってみませう。コマンドは`pry`です。rbenvを使ってる人は`gem install`した後に`rbenv rehash`を忘れないでね。 **Pry**は、Rubyに最初からくっついてきてる**IRB**よりも、さらに強いやつです。色がついたり、キーボードの`TAB`キーでメソッド名補完もできたり、いたれりつくせりですね。 ##Pryでメソッドの説明を読む この記事でわざわざ**Pry**の話から始めたのは、この説明がしたかったからです。 Pryでは`show-doc`コマンドで、メソッドの説明を読むことができます。 ```ruby:`pry` pry(main)> show-doc Ripper.lex From: /Users/megurine/.rbenv/versions/2.0.0-preview1/lib/ruby/2.0.0/ripper/lexer.rb @ line 38: Number of lines: 18 Owner: #<Class:Ripper> Visibility: public Signature: lex(src, filename=?, lineno=?) Tokenizes the Ruby program and returns an Array of an Array, which is formatted like [[lineno, column], type, token]. require 'ripper' require 'pp' p Ripper.lex("def m(a) nil end") #=> [[[1, 0], :on_kw, "def"], [[1, 3], :on_sp, " " ], [[1, 4], :on_ident, "m" ], [[1, 5], :on_lparen, "(" ], [[1, 6], :on_ident, "a" ], [[1, 7], :on_rparen, ")" ], [[1, 8], :on_sp, " " ], [[1, 9], :on_kw, "nil"], [[1, 12], :on_sp, " " ], [[1, 13], :on_kw, "end"]] ``` これ、実際に端末で実行してみてもちゃんと色がつきます。かっこいいですね。 注意なのですが、Rubyレベルではクラスメソッドの呼び出しは、たとへば`String::new`でも`String.new`でも良いのですが、(現在のバージョンのpry-docでは)`show-doc`コマンドの引数としては`String.new`と書かないとだめです。気をつけてくださいね。 > 閑話休題。Pryの実装は読んでないのですが、`method(:'show-doc')`とかやっても`Method`オブジェクトを得られないので、Rubyレベルのメソッドではないコマンドみたいですね。 ##クラスの継承関係を辿る ```ruby:`pry` pry(main)> class Foo pry(main)* nil pry(main)* end => nil pry(main)> Foo.ancestors => [Foo, Object, PP::ObjectMixin, Kernel, BasicObject] ``` `Class.ancestors`メソッドはクラスの継承関係を配列で並べて返します。**Rubyに多重継承はない**ので、常に直線です。 ##オブジェクトのメソッド一覧を調べる ```ruby:`pry` pry(main)> class Z;end => nil pry(main)> z = Z.new => #<Z:0x007f990509cdd8> pry(main)> z.methods => [:pry, :__binding__, :pretty_print, :pretty_print_cycle, :pretty_print_instance_variables, :pretty_print_inspect, … ``` 実際にはもっとたくさんのメソッドが定義されますし、表示することもできます。 じゃあ定義されたメソッドの数を数へみましょっかー、といふときにはどうするのが良いですかね。 ```ruby:`pry` pry(main)> z.methods.size => 63 ``` はい、お手軽にできました。 ここからはちょっとした応用篇。 ##`Fixnum`オブジェクトの`to_xxx`メソッドを列挙したい `Array#grep`メソッドと正規表現で、お望みのメソッド名を抽出してみるのが良さげですね。 ```ruby:`pry` pry(main)> 1.methods.grep(/^to_.*/) => [:to_s, :to_f, :to_i, :to_int, :to_r, :to_c, :to_enum] ``` ##「シンボルにあって文字列にないメソッド」を列挙したい 配列同士の引き算を利用してらどうですかね。 ```ruby:`pry` pry(main)> :''.methods - ''.methods => [:id2name, :to_proc] pry(main)> ''.methods - :''.methods => "[:+, :*, :%, :[]=, :insert, :bytesize, :succ!, :next!, :upto, :index, :rindex, :replace, :clear, :chr, :getbyte, :setbyte, :byteslice, :to_i, :to_f, :to_str, :dump, :upcase!, :downcase!, :capitalize!, :swapcase!, :hex, :oct, :split, :lines, :bytes, :chars, :codepoints, :reverse, :reverse!, :concat, :<<, :prepend, :crypt, :ord, :include?, :start_with?, :end_with?, :scan, :ljust, :rjust, :center, :sub, :gsub, :chop, :chomp, :strip, :lstrip, :rstrip, :sub!, :gsub!, :chop!, :chomp!, :strip!, :lstrip!, :rstrip!, :tr, :tr_s, :delete, :squeeze, :count, :tr!, :tr_s!, :delete!, :squeeze!, :each_line, :each_byte, :each_char, :each_codepoint, :sum, :slice!, :partition, :rpartition, :force_encoding, :valid_encoding?, :ascii_only?, :unpack, :encode, :encode!, :to_r, :to_c, :shellsplit, :shellescape, :shell_split] ``` `Symbol`と`String`は相互変換可能だけど全然別物なんだぜ? みたいな傍證のひとつでした。 ##ソースコードを字句解析してみる **字句解析**(*lexical analysis*)とは、文字列をプログラミング言語のソースコードとして認識するためのプロセスのひとつです。 たとへば `def f(a,b);nil;end` といふ文字の並びを、Rubyはどのように字句解析するのか調べてみませう。 Ruby本体に添付されてゐる**Ripper**といふライブラリを利用することで、ソースコードに対してRubyと同じ字句解析を行うことができます。 **Ripper**は標準添付のライブラリなので、Rubyをちゃんとインストールされた方ならば、特にインストール作業の必要ありません。 `Ripper.tokenize`は、文字列を単純にトークン単位に分解します。 **トークン**とは、意味をもった文字の塊の単位のことです。 そして、`Ripper.lex`は、その切り分けられたトークンの位置と、そのトークンの具体的な意味、つまり何者であるかを調べることができます。 ```ruby:`pry` pry(main)> require 'ripper' => true pry(main)> Ripper.tokenize("def f(a,b);nil;end") => ["def", " ", "f", "(", "a", ",", "b", ")", ";", "nil", ";", "end"] pry(main)> Ripper.lex("def f(a,b);nil;end") => [[[1, 0], :on_kw, "def"], [[1, 3], :on_sp, " "], [[1, 4], :on_ident, "f"], [[1, 5], :on_lparen, "("], [[1, 6], :on_ident, "a"], [[1, 7], :on_comma, ","], [[1, 8], :on_ident, "b"], [[1, 9], :on_rparen, ")"], [[1, 10], :on_semicolon, ";"], [[1, 11], :on_kw, "nil"], [[1, 14], :on_semicolon, ";"], [[1, 15], :on_kw, "end"]] ``` はい、綺麗に分解してくれましたね。`on_kw`は、いはゆる予約語です。 `on_ident`は**識別子**です。この場合での`f`はメソッド名、`a`と`b`は仮引数名と素性はあきらかですが、いつでもそうとは限りません。識別子はメソッド呼び出しかもしれませんし、変数名かもしれません。 ```ruby:`pry` pry(main)> Ripper.lex('a = "foo"') => [[[1, 0], :on_ident, "a"], [[1, 1], :on_sp, " "], [[1, 2], :on_op, "="], [[1, 3], :on_sp, " "], [[1, 4], :on_tstring_beg, "\""], [[1, 5], :on_tstring_content, "foo"], [[1, 8], :on_tstring_end, "\""]] ``` > 話は逸れますが、`(a??a)?:a: :a`や`_?(?:,:'?')`は、まったく有効なRubyのコードです。このコードをRipperで字句解析してみたりするとおもしろいですね。 > あと、実際にこのコードを動作させるにはどんな準備が必要なのかチャレンジしてみませうヾ(〃><)ノ゙☆ ## parsetreeで構文木をダンプする こんな感じのファイルを用意します。 ```ruby:foo1.rb a = "foo" puts a ``` `ruby --dump parsetree ./foo1.rb` みたいにして実行してみませう。 ```zsh % vim foo1.rb % ruby --dump parsetree ./foo1.rb ########################################################### ## Do NOT use this node dump for any purpose other than ## ## debug and research. Compatibility is not guaranteed. ## ########################################################### # @ NODE_SCOPE (line: 3) # +- nd_tbl: :a # +- nd_args: # | (null node) # +- nd_body: # @ NODE_BLOCK (line: 1) # +- nd_head: # | @ NODE_DASGN_CURR (line: 1) # | +- nd_vid: :a # | +- nd_value: # | @ NODE_STR (line: 1) # | +- nd_lit: "foo" # +- nd_next: # @ NODE_BLOCK (line: 2) # +- nd_head: # | @ NODE_FCALL (line: 2) # | +- nd_mid: :puts # | +- nd_args: # | @ NODE_ARRAY (line: 2) # | +- nd_alen: 1 # | +- nd_head: # | | @ NODE_DVAR (line: 2) # | | +- nd_vid: :a # | +- nd_next: # | (null node) # +- nd_next: # (null node) ``` 構文木とは… とか説明を書いてる時間はないのですが、この構文木はCのデータ構造になってます。Ruby1.8以前はこの構文木を辿って実行されるインタプリタでした。 `ruby`に`--dump parsetree`を付けて実行すると、Rubyは実行時の構文木を人間に見やすい形式で出力してくれます。 さて、`NODE_DASGN_CURR`は**代入文**、`NODE_DVAR`は**変数の参照**、それぞれの下にぶら下がってる`nd_vid: :a`が変数名、みたいな感じですね。 じゃあ今度はこんなコード。 ```ruby:foo2.rb def a () return "Foo" end puts a ``` 実行してみると… ``` % ruby --dump parsetree ./foo2.rb ########################################################### ## Do NOT use this node dump for any purpose other than ## ## debug and research. Compatibility is not guaranteed. ## ########################################################### # @ NODE_SCOPE (line: 7) # +- nd_tbl: (empty) # +- nd_args: # | (null node) # +- nd_body: # @ NODE_BLOCK (line: 1) # +- nd_head: # | @ NODE_DEFN (line: 1) # | +- nd_mid: :a # | +- nd_defn: # | @ NODE_SCOPE (line: 3) # | +- nd_tbl: (empty) # | +- nd_args: # | | @ NODE_ARGS (line: 1) # | | +- nd_frml: 0 # | | +- nd_next: # | | | @ NODE_ARGS_AUX (line: 1) # | | | +- nd_rest: (null) # | | | +- nd_body: (null) # | | | +- nd_next: # | | | (null node) # | | +- nd_opt: # | | (null node) # | +- nd_body: # | @ NODE_STR (line: 2) # | +- nd_lit: "Foo" # +- nd_next: # @ NODE_BLOCK (line: 6) # +- nd_head: # | @ NODE_FCALL (line: 6) # | +- nd_mid: :puts # | +- nd_args: # | @ NODE_ARRAY (line: 6) # | +- nd_alen: 1 # | +- nd_head: # | | @ NODE_VCALL (line: 6) # | | +- nd_mid: :a # | +- nd_next: # | (null node) # +- nd_next: # (null node) ``` よく見てくださいよ奥さん、`print a`の行はまったく同じはずなのに、出来上がった構文木では`NODE_DVAR`だった箇所が`NODE_VCALL`になってますよ… おっかないですね… じゃあ、このコードのどこかに`a = "foo"`って足したらどうなるんですかね……? ##VMのバイトコードを読む 先程の章で1.8では構文木を辿って実行されるのだ、と書きましたが、1.9ではさらに通称**YARV**と呼ばれるVMで実行されるバイトコードにコンパイルされて実行されます。 [バイトコード](http://ja.wikipedia.org/wiki/%E3%83%90%E3%82%A4%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89)とは… って、やっぱりja.Wpでも読んだ方が良いですね。 ```ruby:`pry` pry(main)> puts RubyVM::InstructionSequence.compile("puts !true").disasm == disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>========== 0000 trace 1 ( 1) 0002 putself 0003 putobject true 0005 opt_not <callinfo!mid:!, argc:0, ARGS_SKIP> 0007 send <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP> 0009 leave ``` 流れでバイトコードの見方も紹介しましたが、正直僕自身は構文木ほどには心惹かれるものは… けふんけふん。 > ちなみにPythonはバイトコード形式にコンパイルしたものが`.pyc`や`.pyo`の拡張子のファイルとして保存されますが、Rubyにはそれに相当するようなファイル形式は存在せず、毎度毎度バイトコードにコンパイルをしてから実行するようになってます。 > Python方式とRuby方式のメリットデメリットを比較してみるとおもしろいかもしれませんねヾ(〃><)ノ゙☆ ## 参考資料 この記事を書く参考にはあまりしてなくて、ふつうにRubyの勉強に役立つんじゃないかなー、って資料ですねヾ(〃><)ノ゙ ###Rubyソースコード完全解説(RHG) * [『Rubyソースコード完全解説』サポートページ](http://i.loveruby.net/ja/rhg/) えっと、バイブルです。通称:**RHG**。 出版された本ですが全文無料で読めます(書籍は入手困難)。ベースのRubyが古いですが、Rubyの**基礎**([foundation的な意味で](http://togetter.com/li/333205))を理解するには、未だにこれ以上ない金字塔。 ###Rubyist Magazine - YARV Maniacs * [Rubyist Magazine - YARV Maniacs 【第 1 回】 『Ruby ソースコード完全解説』不完全解説](http://jp.rubyist.net/magazine/?0006-YarvManiacs) 若き日のささだこういち先生の書かれた連載記事(の第1回)です。 **RHG**をつまみ食ひするための手がかりとしても、Ruby1.9と1.8以前の差異のまとめとしても、たいへん勉強になります。 |
|
| 640位 |
|
|||
|
20:39:23 |
(Ponksoft 所属) |
|
## AngularJS+PHP+MySQL
AngularJSとPHPを組み合わせたCRUDのサンプルです。なるべくシンプルになるように作ってみました。  ## 全ソースコード [https://github.com/naga3/angular-php-basic](https://github.com/naga3/angular-php-basic) 以下のデータベースとテーブルを作成して、それ以下の3ファイルを入力してindex.htmlを開くと動きます。 ### SQL ```mysql CREATE DATABASE school; USE school; CREATE TABLE students( id SERIAL PRIMARY KEY, name VARCHAR(255), age INT, comment TEXT ); ``` 学生テーブルがひとつだけのシンプルなDBです。 ### HTML ```html:index.html <!doctype html> <html lang="ja" ng-app="app"> <head> <meta charset="utf-8"> <title>名簿</title> </head> <body ng-controller="MainCtrl"> <table border="1"> <tr><th>ID</th><th>名前</th><th>年齢</th><th>コメント</th></tr> <tr ng-controller="DetailCtrl" ng-repeat="student in students"> <td>{{student.id}}</td> <td><input ng-model="student.name"></td> <td><input ng-model="student.age"></td> <td><input ng-model="student.comment"></td> <td><button ng-click="update()">更新</button></td> <td><button ng-click="delete()">削除</button></td> </tr> <tr> <td> </td> <td><input ng-model="new_student.name"></td> <td><input ng-model="new_student.age"></td> <td><input ng-model="new_student.comment"></td> <td><button ng-click="add()">追加</button></td> <td> </td> </tr> </table> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js"></script> <script src="controller.js"></script> </body> </html> ``` ### JavaScript ```js:controller.js var app = angular.module('app', ['ngResource']); app.controller('MainCtrl', function($scope, $resource, $window) { var Student = $resource('students.php', {id: '@id'}); $scope.students = Student.query(); $scope.add = function() { Student.save($scope.new_student, function() { alert("追加しました。"); $window.location.reload(); }); }; }); app.controller('DetailCtrl', function($scope, $window) { $scope.update = function() { $scope.student.$save(function() { alert("更新しました。"); }); }; $scope.delete = function(index) { $scope.student.$delete(); alert("削除しました。"); $window.location.reload(); }; }); ``` ### PHP ```php:students.php <?php $pdo = new PDO('mysql:dbname=school', 'root'); switch ($_SERVER['REQUEST_METHOD']) { case 'GET': $st = $pdo->query("SELECT * FROM students"); echo json_encode($st->fetchAll(PDO::FETCH_ASSOC)); break; case 'POST': $in = json_decode(file_get_contents('php://input'), true); if (isset($in['id'])) { $st = $pdo->prepare("UPDATE students SET name=:name,age=:age,comment=:comment WHERE id=:id"); } else { $st = $pdo->prepare("INSERT INTO students(name,age,comment) VALUES(:name,:age,:comment)"); } $st->execute($in); break; case 'DELETE': $st = $pdo->prepare("DELETE FROM students WHERE id=?"); $st->execute([$_GET['id']]); break; } ``` MySQLのログインユーザーはroot、パスワードは無し(XAMPPのデフォルトの危ないやつ)を想定しています。 ## HTMLの解説 ```html <html lang="ja" ng-app="app"> ``` AngularJSはタグの入れ子構造がそのままスコープとして使うことが出来て直感的です。ng-appの付いている要素内がAngularJSの管轄となります。 ```html <body ng-controller="MainCtrl"> ``` body要素内をコントローラ`MainCtrl`で制御します。 ```html <tr ng-controller="DetailCtrl" ng-repeat="student in students"> ``` tr要素内をコントローラ`DetailCtrl`で制御します。`students`はJavaScript側で設定した変数`$scope.students`を参照します。そこに`students`テーブルの内容が入りますので要素を`$scope.student`に代入しつつループします。 ```html <td>{{student.id}}</td> ``` オブジェクト`$scope.student`のプロパティ`id`をこの場にバインドしています。JavaScript側で`id`の値を変更すると自動的に反映されます。 ```html <td><input ng-model="student.name"></td> <td><input ng-model="student.age"></td> <td><input ng-model="student.comment"></td> ``` テキストフィールドの内容とオブジェクト`$scope.student`のプロパティを双方向バインドしています。テキストフィールドの内容を変更するとプロパティの値が変わるのはもちろん、JavaScript側でプロパティの値を変更しても自動的にテキストフィールドの内容が変わります。 ```html <td><button ng-click="update()">更新</button></td> <td><button ng-click="delete()">削除</button></td> ``` ng-clickでボタンをクリックしたときに呼び出される関数を指定します。 ```html <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js"></script> ``` angular.min.jsがAngularJS本体、angular-resource.min.jsはAngularJSにngResourceという機能を追加します。これはREST APIによりバックエンドとのやりとりをサポートしてくれます。 ## コントローラJavaScriptの解説 ```js var app = angular.module('app', ['ngResource']); ``` モジュール宣言です。ngResourceを使えるようにしています。 ```js app.controller('MainCtrl', function($scope, $resource, $window) { ``` HTML側でbody要素に割り当てたコントローラMainCtrlの中身を定義しています。 `$scope`によってコントローラが定義された場所にスコープを割り当て、データバインディングを可能にします。 `$resource`でngResourceの機能をここから呼び出せます。 ```js var Student = $resource('students.php', {id: '@id'}); ``` REST APIを呼び出すURLを`students.php`とします。`{id: '@id'}`の部分はAPI側に渡されるデフォルトのキーと値です。この設定では渡されるオブジェクトに`id`というキーがあったら自動的にそれが付加されます。 ```js $scope.students = Student.query(); ``` queryメソッドはAPI側をGETで呼び出し配列で結果を受け取ります。ここではstudents.phpから`students`テーブルのレコード全てをJSONで貰い、オブジェクト`$scope.students`に格納します。この`students`プロパティはHTML側の`MainCtrl`配下の`students`とバインディングされます。 ```js $scope.add = function() { Student.save($scope.new_student, function() { alert("追加しました。"); $window.location.reload(); }); }; ``` 追加ボタンを押したときは、saveメソッドによりPOSTでPHPを呼び出し、レコードを挿入してリロードしています。 ```js app.controller('DetailCtrl', function($scope, $window) { ``` HTML側でtr要素に割り当てたコントローラ`DetailCtrl`の中身を定義しています。 ```js $scope.update = function() { $scope.student.$save(function() { alert("更新しました。"); }); }; ``` 更新ボタンを押したときは、該当の行をDBに保存します。`$scope.student.$save()`は`$resource('students.php').save($scope.student)`とほぼ同じです。saveメソッドによりPOSTでPHPを呼び出し、レコードを更新します。 ```js $scope.delete = function() { $scope.student.$delete(); alert("削除しました。"); $window.location.reload(); }; ``` 削除ボタンを押したときは、該当の行をDBから削除しリロードします。`$scope.student.$delete()`は`$resource('students.php').delete({id: $scope.student.id})`とほぼ同じです。deleteメソッドによりDELETEでPHPを呼び出し、レコードを削除します。 ## PHPの解説 バックエンド側は完全にAPI提供に徹するのがナウい作りだそうなので、それに従います。 ```php switch ($_SERVER['REQUEST_METHOD']) { ``` メソッドの種類で分岐します。 ```php case 'GET': ``` `$resource`の`query`メソッドが呼ばれた場合はここに来ます。レコード一覧を返しています。 ```php case 'POST': ``` `$resource`の`save`メソッドが呼ばれた場合はここに来ます。 ```php $in = json_decode(file_get_contents('php://input'), true); ``` `save`メソッドの場合は渡されたパラメータがリクエストボディにJSON形式で入るので配列として取得します。 ```php if (isset($in['id'])) { ``` idが設定されている場合はUPDATE、設定されていない場合はINSERTに分岐します。 ```php case 'DELETE': ``` JavaScript側から`delete`メソッドが呼ばれた場合はここに来ます。指定されたidのレコードを削除しています。 ## まとめ * AngularJSを使うと驚くほどスッキリとデータバインディングの仕組みが記述できることが分かりました。 * エラーチェックは全くやっていません。PHP側でステータスコードを返すのがRESTの作法だと思います。正常の場合もレスポンスの内容がないときは204を返したほうが良いのかも知れません。 |
|
| 641位 |
|
|||
|
15:59:44 |
(コロプラ 所属) |
|
いまだになにがどうしてどうなる、みたいなところが明確ではありません。 ので、把握するという意味でメモを残しておこうと思います。 ## iOS実機にアプリをインストールするために必要なもの * 正規開発者証明書 * デバイス識別子 * アプリケーションID * プロビジョニングファイル ### デバイス識別子 開発中(やアドホック用)の、アプリを動かしたいデバイスの識別子 ### アプリケーションID アプリケーションを識別するID ### プロビジョニングファイル 開発中(やアドホック用)アプリを、iPhone/iPadの実機にインストールするために必要。 プロビジョニングファイルは、`アプリケーションID`、`デバイス識別子`、`正規開発者証明書`の関係をまとめたファイルです。 これを実機に登録しておくことで、条件にあったアプリをインストールすることが可能になります。 ##大まかな流れ 大まかな流れは以下になります。 1. Mac上で公開鍵・秘密鍵を生成し、公開鍵を含めたCSR(Certificate Signing Request - 証明書署名要求)をAppleに送り、 **正規開発者証明書** を発行してもらう ※1 2. 取得後、証明書をMacのキーチェーンに登録する 3. 証明書やアプリID、デバイスなどの情報を元に **プロビジョニングファイル** を作成する ※1...この証明書は、開発用・配布用の1つずつ必要となります。 ### なぜこうする? なんでこんなめんどくさいことをするのでしょうか。 アプリを実機にインストールするには色々な懸念事項を払拭する必要があるのです。 (例えば悪意ある第三者からのアプリを本物と間違えてインストールしないようにしたり、など) そこで、「誰のどのマシンでどのデバイス向けに作られたアプリか」、というのを色々な技術を利用して「本物のアプリである」というのをiOSデバイスに識別させているのです。 発行手順やら登録手順やら色々あって混乱してしまいますが、一言で言えば **信頼のおける相手からのアプリだけインストールできるようにする** ということをあれこれやっている、というわけですね。 ### つまり 以上を前提に必要な情報を見返してみると、「証明書」と「デバイス情報」と「アプリID」を明確にした上で、それらの関係をまとめる「プロビジョニングファイル」を作成し、それを実機に登録することで「本物のアプリを識別する」という内容になるわけです。 仕組みを理解しておくと問題が起きたときに問題箇所を特定するのに役立つので覚えておくといいと思います。 *やっと、プロビジョニングファイルの仕組みを把握できた気がします( ;´Д`)* ## 証明書について これはiOS関係ないですが、CSRとかなんでやるのでしょうか。 自分もまだ本を読んでなんとなく理解したにすぎないですが、公開鍵・秘密鍵だけでは問題があるからのようです。 秘密鍵で暗号化するだけでは、悪意ある第三者になりすましをされる可能性がある、と。 (公開鍵を偽装して渡されたら本物のアプリがインストールできない?) そこでこの鍵の仕組みを、Appleの証明書を利用することで解決しよう、というのがここでの話になります。 つまり、第三者的な位置にAppleが立つことによって、「渡された公開鍵が本物であるという証明をする」というわけです。 (ちょっと具体的なことはまだ理解しきれていないので説明できませんが、要は公開鍵・秘密鍵方式の鍵を証明する、ということが目的です) ・・iOSアプリの申請大変ですね( ;´Д`) ## 参考記事 ちなみにこちらの[iOSアプリのプロビジョニング周りを図にしてみる](http://qiita.com/fujisan3/items/d037e3c40a0acc46f618)という記事に、内部でどういうことをやっているのか、というのを図解してくれていました。 これをしっかり把握できれば、多少のエラーや問題が起きた時は対処できそうな気がします。 ### 手順 ちなみに上記で図解しているものを箇条書きにすると以下の感じ。 1. 自分のPC上で、秘密鍵/公開鍵ペアを生成する 2. CSR(Certificate Signing Request - 証明書署名要求)を、(1)の公開鍵を付けてAppleに送る 3. (2)の公開鍵を含む証明書を作り、認証局の秘密鍵で暗号化 4. (3)の証明書をダウンロードし、キーチェーンへ登録 5. [DevCenter] App IDを登録 6. [DevCenter] Deviceを登録 7. (3), (5), (6)の情報を関連付ける=プロビジョニングファイルの作成(つまりプロビジョニングファイルにはこの3つの情報が格納される) 8. プロビジョニングファイルをダウンロードし登録 9. ビルド設定を行う(BundleID, コードサイニング) 10. ビルド、アーカイブ(ipaファイル作成 ※) ※ ipaファイルにはアプリ本体とプロビジョニングファイルを含み、さらにそれ全体を(1)で生成した秘密鍵で暗号化(コードサイニング) つまり、最初のほうで送った公開鍵がプロビジョニングファイルに含まれ、さらにそれが認証局によって暗号化されています。 この仕組が第三者からの改変を防いでくれる、ということのようです。 |
|
| 642位 |
|
|||
|
02:24:33 |
(giftee inc. 所属) |
|
# Yeomanって何?
フロントの開発に必要なツール郡です。主な機能として、 1. プロジェクトの雛形の作成 (yo) 2. 依存するライブラリの管理 (bower) 3. altJSのコンパイル、css/jsファイルのminify、画像の圧縮などタスク実行(grunt) が行えます。詳しくは下記サイトの「Yeoman(ヨーマン)とは」がとても参考になると思います。 参照:[[Grunt]Yeomanで開発ワークフローを楽にしよう[bower] | Developers.IO](http://dev.classmethod.jp/etc/yeoman/) # node.jsのインストール Yeomanはnpmからインストールするため、node.jsをあらかじめインストールしておく必要があります。 ## mac macでのインストールはhomebrewがおすすめです。 MacOSX 10.9にhomebrewをインストールする場合、下記の記事を参考にしてください。 参照:[Python - MacOSX Mavericks(10.9)にhomebrewを使ってDjangoの開発環境を整える - Qiita [キータ]](http://qiita.com/tetsuya/items/f9a01d6bdea9639aff26) brewのインストールが完了したら、node.jsのインストールを行います。 ``` $ brew install node ``` brewコマンドでnode.jsをインストールするとnpmも一緒にインストールしてくれます。 ``` $ brew install npm Warning: node-0.10.24 already installed ``` ## windows 下記URLから、環境に合わせたインストーラをダウンロードし、インストールを行います。 [Download - Node.js](http://nodejs.org/download/) exeファイルを実行するとnode.jsおよびnpmのインストールが完了します。 ## インストールの確認 node.jsおよびnpmがインストールされた事を確認しましょう。 ``` $ node -v v0.10.24 $ npm -v 1.3.21 ``` これでYeomanをインストールするための環境が整いました。 # Yeomanのインストール ``` $ npm install -g yo ``` これでYeomanのインストールは完了です。 # AngularJS用のYeomanジェネレータのインストール 前述の通り、Yeomanは雛形の作成が行えます。ここでは、AngularJS用の雛形を作成するためのジェネレータをインストールします。 ``` $ npm install -g generator-angular ``` Yeomanでは、Backbone.js用のジェネレータや、Chrome Extensionsを作成するためのジェネレータも用意されています。また、サードパーティ製のgeneratorも数多くあるようです。 # AngularJSアプリの作成 ``` $ mkdir firstapp $ cd firstapp $ yo angular ``` # Gruntサーバ起動の確認 Gruntのタスクは、yoで自動生成されたGruntfile.jsに定義されています。その中には、静的サーバを立ち上げるタスクの記述もされています。 ``` $ grunt serve ``` しばらくするとブラウザが立ち上がり、以下のような画面が表示されます。これで、Yeomanのインストールは完了です。  # AngularJSアプリの拡張 開発を進めて行く中で、アプリが大きくなるにつれcontrollerを増やしたり、serviceにロジックを分割が必要になってきます。 AngularJS用のYeomanジェネレータには、それぞれ専用のジェネレータが用意されいます。 参照:[yeoman/generator-angular · GitHub](https://github.com/yeoman/generator-angular#generators) それでは、serviceをひとつ追加してみましょう。 ``` $ yo angular:service myService create app\scripts\services\myservice.js create test\spec\services\myservice.js ``` ```index.html <!-- build:js({.tmp,app}) scripts/scripts.js --> <script src="scripts/app.js"></script> <script src="scripts/controllers/main.js"></script> <script src="scripts/services/myService.js"></script><!-- ← 追加されている --> <!-- endbuild --> ``` # Bowerでのパッケージ管理 開発を進めて行く中でライブラリの追加が必要になったら、まずはbowerで提供されていないか検索してみましょう。 今回の例ではFont Awesomeを探してみます。 ## ライブラリの検索 ``` $ bower search awesome Search results: font-awesome git://github.com/FortAwesome/Font-Awesome.git components-font-awesome git://github.com/components/font-awesome.git font-awesome-more git://github.com/hectorcenteno/Font-Awesome-More.git awesomecloud git://github.com/rajenms/jQuery.awesomeCloud.plugin.git Leaflet.awesome-markers git://github.com/lvoogdt/Leaflet.awesome-markers.git underscore-awesomer git://github.com/kmalakoff/underscore-awesomer.git font-awesome-animation git://github.com/l-lin/font-awesome-animation.git awesome-form git://github.com/blakeembrey/awesome-form.git myawesomepackagename git://github.com/tomandhisjones/Reddit-Upvoter ``` いくつか見つかりました。 ## ライブラリの追加 それでは、ライブラリのインストールを行いましょう。 ``` $ bower install --save font-awesome ``` bower.jsonに依存関係を記述するために、`--save`オプションをつけます。 追加されているかどうかは、bower.jsonを見ることで確認できます。 ```bower.json { "name": "tokubai-info", "version": "0.0.0", "dependencies": { "angular": "1.2.6", "json3": "~3.2.6", "es5-shim": "~2.1.0", "jquery": "~1.10.2", "bootstrap": "~3.0.3", "font-awesome": "~4.0.3" //← 追加されている ※1 }, "devDependencies": { "angular-mocks": "1.2.6", "angular-scenario": "1.2.6" } } ``` ## ライブラリの削除 追加するライブラリを間違えました。bowerからfont-awesomeは使えないので削除します。※2 ``` $ bower uninstall --save font-awesome ``` `--save`でbower.jsに追加した記述は同じく`--save`オプションをつけることで削除できます。 ## htmlからライブラリの読み込み ``` $ bower install --save components-font-awesome $ grunt bowerInstall ``` `grunt bowerInstall`を行うことで、外部CSSやJavaScriptを読み込むタグを記述してくれます。 追加されているかどうかは、appディレクトリ下のindex.htmlを見ることで確認できます。 ```app/index.html <!-- bower:css --> <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css" /> <link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css" /><!-- ← 追加されている --> <!-- endbower --> ``` CSSは以下の間に ```app/index.html <!-- bower:css --> <!-- endbower --> ``` JSは以下の間に ```app/index.html <!-- bower:js --> <!-- endbower --> ```` 追記されます。しかし、読み込む順番までは考慮してくれないので、必要に応じて手修正する必要があります。 # バージョン管理 Yeomanで自動生成したプロジェクトには.gitignoreが含まれています。 ```.gitignore node_modules dist .tmp .sass-cache app/bower_components ``` npmやbowerで管理されているライブラリ群、リリース用のディレクトリやtmpディレクトリがgit管理から外されているようです。これは、このままで問題がなさそうなので用意しておいたgithubのレポジトリにpushまで行ってみましょう。 ``` $ git init $ git add -A $ git commit -m "first commit" $ git remote add origin git@github.com:tetsuya/firstapp.git $ git push -u origin master ``` # 共同開発 Yeomanで生成したプロジェクトは、node.jsとYeomanのインストールが完了している環境であれば、 ``` $ git clone git@github.com:tetsuya/firstapp.git $ cd firstapp $ npm install & bower install ``` # リリース用ファイルの生成 ``` $ grunt build ``` 実行後`dist`ディレクトリが作成されます。この中身をドキュメントルート下に配置することでデプロイが完了します。 # 補足 ## ※1 便宜上コメント行に「//」を使っていますが、jsonとして正しくありません。 参照:[JSONにはコメント行が付けられない?ネットで見つけた方法の有用性を試してみた | Developers.IO](dev.classmethod.jp/etc/adding-comment-to-json/) ## ※2 font-awsomeをインストールした状態で`grunt bower-install`を実行すると下記のようなエラーメッセージが表示されます。 >font-awesome was not injected in your file. >Please go take a look in "app\bower_components\font-awesome" for the file you need, then manually include it in your file. これはfont-awesome内にbower.jsが含まれていないためです。 >There are many packages managers... More than for each language ! And for each one you need a mirror repository with built *.js. > >Components is here to centralize this, having only one repository by project, supporting every package manager there is and keeping project repositories up-to-date. >[components/components · GitHub](https://github.com/components/components) bowerをはじめとするパッケージ管理ツールは数多く存在しますが、ツールごとに異なる`.js`ファイルが必要となります。そこで、Componentsという様々なパッケージ管理ツールに対応した版の配布を行っている方を利用します。 >The Bower repositories try to have a standardized name of components-*. >[components/components · GitHub](https://github.com/components/components) 原則として、bowerのレポジトリは`components-`から始まるようになっています。 |
|
| 643位 |
|
|||
|
15:29:03 |
|
|
# neocomplcache.vim とは 入力補完機能を提供する Vim のプラグイン。 GitHub で公開されている。 [Shougo/neocomplcache.vim](https://github.com/Shougo/neocomplcache.vim) なお、 neocomplcache は "neo-completion with cache" の略。 # 操作イメージ 文字を入力すると、自動で補完候補が表示される。 (下の絵の水色の文字が補完候補。)  文字を入力する毎に、補完候補が絞られていく。  そして補完候補を選択すると、 neocomplacache が残りの文字を自動で入力してくれる。  # 導入方法 NeoBundle を導入済みの場合は、まずは以下を .vimrc に記述し、 Vim を起動する。 NeoBundle 'Shougo/neocomplcache' そしてノーマルモードで以下のコマンドを実行すると、 neocomplcache.vim をインストールできる。 :NeoBundleInstall # 設定 以下は、私の .vimrc に書いてある neocomplcache.vim の設定。 neocomplcache.vim の README に書いてある設定をほぼそのまま利用した。 [Shougo/neocomplcache.vim](https://github.com/Shougo/neocomplcache.vim) ```vim:.vimrc "" neocomplcache NeoBundle 'Shougo/neocomplcache' " Disable AutoComplPop. let g:acp_enableAtStartup = 0 " Use neocomplcache. let g:neocomplcache_enable_at_startup = 1 " Use smartcase. let g:neocomplcache_enable_smart_case = 1 " Set minimum syntax keyword length. let g:neocomplcache_min_syntax_length = 3 let g:neocomplcache_lock_buffer_name_pattern = '\*ku\*' " Define dictionary. let g:neocomplcache_dictionary_filetype_lists = { \ 'default' : '' \ } " Plugin key-mappings. inoremap <expr><C-g> neocomplcache#undo_completion() inoremap <expr><C-l> neocomplcache#complete_common_string() " Recommended key-mappings. " <CR>: close popup and save indent. inoremap <silent> <CR> <C-r>=<SID>my_cr_function()<CR> function! s:my_cr_function() return neocomplcache#smart_close_popup() . "\<CR>" endfunction " <TAB>: completion. inoremap <expr><TAB> pumvisible() ? "\<C-n>" : "\<TAB>" " <C-h>, <BS>: close popup and delete backword char. inoremap <expr><C-h> neocomplcache#smart_close_popup()."\<C-h>" inoremap <expr><BS> neocomplcache#smart_close_popup()."\<C-h>" inoremap <expr><C-y> neocomplcache#close_popup() inoremap <expr><C-e> neocomplcache#cancel_popup() ``` 上記設定の場合、 insert モードで以下の操作ができる。 | キー | 操作 | |----------|-------| | \<Tab\> | 補完候補の選択。 | Ctrl + g | 前回行われた補完をキャンセル。補完した文字を消す。 | Ctrl + l | 補完候補の中から、共通する部分を補完。 各設定の意味は、以下のページで詳しく説明されている。 [Vim-users.jp - Hack #177: neocomplcacheの設定について知る 前編](http://vim-jp.org/vim-users-jp/2010/10/17/Hack-177.html) [Vim-users.jp - Hack #185: neocomplcacheの設定について知る 中編](http://vim-jp.org/vim-users-jp/2010/11/27/Hack-185.html) [Vim-users.jp - Hack #193: neocomplcacheの設定について知る 後編](http://vim-jp.org/vim-users-jp/2011/01/06/Hack-193.html) # 参考 * [Vim初心者によるVim導入記録(neocomplcache設定編) | Code Life](http://code-life.net/?p=2308) * 設定(.vimrc) の説明が分かりやすい |
|
| 644位 |
|
|||
|
15:40:02 |
(フリーランス 所属) |
|
Rails 4.0.0がリリースされたので今参加しているプロジェクトのアプリのアップデートが出来るかを試してみました。
やってみた結果としては、2.3 -> 3.0や、3.0 -> 3.1に比べると大分楽にバージョンアップできる感じです。 たまたま今のプロジェクトで引っかかった問題について、メモがてらまとめておきます。 ## rake rails:update diffを見ながらconfigファイルを書き換える。 そんなに大きな変更は無い。 rails4からはinitializerに色々移せって感じで、application.rbが軽量化されてるけど、元のままでも別に問題無い。 設定項目の重複だけ注意。 ## strong_parameters 元々、gemで先行導入して取り入れていたので概ね問題無く移行できました。 attr_accesibleを利用したセキュリティ対策は、外部のgemに分離されているため、 `config.active_record.whitelist_attributes = false`が残っていると警告されます。 attr_accesibleを使いたい場合は、protected_attributesというgemで対応できますが、素直にstrong_parametersに移行する方が良いんじゃないでしょうか。 気を付けておきたいのはdeviseです。 deviseのカスタムコントローラー等を利用していると、strong_parametersの対応を忘れてしまって、いきなりログインできなくなったり、ユーザー登録出来なくなったりします。 というか私は、いきなりテストが落ちてしばらく悩みましたw 後、rails_adminはとりあえず大丈夫でした。 ## ActiveRecordの関連の記述方法 has_many等の関連は、conditionsというオプションでベースとなるレコードの情報を利用して条件指定を追加できる機能がありましたが、deprecatedになっています。 こんな感じで書けって事らしい。 ```rb: # before has_many :comments, conditions: ->(record) { ["comments.writer_id = ?", record.owner_id] } # after has_many :comments, ->(record) { where(writer_id: record.owner_id) } ``` ## ActiveRecordのfinderメソッドとSunspot SunspotというSolrから検索した結果とActiveRecordを連携させるgemを利用しています。 Rails4で動作こそしますが、内部で`scoped`を利用しているため、deprecatedがガンガン出ます。 Rails4からは`all`を使うのが正しいですね。 現時点(2013/7/8)でpull requestは既に上がっていますが、まだマージされていないので、ちゃんと直るのはもうちょっと先じゃないかな。 わざわざフォークするのも面倒だし、様子見中です。 ## ActiveRecordのincludesで読み込んだ関連の値を利用するためのreferencesメソッド 今まで以下のような感じでincludesでeager loadingした関連レコードの値をメソッドチェインで利用して、検索条件に含める事ができました。 ```rb: Comment.includes(:post).where("posts.published = ?", true) ``` 4.0.0ではこの記述はdeprecatedとなり、includesで読み込んだレコードの値を利用したい時は明示的にreferencesを呼ぶようになりました。 ```rb: Comment.includes(:post).where("posts.published = ?", true).references(:posts) ``` 以前のままだとSQLのパーサー書くのが大変になるかららしいです。 ちょっと面倒な気がする。 これの影響で、Sunspotでまたdeprecatedが出ます。 <del>## find_by_xxxメソッド無くなった</del> (ぐあっと書いたら嘘書いてたので修正) <del>これは、一切使ってなかったので問題無し。<del> ## find_all_by_xxxとかがdeprecatedになった。 これは、一切使ってなかったので問題無し。 `find_by`を使うようにする。 ## rspecでトランザクションが上手く動かない rspecとdatabase_cleanerを利用しているのですが、トランザクションのロールバックを期待して記述していたテストケースが急に落ちるようになりました。 どうもfixtureの読み込み処理が修正されたのが原因らしいです。 rspec v2.14 database_cleaner githubのmaster を利用していますが、詳細まで追っかけてないので詳しくは不明です。 どなたかご存知でしたら解決策を教えてください…。 追記 (2013/7/9) @yuki24 さんのコメントを受けて、再現するテストケースとログを用意してみました。 データベースはmysql2です。 v4.0.0: https://github.com/joker1007/transaction_test v3.2.13: https://github.com/joker1007/transaction_test3 4.0.0 のtest.log ``` [1m[36mActiveRecord::SchemaMigration Load (0.2ms)[0m [1mSELECT `schema_migrations`.* FROM `schema_migrations`[0m [1m[35m (0.2ms)[0m SELECT @@FOREIGN_KEY_CHECKS [1m[36m (0.1ms)[0m [1mSET FOREIGN_KEY_CHECKS = 0[0m [1m[35m (0.1ms)[0m SELECT DATABASE() as db [1m[36m (0.3ms)[0m [1mselect table_name from information_schema.views where table_schema = 'transaction_test_test'[0m [1m[35m (5.2ms)[0m TRUNCATE TABLE `posts`; [1m[36m (0.1ms)[0m [1mSET FOREIGN_KEY_CHECKS = 1[0m [1m[35m (0.1ms)[0m BEGIN [1m[36m (0.1ms)[0m [1mCOMMIT[0m [1m[35m (0.0ms)[0m BEGIN [1m[36m (0.2ms)[0m [1mSELECT COUNT(*) FROM `posts`[0m [1m[35mSQL (0.2ms)[0m INSERT INTO `posts` (`created_at`, `title`, `updated_at`) VALUES ('2013-07-09 05:08:29', 'title0', '2013-07-09 05:08:29') [1m[36mSQL (0.1ms)[0m [1mINSERT INTO `posts` (`created_at`, `title`, `updated_at`) VALUES ('2013-07-09 05:08:29', 'title1', '2013-07-09 05:08:29')[0m [1m[35mSQL (0.1ms)[0m INSERT INTO `posts` (`created_at`, `title`, `updated_at`) VALUES ('2013-07-09 05:08:29', 'title2', '2013-07-09 05:08:29') [1m[36m (0.1ms)[0m [1mSELECT COUNT(*) FROM `posts`[0m [1m[35m (0.8ms)[0m ROLLBACK ``` 3.2.13 のtest.log ``` Connecting to database specified by database.yml [1m[36m (0.1ms)[0m [1mSELECT @@FOREIGN_KEY_CHECKS[0m [1m[35m (0.1ms)[0m SET FOREIGN_KEY_CHECKS = 0 [1m[36m (0.1ms)[0m [1mSELECT DATABASE() as db[0m [1m[35m (0.2ms)[0m select table_name from information_schema.views where table_schema = 'transaction_test3_test' [1m[36m (5.7ms)[0m [1mTRUNCATE TABLE `posts`;[0m [1m[35m (0.1ms)[0m SET FOREIGN_KEY_CHECKS = 1 [1m[36m (0.1ms)[0m [1mBEGIN[0m [1m[35m (0.1ms)[0m SAVEPOINT active_record_1 [1m[36m (0.1ms)[0m [1mRELEASE SAVEPOINT active_record_1[0m [1m[35m (0.0ms)[0m BEGIN [1m[36m (0.1ms)[0m [1mSELECT COUNT(*) FROM `posts` [0m [1m[35m (0.1ms)[0m SAVEPOINT active_record_2 [1m[36mSQL (0.1ms)[0m [1mINSERT INTO `posts` (`created_at`, `title`, `updated_at`) VALUES ('2013-07-09 05:07:08', 'title0', '2013-07-09 05:07:08')[0m [1m[35mSQL (0.2ms)[0m INSERT INTO `posts` (`created_at`, `title`, `updated_at`) VALUES ('2013-07-09 05:07:08', 'title1', '2013-07-09 05:07:08') [1m[36mSQL (0.1ms)[0m [1mINSERT INTO `posts` (`created_at`, `title`, `updated_at`) VALUES ('2013-07-09 05:07:08', 'title2', '2013-07-09 05:07:08')[0m [1m[35m (0.1ms)[0m ROLLBACK TO SAVEPOINT active_record_2 [1m[36m (0.1ms)[0m [1mSELECT COUNT(*) FROM `posts` [0m [1m[35m (0.7ms)[0m ROLLBACK [1m[36m (0.1ms)[0m [1mROLLBACK[0m ``` 色々試してみた所、`DatabaseCleaner.strategy`が`:truncation`だと4.0.0でも正常に終了するようです。 `:trunsaction`時はsavepointを利用する事でARのtransactionを実現していた部分が、4.0.0で呼ばれなくなってる事が、期待通りの結果にならない理由のようです。 引っかかった点といえば、こんな所です。 |
|
| 645位 |
|
|||
|
21:05:27 |
|
|
## 複数カーソルとは Sublime Text2では複数のカーソルを出して複数の箇所を同時に編集できます。 その基本的な使い方をまとめました。 ## ソースコード内の複数の単語を変更する [公式ページ](http://www.sublimetext.com/)の1番目の例です。複数箇所に存在する単語を別の文字列に置き換えてみます。 全てのdouble型の変数をfloat型に変換してみます。 ```C#:編集前 public class Test { private double aaa; private double bbb; public double ccc; } ``` ```C#:編集後 public class Test { private float aaa; private float bbb; public float ccc; } ``` 1. 変更したい対象の単語(double)にカーソルを合わせて**Cmd+D**を押す 2. この状態で編集対象の選択モードになる。**Cmd+Ctrl+G**で同じ単語をすべて選択する 3. すべての"double"に入力カーソルが作成されるので"float"を入力する  ### tips * escで単一のカーソルに戻る * **Cmd+D**でフォーカスが当たっている要素を選択状態にして次の要素へ(**Cmd+Shift+G**で前の要素へ) * **Cmd+L**でカーソルの1行を選択状態にする * **Cmd+D**で単語を選択状態にする * **Cmd+G**でフォーカスを移動(単一の選択のみ) * **Cmd+Ctrl+G**でフォーカスが当たっている単語と同じ単語をすべて選択 * **Shift+Ctrl+UP**で上の行を選択状態にする * **Shift+Ctrl+DOWN**で下の行をを選択状態にする ## 複数行をまとめて編集する+α [公式ページ](http://www.sublimetext.com/)の2番目の例です。改行されている複数の文字列の前後にダブルクォートを追加し、コンマを追加して、鍵括弧を前後に追加するということをやっていました。同じ操作をやってみます。 ```JavaScript:編集前 Mon Tue Wed Thu Fri Sat ``` ```JavaScript:編集後 date = ["Mon","Tue","Wed","Thu","Fri","Sat"] ``` 1. **Cmd+A**ですべて選択 2. **Cmd+Shift+L**で選択中の要素の末尾にカーソルを作る 3. **Ctrl+A**で行頭に戻りダブルクォート(")を入力 4. **Ctrl+E**で行末に移動してダブルクォート(")とカンマ(,)を入力 5. **fn+delete**で改行を削除 6. **Esc**で複数行編集モードを抜ける 7. **Control+E**で末尾に戻り、最後のカンマを消す 8. **Cmd+L**で1行選択し、開きかっこ([)を入力する。同時に閉じ括弧(])も入力される 9. **Ctrl+A**で行頭に戻って変数名とイコールを入力  ## 検索した結果を使って編集する カンマ区切りになっているテキストに対して、カンマの後にスペースを挿入したいケースを考えます。 こういう場合は検索した結果から複数のカーソルを作成し、編集すると便利です。 ```JavaScript:編集前 date = ["Mon","Tue","Wed","Thu","Fri","Sat"] ``` ```JavaScript:編集後 date = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] ``` 1. **Cmd+F**で検索バーを開く 2. テキストボックスにカンマ(,)を入力する 3. マッチしたもの全てを**Cmd+Ctrl+G**で編集状態にする 4. **Esc**で検索バーを閉じる 5. カンマとスペースを入力する  ###tips * 置換と違うのは入力時に補完が効くところ。長いクラス名や型名を置換するときに便利です * 検索バーを出した状態で**Cmd+Alt+R**を押すと正規表現も使えます  |
|
| 646位 |
|
|||
|
01:48:01 |
|
|
誰のための文書?
------------------------------- リーダーや同僚から「Vagrant, Chef を使うと環境構築楽ちんだから、これこれのファイル用意して vagrant up してみなー」と言われるがままに環境構築してみたはいいが、何なのかよくわかっていない人。 Vagrant, Chef と組み合わせて VirtualBox を使っているのに VirtualBox のGUI から起動や終了をしている人。 コマンドラインで構築するやり方はたくさん見たが、それぞれのツールの役割がわかっていない人。 2週間前の自分。 概要 ------------------ 図にまとめてみました <a href='https://qiita-image-store.s3.amazonaws.com/0/3284/23af7731-1be7-e68d-7ae7-c59ab59cf739.png' target='_blank'>拡大して表示</a>  <a href='https://qiita-image-store.s3.amazonaws.com/0/3284/23af7731-1be7-e68d-7ae7-c59ab59cf739.png' target='_blank'>拡大して表示</a> ちょっと解説 --------------------- 1, 2ヶ月前に同僚から Vagrant と Chef を使うと便利なことを教わったのですが、中身についてはよくわかっていませんでした。この1週間、Vagrant と Chef と格闘してみて、理解したことをまとめたのが上図です。 Vagrant は Provisioner と Provider を組み合わせて、Provider 上で動作する環境を簡単に構築することができるツールで、Provisioner には Chef や Puppet、Provider には VirtualBox や Amazon EC2 などを選択することができます。 両者を「コントロールする」のが Vagrant なので、基本的には vagrant コマンドで VM の構築や起動をするのが自然だと思います。VirtualBox の GUI から VM を起動することもできますが、そうすると Chef が実行されないので注意しましょう。 Vagrant は VM の追加等を外から管理できるので、sahara 等のプラグインを使うことで、VM の Snapshot を取って、昔の状態に簡単に戻したりすることができます。 詳しい解説 --------------------------- <a href="http://www.amazon.co.jp/gp/product/B00BSPH158/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=B00BSPH158&linkCode=as2&tag=f83wffq-22">入門Chef Solo - Infrastructure as Code</a><img src="http://www.assoc-amazon.jp/e/ir?t=f83wffq-22&l=as2&o=9&a=B00BSPH158" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> |
|
| 647位 |
|
|||
|
00:07:23 |
|
|
# 2016/12/06 注意事項!
* Mac OS Sierraにアップデート(またはこの新しいMacを購入)した場合は、現時点2016/12/06 でこの記事で使用しているソフトウェア”Karabiner”は動作しません。 * 詳細→ https://pqrs.org/osx/karabiner/ * すでにアップデートしてしまった方に朗報!MacはTimeMachineからリストアすることでOSのダウングレードが可能です。(おまけでデータも”ダウングレード”(消えるか、元に戻る)されますが・・・)私はこの方法でmacOS SirraからOS X El Capitanに戻しました。 ↓それでも興味がある方(または上のWebサイトでMac OS Sierraサポートが確認できた方)はどうぞ下へお進み下さい♪↓ # 概要 ※こちらはMacユーザー向けの記事です。[→Windows向けの記事](http://qiita.com/daichi87gi/items/fefb7f9bbb4f86ebb670) プログラミングにとっても使いやすい(私の偏見)Apple US Keyboard(英字配列)を使うメリット(下記)は多々あります。 しかしJISキーボードには存在する入力切り替え用の`英数`キー `かな`キーがないため、最初は`Command+Space`で日本語入力の切り替えを行わなければなりません。が・・・ このままでは **テキストエディタの入力補完機能** (大抵は`Command+Space`、Windowsなら`Ctrl+Space`)と衝突する場合があります! 個人的には **入力補完機能** は'Command+Space'に割り当て、 **日本語入力切り替え** は別の方法で行いたい。 それがフリーソフトを使って簡単に実現できます。 この記事では、左Commandを`英数キー`、右Commandを`かなキー`に割り当てるように設定します。もちろん、元々のCommandの機能は維持され、コピー(Command+C)や貼り付け(Command+V)などのショートカットも利用でき、Commandを2回連続で押すことで入力中の文字の英字・日本語の変換も可能です。 ※入力補完機能:関数名などを途中まで入力すると候補が表示される機能のこと。Microsoft Visual Studioではインテリセンスと言ったりしますね。  # 手順 ## 動作確認環境 MacbookAir + Apple Wireless Keyboard US (MacbookAir付属キーボードはJIS)で動作確認済み。似たような環境なら動作すると思います。 ## ダウンロードするもの * [~~KeyRemap4MacBook~~ → Karabiner(名前が変わりました)](https://pqrs.org/osx/karabiner/index.html.ja) ## ~~KeyRemap4MacBook~~ Karabinerの設定 * 一覧からCommand_L to Command_L(+ When you type Command_L only, send EISUU)をチェックして、左コマンドだけの入力を`英数キー`として使用します * 一覧からCommand_R to Command_R(+ When you type Command_R only, send KANA)をチェックして、右コマンドだけの入力を`かなキー`として使用します  私のようにMacbookはJISキーボードで買っちゃったけど、後からワイヤレスのUSキーボードを買い足して使っている方は下の画像のSettingListのように設定を切り替えできるようにすると便利かもしれません。  ## Command+Spaceによる入力切り替えの無効化 * 左上のメニューからシステム設定>キーボードと進み、画像のようにCommand+スペースが割り当てられている設定のチェックを外します  OS X Yosemiteの場合はここにありました。(英語で使っていますが、日本語でもおそらく同じような配置だと思います)  # そもそもなぜUSキーボード? 興味本位でこの記事を見ている人のために、英字配列のメリットとデメリットも書いてみました。 ## US(英字配列)を使うメリット * 記号の配置が大きく異なる * 括弧の入力キーが横並びになっている 例えば{ }や[ ]など直感的に入力できる * UNIX操作時の記号入力が楽ちん(あまりShiftを押さなくてOK) * そんなわけでプログラミングに最適 * `英数キー`、`かなキー`が無い分、`Commandキー`が`Spaceキー`に近い * Commandキーに楽々親指が届く これに慣れたらもう抜け出せない… * ショートカットを駆使する人にとって、入力がとても楽になる * Macbookの場合、ホームポジションに手を置いたときにトラックパッドが両手のちょうど間に位置する * JISの場合は右手が触れてしまう位置にトラックパッドが配置されているのに対し、USなら問題なし ## US(英字配列)を使うデメリット * 日本ではほとんど販売されていないため入手しづらい(Apple Storeなら買えるが、Mac向けである) * MacbookではUSキーボードを選択できるが、日本では他のほとんどのノートパソコンにおいてJISキーボードが採用されているため選択肢が限定的 * 日本ではやっぱりマイナーなので、他の人が使った際に記号の入力などで困る場合がある * 最初に日本語入力切り替えのための設定が必要(というより、推奨) ※ちなみに英字配列の場合、デフォルトでは`Alt+~`で入力切替を行う 以上です。お気に入りの開発環境を作っていきましょう。 |
|
| 648位 |
|
|||
|
17:48:32 |
(DTS Corporation 所属) |
|
# 科学技術計算用言語としての Python
そもそもなぜデータ分析などの科学技術計算を Python でやるのでしょうか。主に次の二点によります。 1. NumPy, pandas, matplotlib など豊富なライブラリが揃っている 2. 汎用性の高いグルー言語として利用できる データフレームを利用した計算とそのグラフ描画 (プロッティング) のみであれば R のほうがどちらかといえば簡単かもしれません。しかし統計解析を汎用性の高い Python で完結させることで様々な分野へのより幅広い応用が可能になります。 ## NumPy 統計解析の多くはベクトル演算を伴います。 NumPy は高速でメモリ効率の良い多次元配列の実装である ndarray を備えています。プログラミング言語に元から備わっている配列・ハッシュオブジェクトでは到底かなわないような高次元のベクトル演算を可能にします。またファンシーインデックス参照 (= インデックス参照に整数配列を用いる) といったこともできます。 ### ベクトルのスカラー演算 科学技術計算は複雑ですのでベクトルの要素ごとにループを書いていたら大変なことになります。そこでベクトル演算はほぼ必須とも言えます。 NumPy では ndarray とスカラーの演算も次のように記述できます。 ```py3 arr = np.array ( [[1., 2., 3.], [4., 5., 6.]] ) # ndarray オブジェクト arr * arr # ベクトルの積算 # => array([[ 1., 4., 9.], # [ 16., 25., 36.]]) arr - arr # ベクトルの減算 # => array([[ 0., 0., 0.], # [ 0., 0., 0.]]) 1 / arr # スカラーと ndarray の算術演算 # => array([[ 1. , 0.5 , 0.33333333], # [ 0.25 , 0.2 , 0.16666667]]) arr2d = np.array ([[1,2,3],[4,5,6],[7,8,9]]) arr2d[:2] # スライスによるインデックス参照 # => array([[1, 2, 3], # [4, 5, 6]]) arrf = np.arange(32).reshape((8,4)) arrf # => array([[ 0, 1, 2, 3], # [ 4, 5, 6, 7], # [ 8, 9, 10, 11], # [12, 13, 14, 15], # [16, 17, 18, 19], # [20, 21, 22, 23], # [24, 25, 26, 27], # [28, 29, 30, 31]]) arrf[[1,5,7,2]][:,[0,3,1,2]] # ファンシーインデックス参照 # => array([[ 4, 7, 5, 6], # [20, 23, 21, 22], # [28, 31, 29, 30], # [ 8, 11, 9, 10]]) ``` ## pandas によるデータ構造の再定義 NumPy だけでも非常に有益なのですが pandas ではさらに R ライクなデータ構造を提供しています。これがシリーズ (Series) とデータフレーム (DataFrame) です。データフレームという言葉は R でも頻出しますので R を使ったことのあるかたは聞き慣れているでしょう。シリーズは 1 次元の配列のようなオブジェクトで、データフレームはテーブル形式の行と列によるデータ構造を持ちます。 ## pandas + matplotlib によるプロッティング [昨日](http://qiita.com/ynakayama/items/d6e8513791b43f18d28b)までの記事の中にしばしば出てきた matplotlib はデータ可視化における強力なライブラリです。これを pandas と組み合わせることでデータ分析結果をさまざまに描画して可視化することができます。詳細な説明は教科書や公式サイトに譲るとしてさっそく手を動かしてみましょう。 ### シリーズのプロッティング ```py3 from pylab import * from pandas import * import matplotlib.pyplot as plt import numpy as np ts = Series(randn(1000), index=date_range('1/1/2000', periods=1000)) ts = ts.cumsum() ts.plot() plt.show() plt.savefig("image.png") ```  ### データフレームのプロッティング matplotlib で日本語を扱うためにはフォントの指定が必要です。試しに日本語を利用してみましょう。 ```py3 # -*- coding: utf-8 -*- from pylab import * from pandas import * import matplotlib.pyplot as plt import numpy as np from matplotlib import font_manager # 日本語を使うために必要 fontprop = matplotlib.font_manager.FontProperties(fname="/usr/share/fonts/truetype/fonts-japanese-gothic.ttf") # フォントファイルの場所を指定 df = DataFrame(randn(1000, 4), index=ts.index, columns=list('ABCD')) df = df.cumsum() plt.figure() df.plot() plt.legend(loc='best') ax = df.plot(secondary_y=['A', 'B']) ax.set_ylabel('CD の売上', fontdict = {"fontproperties": fontprop}) ax.right_ax.set_ylabel('AB スケール', fontdict = {"fontproperties": fontprop}) plt.show() plt.savefig("image2.png") ```  日本語が表示されました。実際にはもう少しフォントのパラメーターを調整したほうが良いでしょう。 ### サブプロッティング matplotlib ではプロットの中にプロットを描くサブプロッティングもできます。 ```py3 df.plot(subplots=True, figsize=(6, 6)); plt.legend(loc='best') plt.show() plt.savefig("image3.png") ```  ```py3 fig, axes = plt.subplots(nrows=2, ncols=2) df['A'].plot(ax=axes[0,0]); axes[0,0].set_title('A') df['B'].plot(ax=axes[0,1]); axes[0,1].set_title('B') df['C'].plot(ax=axes[1,0]); axes[1,0].set_title('C') df['D'].plot(ax=axes[1,1]); axes[1,1].set_title('D') plt.show() plt.savefig("image4.png") ```  ### 棒グラフのプロッティング ```py3 plt.figure(); df.ix[5].plot(kind='bar'); plt.axhline(0, color='k') plt.show() plt.savefig("image5.png") ```  ```py3 df2 = DataFrame(rand(10, 4), columns=['a', 'b', 'c', 'd']) df2.plot(kind='bar'); plt.show() plt.savefig("image6.png") ```  ### 棒グラフのサブプロッティング もちろん棒グラフでも (他のいずれでも) サブプロッティングが可能です。 ```py3 df.diff().hist(color='k', alpha=0.5, bins=50) plt.show() plt.savefig("image8.png") ```  ```py3 data = Series(randn(1000)) data.hist(by=randint(0, 4, 1000), figsize=(6, 4)) plt.show() plt.savefig("image9.png") ```  ### さまざまなデータ可視化 matplotlib は他にも実にさまざまなグラフをプロッティングできるのですが、その一部を紹介します。 ```py3 from pandas.tools.plotting import bootstrap_plot data = Series(rand(1000)) bootstrap_plot(data, size=50, samples=500, color='grey') plt.show() plt.savefig("image12.png") ```  ```py3 df = DataFrame(randn(1000, 10), index=ts.index) df = df.cumsum() plt.figure() df.plot(colormap='jet') plt.show() plt.savefig("image13.png") ```  ``` dd = DataFrame(randn(10, 10)).applymap(abs) dd = dd.cumsum() plt.figure() dd.plot(kind='bar', colormap='Greens') plt.show() plt.savefig("image14.png") ```  ```py3 from pandas.tools.plotting import scatter_matrix df = DataFrame(randn(1000, 4), columns=['a', 'b', 'c', 'd']) scatter_matrix(df, alpha=0.2, figsize=(6, 6), diagonal='kde') plt.show() plt.savefig("image11.png") ```  ## 考察 NumPy で高度なベクトル演算が、 pandas で R ライクなデータ構造が、そして matplotlib でそれらによる計算結果をわかりやすく可視化できることがわかりました。これだけ強力なツールがあればデータ分析においても大変心強いですね。統計や機械学習では線形代数に係る計算がほぼ必須ともいえるほど頻出しますが、そのような知識と共にまずはこれらのライブラリに慣れておくことが大切です。 ## 参考 詳解はそれぞれの公式サイトの情報を参照すると良いでしょう。 NumPy http://www.numpy.org/ pandas http://pandas.pydata.org/ matplotlib http://matplotlib.org/# |
|
| 649位 |
|
|||
|
06:55:29 |
|
|
# 自分のIPアドレスを知る方法
と言った場合、eth0に割り当てられてるIPアドレス、外向き(external)のIPアドレス、などを意味する場合がほとんどでしょう。 ## よく知られた方法 ```bash $ ifconfig eth0 ``` IPアドレス部分だけ抜き出すにはgrepとかsedを使う必要があり、ちょっと面倒。 それに、ifconfigを含むnet-toolsはメンテナンスされておらず、数年前にdeprecatedとなったようです。 [Deprecated Linux networking commands and their replacements](https://dougvitale.wordpress.com/2011/12/21/deprecated-linux-networking-commands-and-their-replacements/) [訳:非推奨になったLinuxネットワークコマンドの代替コマンド](http://understeer.hatenablog.com/entry/2012/03/24/184346) ## iproute2を使う @tukiyoさんに教えてもらった方法です。ipコマンドはifconfigと違いメンテナンスされ続けており、こちらがLinux Foundation推奨の方法のようです。 ```bash $ ip -f inet addr $ ip route ``` ## /etc/hostsをちゃんと設定している場合 ```bash $ hostname -i ``` これだと127.0.0.1も出てきてしまったりするが、ifconfigよりは整形しやすいかも。 ## 外部に訊く方法 ```bash $ curl ifconfig.co ``` 外部へのリクエストが入りますが、この方法だと例えばコンテナからホストのアドレスを知ることができそうですね。 同等のサービスがいくつか知られています。 - curl ifconfig.co - <strike>curl inet-ip.info</strike> (2016年8月現在使えないようです) (@negabaroさんに教えていただきました) - curl ipinfo.io/ip - curl ipecho.net/plain - <strike>curl ifconfig.me</strike> (2016年8月現在使えないようです) - curl checkip.amazonaws.com (@metaleftyさんに教えていただきました) - curl httpbin.org/ip (json形式で返ってきます) ## まとめ? ifconfigが非推奨になってたとは知りませんでした。そういえばディストリによってはデフォルトで入ってませんね。今後はiproute2を使いましょう(とは言っても手が慣れてしまっている…)。 (一応、生きてはいるようです [http://sourceforge.net/p/net-tools/code/commit_browser](http://sourceforge.net/p/net-tools/code/commit_browser)) 参考:[訳:非推奨になったLinuxネットワークコマンドの代替コマンド - aws memo](http://understeer.hatenablog.com/entry/2012/03/24/184346) |
|
| 650位 |
|
|||
|
18:01:42 |
|
|
[MAMP](http://www.mamp.info/en/index.html)を使わず標準でインストールされているApacheとPHPを使い、複数のWEBサイトをローカルで開発できるようにします。 MAMPを使っている場合は、停止させること。 ## Apacheの設定 複数のWEBサイトをローカルで開発できるようにするには、以下のファイルを設定する必要があります。 ``` /etc/apache2/httpd.conf /etc/hosts /etc/apache2/extra/httpd-vhosts.conf ``` まずは、Apacheの起動です。 ``` $ sudo /usr/sbin/apachectl start ``` ### 1. /etc/apache2/httpd.confの設定 ApacheでPHPを有効にしつつ、複数サイトを動かすための設定を行います。 ``` $ sudo vi /etc/apache2/httpd.conf ``` #### 1-1. PHP5モジュールを有効化する コメントアウトを外してPHPを有効化します。 ``` - #LoadModule php5_module libexec/apache2/libphp5.so + LoadModule php5_module libexec/apache2/libphp5.so ``` #### 1-2. バーチャルホスト設定を有効化する コメントアウトを外し、後述するバーチャルホストの設定を有効化します。 ``` # Virtual hosts - #Include /private/etc/apache2/extra/httpd-vhosts.conf + Include /private/etc/apache2/extra/httpd-vhosts.conf ``` ### 2. /etc/hostsの設定 特定のドメイン名は、ネットではなくローカルを参照するようにします。 #### 2-1. アクセスする時の名前を追記 ``` $ sudo vi /etc/hosts ``` 例えば以下のように書くと、sandbox.localhostでアクセスできるようになります。 ``` ## # Host Database # # localhost is used to configure the loopback interface # when the system is booting. Do not change this entry. ## 127.0.0.1 localhost 255.255.255.255 broadcasthost ::1 localhost fe80::1%lo0 localhost 127.0.0.1 sandbox.localhost ``` #### 2-2. /etc/apache2/extra/httpd-vhosts.confの設定 バーチャルホストの設定です。 以下をhttpd-vhosts.confに追記し、サイトのパス(/Users/is0me/Dev/sandbox)を自分のパスに書き換えてください。 先ほど/etc/hostsに追記した名前をServerNameに指定します。 そして、参照先ディレクトリをDocumentRootに指定します。 ``` <VirtualHost *:80> DocumentRoot "/Users/is0me/Dev/sandbox" ServerName sandbox.localhost ErrorLog "/private/var/log/apache2/sandbox-error_log" CustomLog "/private/var/log/apache2/sandbox-access_log" common DirectoryIndex index.php index.html <Directory "/Users/is0me/Dev/sandbox"> Options Includes ExecCGI FollowSymLinks AllowOverride All order deny,allow allow from All </Directory> </VirtualHost> ``` ## 確認 DocumentRootに指定したディレクトリにテストファイルを作ってください。 ``` $ touch /Users/is0me/Dev/sandbox/index.php && echo '<?php phpinfo();' >> /Users/is0me/Dev/sandbox/index.php ``` ### Apacheを再起動 ``` $ sudo /usr/sbin/apachectl restart ``` [sandbox.localhost](http://sandbox.localhost/index.php)へアクセスして、phpinfoが表示されたら完了です。 ### 完了後にサイトを追加する場合 /etc/hostsとhttpd-vhosts.confに追記しApacheを再起動してください。 |
|
| 651位 |
|
|||
|
20:48:21 |
(Wondershake, Inc. 所属) |
|
Rails 4.0.0 ruby 2.0.0p247 ## Draperとは? 具体例を書いた方が分かりやすいと思うのでコードで。 ### Draperなし(helper頼み) ```ruby:controllers/products_controller.rb class ProductsController < ApplicationController def show @product = Product.find(params[:id]) end end ``` ```ruby:helpers/products_helper.rb module ProductsHelper def product_name_with_hoge(product) "#{product.name} hoge" end end ``` ```ruby:views/products/show.html.haml %p=product_name_with_hoge(@product) ``` みたいな感じになってしまうので、ここだけオブジェクト志向の世界から抜け出た感じになってしまいます。 viewのコードにproductって単語が繰り返されているのもまた気持ち悪いです。 でも、`name_with_hoge`みたいなメソッド名にしてしまうと別のhelperと競合してしまう可能性があるので仕方ないですね。 ### Draperあり(helperなし) ```ruby:controllers/products_controller.rb class ProductsController < ApplicationController def show @product = Product.find(params[:id]).decorate end end ``` あらたに`#decorate`を呼び出しています。 ```ruby:decorators/products_decorator.rb class ProductDecorator < Draper::Decorator def name_with_hoge "#{object.name} hoge" end end ``` ```ruby:views/products/show.html.haml %p=@product.name_with_hoge ``` Viewのコードがスッキリになりましたね。 なにより直感的なコードになったと思います。 helperはちょこっと何かをしたいという時は便利だと思うのですが、Viewがゴチャゴチャしがちな気がします。 ## ほかにできる便利なこと ### helperにアクセスできる ```ruby:decorators/product_decorator.rb class ProductDecorator < Draper::Decorator def name_with_icon content = h.content_tag(:i, nil, class: 'icon-user') content << object.name end end ``` `#h`で既存のhelperにアクセス出来ます。 Railsは便利なhelperがたくさんありますからね。 ### 関連モデルを自動的に`#decorate`する ```ruby:decorators/product_decorator.rb class ProductDecorator < Draper::Decorator decorates_association :price end ``` ```ruby:decorators/price_decorator.rb class PriceDecorator < Draper::Decorator def created_at_with_hoge "#{object.created_at} hoge" end end ``` ```ruby:views/products/show.html.haml %p=@product.price.created_at_with_hoge ``` のようにいちいち`#decorate`しなくても勝手にdecoratedなmodelがもらえます。 ### attributeをフィルターする Viewからアクセスできるattributeを制限したい場合などもたぶん良いです。 アクセスしたいattributeだけを明示的に`#delegate`します。 (この場合、ふだん`#delegate`に必要な`to:`は推測できるので省略できます) ```ruby:decorators/product_decorator.rb class ProductDecorator < Draper::Decorator delegate :name end ``` そんなこと気にしない場合は、 ```ruby:decorators/product_decorator.rb class ProductDecorator < Draper::Decorator delegate_all end ``` とすれば、Decoratorに定義されていないメソッドはmodelに渡されます。 ### 共通のメソッドはmoduleで書きだしたり Rails4(37signales)なスタイルを真似て、`decorators/concerns`みたいなディレクトリ作って書き出してもいいかもね。 ```ruby:decorators/concerns/timestamps_decoration.rb module TimestampsDecoration def created_at object.created_at.strftime('%F %R') end end ``` ```ruby:config/application.rb config.eager_load_paths += Dir[Rails.root.join('app', 'decorators', 'concerns')] ``` ```ruby:decorators/product_decorator.rb class ProductDecorator < Draper::Decorator include TimestampsDecoration end ``` でも、`TimestampDecoration `に対して`concerns`ってフォルダ名は適切なじゃない気もするな。。 ## まとめ modelに属さない便利メソッドはまたhelperとして居続けていいと思うんですよね。 ただ、modelに`name_with_hoge`みたいなメソッドがあると気持ち悪し、helperに書いてもイマイチなので、decorator(presenter)として書き出すのがいいんじゃないかなと思います。 ### DraperのほかにもActiveDecorator ほかに、同様のことが出来るものにamatsudaさんの[ActiveDecorator](https://github.com/amatsuda/active_decorator)があります。 その他は、[Ruby Tool Box - Rails Presenters](https://www.ruby-toolbox.com/categories/rails_presenters)にありますが、ActiveDecoratorかDraperを選ぶ人が多そうです。 好みでどちらを使うか選んだらいいかなと思います。 ### あとがき ほんとうは`concerns`のところだけ書く予定が、Draperの記事がQiitaに見当たらなかったので、まるっと書いてみた次第です。 ほとんど`README.md`の和訳だが… 詳しくは、[drapergem/draper](https://github.com/drapergem/draper)を参照のこと! |
|
| 652位 |
|
|||
|
13:59:25 |
(フリーランス 所属) |
|
もしかしたら、Rubyに慣れてない人には気付いていない人も居るかと思ったので、
カジュアルにRubyGemsを活用する事のメリットについて書いておきます。 普通、Rubyのgemパッケージは、`gem install`でインストールできるようにrubygems.orgにホスティングしておくのが基本です。 とは言え、世界的に公開されるものなので、ちゃんとgemとしての体裁を整えておかないと何か恥ずかしいし、説明とかも英語でちゃんと書いとかないと、って考えると面倒なレベルのツールとかあると思います。 API叩くための簡易コマンドとか、社内ツールの処理自動化とか。 しかし、Bundlerとgitのおかげで、現在はそういった事を余り気にする必要が無くなっています。 Bundlerは、gitリポジトリから直接コードをクローンして、パッケージ化されたgemと同様に扱う事ができます。 参考: [Bundler: The best way to manage a Ruby application's gems](http://gembundler.com/v1.3/git.html "Bundler: The best way to manage a Ruby application's gems") そのためrubygems.orgにホスティングする必要はありません。 公開しても問題ないものならGitHubに、クローズドな情報が入っているものは社内のgitlabやGitHubのプライベートリポジトリに配置しておけばOKです。 後は、Gemfileに書けば、gemとして利用できます。 gitリポジトリから参照しているgemは`Gemfile.lock`でこんな感じで管理されています。 ``` GIT remote: git://github.com/joker1007/ghost_writer.git revision: bdee7051d5555d8be92045c1d45cd4ac0dee40b1 specs: ghost_writer (0.2.0) activesupport (>= 3.0.0) rspec-rails (~> 2.11) trollop ``` この方法には以下のようなメリットがあります。 - READMEにGemfileの書き方さえ書いておけば簡単にチームメンバーがインストールできる事。 - 更新したい時は、gitリポジトリにpushして、bundle updateを実行するだけ - Bundlerはコミットハッシュでgemを特定しているので、バージョン番号を更新する必要もない - Jenkins等で利用するのが楽になる - 他の人の改善案もpull request等で容易に取り込める というわけで、ツールの更新と配布をほとんどgitの機能に任せる事ができます。 gem化するのにも大した手間はかかりません。 ```sh % bundle gem mytool -b create mytool/Gemfile create mytool/Rakefile create mytool/LICENSE.txt create mytool/README.md create mytool/.gitignore create mytool/mytool.gemspec create mytool/lib/mytool.rb create mytool/lib/mytool/version.rb create mytool/bin/mytool Initializating git repo in /home/joker/mytool ``` 後は、bin/mytoolを編集して、コマンドラインツールを簡単に作れる[wycats/thor](https://github.com/wycats/thor "wycats/thor · GitHub")などを利用すれば、お手軽コマンドツールが作れます。 後は、このディレクトリをgitリポジトリに上げてしまうだけです。 こんな感じで、カジュアルにGem化してツールを広めていくと、色々楽になるんで良いんじゃないかなと思っています。 |
|
| 653位 |
|
|||
|
20:53:35 |
(KAYAC Inc. 所属) |
|
CreateJSをはじめる人向けに、自分が使った時に手間取った所をまとめておきます。 ドキュメントには書いてあることだけど、英語わからなかったりすると意外と手間取るとこ。 CreateJSをかるく触ったことある人向け。 ## createJSのプロパティをグローバルから参照する createJSの各プロパティは、通常createjsオブジェクトから参照する必要があるが、 以下のようにすることで、グローバルに参照できるようになる。 ```javascript:JavaScript createjs = window; // EaselJS、TweenJS などの読み込み前に行う ``` ```javascript:JavaScript var stage = new Stage("sample_canvas"); // グローバルに参照できるようになる ``` (この記事の下記のサンプルコードでは、一応createjsから参照します) ## Canvasの更新はTickerで行う CreateJSでは、Canvas描画の更新はStageのインスタンス上で update()を実行することで行うわけですが、 基本的には、FPSを指定したTickerのtickイベント時にのみ行うことがおすすめ。 ```javascript:JavaScript var stage = new createjs.Stage("sample_canvas"); createjs.Ticker.setFPS(30); createjs.Ticker.addEventListener("tick", function() { stage.update(); // 30fpsでステージの描画が更新されるようになる }); ``` こうすることで、Canvasの更新タイミングを気にせず処理を書けるようになる。 更新を止めたいときは、removeEventListenerを使えばOK。 ## cache() メソッドを使って処理をかるくする <del>作成したBitmapやShapeをキャッシュしておくことで、 描画処理をかるくできる。</del> (2013/5/18 修正)Bitmapにはcacheを使ってもパフォーマンスはあがらないようです。むしろ下がるみたい。cacheメソッドは複雑なShape、複数のBitmapでグループ化したContainer などに有効なようです。(公式ドキュメントに普通に書いてあった.. ```javascript:JavaScript var bitmap = new createjs.Bitmap("sample.png"); // sample.png の実際のサイズ 100x100 bitmap.cache(0,0,100,100); // 第3引数と第4引数は、画像のサイズを指定する ``` cache()を使うと、インスタンスそれぞれに紐付いた別のCanvasに描画が行われ、 ステージへの描画はその別Canvasから行われるようになる。 ## DOM要素を扱う CreateJSは、DOM要素も扱える。 ```javascript:JavaScript var html_element = document.getElementById("sample_element"); var element = new createjs.DOMElement(html_element); ``` 他のインスタンス同様、CreateJS のマウスイベントなどをバインドできる。 ## スプライト画像を扱う DOM実装でいうところのCSSスプライトのように、 複数の画像を1つにくっつけて扱いたいとき。 ```javascript:JavaScript var bitmap = new createjs.Bitmap("sample.png"); // 100x100 の画像を縦に5個つないだ100x500の画像 bitmap.set({ sourceRect : new createjs.Rectangle(0,200,100,100) // 上から3つめの100x100部分を表示 }); ``` BitmapAnimationを使う方法でも実現できるけど、こっちのほうがらく。 ## マウスイベントのヒットエリアを拡張する Bitmapなどの設定するマウスイベントは、 画像の大きさがそのままヒットエリアになっています。 (透過pngだと、透明部分は反応しないようになってる) ヒットエリアを拡張したいときは以下のようにする。 ```javascript:JavaScript var bitmap = new createjs.Bitmap("sample.png"); // sample.png の実際のサイズ 100x100 var hitAreaShape = new createjs.Shape(); hitAreaShape.set({ x : -50, y : -100, graphics : new createjs.Graphics().beginFill("#FFF").drawEllipse(0,0,300,200) // 300x200 の楕円形 }) bitmap.set({ hitArea : hitAreaShape }); ``` これでBitmapのマウスイベントのヒットエリアが300x200の楕円形に拡張される。 ## マスクの使い方 マスクを使いたいときはこんな感じ。 ```javascript:JavaScript var bitmap = new createjs.Bitmap("sample.png"); // sample.png の実際のサイズ 100x100 var maskShape = new createjs.Shape(); maskShape.set({ x : 50, y : 50, graphics : new createjs.Graphics().beginFill("#FFF").drawEllipse(0,0,300,200) // 300x200 の楕円形 }) bitmap.set({ mask : maskShape }); ``` ヒットエリアと違い、座標はターゲットが起点にならないので注意。 ## おわりに とりあえず、自分が使った範囲で手間取ったところをまとめました。 CreateJSはほかにも、 * CSSPluinでDOM要素のCSSを扱えるようになる! * MotionGuidePlugin でくねくねしたアニメーション! * Filterを使ってぼかしとか! など、便利な機能盛りだくさんッ CreateJSをAndroid端末で使おうと思っている人向けに、 [CreateJS × Android でハマったこと](http://qiita.com/items/6df5e01a7a29417e55c6) という投稿もしています。 |
|
| 654位 |
|
|||
|
00:02:28 |
|
|
Heroku Advent Calendar (がもしもあったらそちら) に投稿するような内容だと思いますが,無かったので Git ネタということで.
# 複数マシンから 1 つの Heroku で動くアプリを開発する まず,何かしらのアプリをある 1 台のマシン (例えばデスクトップ PC) で作成します. それを Git リポジトリとして Heroku のサーバに push します.それにより deploy が行えるのが Heroku の大きな特徴の一つだと言えます. さて,最初に push する人は,Heroku 上にアプリを作る作業を行いますから, ``` heroku create APPNAME ``` を行うと思います.するとこのコマンドが自動的にリモートリポジトリとして `heroku` という名前で `git@heroku.com:APPNAME.git` を登録してくれます.なので,この後は ``` git push heroku master ``` としてやるだけで deploy が出来るのですね.この辺りは Heroku 入門的なチュートリアルでよく見るところだと思います. ここで,別のマシン (例えばノート PC) でこのアプリの開発と deploy を引き続き行いたくなったとします. まず,先ほどのマシンを参照するなり Github や Bitbucket や自前の gitosis 等を参照したりするなりして,リポジトリを clone してくると思います.すると,リモートリポジトリとして `heroku` が登録されていません.なので ``` git remote add heroku git@heroku.com:APPNAME.git ``` をする必要があります.これが面倒だからと言って ``` git clone git@heroku.com:APPNAME.git ``` すると,今度は `origin` が `git@heroku.com:APPNAME.git` になって何だか変な気分です. # いちいちリポジトリの URI を打ち込むのは面倒だ 今まで自分はずっと `git remote add heroku ...` をしていたのですが,先日こんなことをしなくてもいいんだと `heroku help` を見ていて気付きました. サブコマンド `git` です. ``` heroku git:remote --app APPNAME ``` Github なり何なりから clone してきたリポジトリ内で,このコマンドを走らせるだけでリモートリポジトリ `heroku` が登録されます. また,Heroku からまず第一に clone するのであれば, ``` heroku git:clone APPNAME ``` すると,`APPNAME` ディレクトリが作られそれがリポジトリになっていて,やはりリモートリポジトリ `heroku` が登録されています.`origin` はありません. これは楽でいいですね. # heroku git のその他機能 * clone するときにディレクトリ名を変えたい → `heroku git:clone APPNAME DIRNAME` * リモートリポジトリ名を heroku 以外 (例えば staging) にしたい → `heroku git:remote APPNAME -r staging` まぁ, ``` heroku help git heroku help git:remote heroku help git:clone ``` を見れば大体分かります. # 実装はどうなっているのか いちいち `git remote add heroku ...` しなくてよくなったのはいいですが,実際このコマンドはどうやって実現されているのでしょうか? * git:clone の方 https://github.com/heroku/heroku/blob/2b67489173464420248c61c88ee98ee628067988/lib/heroku/command/git.rb#L31 * git:remote の方 https://github.com/heroku/heroku/blob/2b67489173464420248c61c88ee98ee628067988/lib/heroku/helpers.rb#L110 …普通に git コマンドの文字列を作って実行しているだけですね.安心です. ここのソースコードを読んで勉強するべきなのは,むしろ `git:clone`, `git:remote` を `heroku` コマンドの引数で与えると,どのようにして関数を呼び出すところに到達するか,だと思います. # バージョン情報 今回確認に使った環境・ツールのバージョンは以下の通りです. * Ruby 1.9.3-p194 * Heroku Toolbelt 2.33.6 * OS * Ubuntu 12.04 amd64 Desktop * Mac OS X 10.7.5 # 参考 * https://devcenter.heroku.com/articles/clone-heroku-app |
|
| 655位 |
|
|||
|
17:52:37 |
|
|
ヘッダーファイルにおいて、#define(マクロ)を使って、(公開/非公開に関わらず)全ての定数にプレフィックスkをつけて定義しているコードを見かけることがあるが、下記理由から個人的にはあまり良くないのではないかと思っている。 - 型情報がない - ヘッダーファイルインポート条件によっては、名前衝突により開発者が意図しないファイルにおいても定数置換が行われてしまう危険性がある **実際のところ、私は下記のように定数定義を行っている。** ## 外部に公開する定数 - extern constを付与 - 定数にはプレフィックスとしてクラス名をつける(名前衝突を避けるため) ## 内部でのみ使用する定数 - static constを付与 - 定数にはプレフィックスとしてkをつける(よく見かけるパターン) ## コーディング例 以下は、BLGClassAクラスにおいて外部に公開する定数を定義、BLGClassBクラスにおいて内部で使用する定数を定義、して利用する場合の例。 ```objectivec:BLGClassA.h #import <Foundation/Foundation.h> @interface BLGClassA : NSObject extern const NSInteger BLGClassAIntegerConstant; extern NSString *const BLGClassAStringConstant; @end ``` ```objectivec:BLGClassA.m #import “BLGClassA.h" const NSInteger BLGClassAIntegerConstant = 3; NSString *const BLGClassAStringConstant = @"hogePublic"; @implementation BLGClassA // NSObjectクラスにはinitメソッドが実装されているため、省略可能 //- (id)init { // if (self = [super init]) { // } // return self; //} @end ``` ```objectivec:BLGClassB.h #import <Foundation/Foundation.h> @interface BLGClassB : NSObject - (void)doSomething; @end ``` ```objectivec:BLGClassB.m #import “BLGClassB.h" #import “BLGClassA.h" static const NSInteger kLocalIntegerConstant = 30; static NSString *const kLocalStringConstant = @"fooLocal"; @implementation BLGClassB // NSObjectクラスにはinitメソッドが実装されているため、省略可能 //- (id)init { // if (self = [super init]) { // } // return self; //} - (void)doSomething { NSLog(@"Local Constants: %ld %@", (long)kLocalIntegerConstant, kLocalStringConstant); NSLog(@"Imported Constants: %ld %@", (long)BLGClassAIntegerConstant, BLGClassAStringConstant); } @end ``` ```objectivec:main.m #import <Foundation/Foundation.h> #import “BLGClassB.h" int main(int argc, const char * argv[]) { @autoreleasepool { BLGClassB *b = [BLGClassB new]; [b doSomething]; // Local Constants: 30 fooLocal // Imported Constants: 3 hogePublic } return 0; } ``` |
|
| 656位 |
|
|||
|
15:45:08 |
(Atrae, Inc. 所属) |
|
社内勉強会用にいくつかまとめたやつ。 ## ActionView ### distance_of_time_in_words_to_now(from_time, include_seconds = false, options = {}) 任意の時間からの差分を表示。(~分、~日) ``` "#{distance_of_time_in_words_to_now(article.updated_at)}前に更新" # => "2日前に更新" ``` ### number_with_delimiter(number, options = {}) 数字に区切り文字を入れてくれる。 ``` "#{number_with_delimiter(12345678)}" # => 12,345,678 ``` ### highlight(text, phrases, *args) textの中でphraseに一致するものがあれば、タグで囲ってくれる。 ``` "#{highlight('You searched for: rails', 'rails')}" # => "You searched for: <strong class='highlight'>rails</strong>" ``` ## ActiveRecord ### ActiveRecord::Relation#first_or_create そのクラスのテーブルの最初の要素があればそれを返し、無いならレコード作成。ブロックで使うと便利そう。 ``` User.where(last_name: 'Elen').first_or_create # 既にあった場合 # => <User id: 1, first_name: 'Jaeger', last_name: 'Elen'> # 無かった場合 # => <User id: 2, first_name: nil, last_name: 'Elen'> ``` ### ActiveRecord::Relation#first_or_initialize `first_or_create`メソッドと異なり、まだcreateしたくない場合はこっち。 ### ActiveRecord::Scoping::Named::ClassMethods#scoped あるクラスの持つ全てのレコードを表したActiveRecord::Relationが欲しい時に使う。`all`だとArrayになっちゃうので。 ※追記:Rails4.0からは、`all`メソッドでActiveRecord::Relationが返るようです。`scoped`はdeprecated? ``` User.scoped.class # => ActiveRecord::Relation User.all.class # => Array ``` ### ActiveRecord::Persistence#toggle booleanなカラム名を渡すと、trueとfalse入れ替え。 ``` p @user.mail_flg # => true @user.toggle(:mail_flg) p @user.mail_flg # => false ``` ### ActiveSupport::Concern#included(base = nil, &block) `include`されたら、呼び出し側のクラスのコンテキストで実行される処理をブロックで書ける。 ``` module Hoge included do scope :is_hoge, ->{where(hoge: true)} end end ``` ### ActiveRecord::Batches#find_each(options = {}) あるテーブルの全データ更新バッチ処理など、大量のデータをまとめて処理する時には必ず使うべき。 例えば以下の書き方だと、全ユーザーのActiveRecordオブジェクトの配列が`all`メソッドで作られ、メモリ上に展開される ``` User.all.each do |user| ... end ``` `find_each`を使えば、デフォルトで1,000件ずつ数え上げを行うので、メモリの使用量を節約できる。 ``` User.find_each do |user| ... end ``` ※似たメソッドに、`find_in_batches`というのもある。 ### ActiveRecord::Calculation#pluck(column_name) あるテーブルの特定のカラムを配列にしたいときに使う。 `map`を利用するより高速に動作する。 下記のようなよくある書き方だと、内部的に10,000万件のActiveRecordオブジェクトが作られて遅い。 ``` User.order('id DESC').limit(10000).map(&:name) ``` ``` User.order('id DESC').limit(10000).select(:name).map(&:name) ``` `pluck`を使うことで、ActiveRecordオブジェクトの生成を経由せずに、配列が返る。 ``` User.order('id DESC').limit(10000).pluck(:name) ``` こちらのブログ記事でパフォーマンス検証をやっているが、`map`を使う時と比較してかなり高速になってるのが分かる http://blog.livedoor.jp/sasata299/archives/51847390.html ※ただし、Rails 3.2以上でしか使えない。 ※Rails 3.2では、一度に`pluck`できるのは単一のカラムのみ。 => [multipluck](https://github.com/hanzq/multipluck)というgemで複数カラムいけるらしいです ※追記:Rails4.0からはデフォルトで複数カラムの`pluck`が可能(なぜ初めからその仕様にしなかったのか...) ※Web+DBのrails高速化の回に載っていたが、Rails 3.0, 3.1のアプリは、 [activerecord-raw-data](https://github.com/hotchpotch/activerecord-raw-data)というgemを使うことで、同等な効果を得ることができるそう ## そのほか拡張など ### Object#presence `present?`メソッドの評価結果がtrueのときはselfを、falseの時はnilを返す。 ``` 'hoge'.presence # => 'hoge' [].presence # => nil # 1 => 2のように、書き方がまどろっこしくなくなる! # 1 name = params[:name].present? ? params[:name] : 'no name' # 2 name = params[:name].presence || 'no name' ``` ### Object#try こちらもまどろっこしい書き方が減って良い ``` # 1 user.name ? user.name : 'no name' # 2 user.try(:name) || 'no name' ``` ### Array#from(position) positionに渡したインデックスから配列の最後までを返す。 ``` %w( a b c d ).from(2) # => %w( c d ) ``` ### Array#forty_two ``` [1, 100].forty_two # => 42 ``` 人生、宇宙、すべての答えは「42」なのである。 |
|
| 657位 |
|
|||
|
18:15:04 |
|
|
「Lombok、便利そうだけどまあそんなに使わないかなあ」と思ってたんだけど試しに使ってみたらかなり快感だったのでメモ。
## Javaのだるさ Javaでプログラムを書いているとよく「intのtype, Stringのnameというフィールドを持つだけの、特定の処理などは含まないHogeクラスを作ろう。まあ普通に文字列表現もあってequalsによる比較とかもできるように」のように思います。 ```java:Hoge.java package org.hogel; public class Hoge { private int type; private String name; public Hoge() { } public Hoge(int type, String name) { this.type = type; this.name = name; } public int getType() { return type; } public void setType(int type) { this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { return type + (name == null ? 0 : name.hashCode()); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj == null || !(obj instanceof Hoge)) { return false; } Hoge other = (Hoge) obj; if (type != other.getType()) { return false; } return name == other.getName() || name != null && name.equals(other.getName()); } @Override public String toString() { StringBuilder builder = new StringBuilder(); return builder.append("Hoge(type=").append(type).append(", name=").append(name).append(")").toString(); } } ``` うっ、だるい なんてコードの長さでしょう。ディスプレイのサイズは有限なのです、こんな長いコードを表示するのには何年もかかってしまいます。 ## そうだ、困ったときのCommonsだ Javaで困ったときはとりあえず[Apache Commons](http://commons.apache.org/)を頼りにしましょう。 Commons Langという便利なライブラリにはこういった独自クラスを作るときに有用な機能が含まれています。 ```java:Hoge.java package org.hogel; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; public class Hoge { private int type; private String name; public Hoge() { } public Hoge(int type, String name) { this.type = type; this.name = name; } public int getType() { return type; } public void setType(int type) { this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } @Override public boolean equals(Object obj) { return EqualsBuilder.reflectionEquals(this, obj); } @Override public String toString() { // Hoge[type=1,name=a]のような形式で出力される return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); } } ``` hashCode, equals, toStringメソッドの中身がすべて一行になりました。ああしかしやっぱりめんどうです、めんどうなので 一々こんなクラス作るのをやめてListやらMapやらを組み合わせてしまおうか、なんて思ってしまいます。 ## そうだ、Lombokがある さてここで[Lombok](http://projectlombok.org/)。百聞は一見にしかず。Lombokを使うと上述したコードがこんなことになります。 ```java:Hoge.java package org.hogel; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class Hoge { private int type; private String name; } ``` おや、ほとんど何もなくなっていますね。でもこれで上に書いた2つのコードとほとんど同じ挙動をするのです。試しにこんなテストコードで実行してみます。 ```java:HogeTest.java package org.hogel; import org.junit.Test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertThat; public class HogeTest { @Test public void hogeBean() { Hoge a1 = new Hoge(1, "a"); Hoge a2 = new Hoge(1, "a"); Hoge b = new Hoge(2, "b"); System.err.println("a1.getType(): "+ a1.getType()); System.err.println("a1.getName(): "+ a1.getName()); System.err.println(); System.err.println("a1: " + a1); System.err.println("b: " + b); System.err.println(); System.err.println("a1 == a2: " + (a1 == a2)); System.err.println("a1 == b: " + (a1 == b)); System.err.println(); System.err.println("a1.equals(a2): " + a1.equals(a2)); System.err.println("a1.equals(b): " + a1.equals(b)); System.err.println(); System.err.println("a1.hashCode(): " + a1.hashCode()); System.err.println("a2.hashCode(): " + a2.hashCode()); System.err.println("b.hashCode(): " + b.hashCode()); System.err.println(); assertThat(a1, is(a2)); assertThat(a1, not(b)); assertThat(a1.hashCode(), is(a2.hashCode())); } } ``` 定義もしていないコンストラクタやgetType、getNameなどを呼んでいます。それの実行結果がこうなります。 a1.getType(): 1 a1.getName(): a a1: Hoge(type=1, name=a) b: Hoge(type=2, name=b) a1 == a2: false a1 == b: false a1.equals(a2): true a1.equals(b): false a1.hashCode(): 1089 a2.hashCode(): 1089 b.hashCode(): 1121 なんだか知らんがうまいこと動作していますね。Lombok、すげー。 IDEなしでJavaを書くなんてのはただの苦行ですし、[Lombokは各IDEにも対応しています](http://projectlombok.org/download.html)。  詳細や更に便利な機能などは[Lombokのページ](http://projectlombok.org/features/)でご確認ください。[val](http://projectlombok.org/features/val.html)なんて使いはじめるともうJavaなのかなんなのかわからなくなってきてとても楽しいと思います。 |
|
| 658位 |
|
|||
|
14:06:51 |
(株式会社ずんシステム 所属) |
|
今日自分が体験したトラブル事例に、ユーザビリティ向上のための気づきがあったのでメモしておきます。
※本文よりまず、__[このコメント](http://qiita.com/kawaz/items/d462a05c113b8e063736#comment-31858b02023fac3424de)__、こっちに言いたいことが集約されてるのでまずはこっちを読むと早いです。 ※本文よりまず、__[このコメント](http://qiita.com/kawaz/items/d462a05c113b8e063736#comment-31858b02023fac3424de)__、こっちに言いたいことが集約されてるのでまずはこっちを読むと早いです。 ※本文よりまず、__[このコメント](http://qiita.com/kawaz/items/d462a05c113b8e063736#comment-31858b02023fac3424de)__、こっちに言いたいことが集約されてるのでまずはこっちを読むと早いです。 # 事例:登録時のパスワードでログイン出来ない!? 今日、とあるWEBサイトでユーザ登録を行った際に、登録時に入力したパスワードでログイン出来ないというトラブルがありました。 普通は自分の入力ミスを疑うところですが、登録もログインもコピペで入力しているので入力ミスはありえ無いと思っていました。 ## お問い合わせフォームからバグ報告 というわけでこれはバグだろうと、お問い合せフォームから以下のような内容の報告をしました。 > バグ報告 > > 新規登録時に設定するパスワード入力フォームでは20文字のパスワードで新規登録を行いました。 ですがそのパスワードでログインしようとするとパスワードが間違っているとのエラーでログインが出来ません。 > 仕方ないのでパスワード再発行ページから新しいパスワードを入力した所、パスワードは12文字以内というエラーが出ました。 > もしかしてと思い、最初に入力した20文字のパスワードの頭12文字だけを入力したらログインできてしまいました。 > 恐らく登録時にサーバ側では何も言わずに12文字で切って保存されているのではないでしょうか? > > これは登録フォームのバグだと思うので修正された方が良いかと思います。 上記の問い合わせを行ってから30分ほどするとサポートから以下の返答がありました。 > サーバ側ではそのような処理を行ってないのですが、どこの登録フォームから登録したか教えてもらえますでしょうか? なので、僕も確認のためにシークレットウィンドウを開いて、再度最初の登録からやりなおして再現手順を報告しなおすことにしました。 ## 再度のお問い合せ報告 再現手順を確認してみた結果、僕の最初の報告内容に間違いがあったことに気が付いたので以下の内容の報告を再度行いました。 > 再度登録処理を行なってみたところ、先ほどの問い合わせに書いた内容とは少し違っていたようですので改めてご説明いたします。 > > ■問題の説明 > 問題のフォームですが、登録自体はどこから行なってもよく、新規登録時に送られてくる確認メール内のワンタイムURLをクリックした後のパスワード登録フォームが問題のフォームになります。 > > ここで僕は、他ウィンドウで生成したランダムな20文字のパスワードをフォームに **コピペで貼り付け** て登録をしました。 > ですがパスワードのinputタグに`maxlength="12"`が付いていたために実際は12文字以降の文字が削られており、 **実際フォームに入力されていたのは12文字だけ** でした。その為、サーバ側には12文字の値が送信されており問題なく登録が行われていた、というのが真相でした。 > > maxlengthがついたフォームは手動入力時には制限に気づけるかもしれませんが、コピペ入力の場合はエラーにもならず勝手に後ろがなくなるというのがブラウザの挙動です。 > これは特に、パスワードのように伏字になる入力欄でかつ12文字や20文字などの長い文字列では、後ろが削られたことに非常に気づきにくいです。 > その為、 **僕は20文字のパスワードを入力したと誤解したまま** 登録が完了してしまったわけです。 > > それに対してログインフォームのパスワード欄にはmaxlengthの指定がなく20文字の貼り付けが出来てしまうので、パスワードが一致しないという自体に陥っていたようです。 > > > ■対応方法 > 対応方法は2つあると思います。 > > 一つはログインフォームのパスワード入力欄にも maxlength="12" を付けてしまうことです。この場合は登録フォームと同様に余分な文字が勝手に削られるので同じくコピペでログインしようとする限りは気づかずにログインできてしまうでしょう。 > ですが、ユーザは「 **20文字のパスワードを設定したはず** 」と誤解したまま利用を続けることになり、将来的に入力フォームの改修があった際に同様の問題が顕在化する危険があります。 > > より良いのは、登録フォームのmaxlengthを外して12文字を超える入力が出来るようにした上で、Javascriptやサーバサイドでのチェックでエラー表示をすることだと思います。 > こうしたやりかたなら、ユーザは改めてパスワードは12文字までと意識した上で登録をすることになるので問題は起きにくいと思います。 # 教訓:最大長チェックはmaxlengthではなくJavascriptとサーバサイドでやろう! ここで言うJavascriptってのは入力欄がリアルタイムで赤くなったり「12文字以内で入力してください」て横に表示されたりするアレのことです。 ## パスワード入力欄にmaxlengthを設定してはいけない maxlengthが設定されていると、コピペ入力時に後ろの文字列が削られたことに気づかないということが分かりました。 **特にパスワード入力欄のような伏字のフォームでは後ろの文字が切れたことに気付けません!** なので最大長を超えた入力を許した上で、Javascriptとサーバサイドのバリデーションでエラー表示をするのが親切だと思います。 ## パスワード入力欄以外でもmaxlengthは使わないほうが良い 郵便番号や電話番号の入力欄でもハイフンや空白の考慮が無く、277-0001 と入力したつもりが 277-000 となったり、090-1234-5678 と入力したつもりが 090-1234-56 となってしまったりして、イラッとしながらハイフンを削った後に更に後ろの数字を入力しなおしたりといった経験があると思います。 こんな場合も maxlength は外しておいてくれた方が嬉しいです。 これもやはり最大長を超えた入力を許した上で、Javascriptとサーバサイドのバリデーションでエラー表示をするのが親切だと思います。 ## 例外:使っても良いケース 例えばどういうケースで嬉しいか再度考えてみた。 その結果、変換不要で分割不要な1~4文字くらいまでの入力制限をしたい用途ならあってもいいのかな?と思えてきた。 - 4文字くらいまでの固定長の入力 - 例えば暗証番号とかクレジットカードのセキュリティコードとか - もし他に目から鱗で便利になるケースがあるようでしたら参考までに是非教えてください! # 補足 誤解される方もいるようなので補足しておきます。 ここでのmaxlengthの代わりのJavascript推奨は、あくまでUIの使い勝手向上の為であり、不正な値のポストを防ごうというのが目的ではありません。ぶっちゃけJSは無くても構いません。 ですが当然、 **サーバサイドでのバリデーションは必須** ですよ。 本質的にはJavascriptは不要で「maxlengthを外してサーバサイドバリデーション」これが提言の最小形です。 |
|
| 659位 |
|
|||
|
02:37:00 |
|
|
Grape は Ruby で API を書くのに便利なフレームワークです。 Grape 自体については fakestarbaby 氏がすでにすてきなエントリを書いてくださっています。 [Grape | API生成マイクロフレームワーク #Rails #Gems #Ruby #grape #api_builders - Qiita](http://qiita.com/items/fa03cf333744b7c70e72) ここではどうやってテストを書くのかということについて書いてみたいと思います。 ## 想定 * RSpec の受け入れテストの request_spec を使うよ * API は JSON を返すよ * API 用のサブドメイン(api.foobar.com)を切っているよ * JSON のテストは json_expressions を使うよ([参考](http://qiita.com/items/72bf43a0d07321d072dd)) * OAuth 2.0 の Provider になって Web Application Flow とかで認証しちゃったり * モックは FactoryGirl で作っちゃうよ ## helper まずは helper メソッドをいくつか api_helper.rb に定義します。 ### include Rack::Test::Methods Rack::Test::Methods をインクルードすることで、get, post 等々を事項した結果のテストをおこなえるようになります。 ```ruby:spec/support/api_helper.rb module ApiHelper include Rack::Test::Methods end ``` ### API 用のサブドメインのための設定 API 用のサブドメインを切ったのでそれに応じるように、ヘルパーメソッドを定義しておきます。 テストの場合、ドメイン名は example.com になるようなので、サブドメインは api.example.com にしています。さきほど api.foobar.com と書いたのはこのときの api.example.com と混同しないようにするためです。 ```ruby:spec/support/api_helper.rb module ApiHelper # (略) def domain 'api.example.com' end def default_rack_env { 'HTTP_HOST' => domain } end end ``` ### shared_context で全体の前提を定義! ```ruby:spec/support/api_helper.rb module ApiHelper # (略) shared_context 'api' do subject { eval("#{method}(url, parameters, rack_env)") } let(:parameters) { '' } let(:rack_env) { default_rack_env } end end ``` テスト用の request は HTTPメソッド名(url, parameters, rack_env) のようになるのですが、毎回書くのはかったるいので、subject に eval で押し込めました。 * [sinatraでテストの入門の入門(その1) - tomiの日記](http://tomiacannondale.hatenablog.com/entry/20110801/1312175754) * [sinatraでテストの入門の入門(その2)sinatraでrspec - tomiの日記](http://tomiacannondale.hatenablog.com/entry/20110829/1314631619) RSpec の subject を使うことで、it do end の中身は subject が実行された結果を受けるので、いきなり should から書き始めることができます。 ### HTTP の status と body を一気に試す shared_examples_for を定義! ```ruby:spec/support/api_helper.rb modulde ApiHelper # (略) shared_examples_for '200 Success' do its(:status) { should be(200) } its(:body) { should match_json_expression(result) } end shared_examples_for '201 Created' do its(:status) { should be(201) } its(:body) { should match_json_expression(result) } end shared_examples_for '204 No Content' do its(:status) { should be(204) } its(:body) { should eq('') } end shared_examples_for '404 Not Found' do its(:status) { should be(404) } its(:body) { should match_json_expression({ message: '404 Not Found' }) } end shared_examples_for '422 Unprocessable Entity' do its(:status) { should be(422) } its(:body) { should match_json_expression({ message: '422 Unprocessable Entity' }) } end end ``` 先ほどの subject の結果で得られる last_response は status / body などのプロパティを持っているので、RSpec の its でさらに取り出してしまいます。 20x などの成功時の match_json_expression の中身は、使用時に let を使い result という名前で定義します。 * [すごいぞRSpec(attribute of subject即ちits編) - ぷろぐらまねが](http://d.hatena.ne.jp/yohfee/20110225/1298639902) ### OAuth 2 認証 OAuth 2 の認証が必要な場合は、先ほどの shared_context を拡張します。 ```ruby:spec/support/api_helper.rb module ApiHelper # (略) def oauth2_auth(token) header('Authorization', "Bearer #{token}") end shared_context 'api' do let(:user) { FactoryGirl.create(:user) } let(:client_application) { FactoryGirl.create(:client_application, user: user) } let(:access_token) { FactoryGirl.create(:access_token, user: user, client_application: client_application) } before(:each) { oauth2_auth(access_token.token) } subject { eval("#{method}(url, parameters, rack_env)") } let(:parameters) { '' } let(:rack_env) { default_rack_env } end end ``` ## 実際の使い方 ここまで作った helper を使って、実際にテストを書いてみましょう。 ```ruby:spec/requests/api/articles_spec.rb describe Api do include ApiHelper include_context 'api' describe 'Articles' do let(:article) { FactoryGirl.create(:article, user: user) } describe 'GET /articles/:article_id' do let(:method) { 'get' } let(:url) { "/articles/#{article.id}" } let(:result) do { id: article.id, title: article.title, content: article.content, user: { id: user.id, name: user.name, profile_image: user.profile_image.url(:small), created_at: user.created_at.as_json, updated_at: user.created_at.as_json, } } end it_behaves_like('200 Success') end describe 'DELETE /articles/:article_id' do let(:method) { 'delete' } let(:url) { "/articles/#{article.id}" } it_behaves_like('204 No Content') it 'The article is successfully deleted.' do subject expect { Article.find(article.id) }.to raise_error ActiveRecord::RecordNotFound end end end end ``` リクエスト時の parameter については必要があれば let は上書きができるので、上書きします。method や url は上の方で定義しておいて、context をネストさせれば、無駄なコードを書くことなくテストのコードの集中することが可能だと思います。 subject を it do の中で呼び出せば、response でない項目もテストが可能です。 ## 全体として参考にしたもの * [RSpec の入門とその一歩先へ - t-wadaの日記](http://d.hatena.ne.jp/t-wada/20100228/p1) |
|
| 660位 |
|
|||
|
19:56:05 |
(Wantedly, Inc. 所属) |
|
インデントを変えた時のDiffってめっちゃ見にくいですよね。インデントを変えただけなのか、実は内容も変わっているのかGithub上で判断したい! そんな時はURLパラメーターに ``` ?w= ``` をつければ、githubが空白を無視した上でdiffのある部分だけを表示してくれます。 例 https://github.com/sixthsense/sixthsense/pull/16/files https://github.com/sixthsense/sixthsense/pull/16/files?w= |
|
| 661位 |
|
|||
|
04:12:11 |
(大阪大学 → 某社 所属) |
|
クラスメソッドは特異メソッドやシングルトンメソッドとも呼ばれる,インスタンスではなくクラス本体に紐付けられるメソッドです.
C++ や Java などでいう static メソッドのような感じのものです. ##一番オーソドックスなやり方 ```ruby class Hoge def self.hoge "hoge" end end # クラス名を直接指定して呼び出し Hoge.hoge #=> "hoge" # ::を使っても OK Hoge::hoge #=> "hoge" ``` 頭に self. を付けます. あまり使わないですが,外からでも定義できます. ```ruby def Hoge.huga "huga" end Hoge.huga #=>"huga" ``` クラス定義スコープ内では`self`が定義するクラス自身を指すため,`self.hoge`で良かったですが,外から定義するときは直接クラス名を指定する必要があります. また,クラス`Hoge`は定義済みである必要があります. ##複数定義する時 毎度頭に`self.`を付けるのが面倒な場合は ```ruby class Hoge class << self def hoge "hoge" end def piyo "piyo" end end end Hoge.hoge #=> "hoge" Hoge.piyo #=> "piyo" ``` のように書けます. これは1つ上のものと同様にクラス定義スコープ外でも定義できます. ```ruby class << Hoge def hoge "hoge" end def piyo "piyo" end end ``` 更に,`Hoge` がモジュールの場合は `extend`を使って書くことができ, ```ruby module Hoge extend self def hoge "hoge" end def piyo "piyo" end end Hoge.hoge #=> "hoge" Hoge.piyo #=> "piyo" ``` と書けます. `class << self` に比べてインデントが深くならないので,module の場合はこちらのほうがおすすめです. ##外の変数を取り込んで定義 あまりないですが,外の変数を取り込んで定義したい場合は`define_singleton_method`メソッドが使えます. ```ruby class Hoge a = 3 define_singleton_method :hoge do a end end Hoge.hoge #=> 3 ``` また,クラス定義スコープ外でも定義できます. ```ruby class Hoge # クラス定義スコープ end m = "hello " Hoge.define_singleton_method :greet do |i| m * i end Hoge.greet 3 #=> "hello hello hello " m = "goodby " Hoge.greet 2 #=> "goodby goodby " ``` |
|
| 662位 |
|
|||
|
23:42:27 |
|
|
Sassと比べると日本語の情報が少ないと感じたので、 Stylusを始めるときに、 **最低限これだけ知っておきたい**書き方をまとめてみた。 ## Stylusとは [Stylus](http://learnboost.github.io/stylus/)はnode製のCSSプリプロセッサ。 node.jsのexpressや、jadeと同じ人が開発してる。 **SassとLessより後発で、2つの良いとこ取り**をしていて、機能も豊富(らしい)。 インストールはnpmから ``` $ npm install -g stylus ``` 記述はscss記法のように、CSSっぽい書き方と ``` .hoge { color: red; &:hover { color: blue; } } ``` sass記法のように、インデントベースの記述もできる。 :(コロン)や;(セミコロン)も省略可能。 せっかくなのでこの記法で書いてみた。 ```` .hoge color red &:hover color blue ``` CSSっぽい書き方とインデントの書き方ができるが、拡張子「.styl」のみで、記法を混在させることもできる。 ## 変数と演算 変数に値を代入しておくことで、変数名で任意の場所から値を参照することができる。 ``` base-color = #00a6fc .title color base-color ``` "@"をつけると、直前のそのプロパティを参照することもできる。かっこ必要。 演算もできる。 ``` .container width 100% .sidebar width (@width * 0.3) ``` ## 条件分岐と繰り返し if/else文 ``` liquid = true .hoge if liquid width 100% else width 960px ... ``` for/in文 ``` array = 1 2 3 4 for num in array .mg-{num * 5} margin num * 5px ``` 1行目が **配列の定義**。,(カンマ)区切ることもできる。また、 **変数の展開**は{}でできる。 ## パーシャル StylusでもSassと同じように、ファイル名の前に\_(アンダースコア)を付けることで、コンパイル後にCSSファイルが生成されない。 Sassと違って、@import するときに_を省略することはできない。 ``` @import '_setting' .hoge ... ``` ## ミックスイン ミックスインの定義と参照方法は以下の通り。 ``` box-sizing(arg) -webkit-box-sizing arg -moz-box-sizing arg box-sizing arg .hoge box-sizing border-box ``` Sassのように、@mixinとか@includeとか付けなくてもいい。 ミックスインを参照しているところを見ると、プロパティと同じように使えている( **透過的mixin** )。 Stylusでは、 **CSSと全く同じ記述方法で抽象化**することができる。 ## 継承(extend) Stylusには、extendの機能もある。 ``` .btn ... ... .btn-blue @extend .btn background-color ``` ちなみに、@extendでも@extendsのどっちでもいい。 ## Stylusを使ってみて 個人的に、GruntやCoffeeScript、Jade、Yoeman、そしてStylus、フロント開発で使うツールをすべてnpmで管理できるのはいいと思った。 StylusはCSSと全く同じ記述で機能をラップできるので、includeするファイルだけ作っておけば、生のCSSしか書けない人でも使える。 SassかLessがプリプロセッサのデファクトになってる感はあるけど、Stylusという選択肢もありだと思う。 ## 参考にしたサイト - [公式サイト](http://learnboost.github.io/stylus/) - [stylus ドキュメント日本語訳](https://github.com/enja-oss/stylus) - [Stylusが目指すCSSプリプロセッサ](http://sssslide.com/speakerdeck.com/ahomu/stylusgamu-zhi-sucsspuripurosetusa) |
|
| 663位 |
|
|||
|
19:24:49 |
(フリーランス 所属) |
|
(2014-07-07 追記)
フォーム値取得&設定するjQueryプラグイン作ったので、良ければそちらも。 * https://github.com/ginpei/jQuery.formval.js (追記おわり) # 共通: 要素検索 `name`属性で検索するのが手っ取り早くて良いと思います。 ```html: <form id="my-form"> <input type="text" name="my-text" value="This is text." /> </form> ``` ```js: var $input = $('#my-form [name=my-text]'); ``` # テキスト ```html: <form id="my-form"> <input type="text" name="my-text" value="This is text." /> </form> ``` ```js: var val = $('#my-form [name=my-text]').val(); console.log(val); // => "This is text." ``` 数字のときは`val=Number(val)||0;`とかしてください。 # ラジオボタン ```html: <form id="my-form"> <input type="radio" name="my-radio" value="A" /> <input type="radio" name="my-radio" value="B" checked /> <input type="radio" name="my-radio" value="C" /> </form> ``` ```js: var val = $('#my-form [name=my-radio]:checked').val(); console.log(val); // => "B" ``` # チェックボックス ```html: <form id="my-form"> <input type="checkbox" name="my-checkbox" value="A" /> <input type="checkbox" name="my-checkbox" value="B" checked /> <input type="checkbox" name="my-checkbox" value="C" checked /> </form> ``` ```js: var $checked = $('#my-form [name=my-checkbox]:checked'); var valList = $checked.map(function(index, el) { return $(this).val(); }); console.log(valList); // => ["B", "C"] ``` # セレクトボックス(単数) ```html: <form id="my-form"> <select name="my-select"> <option value="A">This is A</option> <option value="B" selected>This is B</option> <option value="C">This is C</option> </select> </form> ``` ```js: var val = $('#my-form [name=my-select]').val(); console.log(val); // => "B" // おまけ: 値ではなくラベル文字列が欲しいとき var text = $('#my-form [name=my-select] option:selected').text(); console.log(text); // => "This is B" ``` # セレクトボックス(複数) ```html: <form id="my-form"> <select name="my-multi-select" size="3" multiple> <option value="A">This is A</option> <option value="B" selected>This is B</option> <option value="C" selected>This is C</option> </select> </form> ``` ```js: var val = $('#my-form [name=my-multi-select]').val(); console.log(val); // => ["B", "C"] ``` # 元ネタ というか、そのまんま。自分のブログから転載。 * [jQueryでフォームの値を取得する方法をまとめておくので、コピペでご利用ください。 | Ginpen.com](http://ginpen.com/2012/05/21/how-to-get-values-from-form-with-jquery/) |
|
| 664位 |
|
|||
|
17:17:21 |
|
|
そもそもプリペアドステートメントって何なんだ…。
PHP5.5からmysql...系が非推奨となり、将来的には削除されると言うことで、急遽PDOに全部変更する事になったので、「それって何だよ」から色々調べたのでメモしておきます。 あんまり頭良くないからManual読んでも分からないんですよね。 全部自分なりの解釈なので分かりにくかったらすみません。 間違ってたら指摘してください。 こちらがメイン記事 [▶︎ LABO IWASAKI 「PDOで色々やる」](http://labo-iwasaki.com/code/pdo-index.html) (他にも色々書いてます)  半泣きで覚書 by Takayoshi.Iwasaki |目次|内容| |:-----|:-----| |PDOとは|まずPDOって何?って所から| |プリペアドステートメント|もぅ意味分かんない言葉ばっか…。調べました。| |PDOで接続する|さっそく使ってみる。DBに接続| |PDOでデータを呼び出す|SELECT文です。間違い易い所の解説あり。| |PDOでfetch|mysql_fetchみたいなヤツです。| |INSERTする|[PDOでINSERT](http://qiita.com/items/0a69fd48018c4ebfd2f2)のページで解説しています。 |コードをまとめたヤツ|[こっちページ](http://qiita.com/items/2575a58c54e43cd59630)にまとめてある。INSERT,UPDATE,DELETEなど| |ログインフォーム|[【PDOでログインとパスワードのハッシュ】](http://qiita.com/items/7fb077ddeb06dbe427ff) ##PDOって何なんだ。 PDOは「PHP Data Objects」の頭文字をとった名称です。 って言われても「何がどうなってんの?」と思い、なかなか分からなかった。 データアクセス抽象化レイヤとか言われてるけど、「いや、だからソレ何?」って感じです。 調べたので解説します。 ####データアクセス抽象化レイヤ アプリケーションとDBMS(データベース管理システム)の間に入ってDBMSの違いを意識せずにアプリケーションを作成するもの。 要するに「色んなデータベースがあるけど、同じ様に書いても全部のデータベースに対応してますよ」って事です。(若干の差異はあるらしい) ####つまりPDOって… そのデータアクセス抽象化レイヤの一つで、PHP5.1からバンドルされてる(標準で使えるようになってる)ヤツです。 データベースを途中で変更しなければならない際などを考えると使いやすい。 プリペアドステートメントで結構動作が早いらしい。(自分では検証してない) 解説がややこしい。読んでて寝そう。 ##プリペアドステートメントって? これも意味分からん。 何でこうも馴染みのない横文字がいっぱい出てくるのか…。 プリペアドステートメントってのは、SQLを使う際に、その条件とか値が異なる場合、それをパラメータ化して色んな値や条件で使い回し(言い方悪いか?)が出来るものです。 ####プリペアド preparedは「用意された」とか「心構え」って意味です。 要するに実行する前に構文用意するよ。って事ですね。 ####ステートメント statementは「陳述」とか「声明」って意味です。 プリペアドで用意したものを実行します。って意味になるのかな。 ####つまりどういう意味? SQLを実行する前に、色々用意して、実行するって事。 そのSQLの条件とか値が異なる時は、そこだけ入れ替えて使えるもの。 これ、みんな理解して使ってるの?スゲーな。全然分からんかったわ。 ではでは、DBに接続してみましょう。 |PDOでMySQLに接続| |:--------------------------| ##PDOで接続する(DSN) PDOでMySQLに接続してみます。 DSNって言うのはData Source Name(データソースネーム)で、接続とか送信する時のデータを色々書いてあるよ。って所です。 ```HTML:接続 <?php try { $pdo = new PDO('mysql:host=ホスト名;dbname=DB名;charset=utf8','ユーザー名','パスワード', array(PDO::ATTR_EMULATE_PREPARES => false)); } catch (PDOException $e) { exit('データベース接続失敗。'.$e->getMessage()); } ?> ``` try に接続する情報を書いて catchでエラー情報を投げてます。 $pdo は変数名なので何でもイイ。$dbh とか $dsn とか書いてる人もいる。 ####PDO::ATTR_EMULATE_PREPARES また難しいのが出てきやがったな。 エミュレートプリペアーズ?? EMULATEは「模倣する」とか「手本とする」みたいな意味です。 主にコンピュータシステムに関して使われてるみたい。 プリペアドステートメントは、文を用意して、値や条件をバインドして、実行。みたいな感じなので、通信を2回するみたいで、ちょっと効率よくないんだって。 そういうのを「無駄な事せずに効率よくやるよ」っていうヤツ。 これを設定してるとSQLを正しく実行してくれる。(PHP5.2以降はデフォルトなので設定しなくて大丈夫) 誰も教えてくれないからハマって調べてしまった…。 ```HTML:PDO::ATTR_EMULATE_PREPARESなし <?php try { $pdo1 = new PDO('mysql:host=ホスト名;dbname=DB名;charset=utf8','ユーザー名','パスワード'); } catch (PDOException $e) { exit('データベース接続失敗。'.$e->getMessage()); } ?> ``` |PDOでデータを呼び出す(SELECT)文| |:----------------------------------------| ##PDOでデータを呼び出すSELECT文 ```HTML:SELECT <?php $stmt = $pdo->query("SELECT * FROM テーブル名 ORDER BY no ASC"); while($row = $stmt -> fetch(PDO::FETCH_ASSOC)) { $ttitle = $row["title"]; $tr = $row["r"]; $tk = $row["k"]; $tt = $row["t"]; $tm = $row["m"]; echo<<<EOF ヒアドキュメント内の表示部分 EOF; } ?> ``` 今まで使ってたmysql...系とあんまり変わらないけど、注意点がひとつあります。 始めてPDO使う時は意味分からない状態だったのでメモしときます。 ● -> query を使ってます。 ● 前の項目で接続した $pdoで query 以降の構文をやっちゃうよ。って事。 ● -> は「アロー演算子」って言って。日本語の「〜の」って考えると近いかもです。 ● あとに出てくる「execute();」と間違って使わない事に注意。[▶こっち INSERTの解説](http://qiita.com/tabo_purify/items/0a69fd48018c4ebfd2f2) ####PDO::FETCH_ASSOC これは mysql_fetch_assoc($変数) みたいなヤツ。 色んな記事読んでると「カラム名で添字を付けた配列を返します。」とかManual丸写しみたいな言葉が書いてあるけど、これってみんな理解してるの? バカだから分からんわ。 コレはつまり、そのSQLの中を連想配列で取得するよ。って事。 他にもPDO::FETCH_NUMとか色々あるけど、そいつらは次の機会に触れようと思っています。 最後にひとこと… ####言葉が難しい…。 プログラマーの人達ってスゴイと思うわ。 まず言葉が分からん。日本語が難しいって思ってしまう。 でも、そんなに頭良くないけどプログラマーになりたい!とか、自分でサイト作りたい!とか、何か仕事でやる事になったから…。とか、そういう人もいる。 自分は頭悪いけど、自分で何でも作りたいし、理解したい。「何となく」は嫌だ。 そんな気持ちでまとめてます。 |
|
| 665位 |
|
|||
|
01:47:15 |
|
|
JavaScriptの上位言語ですが、いろいろありすぎてよくわからない状況になっているので表にまとめてみました。 | |[Haxe](http://haxe.org)|[CoffeeScript](http://http://coffeescript.org/)|[Dart](http://http://www.dartlang.org/)|[Kotlin](http://kotlin.jetbrains.org/)|[JSX](http://jsx.github.com/)|[TypeScript](http://www.typescriptlang.org/)| |:-:|:--:|:----------:|:--:|:----:|:-:|:--------:| |登場時期|2005|2009|2011|2011|2012|2012| |主要開発元|HAXE FOUNDATION|Jeremy Ashkenas|Google|JetBrain|DeNA|Microsoft| |静的型付け|○|-|○|○|○|○| |型推論|○|-|-|○|△|△| |コード最適化|○|-|○|-|○|-| |JS互換|-|-|-|-|-|○| |クラス|○|○|○|○|○|○| |mixin|○|-|○|○|○|-| |名前空間|○|-|-|○|○|○| |構造的部分型|○|-|-|-|-|○| |ジェネリクス|○|-|○|○|○|-| |関数のオプション引数|○|○|○|○|○|○| |不変変数|-|-|○|○|△|-| |列挙型|○|-|-|○|-|○| |パターンマッチ|○|○|-|○|-|-| |全て式|○|○|-|-|-|-| |ラムダ式|△|○|○|○|○|○| |リスト内包表記|-|○|-|-|-|-| |コンパイル出力|JS/いろいろ|JS|JS/ネイティブ|JS/JVM|JS|JS |その他の特徴||インデント|C風の構文|Scalaの競合的な||ECMAScript6ベース| この表だけ見るとHaxe最強なんじゃないかという気がしていますが実際どうなんでしょうかね。 間違っている所とか、追加の項目とかコメントいただけると嬉しいです。 |
|
| 666位 |
|
|||
|
16:10:44 |
|
|
- [https://gist.github.com/PGMY/4991912](https://gist.github.com/PGMY/4991912) ⇒Gistに貯めていってたのをもってきました。 - [http://qiita.com/PGMY/items/2a9d1f6355693b6e9712](http://qiita.com/PGMY/items/2a9d1f6355693b6e9712) ⇒Qiita編 ----- 自分用めも。気になるもの・使ってみたいものと、実際利用して比較してみたいなーと思ってるチェックリスト的な for ios dev TODO : もうちょっと見やすく整理したい ----- # Library ## Coding - [REKit](https://github.com/zuccoi/REKit)⇒[公式ブログ](http://runlooprun.wordpress.com/2013/02/12/rekit-intro/) iOS, OS X の開発で使える NSObject の拡張コレクション ・REResponder: Block を使ったインスタンスの動的メソッド実装/上書き機能 ・REObserver: Block を使って KVO (Key-Value Observing) を実現する機能 + α ## Network - [AFNetworking](https://github.com/AFNetworking/AFNetworking) ASIHTTPRequestから乗り換えている人もいるみたい?⇒試してみたい - [ASIHTTPRequest](https://github.com/pokeb/asi-http-request/) 定番HTTP通信ライブラリ ## 認証 - [gtm-oauth](https://code.google.com/p/gtm-oauth/) GoogleのOAuth認証ライブラリ - [VENTouchLock](https://github.com/venmo/VENTouchLock) TouchID用ライブラリ ## Alert - [CustomAlertView](http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-uialertview-custom-graphics/) UIAlartのカスタムアラートビュー ## プログレス - [SVProgressHUD](https://github.com/samvermette/SVProgressHUD) かっこよくぐるぐるまわるプログレス。 - [MBProgressHUD](https://github.com/jdg/MBProgressHUD) これもぐるぐるまわしたりできるカスタムアクティビティダイアログ。 - [DejalActivityView](https://github.com/Dejal/DejalActivityView)⇒[公式](http://www.dejal.com/developer/?q=developer/dsactivityview) シンプルなLoading…表示のアクティビティ ## レビュー - [Appirater](https://github.com/arashpayan/appirater) レビューしてください!を表示してくれるライブラリ。 ## ウォークスルー・チュートリアル - [EAIntroView](https://github.com/ealeksandrov/EAIntroView) - [JazzHands](https://github.com/IFTTT/JazzHands) アニメーション・トランジションを使ったウォークスルーを作成できるIFTTT製ライブラリ ## フィードバック - [AAMFeedback](https://github.com/fladdict/AAMFeedback) ユーザーフィードバックフォーム ## 画像フィルタ・選択・表示 - [GPUImage](https://github.com/BradLarson/GPUImage) 高速フィルタライブラリ - [ELCImagePickerController](https://github.com/elc/ELCImagePickerController) UIImagePicker風にカメラロールの画像を複数選択できるライブラリ - [AQGridView](https://github.com/AlanQuatermain/AQGridView) 画像の上にチェックマークを置いて複数画像を選択できるライブラリ - [PSCollectionView](https://github.com/ptshih/PSCollectionView) Pinterest風に画像をタイル表示するライブラリ - [iCarousel](https://github.com/nicklockwood/iCarousel) ミュージックのアルバムが表示される時みたいなカバーフローUIを実装できるライブラリ - [grabKit](https://github.com/pierrotsmnrd/grabKit) 各種Web画像サービスに対応した、画像表示ライブラリ ## グラフ系 - [core plot](https://code.google.com/p/core-plot/) OSX,iOS向けグラフ描画ライブラリ - [iOSPlot](https://github.com/honcheng/iOSPlot) ## アニメーション - [BCGenieEffect](https://github.com/Ciechan/BCGenieEffect) ぐにゅっとまがるエフェクト ## TableView - [TISwipeableTableView](https://github.com/thermogl/TISwipeableTableView)⇒[公式](http://thermoglobalnuclearwar.com/opensource/) Twitterクライアント等で使われているスワイプして別のViewがCellに表示されるライブラリ ## CollectionView - [CircleLayout](https://github.com/mpospese/CircleLayout) 円形に並ぶレイアウト ## 引っ張って更新 - [EGOTableViewPullRefresh](https://github.com/enormego/EGOTableViewPullRefresh) ## ステータスバー - [JDStatusBarNotification](https://github.com/jaydee3/JDStatusBarNotification) ## ModalView - [SimpleModal](https://github.com/plasm/simplemodal)⇒[公式](http://simplemodal.plasm.it/) ## TextView - [GrowingTextView](https://github.com/HansPinckaers/GrowingTextView) Messangerの入力するところみたいに入力によって大きくなるテキストビュー ## セグメントコントロール - [AKSegmentedControl](https://github.com/alikaragoz/AKSegmentedControl) ## Menu - [AwesomeMenu](https://github.com/levey/AwesomeMenu) Pathで使われているメニューナビゲーション - [RRCircularMenu](https://github.com/iartem/RRCircularMenu) Catchで使われていた円形メニュー - [TimeScroller](https://github.com/andrewroycarter/TimeScroller) Pathで使われている時計の表示を再現させたライブラリ - [ECSlidingViewController](https://github.com/ECSlidingViewController/ECSlidingViewController) 高速でスライドしてメニューが表示されるライブラリ ## データ - [sqlcipher](https://github.com/sqlcipher/sqlcipher)⇒[公式](http://sqlcipher.net/) SQLiteのデータを暗号化するライブラリ - [MagicalRecord](https://github.com/magicalpanda/MagicalRecord) CoreDataをより簡単に扱いやすくするライブラリ - [ObjectiveRecord](https://github.com/supermarin/ObjectiveRecord) Ruby on Rails風にCoreDataを利用できる軽量ライブラリ ## Keychain - [sskeychain](https://github.com/soffes/sskeychain) - [LUKeychainAccess](https://github.com/TheLevelUp/LUKeychainAccess) ## SNS - [FacebookLikeView](https://github.com/brow/FacebookLikeView) FacebookのLikeボタン ## StatusBar - [MTStatusBarOverlay](https://github.com/myell0w/MTStatusBarOverlay) ステータスバーのオーバーレイを実装できるライブラリ ## 表示・デザイン - [iOS-blur](https://github.com/JagCesar/iOS-blur) iOS7っぽいブラーを実装できるライブラリ - [UI7Kit](https://github.com/youknowone/UI7Kit) iOS7以前のバージョンでもiOS7風デザインにできるUIKitライブラリ ---- # Web ## ホーム画面に追加 - [mobile-bookmark-bubble](http://code.google.com/p/mobile-bookmark-bubble/) iPhone/iPad用ブックマークをホーム画面に追加を促す ---- # Service ## プッシュ通知 - [Parse](https://www.parse.com/) プッシュ通知をするためのBaaSサービス。無料枠あり。100万まで? ## クラッシュレポート - [Crashlytics](http://try.crashlytics.com/) ## 開発&テスト - [TestFlight](https://testflightapp.com) 無料。デバッグ用のレポート送信SDKあり - [Appium](https://github.com/appium/appium)⇒[公式](http://appium.io/) iPhone自動操作テストツール - [AppSales-Mobile](https://github.com/omz/AppSales-Mobile) AppStoreの売り上げを確認するアプリ ---- # その他 ## xcode - [XcodeColors](https://github.com/robbiehanson/XcodeColors) NSLogをカラーリング # まとめサイト - [cocoa CONTROLS](http://www.cocoacontrols.com/) ソースと一緒にiOS,OSX用のUIが載ってるサイト - [lamb](http://lamb-inside.appspot.com/) iOSで使われるライブラリを画像つけて紹介してるサイト - [iOS Frameworks](http://iosframeworks.com/) # 開発役立ちサイト - [スマホの数字あれこれを探す時に役立つサイト](http://case-mobile-design.com/smartphonetoukei/) - [[iOSのバージョン別シェアを知る]Apple App Store Distribution](https://developer.apple.com/support/appstore/) - [[Androidのバージョン別シェアを知る]Google Android Developers](http://developer.android.com/about/dashboards/index.html) - [[スマホ別の画面解像度を知る]casemobile > スマートフォン画面解像度一覧](http://case-mobile-design.com/devices/resolution-list/) - [iOS デバイス一覧表](http://serennz.sakura.ne.jp/sb/log/eid225.html) |
|
| 667位 |
|
|||
|
20:03:37 |
(+Beans 所属) |
|
今さら感たっぷりですが、iOS 6から(?)UILabelで行間や文字間を簡単に調整出来るようになっていたのでメモ。 iOS 7では必須かも。 # 文字間を調整する。 まずは文字間(LetterSpacing)です。 **NSMutableAttributedString**の**NSKernAttributeName**を利用します。 ```objc:XxxViewController.m #define FONT_SIZE 18.0f - (void)viewDidLoad { [super viewDidLoad]; NSString *text = @"テキスト"; CGFloat customLetterSpacing = 10.0f; // NSAttributedStringを生成してLetterSpacingをセット NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text]; [attributedText addAttribute:NSKernAttributeName value:[NSNumber numberWithFloat:customLetterSpacing] range:NSMakeRange(0, attributedText.length)]; UILabel *label = [[UILabel alloc] initWithFrame:FRAME]; label.font = [UIFont systemFontOfSize:FONT_SIZE]; // label.textではなくattributedTextを使用。 label.attributedText = attributedText; [self.view addSubview:label]; } ``` # 行間を調整する。 お次ぎは行間(正確には行の高さ)です。 **NSMutableParagraphStyle**を利用するのですが、残念ながらsetLineHeightというプロパティがないので、**setMinimumLineHeight**と**setMaximumLineHeight**を利用して行間をカスタムします。 ```objc:XxxViewController.m #define FONT_SIZE 18.0f - (void)viewDidLoad { [super viewDidLoad]; UIFont *font = [UIFont systemFontOfSize:FONT_SIZE]; NSString *text = @"複数行分のテキスト"; // カスタムLineHeightを指定 CGFloat customLineHeight = 32.0f; // パラグラフスタイルにlineHeightをセット NSMutableParagraphStyle *paragrahStyle = [[NSMutableParagraphStyle alloc] init]; paragrahStyle.minimumLineHeight = customLineHeight; paragrahStyle.maximumLineHeight = customLineHeight; // NSAttributedStringを生成してパラグラフスタイルをセット NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text]; [attributedText addAttribute:NSParagraphStyleAttributeName value:paragrahStyle range:NSMakeRange(0, attributedText.length)]; UILabel *label = [[UILabel alloc] initWithFrame:FRAME]; label.font = font; label.numberOfLines = 0; label.attributedText = attributedText; [self.view addSubview:label]; } ``` これで、32ptのLineHeightで表示されます。 (シミュレータのキャプチャを計ってみましたがジャスト32ptになってました。) 和欧混合文で英文字のみの行と日本語も含まれる行でLineHeightが違ってしまう。ということもなかったです。 ### 行間を詰めたい場合は? ちなみに前述の場合、デフォルトよりも行間を拡げる場合は良いのですが、詰めることは出来ません。 そんな時はNSMutableParagraphStyleを以下のように変えると詰めも出来ます。 ```objc:XxxViewController.m NSMutableParagraphStyle *paragrahStyle = [[NSMutableParagraphStyle alloc] init]; paragrahStyle.lineSpacing = - 4.0f; ``` ※ 自分の環境では、lineSpacingに負の値を入れた場合で text に改行コードが含まれていると、その行ではlineSpacingが無視されてしまうようでしたのでご注意を。 ### cssのように"1.4em"みたいに指定したい。 ここまではポイント指定でしたが、cssのような指定も出来るっぽいです。 その場合は、同じくNSMutableParagraphStyleを書き換えます。 ```objc:XxxViewController.m NSMutableParagraphStyle *paragrahStyle = [[NSMutableParagraphStyle alloc] init]; paragrahStyle.lineHeightMultiple = 1.4f; ``` この**lineHeightMultiple**は、cssのようなフォントに対しての倍率、というよりはデフォルトのlineHeightに対しての倍率なのかな?って感じですが、正確に計ってみてはいないので詳しい人は教えてもらえると有り難いです(^〜^;) また、paragrahStyleには以下のread, writeなプロパティもあるのでご参考まで。 CGFloat lineSpacing; CGFloat paragraphSpacing; NSTextAlignment alignment; CGFloat firstLineHeadIndent; CGFloat headIndent; CGFloat tailIndent; NSLineBreakMode lineBreakMode; CGFloat minimumLineHeight; CGFloat maximumLineHeight; NSWritingDirection baseWritingDirection; CGFloat lineHeightMultiple; CGFloat paragraphSpacingBefore; float hyphenationFactor; |
|
| 668位 |
|
|||
|
21:54:09 |
|
|
モジュールクラスのオブジェクトの特異メソッド。
モジュールメソッドと呼んで良いのか迷いますが、ここでは、モジュールメソッドと呼んでおきます。 日本語での説明は苦手なので、最初に、何がしたいのかを例として書いておきます。 また長い記事なので、結果だけを知りたい方は、下の方のまとめをご覧ください。 # はじめに例 ```ruby module Hoge def self.hoge puts "hoge" end end Hoge.hoge #=> hoge class Foo include Hoge extend Hoge end Foo.new.hoge #=> undefined method `hoge' for #<Foo:0x0000010105cf60> (NoMethodError) Foo.hoge #=> undefined method `hoge' for Foo:Class (NoMethodError) ``` こんな感じで、mixinやinstance化などされたくない、直接呼ばれるためだけのメソッド。 たまに作りたいですよね? でも、複数書くとなると、クラスメソッドよりめんどくさい * クラスメソッドの場合 ```ruby class FooClass class << self def method1 ; end def method2 ; end end def self.method3 ; end end ``` * モジュールメソッドの場合 ```ruby module HogeModule def self.method1 ; end def self.method2 ; end def self.method3 ; end end ``` 毎回selfが必要になってめんどくさい。 **『なんかスッキリ書く方法ないの?(´・ω・`)』**というのが今回の発端 # 書き方 ## それなりに見かける書き方 ```ruby module Bar extend self def method1 ; end def method2 ; end end ``` この書き方で、『インスタンスメソッドは定義されない』とか、『module_functionの代わりになる』という記述を見かける場合もありますが、そんな事はないです。 ### インスタンスメソッドも定義される ```ruby module Bar extend self def method1 ; end def method2 ; end end puts "-- instance methods" puts Bar.instance_methods.grep(/method\d/) puts "-- private instance methods" puts Bar.private_instance_methods.grep(/method\d/) puts "-- singleton methods" puts Bar.singleton_methods.grep(/method\d/) ``` * 実行結果 ``` -- instance methods method1 method2 -- private instance methods -- singleton methods method1 method2 ``` モジュールメソッドとインスタンスメソッドが作成されます。 インスタンスメソッドを作成した後、自身にextendするからです。 コードをイメージしやすいように書き直すと以下のような感じです。 ```ruby module Bar def method1 ; end def method2 ; end end module Bar extend self end ``` またパブリックなので、以下のようにincludeすれば他のクラスから呼べます ```ruby module Bar extend self def method ; end end class Foo include Bar end Foo.new.method ``` ### module_functionとの違い [module_function](http://doc.ruby-lang.org/ja/1.9.3/class/Module.html#I_MODULE_FUNCTION) > モジュール関数とは、プライベートメソッドであると同時に モジュールの特異メソッドでもあるようなメソッドです。 > 例えば Math モジュールのメソッドはすべてモジュール関数です。 * つまり、モジュールメソッドとプライベートなインスタンスメソッドを作ります ```ruby module Bar def method1 ; end module_function :method1 end puts "-- instance methods" puts Bar.instance_methods.grep(/method\d/) puts "-- private instance methods" puts Bar.private_instance_methods.grep(/method\d/) puts "-- singleton methods" puts Bar.singleton_methods.grep(/method\d/) class Foo include Bar end Foo.new.method1 ``` * 実行結果 ``` -- instance methods -- private instance methods method1 -- singleton methods method1 sample.rb:19:in `<main>': private method `method1' called for #<Foo:0x00000102058338> (NoMethodError) ``` プライベートメソッドも作られる ## モジュールメソッドの書き方 以上までのような事を踏まえて、以下のように落ち着きました。 ```ruby module Bar module ModuleMethods def method1 ; end def method2 ; end end extend ModuleMethods end puts "-- instance methods" puts Bar.instance_methods.grep(/method\d/) puts "-- private instance methods" puts Bar.private_instance_methods.grep(/method\d/) puts "-- singleton methods" puts Bar.singleton_methods.grep(/method\d/) ``` * 実行結果 ``` $ ruby sample.rb -- instance methods -- private instance methods -- singleton methods method1 method2 ``` はい。 モジュールメソッドだけしか居ませんね。目標達成です。 # まとめ ```ruby module Hoge module ModuleMethods def method1 ; end def method2 ; end end extend ModuleMethods end ``` * moduleの中でmoduleを作成して、extendする * module_functionやextend selfはちゃんと使い分ける * アドバイスを頂きましたが、```class << self```を使っても書けるようです * 下記「追記(2015/1/16)」の項に記載しました いや〜。 今日も不毛に悩みましたね!!(^q^ ご指摘・アドバイスお待ちしております〜<(_ _)> ## 追記(2015/1/16):アドバイスをいただきました! hilohiroさんにコメントで教えていただきました。 > モジュールもClassクラスのインスタンスは持つので、クラスメソッドを定義するときと同じように書けると思います。 言われてみればそうですね。 気が付きませんでした。 確かにModuleもClassクラスのインスタンスですので、特異クラスとしてオープン出来る気がします!! という事で、実際にやってみましょう。 ### やってみた ```rb module Bar class << self def method1 ; end def method2 ; end end end puts "-- instance methods" puts Bar.instance_methods.grep(/method\d/) puts "-- private instance methods" puts Bar.private_instance_methods.grep(/method\d/) puts "-- singleton methods" puts Bar.singleton_methods.grep(/method\d/) ``` * 実行結果 ```shell-session $ ruby test.rb -- instance methods -- private instance methods -- singleton methods method1 method2 ``` 得たい結果となっていますね。 こちらの方がスッキリしていると思います。 # おまけ ## 「include 俺」でクラスメソッド モジュールをインクルードしてクラスメソッドにしたい時 ```ruby module Bar def self.included(klass) klass.extend ClassMethods end module ClassMethods def bar puts "bar" end end end class Foo include Bar end Foo.bar #=> bar ``` よく書かれるイディオムとはいえ、漂うやりたい放題感w |
|
| 669位 |
|
|||
|
11:26:25 |
|
|
公開されているコーディング規約をいくつかまとめてみました。
Apple https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html CookPad(日本語) https://github.com/cookpad/styleguide/blob/master/objective-c.ja.md Wantedly https://github.com/wantedly/objective-c-style-guide/tree/master NYTimes https://github.com/NYTimes/objective-c-style-guide http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml (日本語訳)http://www.textdrop.net/google-styleguide-ja/objcguide.xml Luke Redpath http://lukeredpath.co.uk/blog/2011/06/28/my-objective-c-style-guide/ cocoadevcentral http://cocoadevcentral.com/articles/000082.php Sam Soffes https://gist.github.com/soffes/812796 Adium https://trac.adium.im/wiki/CodingStyle github https://github.com/github/objective-c-conventions RobotsAndPencils https://github.com/RobotsAndPencils/objective-c-style-guide raywenderlich https://github.com/raywenderlich/objective-c-style-guide |
|
| 670位 |
|
|||
|
16:44:40 |
|
|
ひとつのMySQLサーバーだけでなく、もうひとつ別のMySQLサーバーに接続したい欲張りさんのための方法。 ```database.yml development: # ひとつめはいつもどおり adapter: mysql2 encoding: utf8 database: database1 username: hoge password: hogehoge host: database1.url port: 3306 pool: 5 timeout: 5000 test: # 略 production: # 略 # ふたつめ database2: adapter: mysql2 encoding: utf8 database: database2 username: fuga password: fugafuga host: database2.url port: 3306 pool: 5 timeout: 5000 ``` modelを作成し、establish_connectionの引数にdatabase.ymlに追加した設定を指定する ```futatsume.rb class Futatsume < ActiveRecord::Base establish_connection(:database2) end ``` ここまでできればあとはcontrollerから同じ使い方ができる ```test_controller.rb def test @futatsume = Futatsume.where(["name = ?", "test"]) end ``` しかししかし、 modelひとつひとつにestablish_connectionでデータベースを指定すると、 model毎にコネクションをはるっぽいので、DB接続コネクション数が異常に増えることになる。 なので、こうするのがベター ```futatsume.rb class Futatsume < ActiveRecord::Base establish_connection(:database2) end ``` ```futasume_user.rb class FutatsumeUser < Futatsume end ``` ```futasume_task.rb class FutasumeTask < Futasume end ``` もとのFutatsumeクラスを継承するかたちに実装する。 使い方は同じ ```test_controller.rb def test @futatsume_user = FutatsumeUser.where(["name = ?", "test"]) @futasume_tasks = FutatsumeUser.where(["id > ?", 10]) end ``` |
|
| 671位 |
|
|||
|
15:33:30 |
|
|
# 書いてる人
プログラミング学習サービスやら、ペットサロン予約サービス、風俗検索サービスなど色々とやっている「かずきち」です。 ■運営サービス一部 http://crazy-wp.com/ http://webukatu.com/ 新宿のホストから不動産・保険の営業を経て、HTMLって何?という状態から3ヶ月独学でプログラミングやデザインを学び、IT業界で1年間実務経験を積んで年収は1本超え。現在は起業家としてサービス運営やら不動産運営をしています。 Qiita内にそれ系の記事も書いてます。 <a href="http://qiita.com/kazukichi/items/7379b75fba2f90d3cf45" target="_blank">エンジニアで稼ぐために大切な13のコト</a> <a href="http://qiita.com/kazukichi/items/aeba286c2a750081e5c0" target="_blank">WEBサービスで起業したい人に読んで欲しい18のコト</a> #アセンブラってなに? アセンブリ言語のこと。C言語よりももっと機械に近く、機械語(1と0の世界)を人間に分かり易くした言語。 C言語などを作るための言語。 C言語には1行のプログラムには何行ものアセンブリ言語が含まれている。 洗濯機など家電に使われるマイコンと呼ばれる小さなコンピュータで使われている。 C言語などで1行で書けるものが、アセンブラだと何行も書かないといけないのでとても大変だが、 少ないメモリの中でやりくりが出来たりする。 #レジスタってなに? CPU内にあるメモリーのこと。すごく高速だが、とても値段が高いのであまり容量はない。 他にも「主記憶装置」と呼ばれるメモリーもあり、CPU内のメモリーよりも大容量で安いが、レジスタよりは低速。 レジスタには、「次に実行する命令」「次に実行する命令のある場所のアドレス」「主記憶装置にアクセスするためのセグメント(リアルモード)もしくはセレクタ (プロテクトモード)」「レジスタと呼ばれる変数」 などが記憶されている。 32ビットCPUというのは、このレジスタが32ビットってこと。 ただ、速度差がありすぎてCPUのパフォーマンスが生かせないので、 最近では、CPUと主記憶装置(メモリー)との間に「キャッシュ」と呼ばれる主記憶装置で使われるメモリーの10倍ほど速いメモリーが使われて、速度差を埋めている。 #メモリーって?命令が入っているってどういうこと? 命令はプログラムのこと。 私たちが何かしらの言語で書いたプログラムは、最終的には1と0の機械語になる。 1と0は結局のところは電気信号のことで、「電気送る、送らない」の羅列。 メモリーがなければ、プログラムを書いた瞬間に電気が「送る、送らない」でCPUへビビビっと送られるので、 その都度リアルタイムでプログラムを書いて動かさないといけない。 メモリーはその電気信号を貯めておける仕組み。 CPUはメモリーに貯めてある電気信号を都度受け取ることで、あらかじめ書かれた長いプログラムが勝手に処理されていく。 #そもそもコンピュータってなに? でっかい電卓。 しかも1と0の2進数しか使えない。 でも、人間が扱う時には2進数では桁が大きくなりすぎて分け分からなくなるから、16進数で表示して使う。 #CPUとメモリのやり取り CPUとメモリを料理に例えると CPU ⇒ コック CPU内にあるレジスタ(高速なメモリ) ⇒ まな板 メモリ(主記憶装置) ⇒ 冷蔵庫 レジスタに全部の命令やそれに使うための値を置いておければいいが、レジスタは高価なのでそんなに記憶容量がない。 なので、冷蔵庫に調理するために必要なもの(命令「この食材を千切りして~という手順」や値)を入れておいて、 必要な時に冷蔵庫からまな板へ移して一つ一つ調理していき、調理したもの(結果)をまた冷蔵庫(メモリ)へ保存するという流れ。 実際のCPUでは、 フェッチ・・・命令文をメモリから取り出す デコード・・・命令文に書いてある値や命令(調理方法)の保管場所(メモリ番地)を取り出す 実行・・・命令が実行される されていて、 「プログラムカウンタ」と呼ばれるメモリに次の命令が格納されたメモリ番地が入っていて、 プログラムカウンタにしたがって命令が取り出されている。 ※プログラムカウンタは電源ONした時点で「0番地」を指すので、そこから命令が実行されていく。 #メモリの中身 メモリは基本、8bit(0か1を8個並べたもの)ごとに1区画として、1区画ごとにアドレス(番地)が振られている。 #アセンブリ、アセンブラ、アセンブル アセンブリ言語で書かれたファイルは機械には理解できないので、言語プロセッサを使って変換する。この、言語プロセッサ(翻訳するソフト)を「アセンブラ」といい、機械語に変換することを「アセンブル」という。 徳川家康、徳川家光、徳川家継、的なややこしさ。 本当は「アセンブラ」というと機械語に翻訳するソフトのことだが、今ではアセンブラ=アセンブリ言語になってる。 ``` pushl %ebp movl %esp, %ebp ``` みたいなアセンブリ言語をアセンブルすると ``` B8FF00 ``` という機械語になる。(機械語は2進数じゃなく、16進数で表す) アセンブルされたバイナリデータは機械が理解できる。 #アセンブリと電気回路 パソコンは結局のところ、豆電球の回路を集めたようなもの。 理科の時間にやったこんなやつ  乾電池部分がスイッチだとして、「スイッチ入れる」「スイッチ入れない」で「1」か「0」かを表してる。 ##豆電球で論理回路を作ってみる AND回路と言われるものを作ってみる。 基本情報技術者試験とかの最初らへんに出てくる記号のやつです。 AとBという2つの入力があって、Yが出力(結果)。 AND回路の場合、AとBが両方「1」の時にYが「1」になり、それ以外は全部Yは「0」になる。 これを豆電球回路で表すとこんな感じ。 ```text: スイッチA スイッチB __/___/___ | | | | | ◎ 豆電球(Y) 電源 = | | | |__________| ``` 機械語で「11」と入力すれば、「1」が出てくるみたいなイメージ。 こんな回路を組み合わせると足し算とかの計算ができる。 こういう回路が複雑にいくつも組み合わさって出来ているのがPC。(あくまでイメージだけど) なので、中の回路によって機械語が異なる。 機械語が異なるということは、アセンブリ言語も異なる。 (使えるアセンブリ言語の命令が限られていたりする。) #マイクロコンピュータとは? 略して、「マイコン」という。パソコンの超ちっちゃい版。アセンブリを最初学ぶにはちょうどいい機器。 CPUやらメモリやら周辺回路が1つのICチップにギュッと集積されたもので、 「ワンチップマイクロコンピュータ」とも呼ばれ、「ワンチップマイコン」ともいう。 名前ありすぎ。 マイコンの初代は「4004」といい、その後Intel社が「8080」という8ビットマイコンを出して爆発的に広まった。 マイコンはこんなやつ。 その後、そのマイコンを使ったキットで「TK-80」「TK-85」というものが登場。 キーボードと表示器(元祖ディスプレイ)がついていて、そいつがパソコンの原型。 その後、16ビットの「8086」から「i486」、「Pentium」となって今に至る。 #PICと他のマイコンの違い PICは「ハーバード型」で一般のPCは「ノイマン型」 ハーバード型では、プログラム(命令)を格納するメモリとデータを格納するメモリが別になっている。 家に冷蔵庫が2つある感じ。 (ノイマン型ではプログラムも扱うデータも一緒のメモリに格納している) #アセンブリの記述方法 2種類ある。アセンブラによって違う。 ``` 1.Intel記法 [ニーモニック] [操作先] [操作元] 2.AT&T記法 [ニーモニック] [操作元] [操作先] ``` ※ニーモニックはpushlとかmovlみたいな英単語っぽいやつ。 LinuxはAT&T記法で出力してくるので、そっちを使った方が良い。 ... To be continued |
|
| 672位 |
|
|||
|
09:57:20 |
(Akatsuki Inc. 所属) |
|
## 概要 - Rubyのprivateメソッドを支える、2つのルール - ルールを知る事によるメリット ※前者は[メタプログラミングRuby](http://www.amazon.co.jp/gp/product/4048687158/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4048687158&linkCode=as2&tag=kidachi-22)より。 ## 2つのルール 1. privateメソッドを呼び出す時は、レシーバは指定できない - 自分(self)以外のオブジェクトのメソッドを呼び出すには、レシーバを指定する必要がある ## ルール1 ### privateのついたメソッドを呼び出す時は、レシーバは指定できない 例として、レシーバ指定しないパターンと、 明示的にレシーバ指定してしまうパターンを試してみます。 ```ruby class Sample1 def s1_call_private s1_private # 通常の呼び出し end def s1_call_private_with_receiver self.s1_private # あえてレシーバ(self)を明示して指定 end private def s1_private p "Welcome to my private room!" end end s1 = Sample1.new s1.s1_call_private => "Welcome to my private room!" s1.s1_call_private_with_receiver => NoMethodError: private method `s1_private' called for.. ``` ルール通り、レシーバを明示した方(`s1_call_private_with_receiver `)はNoMethodErrorが出る。 ## ルール2 ### 自分(self)以外のオブジェクトのメソッドを呼び出すには、レシーバを指定する必要がある ※こちらは「privateメソッドのためのルール」という訳ではなく、至極当然の話ですが ```ruby class Sample2 def s2_call_sample1 s1 = Sample1.new s1.s1_call_private # 通常の呼び出し end def s2_call_sample1_without_receiver s1 = Sample1.new s1_call_private # レシーバ(s1)なしで呼んでみる end end s2 = Sample2.new s2.s2_call_sample1 => "Welcome to my private room!" s2.s2_call_sample1_without_receiver => NameError: undefined local variable or method `s1_call_private' for.. ``` 当たり前ながらs2_call_sample1_without_receiverはエラー。 #### ここで、ルール1とルール2のコンフリクトが起きる。 つまり、 外部(s2)からs1のメソッドを呼び出そうとするなら、 `s1.s1_private `のようにs1というレシーバをつける必要があるが(ルール2)、 <strong>(s1の)privateメソッドはレシーバを付けて呼び出す事を許してくれない</strong>(ルール1)。 コードに示すと以下になります。 ``` class Sample2 def s2_call_sample1_private s1 = Sample1.new s1.s1_private # 外部s1のメソッドを呼びたいので、レシーバs1は必須(ルール2)。 # でもprivateメソッドなのでレシーバは付けられない(ルール1)。 end end ``` 結果として、<strong>privateメソッドは外部から呼び出すことが出来ない</strong>。 これがprivateメソッドの内部的な仕組みです。 ## つまり Rubyのprivateメソッドは、 - 呼び出す際にレシーバを指定できない (し、逆にそれ以外は「ごく普通のメソッド」だと言える) ## このことを理解しておくメリット privateの挙動の混乱を防ぎやすい。 例えば、 JavaやC#の常識が通用しないRubyのprivateメソッド http://blog.jnito.com/entry/20120315/1331754912 上記記事にもあるように、JavaやC#と違いRubyは #### スーパークラスのprivateメソッドはサブクラスからも呼び出すことが出来る。 これは定義だけ覚えているとすぐに分からなくなりそうですが、 #### privateメソッドは呼び出す際にレシーバを指定できない(だけ) という今回学んだルールと、 #### サブクラスからなら、スーパークラスのメソッドはレシーバを指定せずに呼び出せる という(ごく当たり前の)決まりを掛け合わせて考えれば、 #### スーパークラスのprivateメソッドはサブクラスからも呼び出すことが出来る ことは至極当然なものとして頭に入ってくるのではないでしょうか。 |
|
| 673位 |
|
|||
|
11:23:56 |
(株式会社キュリオシティソフトウェア 所属) |
|
# はじめに 「無料で音楽聴き放題!! - ネットラジオ」というアプリをリリースしました。AppleのiTunes Radioが日本ではリリースされないのでそれっぽいものをという感じで、ストリーミングで流れている楽曲で気に入ったものがあればiTunesで購入できるようにしています。 https://itunes.apple.com/jp/app/nettorajio-j-popmo-wu-liaode/id769979888?mt=8 以降のメモはそのとき調査して実装したものです。音楽プレイヤーアプリはゲームアプリともツール系アプリとも違う独特の処理を必要としているので、あれどうやるんだっけ的な物になっています。  # 画面ロック時の処理 ## ロック時でも再生を続ける プロジェクト設定からBackground Audio&AirPlayをONにする。また次のようなコードを実装する。 ```objc AVAudioSession *session = [AVAudioSession sharedInstance]; //ロック時も再生のカテゴリを指定 [session setCategory:AVAudioSessionCategoryPlayback error:nil]; //オーディオセッションを有効化 [session setActive:YES error:nil]; ``` 上記は一度だけ最初に実行すればよい。再生を管理するシングルトンインスタンスの初期化時などにやればいい。 ## コントロールセンターに再生ボタンなどのコントロールを表示する ロック画面やコントロールセンターの再生ボタンを表示するには次のようにする ```objc [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; ``` 再生ボタンなどのイベントを処理するには次のように実装する。 ```objc - (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent { if (receivedEvent.type == UIEventTypeRemoteControl) { switch (receivedEvent.subtype) { case UIEventSubtypeRemoteControlPlay: case UIEventSubtypeRemoteControlPause: case UIEventSubtypeRemoteControlTogglePlayPause: [self playOrPause]; break; default: break; } } } ``` ただし、ViewControllerなどでこの処理を実装する場合、ファーストレスポンダにならなければいけないようなので、もし、上記のメソッドをViewControllerなどで実装するのであればファーストレスポンダの登録を行う。 ```objc - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self becomeFirstResponder]; } ``` 大抵の場合そうだと思うけど、ViewControllerにビジネスロジックを書きたくはないとおもうので、特定のオブジェクトにメッセージを送るようにするにはAppDelegate.mでリモートコントロールイベントを受け取るremoteControlReceivedWithEvent:メソッドをオーバーライドし、ビジネスロジックを記述するインスタンスに処理を渡すようにする。 ```objc:AppDelegate.m - (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent { [[MusicPlayManager sharedManager] remoteControlReceivedWithEvent:receivedEvent]; } ``` 例として、曲の再生管理用にMusicPlayManagerというクラスを作成している。 ## ロック画面にジャケット画像を表示する MPNowPlayingInfoCenterのsetNowPlayingInfo:メソッドにDictionaryで画像を渡す。 ```objc MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"image.png”]]; NSDictionary *songInfo = @{MPMediaItemPropertyArtwork, artwork}; [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo]; ``` このDictionaryでは曲名やアーティスト名なども渡すことが出来る。 # 電話などの割り込みから復帰 AVAudioSessionDelegateを実装すれば良い。 ```objc AVAudioSession *session = [AVAudioSession sharedInstance]; session.delegate = self; ``` ``` #pragma mark - #pragma mark Interruption event handling - (void)beginInterruption { //自動で再生中止するのでそのときの処理 } - (void)endInterruptionWithFlags:(NSUInteger)flags { //再生再開させるようにする } ``` ## ヘッドフォンジャックを抜いた時 ヘッドフォンをジャックから抜くとAVFoundationのAVPlayerは再生が自動で止まる。ボタンも追随して再生中から停止中へと状態を変えてあげる必要があるので 自分の記事だけどこの記事が参考になる http://qiita.com/yimajo/items/7f74536fa2ab3cc437f5 もしくはAVAudioSessionDelegateのinputIsAvailableChangedメソッドを使ってみるのもいいかもしれない。 # AVPlayer再生 ## AVPlayerの状態を知る AVPlayerの状態はAVPlayerStatus列挙体をもつstatusプロパティがあるが、再生中というステータスはない。 ```objc enum { AVPlayerStatusUnknown, AVPlayerStatusReadyToPlay, AVPlayerStatusFailed }; typedef NSInteger AVPlayerStatus; ``` 再生中かどうかを知りたい場合、rateプロパティが0かどうかを判断すれば良いとStackoverflowにある ``` if ([AVPlayer rate] != 0.0) ``` Check play state of AVPlayer http://stackoverflow.com/questions/5655864/check-play-state-of-avplayer たしかに停止中は0.0になるが、ストリーミングなど通信を介してAVPlayerで再生をする場合は再生していなくてもrateが0.0にならないことがあった。 ## タイムアウトを実装する 再生中のステータスが無いため、タイムアウトはReadyToPlayからタイマーを動かし、AVPlayerでメタデータが変わっていないかなどから判断してタイマーを止めるなどで対応するしかない。これはAVPlayerによるストリーミング音楽再生だけでなく映像再生も同じ。 ## 再生させる RadyToPlayの状態から再生メソッドplayを実行するようにする。ストリーミング再生などでURLから初期化後すぐにplayを実行することもできるが、再生中というステータス自体がないこともあり、再生開始の状態からメソッドを実行しそのとき内部の再生状態用のフラグを変更しつつplayメソッドを実行するほうがスマート。 # 音楽再生マネージャクラスの設計 ## シングルトンな再生用マネージャクラスを設計する ストリーミング音楽再生はAVPlayerを使うが、ViewControllerから直接AVPlayerを監視して操作するのはよくない。曲の再生は画面に依存したくないだろうし、複数のAVPlayerを使うこともないだろう。なのでシングルトンな再生用マネージャクラスを設計する。 ## 再生用マネージャクラスの状態通知はKVOよりNotificaitonのほうが便利 AVPlayerからの状態の監視はKVOを行うが、ViewControllerやViewなどに一方的に何かがONになった状態を通知したい場合はNotificationをPOSTするのが良いと思う。ただしON/OFFの状態がトグルになる場合はKVOのほうが分かりやすいかもしれない。blocksやdelegateで1対1になってしまうのも設計上よくない。 ``` AVPlayer =>|KVO|=> 再生用マネージャインスタンス =>|Notification POST|=>ViewController ``` Notificationを使わずKVOで実装してみたがAVPlayerのKVOもあり混乱してしまった。 # 参考 Apple: AVFoundation Framework Reference https://developer.apple.com/library/ios/DOCUMENTATION/AVFoundation/Reference/AVFoundationFramework/_index.html |
|
| 674位 |
|
|||
|
07:10:07 |
|
|
## やりたいこと。
AVFoundationを初挑戦中。AVFoundationのドキュメントを読んでみるも難しい。やりたいことはAVCaptureMovieFileOutputで動画を録画して、その録画した動画にイラストやらコピーライトを追加したい。本当はエフェクトとか色々いれたいとろこだけど、まずは簡単そうなところから着手。今回、取り組む要件としては下記2点。あとは今後の動画関連アプリのベースとして。。 * *動画ファイルにコピーライトを合成する。* * *動画ファイルに会社のロゴを合成する。* ## AVFoundationクラスを整理 AVFoundationのクラスは多く関係性がいまいち掴めないので、まずはAVFoundationのクラスの整理。全クラスではなく動画の録画から加工までの流れで最低限必要になるであろうクラスを列挙。大きく録画に必要なクラス(青)と加工処理に必要なクラス(オレンジ)の2つに別れる。 <img alt="AVFoundationのクラス図" src="http://b.ruyaka.com/wp-content/uploads/2014/03/avfoundation-class.jpg" id="avfoundation-class"> ## 動画合成の流れ 今回実装したいのは録画の後の加工処理なので主に上記の"動画の加工/編集に必要なクラス群"(オレンジ)のクラスを使用する。あとは加工用のテキストやイラストに必要なCALayerクラスも必要。大雑把な流れとしてはこんな感じ。 1. *ベースとなる動画のコンポジションを作成。* 2. *合成したいテキストやイラストをCALayerで作成して、合成用コンポジションを生成。* 3. *AVAssetExportSessionを使用して1と2のコンポジションを合成。* で、下記がその合成までに必要なクラスとメソッドを明記したフロー(細かいけど)。ベースと合成用のコンポジションを作成してAVAssetExportSessionで合成するという流れ。 <img alt="AVFoundationの動画の加工処理の流れ" src="http://b.ruyaka.com/wp-content/uploads/2014/03/avfoundation-composition-flow.jpg" id="avfoundation-composition-flow"> コードは下記。AVCaptureMovieFileOutputで動画を録画し終わった後を想定しているので、CaptureOutputメソッドから記述。 ```objectivec #pragma marks - AVCaptureFileOutputRecordingDelegate methods // 動画撮影終了時に呼び出される。 - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error { // 合成処理実行。動画ファイルのURLを渡す。 [self compositeMovieFromUrl:outputFileURL]; } // 動画の合成処理。コピーライトと会社のロゴを合成する。 - (void)compositeMovieFromUrl:(NSURL *)outputFileURL { //***** 1. ベースとなる動画のコンポジションを作成。*****// // 動画URLからアセットを生成 AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:outputFileURL options:nil]; // コンポジション作成 AVMutableComposition* mixComposition = [AVMutableComposition composition]; AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; // アセットからトラックを取得 AVAssetTrack *videoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; // コンポジションの設定 [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:videoTrack atTime:kCMTimeZero error:nil]; [compositionVideoTrack setPreferredTransform:[[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] preferredTransform]]; //***** 2. 合成したいテキストやイラストをCALayerで作成して、合成用コンポジションを生成。*****// // ロゴのCALayer作成 UIImage *logoImage = [UIImage imageNamed:@"logo.png"]; CALayer *logoLayer = [CALayer layer]; logoLayer.contents = (id) logoImage.CGImage; logoLayer.frame = CGRectMake(5, 25, 57, 57); logoLayer.opacity = 0.9; // 動画のサイズを取得 CGSize videoSize = videoTrack.naturalSize; // コピーライトのCALayerを作成 CATextLayer *copyrightLayer = [CATextLayer layer]; copyrightLayer.string = @"kuman.com"; [copyrightLayer setFont:@"Helvetica"]; copyrightLayer.fontSize = videoSize.height / 6; copyrightLayer.shadowOpacity = 0.5; copyrightLayer.alignmentMode = kCAAlignmentCenter; copyrightLayer.bounds = CGRectMake(0, 0, videoSize.width, videoSize.height / 6); // 親レイヤーを作成 CALayer *parentLayer = [CALayer layer]; CALayer *videoLayer = [CALayer layer]; parentLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height); videoLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height); [parentLayer addSublayer:videoLayer]; [parentLayer addSublayer:logoLayer]; [parentLayer addSublayer:copyrightLayer]; // 合成用コンポジション作成 AVMutableVideoComposition* videoComp = [AVMutableVideoComposition videoComposition]; videoComp.renderSize = videoSize; videoComp.frameDuration = CMTimeMake(1, 30); videoComp.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer]; // インストラクション作成 AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [mixComposition duration]); // 時間を設定 AVMutableVideoCompositionLayerInstruction* layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack]; instruction.layerInstructions = [NSArray arrayWithObject:layerInstruction]; // インストラクションを合成用コンポジションに設定 videoComp.instructions = [NSArray arrayWithObject: instruction]; //***** 3. AVAssetExportSessionを使用して1と2のコンポジションを合成。*****// // 1のコンポジションをベースにAVAssetExportを生成 _assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetMediumQuality]; // 2の合成用コンポジションを設定 _assetExport.videoComposition = videoComp; // エクスポートファイルの設定 NSString* videoName = @"kuman.mov"; NSString *exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:videoName]; NSURL *exportUrl = [NSURL fileURLWithPath:exportPath]; _assetExport.outputFileType = AVFileTypeQuickTimeMovie; _assetExport.outputURL = exportUrl; _assetExport.shouldOptimizeForNetworkUse = YES; // ファイルが存在している場合は削除 if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath]) { [[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil]; } // エクスポード実行 [_assetExport exportAsynchronouslyWithCompletionHandler: ^(void ) { // 端末に保存 ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:exportUrl]) { [library writeVideoAtPathToSavedPhotosAlbum:exportUrl completionBlock:^(NSURL *assetURL, NSError *assetError) { if (assetError) { } }]; } } ]; } ``` ## 参照 * [AVFoundationで動画を加工合成処理](http://kuman.asia/objective-c/2013/04/12/avfoundation-avcapture/) |
|
| 675位 |
|
|||
|
23:39:55 |
(Increments inc. 所属) |
|
今まではバカの一つ覚えのように何でもかんでも`$HOME/.zshrc`に書いていたけど、サーバ管理をよくするようになってきて、複数アカウントの共通設定とかをしたくなったので調べた。
#順番 以下の`$ZDOTDIR`は指定されていない場合`$HOME`になる。 ログイン時 1. /etc/zshenv 2. $ZDOTDIR/.zshenv 3. /etc/zprofile 4. $ZDOTDIR/.zprofile 5. $ZDOTDIR/.zshrc 6. /etc/zlogin 7. $ZDOTDIR/.zlogin ログアウト時 8. $ZDOTDIR/.zlogout 9. /etc/zlogout 言うまでもなく、個人的な設定は`$ZDOTDIR`に、汎用的な設定は`/etc`に書く。 #各種説明 ##zshenv 必ず実行される。`/etc/zshenv`は上書き不可で`$PATH`や`$MANPATH`の設定がされる。 `$ZDOTDIR/.zshenv`にはいかなる場合も設定したい項目を設定する感じ。manファイルではできるだけ小さくシンプルに保つように勧めている。 `ssh`コマンドの引数でログイン後に行うコマンドを指定すると、それだけを実行してssh接続を直ぐに切るが、そのような場合も`zshenv`は実行される。この場合以下のやつは全部実行されない。 ##zprofile ログイン時に一回だけ実行される。なのでzshの関数の定義とかはここに書くといいのかも知れない。あと、外部の設定ファイルを読み込むのもここに書くとよさそう。 ##zshrc インタラクティブシェルの時に実行される。つまり ```sh:zsh # ログイン時は4種類とも実行される $ zsh # 既にログインしている状態で改めてzshを起動する # /etc/zshenv # $HOME/.zshenv # /etc/zshrc # $HOME/.zshrc $ # zprofileとzloginは実行されない ``` ##zlogin ログイン時に一回だけ実行される。`zprofile`があるのにわざわざ用意されているということは`zshrc`で行ったなにかしらの設定に依存するが、一回だけでいいような処理を書く場所だと思われる。イマイチそういう状況が思い浮かばないがきっとそう。 ##zlogout 基本的にログインシェルから抜ける時に実行される。ただし、別プロセスに明け渡す形で終了する場合(`execve`システムコールを呼ぶとか?)は実行されないらしい。 #利用例 例えばマルチユーザモードでrvmをインストールした場合、rvmは標準で`/etc/profile.d/rvm.sh`を作成する。Linuxの`/etc/profile`では`/etc/profile.d/*.sh`が読み込まれるように設定されており、bashがこれを読むので、結果的に`rvm.sh`が読み込まれるけど、zshだと読み込んでくれない。そこで`/etc/zprofile`に以下を追記する ```sh:/etc/zprofile for i in /etc/profile.d/*.sh ; do [ -r $i ] && source $i done ``` こうすればこのサーバでログインシェルとしてzshを使っている人全員がrvmを使えるようになる。 |
|
| 676位 |
|
|||
|
20:46:02 |
(giftee inc. 所属) |
|
いろいろ調べたんですが`$HOME`やら`~/.vimrc`やら初心者には分かりにくい解説ばかりだったので個人的にメモ
##はじめに vimにある程度慣れてくると、いちいち`:set ~`なんてやってられなくなります。いろいろ調べると、あらかじめ設定ファイルを作っておく事で、環境設定ができることが分かりました。 どうやらこの設定ファイルには2種類あり、システムvimrcが読み込まれた後、ユーザvimrcが読み込まれ、重複する設定はユーザvimrcで上書きされるようです。 ユーザが安全に弄れるのはユーザvimrcで、その保存先が先程述べた`$HOME`だったり`~/`と呼ばれる場所のようです。 それでは、`$HOME`やら`~/`と呼ばれる場所を探してみましょう ##$HOMEを探して移動するまで ###$HOMEを探す >echo $HOME /Users/Tetsuya echoで$HOMEディレクトリを表示しないさいってことですね。これで自分の`$HOME`が`/Users/Tetsuya`だったことが分かります ###$HOMEへ移動 >cd /Users/Tetsuya ##.vimrcの編集をする ###.vimrcの作成 >vim .vimrc さて、これであとはネット上に転がってるvim設定をコピペするだけです。 以下、参考までに私の設定 **コメントがあったので追記** [yaotti](http://qiita.com/users/yaotti)さんが指摘してくださっているように、`.vimrc`では`"`(ダブルクオテーション)で始まる行はがコメント行となります。Javaでいうところの`//`、HTMLでいうところの`<!-- -->`ですね。 ```Bash "#####表示設定##### set number "行番号を表示する set title "編集中のファイル名を表示 set showmatch "括弧入力時の対応する括弧を表示 syntax on "コードの色分け set tabstop=4 "インデントをスペース4つ分に設定 set smartindent "オートインデント "#####検索設定##### set ignorecase "大文字/小文字の区別なく検索する set smartcase "検索文字列に大文字が含まれている場合は区別して検索する set wrapscan "検索時に最後まで行ったら最初に戻る ``` |
|
| 677位 |
|
|||
|
07:30:49 |
|
|
# Bolts Framework とは 昨年Facebookに買収されて話題になったParseチームが開発しているiOS/Androidフレームワーク。 Bolts自体はParseとは独立しているため、ParseのBaaSを使っていない人にも役立ちます。 Parseはとても品質の良いサービスですので、Parseチームが作っているということでBoltsを安心して使えると思います。 https://github.com/BoltsFramework/Bolts-iOS Boltsはローレベルライブラリのコレクションだと書かれていますが、今のところは非同期処理の統一インターフェースとなるタスクのみ用意されています。今後いろいろ増えていくのかもしれません。 タスクを使うと何ができるかというと、jQuery.deferredみたいなことです。 ネストしまくりなコールバック地獄をわかりやすく書けたり、エラー処理が統一的に書けたり、直列or並列の連続処理の仕組みを提供してくれたりします。 # 導入方法 CocoaPodsでサクッと導入。 ``` pod "Bolts" ``` # Bolts with AFNetworking とりあえずBolts使うとどんな感じに書けるの?というのがまず最初に知りたい情報だと思います。 Githubの公式ページでは最初のサンプルがParseフレームワークと組み合わせた例になっています。 使ったことがない人にはいまいちわかりずらいので、代わりにAFNetworkingと組み合わせた例を書いてみました。 # BFTask Boltsではタスクという単位で処理を扱います。タスクはPromiseという用語でよく使われているオブジェクトで、BFTaskというクラスになっています。 コールバックベースで書かれたコードをBoltsを使って書き直す場合、引数にコールバック処理のブロックを取るメソッドを、BFTaskを返すように変更します。 ```objc:コールバック版 // 非同期処理の呼び出し側 [self doSomething:^(id result) { ... }]; // 非同期処理を実行するメソッド -(void) doSomethingAsync:(void(^)(id))callback { [[APIClient sharedClient] getSomething:^(id result) { if (callback) { callback(result); } }]; } ``` ```objc:Bolts版 // 非同期処理の呼び出し側 [[self doSomething] continueWithSuccessBlock:^id(BFTask* task) { ... }] // 非同期処理を実行するメソッド -(BFTask*) doSomethingAsync { return [[APIClient sharedClient] getSomething]; } ``` # BFTaskCompletionSource 上記例ではAPIClient.doSomethingの定義もBoltsを使って書き換える必要があります。 返す時点ではBFTaskの状態が確定していない場合は、BFTaskCompletionSourceクラスを使います。 ```objc:コールバック版 @implementation APIClient -(void) getSomething:(void(^)(id))callback { [self getPath:@"getSomething" parameters:@{} success:^(AFHTTPRequestOperation *operation, id res) { if (callback) { callback(res); } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if (callback) { callback(error); } }]; } ``` ```objc:Bolts版 -(BFTask*) getSomething { BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; [self getPath:@"getSomething" parameters:@{} success:^(AFHTTPRequestOperation *operation, id res) { tcs.result = res; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { tcs.error = error; }]; return tcs.task; } ``` ## ダミーレスポンス BFTaskを生成する簡単な方法としてtaskWithResult/taskWithErrorが用意されています。 以下の例は、APIのサーバサイドが未実装な場合に、クライアントサイドでダミーレスポンスを生成しています。 ```objc:Bolts版ダミーAPI -(BFTask*) getSomething { return [BFTask taskWithResult:@{@"name": @"dummy"}]; } ``` # コールバックの入れ子をなくす API呼び出しの入れ子の例として、メッセージを送信して、成功したらメッセージ一覧を更新する、という流れです。 2段くらいだと実はコールバック版の方がわかりやすい気もします。 ```objc:コールバック版 [[APIClient sharedClient] postMessage:msg callback:^(id res, NSError *error) { if (error == nil) { [[APIClient sharedClient] getMessages:nil callback:^(id res, NSError *error) { if (error == nil) { // refresh messages ... } }]; } else { // show error ... } }]; ``` ```objc:Bolts版 [[[[APIClient sharedClient] postMessage:msg] continueWithBlock:^id(BFTask *task) { if (task.error == nil) { return [[APIClient sharedClient] getMessages:nil]; } else { // show error ... return nil; } }] continueWithSuccessBlock:^id(BFTask *task) { // refresh messages ... }]; ``` # 複数タスクを直列実行 単一オブジェクトの削除APIをオブジェクト数分繰り返したい場合を例に書きました。 continueWithBlockで処理を繋げていくことで直列実行が実現できます。 ```objc: BFTask *task = [BFTask taskWithResult:nil]; for (Message *msg in messages) { task = [task continueWithBlock:^id(BFTask *task) { return [[APIClient sharedClient] deleteMessage:msg.id]; }]; } [task continueWithBlock:^id(BFTask *task) { // refresh messages ... return nil; }]; ``` # 複数タスクを並列実行 上記の例を並列実行する場合はtaskForCompletionOfAllTasksを使います。 並列実行の場合は、実行順は保証されません。 ``` NSMutableArray *tasks = [[NSMutableArray alloc] init]; for (Message *msg in messages) { BFTask *task = [[APIClient sharedClient] deleteMessage:msg.id]; [tasks addObject:task]; } [[BFTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id(BFTask *task) { // refresh messages ... return nil; }]; ``` # 実行スレッドの制御 UIコンポーネントを操作する場合はメインスレッド上で実行する必要がある場合があります。 このような場合はBFExecutorを指定してタスクを実行します。 ```objc:メインスレッド上で実行 [task continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id(BFTask *task) { // UIコンポーネント操作 ... }]; ``` # 使ってみた感想 非同期処理のブロックのインターフェースが統一的に書けるという点が一番のメリットだと感じました。 今まではコールバックを成功時と失敗時の両方用意するのか、成功時だけで失敗時は無視するのか、成功時も失敗時も同一のブロックで処理させるのか、という点をその都度考える必要がありました。 その点Boltsを使えばとりあえずBFTaskを返しておけば、あとは呼び出し側でだけ選べばいいので楽です。 # おまけ jQuery.deferredと比較してみました。 |Bolts|jQuery.Defered| |:-:|:-:| |BFTask.continueWithBlock:|deferred.always()| |BFTask.continueWithSuccessBlock:|deferred.done()| |BFTask.continueWithFailureBlock:|deferred.fail()| |BFTask.result|done(args)| |BFTask.error|fail(args)| |BFTask.setResult:|deferred.resolveWith()| |BFTask.setError:|deferred.rejectWith()| |BFTask.taskForCompletionOfAllTasks:|jQuery.when()| |BFTaskCompletionSource::taskCompletionSource |jQuery.Deferred()| |BFTaskCompletionSource.task|defered.promise()| 下記の記事を参考にさせていただきました。 [結局jQuery.Deferredの何が嬉しいのか分からない、という人向けの小話](http://qiita.com/yuku_t/items/1b8ce6bba133a7eaeb23#1-3) |
|
| 678位 |
|
|||
|
01:00:20 |
|
|
phpで外部のサイトにアクセスして様々な情報を取ってきたいときに有効なのがcURL関数。
HTTPリクエストで情報を取るのには、file_get_contentsという関数もあります。以下のようにすることでHTMLを簡単に取得することができます、しかし、リクエストヘッダーを変えたりするなど、リクエスト方法をカスタマイズするには限界があります、そういったカスタマイズを行いたい場合に有用なのがcURLです。しかし、しっかりした情報が少なくて自分が使うの時に苦労したので、様々な使い方をまとめておきます。 ```php:file_get_contents使い方 <?php $url = "http://www.yahoo.co.jp/"; $html = file_get_contents($url); var_dump($html); ``` 以下からcURLの使い方をまとめていきます。 これで、HTMLが出力されます。 ```php:基本的な使い方 <?php $url = "http://www.yahoo.co.jp/"; $ch = curl_init(); // はじめ //オプション curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $html = curl_exec($ch); var_dump($html); curl_close($ch); //終了 ``` HTTP取得情報を確認する ````php:HTTP情報 <?php $url = "http://www.yahoo.co.jp/"; $ch = curl_init(); // はじめ //オプション curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $info = curl_getinfo($ch);//この関数で取得 curl_close($ch); //終了 var_dump($info); >> array(22) { ["url"]=> string(23) "http://www.yahoo.co.jp/" ["content_type"]=> NULL ["http_code"]=> int(0) ["header_size"]=> int(0) ["request_size"]=> int(0) ["filetime"]=> int(0) ["ssl_verify_result"]=> int(0) ["redirect_count"]=> int(0) ["total_time"]=> float(0) ["namelookup_time"]=> float(0) ["connect_time"]=> float(0) ["pretransfer_time"]=> float(0) ["size_upload"]=> float(0) ["size_download"]=> float(0) ["speed_download"]=> float(0) ["speed_upload"]=> float(0) ["download_content_length"]=> float(-1) ["upload_content_length"]=> float(-1) ["starttransfer_time"]=> float(0) ["redirect_time"]=> float(0) ["certinfo"]=> array(0) { } ["redirect_url"]=> string(0) "" } ```` よく使うオプション ```php:基本オプション curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);//変数に保存する curl_setopt($ch, CURLOPT_HEADER, true); // ヘッダも出力したい場合 ``` proxy接続を行う ```php:proxy接続 curl_setopt($ch, CURLOPT_PROXYPORT, 3128);//アクセスポート curl_setopt($ch, CURLOPT_PROXY, "192.168.0.1");//IPアドレスかURL ``` POST送信 ```php:POST送信 <?php //postするデータの配列 $post_data = array('id' => "id" ,'password'=> "pass" ,"mode"=>"login"); curl_setopt($ch,CURLOPT_POST, true); //データの配列を設定する curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data)); ``` Cookieを扱う ```php:Cookieを扱う <?php // Cookie情報を保存する一時ファイルディレクトリにファイルを作成します $tmp_path = tempnam(sys_get_temp_dir(), "CKI"); //postするデータの配列 $account_data = array('id' => "id" ,'password'=> "pass" ,"mode"=>"login"); $url = "http://www.pixiv.net/login.php"; $ch = curl_init(); // はじめ curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //POST送信 curl_setopt($ch,CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($account_data)); //Cookie受信 //cookieオプション curl_setopt($ch,CURLOPT_COOKIEFILE,$tmp_path); curl_setopt($ch, CURLOPT_COOKIEJAR, $tmp_path); curl_exec($ch);//実行 curl_close($ch); //終了 $url = "http://www.pixiv.net/mypage.php"; $ch = curl_init(); // はじめ curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //Cookie送信 //cookieオプション curl_setopt($ch,CURLOPT_COOKIEFILE,$tmp_path); curl_setopt($ch, CURLOPT_COOKIEJAR, $tmp_path); $html = curl_exec($ch);//実行 curl_close($ch); var_dump($html); //一時ファイル削除 unlink($tmp_path); ``` リクエストヘッダーを付加する ````php:リクエストヘッダーを付加 <?php $headers = array( "HTTP/1.0", "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Encoding:gzip ,deflate", "Accept-Language:ja,en-us;q=0.7,en;q=0.3", "Connection:keep-alive", "User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" ); $url = "http://www.ugtop.com/spill.shtml"; $ch = curl_init(); // はじめ curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //ヘッダー追加オプション curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $html = curl_exec($ch); $html = mb_convert_encoding($html,"UTF-8","EUC-JP"); var_dump($html); curl_close($ch); //終了 ```` とりあえず、基本的な部分はまとめられたかと思います。 まだ様々なオプションがありますが、紹介しきれないのでいかに参考になりそうなURLを張っておきます。 公式マニュアル http://php.net/manual/ja/book.curl.php オプション一覧 http://jp2.php.net/curl_setopt cURLのラッパークラスを作ってくださっている http://qiita.com/mpyw/items/c65fb4ec4cef80909a47 CURLのセッションを引き継ぐ方法:PHPプロの記事 http://www.phppro.jp/phptips/archives/vol16/3 |
|
| 679位 |
|
|||
|
11:40:40 |
(バンキングシステムズ 所属) |
|
## エクスポート
mysqldump -u[ユーザー名] -p[パスワード] -r [バックアップファイル名] --single-transaction [データベース名] ``` 「sample」データベースを「sample.bakcup」としてバックアップしたい場合 mysqldump -usample_user -psample_password -r sample.bakcup --single-transaction sample データベースの状態 データベース名:sample ユーザー名:sample_user パスワード:sample_password ``` ### オプションの解説 #### --single-transaction ダンプ中にテーブルをREADロックしないように付与する。 (ただしテーブルのエンジンがMyISAMの場合はテーブルが変更されている場合があるので注意) ## インポート mysql -u[ユーザー名] -p[パスワード] [インポートするデータベース名] < [インポートするファイル名] ``` mysql -usample2_user -psample2_password sample2 < sample.bakcup データベースの状態 データベース名:sample2 ユーザー名:sample2_user パスワード:sample2_password ``` |
|
| 680位 |
|
|||
|
19:58:49 |
|
|
puttyよりcygwin派です。
cygwinでsshする時に、.ssh/configに書くと便利な設定を記載。 ##1.接続が閉じられるのを防ぐ アクセス先のサーバーで何も操作しないと、一定時間で接続が切れてしまうことがあります。 これを防ぐには ``` ServerAliveInterval 30 ``` を追加します。 自動で30秒に1回サーバーに通信し、接続が切れるのを防ぎます。 ※秒数は変更可能。 ##2.別名で接続 接続先サーバー名にホスト名がなかったり、ドメインが長い、分かりづらい場合に設定します。 **[設定前の接続方法]** ``` ssh 123.567.890.001 ``` **<設定>** ``` Host sample1 HostName 123.567.890.001 ``` **[設定後の接続方法]** ``` ssh sample1 ``` (以下の全ての方法でも同じ) ##3.別名で接続+ポート指定 **[設定前の接続方法]** ``` ssh -p10123 123.567.890.001 ``` **<設定>** ``` Host sample1 HostName 123.567.890.001 Port 10123 ``` ##4.別のアカウントで接続 通常使用しているアカウントとは別アカウントで接続。 **[設定前の接続方法]** ``` ssh -i ~/.ssh/sample_id_rsa abc@123.567.890.001 ``` **<設定>** ``` Host sample1 HostName 123.567.890.001 User abc IdentityFile ~/.ssh/sample_id_rsa ``` ##5.多段接続 **[設定前の接続方法]** ``` ssh 111.111.111.111 ssh 222.222.222.222 ``` **<設定>** ``` Host sample1 HostName 222.222.222.222 ProxyCommand ssh -W %h:%p 111.111.111.111 ``` |
|
| 681位 |
|
|||
|
22:53:09 |
|
|
よく間違えて add してしまうので、
そうなってしまったときの対処方法です。 $ git reset HEAD sample.txt 以上! これで sample.txt は git のステージングから取り除かれ、 add する前の状態になります。 ファイルの内容は元のままなので、その心配は不要です。 後、ファイル名のところを . とすると、カレントディレクトリ以下全てのファイルを対象として、 add してしまったファイルを、git のステージングから取り除くことができる。 この操作は git に add してステージングに追加したファイルを、 ステージングから取り除く操作であるため、 この操作を行って add ファイルの内容が変わることは無い!無いんだ! とても便利だけど、いざ使い出すと色々なことで詰まってしまい、 自分の行う操作がやって良いことなのかダメなことなのか悩むときも多い git この操作は大丈夫な部類に属する操作です。 |
|
| 682位 |
|
|||
|
16:26:20 |
|
|
#はじめに
この記事をご覧になる方へ。 僕の浅学のまとめに対して、諸兄方がコメントをくれています。 すごく勉強になるので、コメントまで読んで頂ければ幸いです。 結局この記事は「正しい方法」という訳ではありません。 僕もまだ勉強中です。 --- #ここからが記事 未だに良く分からん。 これキライだわ。 でも入力された値にはホワイトリストにしてマッチするようにしてる。 全然覚えられないからメモ。 #メールアドレス形式のみ許可 `$mail`に入っている文字列をチェックしてます。 ```php:メールアドレスのみOK if (preg_match('|^[0-9a-z_./?-]+@([0-9a-z-]+\.)+[0-9a-z-]+$|', $mail)) { $mail = $mail; }else{ $error = 'メールアドレスが不正です。'; } ``` ##メールアドレスに関してはこちらも読んでください dankogaiさん が 怒っていた記事 http://blog.livedoor.jp/dankogai/archives/51189905.html 本気でメールアドレスを正規表現でマッチさせようとすると、こんな感じになってしまう例 http://www.din.or.jp/~ohzaki/perl.htm#Mail #半角英数のみ許可 `$id`をチェックしてます。 ```php:半角英数 if (preg_match("/^[a-zA-Z0-9]+$/", $id)) { $id = $id; }else{ $error = 'IDは半角英数で登録してください。'; } ``` #半角数字のみ許可 ```php:半角数字のみ if (preg_match("/^[0-9]+$/", $id)) { $id = $id; }else{ $error = 'IDは半角英数で登録してください。'; } ``` #ひらがな、カタカナ、漢字を許可 `$name`をチェックしています。 ```php:全角日本語 mb_regex_encoding("UTF-8"); if (preg_match("/^[ぁ-んァ-ヶー一-龠]+$/u",$name)) { $name = $name; }else{ $error = '日本語のみ使用可能です。'; } ``` 全角文字を許可する場合は `mb_regex_encoding("UTF-8");` を記述し エンコードを UTF-8 にします。 そして最後に「装飾子」と言うものを入れれば大丈夫。 ここでは`if (preg_match("/^[ぁ-んァ-ヶー一-龠]+$/u",$name)) {`の中の `u`がそれです。(UTF-8として扱うという意) 装飾子:詳しくは▶[コチラ【PHPマニュアル】](http://php.net/manual/ja/reference.pcre.pattern.modifiers.php) でも、コレって何回読んでも理解できん。これみんな分かるの?天才だね。 この人のが分かりやすい。▶[PHPプログラミング解説](http://www.crystal-creation.com/web-appli/technical-information/programming/php/grammar/data-type/string/regular-expression.htm) #改行と「、」と「。」も許可 以下を許可してます。 * ひらがな * カタカナ * 漢字 * 「、」 * 「。」 * 改行 * 半角英数(大小) * 半角数字 * 全角数字 ```php:全部許可 if (preg_match("/^[ぁ-んァ-ヶーa-zA-Z0-9一-龠0-9、。\n\r]+$/u",$message)) { $message = $message; }else{ $error = '変な記号は使わないでね。'; } ``` まとめたけど… やっぱキライだわコレ。 |
|
| 683位 |
|
|||
|
01:17:22 |
(Hazel Research Institute, LLC 所属) |
|
[Git Advent Calendar / Jun.](http://qiita.com/advent-calendar/git) 6/12 担当[@T_Hash](https://qiita.com/users/T_Hash)です。 明日も仕事でだるいのですが、怠惰はプログラマの美徳といいます。というわけで僕が日々の仕事で怠惰にgitを使うための設定を共有したいと思います。 ## zsh ↓を参考にした設定を.zshrcに記述して、右プロンプトにブランチ名とステータスを表示させています。コマンドを叩かずに状態が見えて非常に便利です。 [git のブランチ名 *と作業状態* を zsh の右プロンプトに表示+ status に応じて色もつけてみた](http://d.hatena.ne.jp/uasi/20091025/1256458798)   緑だとクリーンな状態、赤だと未コミットの変更があります。「緑が正常な状態、緑に戻って来たら一段落してコーヒー飲もう」とか考えながら作業をしてます。 あと、zshはgitのコマンドも補完してくれるので地味に重宝します。 ## gst: git status git statusは常に叩くクセを付けた方がいいと思います。オプションは-sと-bがおすすめです。   で、これにgstというaliasを付けて使っています。  僕はもう既にgstが手癖になってて、次の作業を考えながら手が無意識にgst, gst, gst…と叩くようになりました。かの[@sotarok](https://qiita.com/users/sotarok)先生も`gst`を使っているらしいです。やったね。 > @Mekajiki ぼくは gst ですねw 考え事してるとだいたい指が gst か ls を打ってます。あとは git branch -a が gb とかですかね。 > Sotaro KARASAWA© (@sotarok) <a href="https://twitter.com/sotarok/status/210388919102095360" data-datetime="2012-06-06T15:13:21+00:00">June 6, 2012</a> ## gst[a/d/v]: status出力のN行目を[add/diff/edit] 今回のメイン(たぶん)。↑のようにgstで表示したファイルに対して「2行目に出てる.vimrcを編集したいな」という時はgstvを使います。  これでvimが開き、.vimrcを編集できます。 addしたいときはgsta.  addされてgst結果が変わっていますね。 一方、「gst4行目の.zshrc、何が変更されているか見たい」という時はgstdを使います。  便利ですね。以下、たいしたことやってないですが、コードです。これをPATHに含まれる~/bin/とかに置いてchmod +xで実行権限つけてやれば使えます。 ~~~~bash:~/bin/gsta #!/bin/sh if [ $# -eq 1 ]; then git add `git status -s -b | grep -v "^#" | awk '{print$1="";print}' | grep -v "^$" | awk "NR==$1"` else exit 1 fi ~~~~~ ~~~~bash:~/bin/gstd #!/bin/sh if [ $# -eq 1 ]; then git diff -- `git status -s -b | grep -v "^#" | awk '{print$1="";print}' | grep -v "^$" | awk "NR==$1"` else exit 1 fi ~~~~~ ~~~~bash:~/bin/gstv #!/bin/sh if [ $# -eq 1 ]; then vim `git status -s -b | grep -v "^#" | awk '{print$1="";print}' | grep -v "^$" | awk "NR==$1"` else exit 1 fi ~~~~~ ## statusついでにstash listも表示する 作業中に急に対応する必要のあるバグが出てきたときなど、stash saveでいったん作業を退避させてからバグ対応、stash popで元に戻すということをやります。 たまにstashしていたことをど忘れすることがあるので、先ほどのgstでstatusを見るとき、ついでにstash中の変更を表示させるようにしてみたところ、とても安心感あって良い感じなのでオススメです。 ~~~~~~~zsh % which gst gst: aliased to git st && g stash list ~~~~~~~~ ~~~~~~~.zshrc alias gst='git st && g stash list' ~~~~~~~~  * 余談 * 確かどこかでgitメンテナの方が、stashはsave -> popで戻すのではなく、save -> apply -> (確認) -> dropというお行儀がよいフローを推奨していた記憶があります。 ## gcm: git commit ~~~~~~bash:~/bin/gcm #!/bin/sh git commit -m "$*" ~~~~~~ そのまんまで、stageしたものをcommitするaliasです。  ## gch: git cherry -v cherry-pickはよく使われているのですが、cherryも結構便利です。 git cherryは、かつて分岐したブランチ間の差分commit一覧を表示します。 例として、`ng_solr`ブランチに含まれているcommitと`ok_solr`ブランチに含まれているcommitの差分を出力してみます。`-v`オプション付きがおすすめです。 ~~~~~~~~~diff:cherry % git checkout ng_solr % git cherry -v ok_solr + 608e08404aa15c7cf1cc3bad4311f4a9edd9e78d install Sunspot (Solr on Rails) + a5d09e3ee1c1daf40a29b0f5a6cc94e4a2ff16d8 Mongoid用Sunspotカスタマイズ + a7c11ee46578d78650512c06446e31722f0568b5 seachable定義を追加 - c13b510b8e1ac02350058ca50c77a8f72ba3cf80 index再生成用rakeタスクの作成 - 5009ffcb5b16cc77ceff2117c2a435017db96247 ArticleControllerのリファクタリング + 67294858d72d4fb8d6ae44e43485e08744bd67de なんかいろいろほげほげ ~~~~~~~~~ ~~~~~.zshrc alias gch='git cherry -v' ~~~~~ これをcutとかawkでいじってgit showに渡してやれば、該当コミットの中身が確認できる。 ~~~~~bash:~/bin/gchs #!/bin/sh if [ $# -eq 2 ]; then git show `git cherry -v $1 | awk '{print $2}' | awk "NR==$2"` else exit 1 fi ~~~~~~~ 名前はgit cherry+showでgchs. 使い方は ~~~~~ $ gchs ok_solr 2 ~~~~~ とやれば2個目のcommit内容が表示される。 masterから作業ブランチの差分を順々に表示していくと、コードレビューなんかに便利かなと。 ## git log log系はいろいろ工夫のしどころがある。一部、最近はTigでよくねと思ってる機能もあるけど一応。 ### glgg 脳内ではゲルググと呼んでる。 ~~~~~.zshrc alias glgg='git logg' ~~~~~ ~~~~~.gitconfig [alias] logg = log --stat --pretty=format:'%Cblue%h %Cgreen%ar %Cred%an %Creset%s %Cred%d' ~~~~~~ ### glg ~~~~~.zshrc alias glg='git logg | head' ~~~~~ 単なるゲルググのheadだが、ゲルググより利用頻度高。直近commitの内容をさっくり確認。  白で表示されて欲しい文字が赤色で出るのが目下の悩み ### その他ログ系tips * loggr ~~~~~~.gitconfig [alias] loggr = log --graph --date-order --pretty=format:'%Cblue%h %Cgreen%ci %Cred%an %Cblue%m %Creset%s %Cred%d' ~~~~~~ 命名: logg + graph。キャプチャは取れないが多人数開発(つまり仕事)でマージ関係を把握する時便利  * tig しっかり見るときはtigオススメ ~~~~ % brew install tig ~~~~  ## いじょ 俺の方がもっと怠惰にgitを使ってる!という人は是非教えて下さい。もっとラクしたいです。 |
|
| 684位 |
|
|||
|
00:59:50 |
|
|
#はじめに [JUnit実践入門](http://www.amazon.co.jp/JUnit%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-~%E4%BD%93%E7%B3%BB%E7%9A%84%E3%81%AB%E5%AD%A6%E3%81%B6%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E6%8A%80%E6%B3%95-WEB-PRESS-plus/dp/477415377X/ref=sr_1_1?ie=UTF8&qid=1392442555&sr=8-1)を読んだのでそのまとめ #Part1 JUnit入門 ##なぜ、ユニットテストを行うのか? どんなプログラマであっても人間であるかぎり間違いをおかす→正しく動くか不安→不安を安心に変える **ユニットテスト** ##JUnitとは? Javaのテスティングフレームワーク。 * テストの実行フレームワーク * テストの期待値と実測値の検証API * テストケースのフォーマット ##日本語のメソッド名を使うメリット 英語で格好よくテスト内容を記述でき、それを英語表記でき、読み取れれば英語の方が望ましい。 日本語でのソフト開発なら、Javadocで出力すれば、テスト項目一覧、テストクラスのアウトライン参照でテストが把握できたり、テスト失敗時の内容が理解しやすいなどメリットが大きくなる。 ##ソフトウェアテストの特徴 1. テストにある条件下という制約があること。これは「前提条件」、「事前条件」などの使用するデータ、環境、事前操作が含まれる。これを明確にする必要がある。 1. ソフトウェアの振る舞いを記録すること。記録できなければ、検証ができない。出力されるデータやDBがどのような状態か確認。場合によっては観測者が変化を知ることができれば十分な場合あり。 1. 期待する結果との検証を行うこと。ランダム性の高い振る舞いは検証が難しい。 ソフトウェア開発では作成が同時にテストを行ったり、実測したテストから得られたフィードバックからテストを追加する方法が有効。 ##テスト技法 * ホワイトボックステストとブラックボックステスト **ホワイトボックステスト** は内部ロジックや仕様を考慮してテストケースを設計。テストが可能な限りすべてのロジックを実行するため、内部仕様を理解しているプログラマが実施。 **ブラックボックステスト** は外部仕様からテストケースを設計。ユーザ受け入れテストや機能テストなどの大きな粒度で検証するテストで採用。 ユニットテストはホワイトボックステストに分類されるが、内部ロジックに依存しすぎたユニットテストは変更にもろくなるため、テスト対象メソッドの外部仕様からテストデータを選択したほうがよいという側面もある。 * 同値クラスに対するテスト 同様の結果をもたらす値を同値クラスとしてグループ化し、テストデータを選択するテスト技法。通常対象グループの中から代表値を選択しテストを行う。 * 境界値に対するテスト 異なる結果をもたらす値(境界値)に直目し、その近傍からテストデータを選択する。 ## ユニットテストのパターン * 自動化するためいつでも繰り返し実行できること→安心してリファクタリングができる * 成功状態を維持するため結果が一定でない不安定なテストはさけること * テストに定義されている動きは保証されるため、最も正確なドキュメント(仕様書)でありサンプルコードとなるので読みやすいテストを心がけること * テスト失敗時に問題を特定しやすいよう、十分に小さな単位で可能な限り作る→失敗した時に影響範囲と条件が絞込みやすい * 前提条件、実行、検証が複雑な可読性の低いテストコードは避ける→メンテナンス性も悪くテストの質に大きく影響を与える * 実行順序に依存しないこと→他のテスト結果に影響を受けるテストは予期しない形で失敗することになるため ## 可読性の高いテストコードの書き方 * 「テストを行う前提条件」「テストに用いる入力値や操作」「テストを行った時に期待する値や動作」が重要ポイント ## JUnitのアノテーション [チートシート](http://qiita.com/disc99/items/4dc78f9a96aa0a9aeb47) #Part2 JUnitの機能と拡張 ## JUnit4のアサーション JUnit4ではassertThat(Matcher APIと併用)とfailでほとんどの判定を行えるあためこの2で行う ## Matcher API [チートシート](http://qiita.com/disc99/items/4dc78f9a96aa0a9aeb47) ## テストランナー ##### JUnitCoreクラス EclipseなどのIDEを使用しているビルドツールを経由してJunitCoreが呼ばれるため意識することがないが、実際には以下の流れで呼び出される。 1. テストケースの収集 1. テストの実行 1. テスト結果の出力(レポート) ##### テストランナー |テストランナー|機能| |:--|:--| |JUnit4|JUnit標準のテストランナー。実行対象は、「publicメソッド」、「Testアノテーション」、「戻り値がvoidで引数がないこと」が条件になる。| |Suite|Suiteクラスは複数のテストクラスを束ねてテストを実行するテストランナー。関連として「~Tests」と複数形にし、「AllTests」はよく使われるテストスイートクラス名。| |Enclosed|構造化したテストクラスを実行する。同じ初期化などをまとめて構造化するときに効果的。| |Theories|パラメータ化したテストケースを実行する。テストケースとテストデータを分割することで同一のテストケースを別パラメータで複数回の実行が可能になる。| |Categories|特定のテストクラスをまとめて実行する。テストケースが増えるとテスト時間が長くなるので、テストケースをカテゴライズして必要なケースに分けて実行する。| JUnitCoreはテストクラス単位なので、全クラスや特定のクラスのテストを行うためにはテストランナーを利用する。 テストランナーはテストクラスごとにorg.junit.runner.RunWithアノテーションを利用して設定する。 省略した場合には、JUnit4が利用される。 ## テストのコンテキスト テストコードはプロダクションコードと同様に可読性が必要だが、テストコードをプロダクションコードと同じ方法で整理すると逆に可読性が下がる場合があるので、共通したコンテキストを持つテストケースをEnclosedなどを利用しグループ化する。 ##### コンテキストパターン |パターン|解説| |:--|:--| |共通データに着目する|入力値や期待値などのテストで使用するパターンに着目し、共通するテストをグループ化する。| |共通の状態に着目する|テスト対象クラスが状態をもつ場合、事前処理として行いテスト対象処理と分離する。| |コンストラクタのテストを分ける|インスタンス化のテストは生成自体がテスト内容のため、他のテストケースと分離する。| ##### テストクラスを横断する共通処理 共通する処理を記述する、基底クラスを定義し、継承するのは極力さける。 理由は、テストクラスに不必要な初期処理を動かしたり、基底クラスの変更が継承したクラスすべてに影響を与えるため。 共通処理はユーティリティクラスに抽出し利用すべき。 ##テストフィクスチャ テストで扱うデータやテスト実行環境、オブジェクトの状態のこと。 ユニットテストのフィクスチャは以下。 * テスト対象オブジェクト * テスト実行に必要なオブジェクト(入力値) * テストの検証に必要なオブジェクト(期待値) * テストの実行までに必要なテストオブジェクト * ファイルなどの外部リソース * データベースやソケットサーバなどの外部システム * 依存クラスや依存外部システムのモックオブジェクト フィクスチャはテストケースごとに独立し、テスト実行ごとに初期化、終了時に開放するのが基本的戦略。 この戦略を **フレッシュフィクスチャ** と呼ぶ。 フィクスチャが共有されると実行順序によって影響を受ける。またユニットテストを並列実行した場合、共有フィクスチャが複数テストから利用されるため問題は深刻。 JUnitではテストケースごとにインスタンス化を行うため、フィクスチャは共有されないが、シングルトンやDBなどの外部リソースは共有されるため注意が必要。 ##フィクスチャのセットアップの種類 |セットアップ|説明|メリット|デメリット| |:--|:--|:--|:--| |インラインセットアップ|テストメソッド内でフィクスチャのセットアップを行う|メソッド内でテストコードが完結し見通しがいい|セットアップ量が多いとメソッドが膨大になり、テスト対象が曖昧になる| |暗黙的セットアップ|セットアップメソッド内でフィクスチャのセットアップを行う|テストメソッド内が実行と検証が中心になり、テスト内容が明確になる|テスト対象メソッドを構造化しない限りすべてのテストメソッドで共通のセットアップしか行えない| |生成メソッドでセットアップ|フィクスチャの生成処理を行うメソッドを定義する。クラス間で共通処理に関しては生成用クラスを作成しstaticメソッドとして定義する。(staticインポートができるため)メソッドは拡張性ではなく、可読性を重視する。|メソッド単位で共通処理を抽出できる。|複雑なオブジェクト生成はなくならず、読みやすくはない。| |外部リソースからのセットアップ|YAML,XML,JSON,CSV,Excelなどの外部ファイルにテストデータを記述し、読み込む。|テストコードがシンプルになる|テストコードとテストデータの相互参照が行いにくい。| |Javaによる宣言的なセットアップ|匿名インナークラスを用いてオブジェクトの初期化を行う|Javaのコードのみで行えるのでIDEの恩恵を厚く受けられる|private,finalフィールドにデータのセットアップができない| |Groovyによる宣言的セットアップ|Groovyを用いてJavaの宣言的セットアップを行う|Javaではできなかったprivate,finalなフィールセットアップもできる|Groovy環境の導入が必要になる| ##パラメータ化テスト テストでは大抵1つの入力値で不十分で複数の入力値と期待値のパターンが必要になる。 ただし、1メソッドに複数の期待値の検証を行ったり、同じ入力値と期待値は別でもテストコードが同一になるテストケースは冗長となる。 そこで適切なデータパターンを抽出し、テストランナーTheoriesを利用してパラメータ化のテストを行う。 ##### Theoriesで使用できるアノテーション |アノテーション|説明| |:--|:--| |@Theory|テストメソッドに指定する。対象メソッドはパラメータを引数で受け取ることができ、複数の引数を受け取った場合にはすべての組み合わせが実行される。| |@DataPoint|パラメータを定義するアノテーション。パタメータはpublc staticなフィールド、またはメソッドで定義する。| |@DataPoints|複数のパラメータを定義する。@DataPoint同様の定義だが、配列で定義するため、複数パターンのパラメータ定義ができる。| ## ルール JUnitではテスト間で共通する処理はサブクラスに抽出するしかなく、テストクラスはテストのメタ情報にアクセスできなかった。 そこでJUnit4.7からルールが追加された。 また、カスタムルールの導入により [チートシート](http://qiita.com/disc99/items/4dc78f9a96aa0a9aeb47) ## カテゴリ化テスト テストが増えてくるとテストに時間のかかる **スローテスト問題** 発生する。この対策として主に以下の4方法がある 1. テストの実行時間を短くする 1. テストの実行環境を強化する 1. テストを並列で実行する 1. 実行するテストを絞り込む カテゴリ化は上記の4が対象になる。 [チートシート](http://qiita.com/disc99/items/4dc78f9a96aa0a9aeb47) #Part3 ユニットテストの活用と実践 ##テストダブル テスト対象クラスが他のクラスに依存していない可能性は少なく、場合によっては外部システムや乱数、システム時間などに依存する場合もあります。 そこで依存するオブジェクトの「代役」としてモックやスタブを利用する。 モックやスタブの作成は複雑な処理が必要な場合もあるので、公開されてるライブラリを利用する。 [チートシート](http://qiita.com/disc99/items/4dc78f9a96aa0a9aeb47) ##データベースのテスト Webアプリケーションなどのソフトウェア開発ではDBを利用しない場合はほとんどない。 DB周りのテストを行う場合、インターフェイスが決まっていればモック化も可能だが、DBを使っていないと発生しない問題を見逃す可能性が発生する。 そこでプロダクション環境に近い環境を構築するとるとテスト環境ごとにセットアップしないといけなくなる。 そこで軽量データベースのH2 DatabaaseやSQLiteを利用する。 ただし、DB固有の方言があるため、HibernateDomaといったORMを使用するとDBごとの差異を吸収できる。 いずれにしてもプロダクション環境と異なると環境で予想外の問題が発生する可能性はリスクとして考慮すべき。 ## コードカバレッジ 実行したユニットテストの品質の基準としてコードカバレッジがある。 カバレッジを測定しその傾向を分析することテストケースの問題を検知することができる。 |名前|説明| |:--|:--| |C0(命令網羅)|プログラム中の全ての命令が1回以上実行を測定| |C1(分岐網羅)|プログラム中の各分岐について1回以上実行を測定| |C2(条件網羅)|プログラム中の判定の組み合わせを測定| ## EclEMMAによるガバレッジ測定 Eclipseのプラグインとして簡単に導入できる。 ガバレッジの測定をグラフィカルに確認でき、既存のプロジェクトの修正を加えず利用できる。 #Part4 開発プロセスの改善 ##継続的テスト ソフトウェア開発では複数のテストを行い欠陥がある場合には修正を行う。 しかし、欠陥を修正した欠陥(リグレッション)が発生することがる。 このようなリグレッションを早期に発見するため継続的なテストを行う。最近の開発では継続的インテグレーションツールを利用することで以下のような恩恵を受けられる。 * 開発チームへの素早いフィードバック * 早期からテストする * 自動的にテストする * 繰り返しテストする また継続的なテストを実現する **「現代ソフトウェア開発の三本柱」** として以下がある。 * ユニットテスト * 自動化(自動ビルド) * バージョン管理 ## ビルドの自動化 Ant,Maven,Gradleなどの自動化ツールを利用することでビルドプロセスの自動化が行える。 Mavenでは多くのプロセスが標準、あるいはプラグインにより提供される。以下は代表的なもの。 * プロジェクトの構成 * 依存ライブラリの管理 * ソースエンコーディングの設定 * ビルドの実行 * カテゴリ化のテスト * カバレッジレポート また継続的なテストツールとして代表的なものとしてJenkinsが用いられる。 Jenkinsを用いることで理解しやすいUIで「現代ソフトウェア開発の三本柱」を実現することができる。 ## テスト駆動開発 テストコードをプロダクションコードより先に書くことを中心としたソフトウェア開発技術。 プロダクションコードをテスタビリティの高いシンプルな設計に導くことができる。 主な目的は以下。 |目的|説明| |:--|:--| |ステップバイステップ|1回の小さなサイクルを繰り返すことでミスを大幅に減らし、問題をしい作することで難易度が下がり、思考停止する時間もへらすことができる。| |自分が最初のユーザ|クラスやメソッドの使いづらさに気づき修正することで、ユーザビリティの高いコードができる。| |動作するきれいなコード|完璧な設計はできず、できてもコストがかかる。テスト駆動開発はテスト成功後リファクタリングを行うことできれいなコードができる。| |すばやいフィードバック|継続的インテグレーションツールを導入していればすばやいフィードバックを得られ、プログラマは安心して開発できる| |メンテナンスされたドキュメント|テストコードは確実に動作することが保証されるため、テストコードがドキュメントであり、サンプルコードになる。| サイクルついては以下。 1. 設計する 1. テストコードを書く(レッド:テスト失敗) 1. プロダクションコードを書く(グリーン:テスト成功) 1. リファクタリングする(グリーン:テスト成功) これを繰り返す。 ## 振舞駆動開発 ユニットテストでは粒度が細かすぎ、顧客要求を満たしているか保証できない。 そこでもっと大きな単位(システムテストや受け入れテスト)を実現するため、シナリオ単位で行う振舞駆動開発が用いられる。 Javaにおいては **cucumber-juit** を使用することで振舞駆動開発が実現できる。 ##おわりに 他に読んだテスト関連の2冊([現場で使えるソフトウェアテスト](http://qiita.com/disc99/items/ebe939f8cc203d59f27e),[経験ゼロでもできるプログラミング現場の単体テスト](http://qiita.com/disc99/items/177bdf6352de463fdc87))の中でタイトル通り特にテストコードの記述に関して深く言及されている。 後半はだいぶ省略してるが、JUnit4で利用できる具体的で実践的な記述も豊富にあるので、テストコードを書く人は是非読んでもらいたい一冊。 |
|
| 685位 |
|
|||
|
22:52:47 |
|
|
`java.util.function` パッケージ以下にある関数インターフェースの使い方をメモする。
#`Function<T, R>` ##`apply(T)` ```java:実装例 package java8sample; import java.util.function.Function; public class Java8Sample { public static void main(String[] args) { Function<String, Integer> function = string -> Integer.parseInt(string); System.out.println(function.apply("12345")); } } ``` ```text:実行結果 12345 ``` `Function<T, R>` の抽象メソッド。 `T` 型の引数を受け取って、 `R` 型の値を返す処理を実装する。 ##`compose(Function<? super V,? extends T>)` ```java:実装例 package java8sample; import java.util.function.Function; public class Java8Sample { public static void main(String[] args) { Function<String, String> wrapDoubleQuotation = str -> "\"" + str + "\""; Function<String, String> wrapSingleQuotation = str -> "'" + str + "'"; Function<String, String> wrapDoubleAndSingleQuotation = wrapDoubleQuotation.compose(wrapSingleQuotation); String result = wrapDoubleAndSingleQuotation.apply("hoge"); System.out.println(result); } } ``` ```text:実行結果 "'hoge'" ``` `compose()` メソッドを実行した `Function` オブジェクトに、引数で渡した `Function` オブジェクトを組み合わせた新しい `Function` オブジェクトが生成される。 ↑の実装は、↓の実装と同じ意味になる。 ```java:実装例 package java8sample; import java.util.function.Function; public class Java8Sample { public static void main(String[] args) { Function<String, String> wrapDoubleQuotation = str -> "\"" + str + "\""; Function<String, String> wrapSingleQuotation = str -> "'" + str + "'"; String result = wrapDoubleQuotation.apply(wrapSingleQuotation.apply("hoge")); System.out.println(result); } } ``` `compose(...)` メソッドに渡した `Function` オブジェクトが実行され、その結果が元の `Function` オブジェクトに渡される。 ##`andThen(Function<? super R,? extends V>)` ```java:実装例 package java8sample; import java.util.function.Function; public class Java8Sample { public static void main(String[] args) { Function<String, String> wrapDoubleQuotation = str -> "\"" + str + "\""; Function<String, String> wrapSingleQuotation = str -> "'" + str + "'"; Function<String, String> wrapDoubleAndSingleQuotation = wrapDoubleQuotation.andThen(wrapSingleQuotation); String result = wrapDoubleAndSingleQuotation.apply("hoge"); System.out.println(result); } } ``` ```text:実行結果 '"hoge"' ``` `compose()` の逆順序で `Function` オブジェクトが適用される。 ##`identity()` ```java:実装例 package java8sample; import java.util.function.Function; public class Java8Sample { public static void main(String[] args) { Function<String, String> function = Function.identity(); System.out.println(function.apply("string message")); } } ``` ```text:実行結果 string message ``` static メソッド。 `apply(T)` メソッドに渡した値をそのまま返す `Function` オブジェクトを生成する。 用途はよくわからない。 #`UnaryOperator<T>` `Function` の拡張インターフェース。 `Function` の型引数が2つとも同じ型、つまり、引数も戻り値も同じ型を返す特別パターンの `Function`。 ```java:実装例 package java8sample; import java.util.function.UnaryOperator; public class Java8Sample { public static void main(String[] args) { UnaryOperator<String> unary = string -> "[" + string + "]"; System.out.println(unary.apply("hoge")); } } ``` ```text:実行結果 [hoge] ``` #`Consumer<T>` ##`accept(T)` ```java:実装例 package java8sample; import java.util.function.Consumer; public class Java8Sample { public static void main(String[] args) { Consumer<String> consumer = string -> System.out.println("Cunsumer : " + string); consumer.accept("hoge"); } } ``` ```text:実行結果 Cunsumer : hoge ``` `Consumer<T>` の抽象メソッド。 `T` 型の値を引数に受け取って、戻り値無しの処理を実装する。 `consumer` の意味は、「消費者」。 戻り値を返さず引数を __消費するだけ__ という感じ? ##`andThen(Consumer<? super T>)` ```java:実装例 package java8sample; import java.util.function.Consumer; public class Java8Sample { public static void main(String[] args) { Consumer<String> hoge = string -> System.out.println("hoge : " + string); Consumer<String> fuga = string -> System.out.println("fuga : " + string); Consumer<String> piyo = hoge.andThen(fuga); piyo.accept("piyo"); } } ``` ```text:実行結果 hoge : piyo fuga : piyo ``` 「`andThen()` メソッドを実行した `Consumer` オブジェクト」→「引数で渡した `Consumer` オブジェクト」の順序で処理を実行する新しい `Consumer` オブジェクトが生成される。 #`Supplier<T>` ##`get()` ```java:実装例 package java8sample; import java.util.function.Supplier; public class Java8Sample { public static void main(String[] args) { Supplier<String> supplier = () -> "hoge"; System.out.println(supplier.get()); } } ``` ```text:実行結果 hoge ``` `Supplier<T>` の抽象メソッド。 `T` 型の値を返す処理を実装する。 `supplier` の意味は、「供給者」。 #`Predicate<T>` ##`test(T)` ```java:実装例 package java8sample; import java.util.function.Predicate; public class Java8Sample { public static void main(String[] args) { Predicate<String> predicate = string -> string.isEmpty(); System.out.println(predicate.test("")); System.out.println(predicate.test("hoge")); } } ``` ```text:実行結果 true false ``` `Predicate<T>` の抽象メソッド。 `T` 型の値を引数に受け取って、 `boolean` の値を返す処理を実装する。 `predicate` の意味は「断言する」「断定する」。 値の検証を明示するのに使う感じかと。 ##`isEqual(Object)` ```java:実装例 package java8sample; import java.util.function.Predicate; public class Java8Sample { public static void main(String[] args) { Predicate<String> isHoge = Predicate.isEqual("hoge"); System.out.println(isHoge.test("hoge")); System.out.println(isHoge.test("fuga")); Predicate<Object> isNull = Predicate.isEqual(null); System.out.println(isNull.test(null)); System.out.println(isNull.test("not null")); } } ``` ```text:実行結果 true false true false ``` 引数で受け取ったオブジェクトと等しいかどうかを判定する新しい `Predicate` オブジェクトを生成する(比較は `Object#equals(Object)` メソッドが使用される)。 `null` セーフになっている。 ##`and(Predicate<? super T>)` ```java:実装例 package java8sample; import java.util.function.Predicate; public class Java8Sample { public static void main(String[] args) { Predicate<String> isUpperCase = string -> string.matches("[A-Z]+"); Predicate<String> isAlphabet = string -> string.matches("[a-zA-Z]+"); Predicate<String> predicate = isAlphabet.and(isUpperCase); System.out.println(predicate.test("HOGE")); System.out.println(predicate.test("hoge")); } } ``` ```text:実行結果 true false ``` 引数で渡した `Predicate` オブジェクトを AND 条件で連結させた新しい `Predicate` オブジェクトを生成する。 ##`or(Predicate<? super T>)` ```java:実装例 package java8sample; import java.util.function.Predicate; public class Java8Sample { public static void main(String[] args) { Predicate<String> isUpperCase = string -> string.matches("[A-Z]+"); Predicate<String> isNumber = string -> string.matches("\\d+"); Predicate<String> predicate = isNumber.or(isUpperCase); System.out.println(predicate.test("HOGE")); System.out.println(predicate.test("1234")); System.out.println(predicate.test("hoge")); } } ``` ```text:実行結果 true true false ``` 引数で渡した `Predicate` オブジェクトを OR 条件で連結させた新しい `Predicate` オブジェクトを生成する。 ##`negate()` ```java:実装例 package java8sample; import java.util.function.Predicate; public class Java8Sample { public static void main(String[] args) { Predicate<String> isEmpty = string -> string.isEmpty(); Predicate<String> isNotEmpty = isEmpty.negate(); System.out.println(isNotEmpty.test("hoge")); System.out.println(isNotEmpty.test("")); } } ``` ```text:実行結果 true false ``` `negate()` を実行した `Predicate` オブジェクトを否定する、新しい `Predicate` オブジェクトが生成される。 #`Bi~` - `BiConsumer<T,U>` - `BiFunction<T,U,R>` - `BiPredicate<T,U>` それぞれ `Consumer<T>` ・ `Function<T, R>` ・ `Predicate<T, U>` の抽象メソッドの引数が2つになっている。 #`BinaryOperator<T>` `BiFunction` の拡張インターフェース。 `T` 型の引数を 2 つ受け取って `T` 型の値を返す `apply(T, T)` メソッドが抽象メソッドとして存在する。 ##`maxBy(Comparator<? super T>)` ```java:実装例 package java8sample; import java.util.function.BinaryOperator; public class Java8Sample { public static void main(String[] args) { BinaryOperator<Integer> maxBy = BinaryOperator.maxBy(Integer::compare); int max = maxBy.apply(21, 10); System.out.println(max); } } ``` ```text:実行結果 21 ``` `apply(T, T)` メソッドに渡された2値を、指定した `Comparator` オブジェクトで比較して大きい方の値を返す `BinaryOperator` オブジェクトを生成する。 ##`minBy(Comparator<? super T>)` ```java:実装例 package java8sample; import java.util.function.BinaryOperator; public class Java8Sample { public static void main(String[] args) { BinaryOperator<Integer> minBy = BinaryOperator.minBy(Integer::compare); int min = minBy.apply(21, 10); System.out.println(min); } } ``` ```text:実行結果 10 ``` `maxBy()` の逆。 #`To~Function` と `To~BiFunction` - `ToIntFunction<T>` - `ToLongFunction<T>` - `ToDoubleFunction<T>` それぞれ、 `T` 型の値を引数に受け取って、プリミティブ型の値を返す `applyAs~(T)` 抽象メソッドを持つ。 ```java:実装例 package java8sample; import java.util.function.ToIntFunction; public class Java8Sample { public static void main(String[] args) { ToIntFunction<String> getLength = string -> string.length(); System.out.println(getLength.applyAsInt("hoge")); } } ``` ```text:実行結果 4 ``` - `ToIntBiFunction<T,U>` - `ToLongBiFunction<T,U>` - `ToDoubleBiFunction<T,U>` それぞれ、 `To~Function` の引数が 2 つになったバージョン。 ```java:実装例 package java8sample; import java.util.function.ToIntBiFunction; public class Java8Sample { public static void main(String[] args) { ToIntBiFunction<Integer, Integer> add = (a, b) -> a + b; System.out.println(add.applyAsInt(12, 53)); } } ``` ```text:実行結果 65 ``` #`~To***Function` - `IntToDoubleFunction` - `DoubleToIntFunction` - `LongToDoubleFunction` - `DoubleToLongFunction` - `IntToLongFunction` - `LongToIntFunction` それぞれ `~` を受け取って `***` を返す `applyAs***(~)` メソッドを抽象メソッドとして持つ。 ```java:実装例 package java8sample; import java.util.function.IntToDoubleFunction; public class Java8Sample { public static void main(String[] args) { IntToDoubleFunction function = i -> (double)i; System.out.println(function.applyAsDouble(20)); } } ``` ```text:実行結果 20.0 ``` #`Obj~Consumer` - `ObjIntConsumer<T>` - `ObjLongConsumer<T>` - `ObjDoubleConsumer<T>` それぞれ `T` 型の値と `~` の値を引数に受け取る `accept(T, ~)` メソッドを抽象メソッドとして持つ。 ```java:実装例 package java8sample; import java.util.Arrays; import java.util.List; import java.util.function.ObjIntConsumer; public class Java8Sample { public static void main(String[] args) { List<String> list = Arrays.asList("hoge", "fuga", "piyo"); forEach(list, (value, index) -> { System.out.printf("list[%d] = %s%n", index, value); }); } public static <T> void forEach(List<T> list, ObjIntConsumer<T> callback) { for (int i=0; i<list.size(); i++) { callback.accept(list.get(i), i); } } } ``` ```text:実行結果 list[0] = hoge list[1] = fuga list[2] = piyo ``` #参考 - [java.util.function (Java Platform SE 8 b120)](http://download.java.net/jdk8/docs/api/java/util/function/package-summary.html) |
|
| 686位 |
|
|||
|
02:11:26 |
(classmethod, Inc. 所属) |
|
iOSアプリのUIテストを取り入れようと思いまして調査中です。
まだ触ってもいないのですが、とりあえずググって出てきた候補を挙げておきます。 #Appium AppiumでiOSを自動で受け入れテスト Rspecでテストできるよ! http://konyu.hatenablog.com/entry/2013/06/08/011118 #Calabash-iOS UIテストフレームワークCalabash-iOSを試す〜ターミナルから遠隔操作!〜 http://dev.classmethod.jp/smartphone/iphone/calabash-ios-2/ iOSの究極テストツール:Calabash-iOSを使いこなすぞ!(1) http://tech.voyagegroup.com/archives/6993550.html iOSの究極テストツール:Calabash-iOSを使いこなすぞ!(2) http://tech.voyagegroup.com/archives/6993860.html #Tuneup JS+Travis CI Tuneup JS+Travis CIによるiPhoneアプリ自動UIテストまとめ http://giginet.hateblo.jp/entry/2013/09/18/002458 Travis CI 使ってみた http://d.hatena.ne.jp/wakabatan/20120408/1333878824 #KIF kif-framework/KIF https://github.com/kif-framework/KIF mixi-inc/iOSTraining 11.4 KIFを用いた結合テスト https://github.com/mixi-inc/iOSTraining/wiki/11.4-KIF%E3%82%92%E7%94%A8%E3%81%84%E3%81%9F%E7%B5%90%E5%90%88%E3%83%86%E3%82%B9%E3%83%88 次世代のKIF(2.0.0)が良さそう http://blog.ishkawa.org/blog/2013/08/31/kif-next/ KIF を使った iOS Integration Test http://blog.jarinosuke.com/entry/ios_integration_test Google+モバイルアプリ開発チームのテスト戦略 http://d.hatena.ne.jp/laiso+iphone/20130831/1377928265 Kifの紹介 http://www.slideshare.net/keitaotsuka21/kif Tag Archive for KIF http://www.savinoordine.com/tag/kif/ 自動テスティングフレームワーク "KIF" http://cocoadays-info.blogspot.jp/2012/01/kif.html 継続的インテグレーションに。iOSを自動操作してテスト「KIF」 http://www.moongift.jp/2012/01/20120118-3/ #UIAutomation UIAutomation のテストコードの自動作成 http://tamotamago.com/?p=462 Xcode プロジェクトの自動ビルドと自動 UIAutomation http://tamotamago.com/?p=506 [iOS] InstrumentsでAutomationを実行しながらメモリ使用状況を監視したい http://dev.classmethod.jp/smartphone/iphone/ios-instruments-automation/ #Jenkinsとの連携 Automated Testing with GHUnit and KIF http://michele.io/blog/automated-testing |
|
| 687位 |
|
|||
|
10:41:54 |
|
|
iOSではViewの位置を、**親のViewの原点からの相対位置**で指定し、保持する仕組みになっています。
そのメリットは2点あります。 1.画面の向きを考慮する必要がない 2.親の位置を考慮する必要がない iPhoneのような画面の向きの自由度の高いモバイル端末では、ユーザーから見た左上の原点座標(0,0)が、物理デバイスの原点座標(0,0)と一致することを保証できません。親の左上原点を(0,0)とした相対座標の仕組みは、この複雑さを隠蔽しています。 またViewが複雑に入れ子状になっているレイアウトを考えると、親の絶対座標を取得/計算する必要がない仕組みはシンプルで便利です。 その反面、ビュー階層内で離れた位置にあるビュー同士の位置関係を参照するには変換処理が必須となります。そのために用意されているのが、convert系の以下の4つのメソッドです。 + ` -convertPoint:toView:` + ` -convertPoint:fromView:` + ` -convertRect:toView:` + ` -convertRect:fromView:` しかしこのメソッド群、使うときにレシーバと引数のViewに何を渡せばいいのか混乱してしまったりします。 #Viewの座標系ってframe?bounds? `-convertRect:toView:` というメソッドのシグネチャから、レシーバのViewの座標系のCGRectを、引数toView:のView座標系に変換するのだ、という当たりは付くと思います。しかしここで、「**Viewが持つ座標系**」という言葉の意味を勘違いすると、嵌ります。 UIViewは二つのCGRectを持ちます。superviewからの相対位置とサイズを保持したframe、そして自身のviewの原点とサイズを持つboundsです。 UIViewの位置指定はframeで行うため、Viewとboundsよりも、Viewとframeに強い結びつきがあると思い込みがちです。このため、**「Viewが持つ座標系の値とは、frameである」という落とし穴に嵌る**のです。 位置指定はsuperviewで行うので、frameとはsuperviewの座標系なのであって、「Viewが持つ座標系」とはboundsのことを指すのです。 あるUIView、hogeViewのframe値を、ビュー階層の異なる位置にあるfugaViewのframe値に変換する処理を考えたとき、 `[hogeView convertRect:hogeView.frame toView:fugaView];` は間違いで、 `[hogeView.superView convertRect:hogeView.frame toView:fugaView];` または `[hogeView convertRect:hogeView.bounds toView:fugaView];` と書かなければなりません。convert~系メソッドで思った値が取得できない場合の原因の9割はここだと思います。[Class Reference](http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIView_Class/UIView/UIView.html)をちゃんと読んでいれば、ビューのローカル座標系とはboundsを指すことまで書いてあります。第一にリファレンスに当たるのは大切です。 #toView:とfromView:ではレシーバと引数が逆転 convertRectおよびconvertPointでは、fromView:とtoView:という異なるシグネチャを持つ二つのメソッドがあります。変換元と変換先のViewが、レシーバと引数とで逆転します。 ```objc:convert.m CGRect frame = CGRectZero; frame = [self convertRect:self.bounds toView:self.superView]; frame = [self.superView convertRect:self.bounds fromView:self]; frame = [self.superView convertRect:self.frame toView:self.superView]; frame = self.frame; ``` 以上のコードでは全て同じ結果を得られます。 `self.frame`とは`[self convertRect:self.bounds toView:self.superView];`のシンタックスシュガーみたいなものである、と覚えておけば、このメソッドで引っかかることもなくなるような気がします。 #convertRectの処理は何をやっているのか? iOSはオープンソースではないので実際にどういう処理が行われているのかは分かりませんが、推測は出来ます。 冒頭の説明の通り、UIViewの座標系では常に親の原点(0,0)が左上になりますが、それは物理デバイスの原点(0,0)と一致するとは限りません。デバイス座標系とビュー座標系の間で相互変換を行っているクラスがいることになります。 その役目を果たすクラスこそ、UIWindowです。 UIWindowのframeは物理デバイスでの座標を保持しています。その原点は左下かもしれませんし、右上かもしれません。しかしsubviewに対して左上原点のビュー座標系を提供することで、物理デバイス座標とビュー座標系の橋渡しを行っているのです。 そしてその逆のことをconvertRectやconvertPointで行っているのでしょう。 親からの相対座標frameでは、ビュー階層の異なる位置にあるViewのframeと直接比較することはできません。そこでまずUIWindow上でのframe値、デバイスの絶対座標に変換するのです。絶対座標同士であれば、相互変換は容易に行えます。 `convertRect:toView:`や`convertPoint:toView:`で引数のViewにnilを指定した場合、返る値がWindowでの座標系になる、という性質もこれで納得できます。 #Windowは一つじゃない これまではiOSのようなモバイル端末を考えていたので、物理デバイスの座標系とWindowの座標系を同一視してきましたが、これは正しくありません。 OSXのような複数のWindowを持つアプリケーションを考えれば自明なのですが、それぞれのWindowの原点がスクリーン上で異なる位置を持つこともあります。そのような場合、異なるWindowのView同士で座標変換するためにはどうすればいいのでしょうか? ここでは`convertRect:toView:`や`convertPoint:toView:`は使えません。UIWindowの次のメソッド群を使います。 + `–convertPoint:toWindow:` + `–convertPoint:fromWindow:` + `–convertRect:toWindow:` + `–convertRect:fromWindow:` これらのメソッドは、それぞれのWindowが持つ、Window座標系の値を、上位の(今度こそ本物の)物理デバイスの論理座標系で比較します。 #座標系階層の整理 ここまでの座標系の階層を整理します。 ##1.物理デバイス論理座標系 物理デバイスの各ピクセルをOSが扱いやすいように写像した座標系です。アプリケーションプログラマ視点ではたぶん最上の座標系です。 retinaディスプレイのように、物理デバイス論理座標系は実際の物理ピクセルと1対1対応していないので、実際にはさらに上位の座標系も存在しています。 ##2.Window座標系 複数のウインドウを持つ(特にデスクトップの)アプリケーションの各々のウインドウがもつ座標系です。スクリーン上での絶対座標を保持していて、画面の向きの自由度が高いモバイルデバイスではその複雑さを吸収する役割も果たしています。 ウインドウをまたいで座標変換する必要がある場合は、Window座標系の値を上位の物理デバイス論理座標系を経由して変換します。 ##3.View座標系 左上を原点(0,0)として、子ビューの位置を管理する、アプリケーションプログラマにとって一番馴染みのある座標系です。 ビューをまたいで座標変換する必要がある場合、同一Window内であれば、`convertRect:toView:`などのメソッドを用いて、一度Window座標系を経由することで他のView座標系の値へと変換することができます。 異なるWindowのView座標系の値へと変換する場合は、Window座標系の値に変換し、その値を物理デバイス論理座標系を経由して別のWindow座標系に渡す…という二手間掛ける必要があります。 #余談。なぜUIKeyboardのNotificationの値はWindow座標系なのか? UIKeyboardの各種Notificationを使われたことのある方は、userInfoの中に格納されたソフトウェアキーボード座標がView座標系ではなくWindow座標系の値であることをご存知だと思います。 キーボードの出現/消去の通知で渡されるソフトウェアキーボードの位置や大きさは、画面の向きによってはoriginやsizeの値が反転しているので、その判定処理を書かなければならないのです。 それを不思議に思われた方もいるのではないでしょうか。 しかしここまでの内容を理解して、かつ**iOSのソフトウェアキーボードが、通常のアプリケーションのView階層が持つWindowとは別の、UITextEffectsWindowというprivateクラスのWindowに属している**、ということに気付けば納得できると思います。 つまり、ソフトウェアキーボードのframeを、アプリケーション側のWindowに渡すためには、 ```objc:convert2.m CGRect windowFrame = [inputView convertRect:inputView.bounds toView:nil]]; CGRect result = [inputView.window convertRect:windowFrame toWindow:[[UIApplication sharedApplication] keyWindow]]; ``` という二段階の座標変換を行わなければならないのです。 この結果は論理デバイス座標系のレイヤを経由しなければならないので、Window座標系の値になるのです。UIKeyboardNotificationに関する定数が、UIWindowのヘッダに記載されているのも納得がいきます。 こういうもやもやとした約束事の意味が分かると、ものすごくスッキリします。 |
|
| 688位 |
|
|||
|
19:22:14 |
|
|
こんなモデルがあるとして
```ruby:user.rb class User < ActiveRecord::Base scope :male, ->{ where(sex: :male) } # 男性 scope :female, ->{ where(sex: :female) } # 女性 scope :adult, ->{ where(arel_table[:age].gteq 20) } # 成人 scope :minor, ->{ where(arel_table[:age].lt 20) } # 未成年 scope :men, ->{ adult.male } # 成人,男性 scope :women, ->{ adult.female } # 成人,女性 scope :boys, ->{ minor.male } # 未成年,男性 scope :girls, ->{ minor.female } # 未成年,女性 end ``` ## 単純なwhereのscopeをorでつなげる場合。 ### ex) 男性 or 未成年 ```ruby User.where(User.male.minor.where_values.reduce(:or)).to_sql # => "SELECT `users`.* FROM `users` WHERE ((`users`.`sex` = 'male' OR `users`.`age` < 20))" # もちろん male.minor と boys は同じなのでこれでもいい User.where(User.boys.where_values.reduce(:or)).to_sql # => "SELECT `users`.* FROM `users` WHERE ((`users`.`sex` = 'male' OR `users`.`age` < 20))" ``` ### ex) 男性 or 未成年 or :role => 'admin' ```ruby User.where(User.male.minor.where(role: 'admin').where_values.reduce(:or)).to_sql # => "SELECT `users`.* FROM `users` WHERE ((`users`.`sex` = 'male' OR `users`.`age` < 20 OR `users`.`role` = 'admin'))" ``` ## and条件も含むちょっと複雑なscopeをorでつなげる ### ex) 成人女性(成人 and 女性) or 未成年男子(未成年 and 男子) ```ruby women = User.women.where_values.reduce(:and) boys = User.boys.where_values.reduce(:and) User.where(women.or boys).to_sql # => "SELECT `users`.* FROM `users` WHERE ((`users`.`age` >= 20 AND `users`.`sex` = 'female' OR `users`.`age` < 20 AND `users`.`sex` = 'male'))" ``` ### ざっくり説明 `ActiveRecord::Relation#where_values`で`Arel::Nodes::Node`の配列が取得できる。 このNodeにはarelの`.and()`やら`.or()`やらが使えるので、そのあたりを組み合わせてからARの`.where()`だったり`.merge()`だったりに渡してあげるとOK。 |
|
| 689位 |
|
|||
|
22:40:51 |
(株式会社メルカリ 所属) |
|
JavaScriptで配列を生成するにはコンストラクタ `new Array()` とリテラル `[]` の二つがあるので違いをまとめておく。
個人的には、簡潔で読みやすいので、なるべく `[]` を使うべきだと思っている。 # 機能面での違い どちらも配列オブジェクトを生成する。 ただし、整数を一つだけ渡した際の挙動が異なる。 * `new Array(3)` はlengthが3の配列を生成する。中身は0から2まですべてundefinedになっている。 * `[3]` は要素をひとつだけ含む配列を生成する。 ```javascript:基本的な使い方 console.log( new Array(1,2,3) ); //1,2,3 console.log( [1,2,3] ); //1,2,3 console.log( new Array(3) ); //undefined, undefined, undefined console.log( [3] ); //3 ``` 逆に言えば、 * new Array()は[3]のように整数一つだけを含む配列を作成できない * []はサイズを指定して配列を作成できない ということになる。 # 速度面での違い…はほとんどない 先に結論を書いてしまった。エンジンにもよるし使い方にもよる。どちらが速いというものでもないので、読みやすさを優先すればよい。 完璧主義者のために、差が出るポイントをまとめてみる。 ## 参照解決のコスト 「Array」は予約語ではないので、ローカル変数を指している可能性がある。JavaScriptエンジンからすれば、「Array」が組み込みオブジェクトのArrayであることを特定する作業が必要になるのだ。わずかだがここにコストがかかる。 これに対してリテラルは変数ではないので参照解決の必要がない。 このため、理論上は[]の方が速い。 ただ、実際に測定してみるとほとんど差が感じられない。with()を10段重ねるとか、わざとらしいコードを書けば影響がわかるが、普通そんなことはしない。JavaScriptエンジンの最適化がよく効いているのだと思う。 むしろ次の配列長の方が影響が大きい。 ## 配列長の変更コスト 配列の長さを変更するのにも少しコストがかかる。 このため、[]で作った配列を埋めていくより、最初から必要な配列長を指定して確保しておくと、速くなることがあった。(過去形です) ```javascript var arr = []; for (var i=0; i<1000; i++) { arr[i] = i; } //古いブラウザでは↓の方が速い var arr = new Array(1000); for (var i=0; i<1000; i++) { arr[i] = i; } ``` 一つずつ要素を入れて配列長をじわじわ伸ばしていくより、最初にまとめて確保しておいた方が軽いという理屈だ。 古いJavaScriptエンジンでは有効なテクニックだったのだが、エンジンの進歩により、逆転現象が起きている。V8で測定すると[]を使った方が2倍ほど速かった。 というわけで、今の時代、タイプ数の多い `new Array` をあえて使う必要はない。 |
|
| 690位 |
|
|||
|
13:03:21 |
(Piece of Cake, Inc 所属) |
|
構築を試みたときに調べたことなどなど
#### なぜunicorn + nginx?  unicornは汎用のRackアプリケーションサーバ。Railsからもsinatraからも使える。 unicornだけでも、任意のポートをlistenすればwebサーバーとして使えるが、 以下のようなメリットがあるため、本番ではnginxをリバースプロキシとして 前に立ててからリクエストをunicornに流すのが良い + 画像やcssなどの静的ファイルはnginxで高速に返せる + リクエストをバッファリングして受信完了してからunicornにリクエストを 投げるため、遅い回線(3Gなど)で接続が来た時にunicornの ワーカプロセスの待ち時間を削減できる 動作原理はgithubの運用例が記載された以下のドキュメントが詳しい https://github.com/blog/517-unicorn #### まずは、unicornだけでサーバーを立てる サンプルのsinatraアプリをunicornから呼んでみる ```rb:config.ru require 'rubygems' require 'sinatra/base' class HelloApp < Sinatra::Base get '/hello' do 'Good Sunday Morning!' end end run HelloApp ``` Gemfileにunicornを足して、bundle installする ```rb:Gemfile gem "unicorn" ``` ```bash:shell $ bundle install $ bundle exec unicorn -v unicorn v4.6.3 ``` unicornの設定ファイルを作る。rubyのコードで書けてgood。 ```rb:unicorn.rb @dir = "/path/to/app/" worker_processes 2 # CPUのコア数に揃える working_directory @dir timeout 300 listen 80 pid "#{@dir}tmp/pids/unicorn.pid" #pidを保存するファイル # unicornは標準出力には何も吐かないのでログ出力を忘れずに stderr_path "#{@dir}log/unicorn.stderr.log" stdout_path "#{@dir}log/unicorn.stdout.log" ``` AWSを使っているなら、セキュリティグループで指定したportを 空けておく(よく忘れてハマる...) これで準備完了。以下のコマンドで起動できる。-Dはdaemonとしての起動。 本番環境で起動する時は-E production とする (※ オプションは設定ファイルより優先される) ```bash:shell $ bundle exec unicorn -c unicorn.rb -D $ ps auxf | grep unicorn | grep -v grep unicorn master -c unicorn.rb -D \_ unicorn worker[0] -c unicorn.rb -D \_ unicorn worker[1] -c unicorn.rb -D ``` psを見ると、workerプロセスとそれを管理するmasterプロセスが 動いていることが確認できる また、unicorn_railsという実行ファイルもあるが、こちらは 過去verのRails向けで、基本的にはunicornを使うことが推奨されている #### nginxをリバースプロキシとしてunicornの前に置く 次に、nginxをunicornの前に置く [unixドメインソケット](http://ja.wikipedia.org/wiki/UNIX%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%BD%E3%82%B1%E3%83%83%E3%83%88)を経由してnginxとunicornがやりとりを行うよう設定 また、/path/to/app/static以下のリクエストはnginxが処理する(unicornまでいかない) ```bash:shell $ sudo yum -y install nginx $ nginx -v nginx version: nginx/1.4.2 ``` ```nginx:/etc/nginx/nginx.conf user root; worker_processes 2; http { upstream unicorn_server { server unix:/path/to/app/tmp/unicorn.sock; } server { listen 80; server_name ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com; root /path/to/app/static; location / { # 静的ファイルが存在しなかった時はUnicornにproxy if (!-f $request_filename) { proxy_pass http://unicorn_server; break; } } } } ``` ```rb:unicorn.rb # listen 80 listen "/path/to/app/tmp/unicorn.sock", backlog: 1024 # UNIXソケットを見るように変更 ``` nginxを起動,unicornを再起動 ブラウザでhttp://xx.xx.xx.xx/hello を叩いて確認 ```bash:shell $ sudo /etc/init.d/nginx start or $ sudo nginx ``` #### ネクストステップ 以上でとりあえずは動くが、他にもunicornには調べておくべきトピックが あるので、いくつか挙げておきます + linuxシグナル送信によるunicornの起動/停止 + capistranoを用いてホットデプロイを実現する + preloadを有効化してリロード速度の応答低下を回避する Have Fun! |
|
| 691位 |
|
|||
|
11:21:46 |
(Increments 所属) |
|
ブロックの外で定義された変数をブロック内で使うとき、その変数はブロック内に `strong` 参照でキャプチャされる。場合によってはこれが循環参照を引き起こすことがある:
```objc // self が block を strong 参照→ block が self をキャプチャ(strong 参照) self.aStrongProperty = ^{ NSLog(@"self = %@", self); }; ``` これを回避するには、問題となる変数の `weak` 参照をブロックに渡すことになる: ```objc __weak typeof(self) wself = self; // __weak TheClass *wself = self; // If Xcode < 4.4 self.aStrongProperty = ^{ NSLog(@"self = %@", wself); }; ``` [libextobjc](https://github.com/jspahrsummers/libextobjc) はこのパターンを簡単にする [weakify/strongify マクロ](https://github.com/jspahrsummers/libextobjc/blob/master/extobjc/EXTScope.h)を提供している。ブロックの外で、 `weak` 参照にしたい変数を引数に `@weakify()` を呼び、ブロックの中で先ほどの変数を引数に `@strongify()` を呼べばよい: ```objc #import <libextobjc/EXTScope.h> ... - (void)weakSelfPattern { @weakify(self); self.aStrongProperty = ^{ @strongify(self); NSLog(@"self = %@", self); }; } ``` マクロの引数には `@weakify(var1, var2, var3)` のように複数の変数を渡せる。また `unsafe_unretained` 参照を作る `@unsafeify()` マクロも用意されている。 `wself = self` パターンと異なり、同名の変数をそのまま使えるので、ブロック内で `wself` の代わりに `self` を使ってしまうミスは起こりえない。 蛇足:マクロ名が `@` で始まっているが、これは Objective-C の組み込みのディレクティブではない。マクロ定義の先頭に空の autoreleasepool 文を置くことでディレクティブのような見た目のマクロを作るテクニックを使っている: ```objc #define strongify(...) \ autoreleasepool {} \ 《マクロ本体》 // `@strongify()` が `@autoreleasepool {} 《マクロ本体》` に展開される ``` |
|
| 692位 |
|
|||
|
08:40:47 |
|
|
productionで運用するときに注意すべきこと。
##application.cssとapplication.jsの動作 アプリケーションで利用するcssとjsをassets:precompileで一つのファイルにまとめてます。その上でlayout/application.html.hamlでincludeして、アプリケーションで利用します。 ##cssとjsの設計に注意 デザイナーさんが画面ごとにCSSやJSを別ファイルにして、名前空間が被ってしまうとapplication.cssやapplication.jsにまとめることができません。そうすると、compileの対象ファイルとならずにproductionだと404になってしまいます。 ##config.assets.precompileに記述 compileの対象とするために、config.assets.precompileに記述をします。 ##assetsの設定はこうしろ * config.serve_static_assets = false これ超重要。nginxで見つからないとunicornにいちいち問い合わせがいくのでサーバーの負荷が劇的にあがってしまう。 * config.assets.compress = true 下りgzにするため必須 * config.assets.compile = false trueにするとサーバーで動的にcompileするためにfalseにしないとだめ * config.assets.digest = false digest付与はお好きに。私は好きじゃないのでオフにしています。 * config.assets.debug = false debug=trueにすると、application.css/jsと個別のファイルの二重読み込みがされます。 ##Railsの本が出ます! [RailsとiPhoneではじめるアプリケーション開発 ](http://www.amazon.co.jp/dp/4844334476)  |
|
| 693位 |
|
|||
|
18:30:08 |
|
|
```objectivec: @property (ここ) NSString *name; ``` 上のカッコの中のやつ。 ## Setter の作成有無の指定 指定無し: readwrite ### readonly setter を作らない。 ### readwrite setter、getterを両方作成する。readonlyプロパティをオーバーライドする時に有用。 ## Setter の書き方の指定 指定無し: strong (非ARCではassign) ### assign 渡された値をそのまま保持。 **参照先のオブジェクトが消えた!** -> ゾンビ状態になり、それにメッセージを送るとプログラムがクラッシュする。 ### weak 渡された値をそのまま保持。 **参照先のオブジェクトが消えた!** -> 自動的に nil に書き換え。 ### strong (昔のretain) 渡されたオブジェクトのオーナーシップを取得して保持。 **参照先のオブジェクトが消えた!** -> いやいや、そんなことはないって。 ### copy 渡されたオブジェクトの copyWithZone メソッドを呼び、結果を strong で保持。 実際にオブジェクトがコピーされるかされないかは、渡されるオブジェクトのクラス次第。 **Mutable (変更可能) なオブジェクト** 渡されたオブジェクトと同じ値が設定された Immutable (変更不能) なオブジェクトを作成する。(例: NSMutableArray -> NSArray) > いや俺はどうしても Mutable なオブジェクトを設定したいんだ! -> mutableCopy メソッドでコピーするような setter を自前で用意すれば可能。 **Immutable (変更不能) なオブジェクト** 無駄なのでコピーは作成しない。つまり、実質 strong と同じ動作。 ## Setter と Getter の排他関係の指定 指定無し: atomic ###atomic 生成された getter と setter の中にロック処理が書き込まれる。 > setter を呼び出して、それを動作している最中に getter を呼んだときには、オブジェクトの値の設定処理が完全に終わるまで待ってからオブジェクトを取得する。getter の処理中に setter が呼ばれたときは、オブジェクトの取得処理が完全に終わってから設定処理を行う。 このため、getter で中途半端なものが返ってきてアプリがクラッシュする可能性が(元々ほとんどないけど...)なくなる。 > ただし、あくまでも値が「中途半端なものになっていない」ってだけで、取得した値が開発者の意図したものになっているかは別の話。それはアプリ側でちゃんと対応する必要がある。 ###nonatomic ロック処理が getter と setter に書き込まれない分、高速に動作する。 ## SetterとGetterのメソッド名の指定 ###setter `setter=setValue:`のようにすると setter のメソッド名を変更できる。引数が一つ付いたセレクタ名にすることに注意。 ###getter `getter=value`のようにすると getter のメソッド名を変更できる。 |
|
| 694位 |
|
|||
|
07:33:17 |
|
|
ここで言う`history`とは、コマンド履歴のことです。シェルはもちろん`zsh`です。この投稿を読んでくれている人は、多分`zsh`を使っていると思いますが、まだ入れてないという人は以下の様な手順を踏むと、`zsh`をインストールすることができます。
#zshのインストール ##Windowsユーザーの場合 `Cygwin`というものを使います。まず、`Ctrl`+`R`を押して、`powershell`と入力し、`Enter`を押しましょう。そして、以下のスクリプトを実行します。または、[こちら](http://www.cygwin.com/)からダウンロードして、実行ファイルをダブルクリックするのもいいかもしれません。その際は、`Wget`だけにはチェックを入れておきましょう。 ``` function Install-Cygwin { param ( $TempCygDir="$env:temp\cygInstall" ) if(!(Test-Path -Path $TempCygDir -PathType Container)) { $null = New-Item -Type Directory -Path $TempCygDir -Force } $client = new-object System.Net.WebClient $client.DownloadFile("http://cygwin.com/setup.exe", "$TempCygDir\setup.exe" ) Start-Process -wait -FilePath "$TempCygDir\setup.exe" -ArgumentList "-q -n -l $TempCygDir -s http://mirror.nyi.net/cygwin/ -R c:\Cygwin" Start-Process -wait -FilePath "$TempCygDir\setup.exe" -ArgumentList "-q -n -l $TempCygDir -s http://mirror.nyi.net/cygwin/ -R c:\Cygwin -P openssh" Start-Process -wait -FilePath "$TempCygDir\setup.exe" -ArgumentList "-q -n -l $TempCygDir -s http://mirror.nyi.net/cygwin/ -R c:\Cygwin -P wget" } ``` ``` Install-Cygwin ``` インストールできたら、早速起動し、[apt-cyg](http://code.google.com/p/apt-cyg/)コマンドを使えるようにします。 ``` wget http://apt-cyg.googlecode.com/svn/trunk/apt-cyg mv apt-cyg /usr/bin chmod +x /usr/bin/apt-cyg ``` 最後に、`zsh`をインストールして使えるようにします。 ``` apt-cyg install zsh vim sed -i 's/bash/zsh/g' /Cygwin.bat touch ~/.zshrc /Cygwin.bat ``` 詳しくは、[こちら](http://yanother.blogspot.jp/2011/02/zsh.html)の記事が参考になります。 ##Linuxユーザーの場合 ```shell:Ubuntu sudo apt-get install -y zsh sudo chsh -s /bin/zsh ``` ##Macユーザーの場合 Macユーザーの場合は、デフォルトでインストールされていると思います。よって、`chsh`コマンドだけでもOKです。しかし、最新版を使いたかったら、`Homebrew`などを使ってインストールします。 ```shell:Homebrewの導入 /usr/bin/ruby -e "$(/usr/bin/curl -fksSL https://raw.github.com/mxcl/homebrew/master/Library/Contributions/install_homebrew.rb)" ``` ``` brew install zsh which zsh chsh -s /usr/local/bin/zsh ``` #zshのhistoryを便利に使うためのTips ##historyを使う 基本的には、`C-r`を押すと、history検索できます。 ```shell:~/.zshrc # 履歴ファイルの保存先 export HISTFILE=${HOME}/.zsh_history # メモリに保存される履歴の件数 export HISTSIZE=1000 # 履歴ファイルに保存される履歴の件数 export SAVEHIST=100000 # 重複を記録しない setopt hist_ignore_dups # 開始と終了を記録 setopt EXTENDED_HISTORY ``` ##全履歴を一覧表示する ```shell:~/.zshrc function history-all { history -E 1 } ``` ここで、特定のキーワードのみを覚えていた場合、それらをつなぎあわせて検索することで、複雑なワンライナーの再利用も簡単になります。 ``` history-all | grep find | grep tr ``` ##historyの選択 historyを選択したいのなら[zaw.zsh](https://github.com/zsh-users/zaw)が便利です。`Git`をインストールした状態で以下のように設定します。 ``` echo -e "function mkcd(){mkdir -p \$1 && cd \$1}" >> ~/.zshrc && exec $SHELL mkcd ~/zsh_plugin/ git clone git://github.com/zsh-users/zaw.git echo "source ${PWD}/zaw/zaw.zsh" >> ~/.zshrc && exec $SHELL ``` デフォルトでは、`Ctrl-x ;`で起動します。 以下のようなキーバインドを設定することで、`C-h`を押すと、容易にhistory一覧を表示し、選択できるようになります。ちなみに、`auto-fu.zsh`などを導入している場合は、`C-g`を押してから入力しましょう。 ```shell:~/.zshrc bindkey '^h' zaw-history ``` ##historyの検索 入力に合わせてhistoryを検索したい場合は、以下のキーバインドを設定しておくと簡単に検索できて便利です。 ```shell:~/.zshrc # history search bindkey '^P' history-beginning-search-backward bindkey '^N' history-beginning-search-forward ``` ##historyの共有 ```~/.zshrc setopt share_history ``` ##その他、便利な設定 ``` # ヒストリに追加されるコマンド行が古いものと同じなら古いものを削除 setopt hist_ignore_all_dups # スペースで始まるコマンド行はヒストリリストから削除 setopt hist_ignore_space # ヒストリを呼び出してから実行する間に一旦編集可能 setopt hist_verify # 余分な空白は詰めて記録 setopt hist_reduce_blanks # 古いコマンドと同じものは無視 setopt hist_save_no_dups # historyコマンドは履歴に登録しない setopt hist_no_store # 補完時にヒストリを自動的に展開 setopt hist_expand # 履歴をインクリメンタルに追加 setopt inc_append_history # インクリメンタルからの検索 bindkey "^R" history-incremental-search-backward bindkey "^S" history-incremental-search-forward ``` |
|
| 695位 |
|
|||
|
10:43:01 |
(qnote,Inc. 所属) |
|
UIScrollView にはループ機能は無いのでスクロールのタイミングでサブビューを再配置+スクロールのオフセットを戻して擬似的にループを再現させる方法がよく取られます。
例えば、 - UIScrollView の中に5つの subview があり、  - 右にスワイプしたとします。  - スクロールが止まったらすぐに一番右のサブビューを抜き出して、一番左になるよう全ての subview を再配置。(A)  - それと同時にスクロールオフセットを元の位置に戻す。(B)  - 1サイクル完成。  これを繰り返せば無限ループしているように見えます。 (A) と (B) の処理を一瞬で行うため、subview の数によっては描画が追いつかないので、pagingEnabled = YES にして、かつ UIScrollView のデリゲートメソッド scrollViewDidEndDecelerating: の中で (A)+(B) の処理をすればスムーズに見えます。 拙作の InfiniteScrollView を使うと簡単。 ■ GitHub InfinitePagingView https://github.com/caesarcat/InfinitePagingView - 使い方 ```Objective-C #import "InfinitePagingView.h" : : /* インスタンス作成 */ InfinitePagingView *pagingView = [[InfinitePagingView alloc] initWithFrame:frame]; [self.view addSubview:pagingView]; /* サブビュー作成 */ UIImageView *page1 = [[UIImageView alloc] initWithImage:image1]; /* ページとして追加 */ [pagingView addPageView:page1]; : : /* 最低5個は必要 */ ```  - AppStore のスクリーンショット風に1ページあたりのサイズ指定も可能 ```Objective-C /* インスタンス作成 */ InfinitePagingView *pagingView = [[InfinitePagingView alloc] initWithFrame:frame]; /* 1ページあたりのサイズを指定 */ pagingView.pageSize = CGSizeMake(120.f, 100.f); ```  - 縦スクロールも可能 ```Objective-C /* インスタンス作成 */ InfinitePagingView *pagingView = [[InfinitePagingView alloc] initWithFrame:frame]; /* 1ページあたりのサイズを指定 */ pagingView.pageSize = CGSizeMake(120.f, 100.f); /* スクロール方向を垂直に指定 */ pagingView.scrollDirection = InfinitePagingViewVerticalScrollDirection; ```  |
|
| 696位 |
|
|||
|
14:16:03 |
(株式会社時雨堂 所属) |
|
## 概要 やりたい事が出来るかどうか、皆がどうやって使ってるのかのメモ ## コマンド ### コンテナ起動してログイン -i と - t をつけて bash を実行することでコンテナにログインする ```bash $ docker run -i -t base /bin/bash ``` ### コンテナ起動時に CPU 相対的使用率を指定する ```bash $ docker run -c 200 -i -t base /bin/bash ``` -c を使う事で CPU の相対的な割合を決められます。詳細は cgroup の cpu.shares を参照して下さい。 たとえば -c 100 と -c 200 であれば -c 100 よりも 2 倍の CPU 時間が提供されることになります。 #### 参考 - [3.2. cpu](https://access.redhat.com/site/documentation/ja-JP/Red_Hat_Enterprise_Linux/6/html/Resource_Management_Guide/sec-cpu.html) ### コンテナ起動時にメモリを指定する ```bash $ docker run -m 512m -i -t base /bin/bash ``` -m を使う事でメモリ容量を指定出来ます。単位は b,k,m,g です。上記例では 512メガバイトを割り当てています。 ### コンテナバックグラウンド起動 ```bash $ docker run -i -t -d base /bin/bash ``` ### コンテナにログイン ```bash $ docker attach {{ CONTAINER-ID }} ``` ### コンテナを起動してログイン -a を付けると attach する ```bash $ docker start -a <CONTAINER-ID> ``` ### コンテナ一覧 ```bash $ docker ps -a ``` ### コンテナ全て削除 ```bash $ docker rm $(docker ps -a -q) ``` ### イメージの一覧 ```bash $ docker images ``` ### イメージの削除 ```bash $ docker rmi {{ IMAGE-ID }} ``` ### ubuntu イメージの 12.04 タグを指定してダウンロード : でタグを指定可能 ```bash $ docker pull ubuntu:12.04 ``` ### ubuntu コンテナの 13.10 タグを指定して実行 ```bash $ docker run ubuntu:13.10 /bin/echo hello world ``` ## Docker Remote API を有効にする /etc/default/docker に以下を追記して再起動します。 ``` DOCKER_OPTS="-H unix:///var/run/docker.sock${DOCKER_OPTS}" DOCKER_OPTS="-H tcp://0.0.0.0:4243 ${DOCKER_OPTS}" ``` Unix Domain Socket と TCP を有効にしています。 ```bash $ curl -X GET http://127.0.0.1:4243/v1.10/images/json [{"Created":1397068284,"Id":"25593492b938f072b2d1194f987bb62807afa8a259875ba86616a7d08d1c5852","ParentId":"d0732e6ce5634e940c924d8646f8081a0fb95c18bf31e978e77fc3396fea6b9a","RepoTags":["ubuntu:13.10","ubuntu:saucy"],"Size":10621789,"VirtualSize":192747614} ,{"Created":1397068120,"Id":"c0fe63f9a4c182d87029fcb87fe88c7eac448226b245926b99ba3d3d082ca468","ParentId":"79fdb1362c848d06252eb2cb466fe196d1f852b11d8c4532b21dfd9c64cfa632","RepoTags":["ubuntu:12.04","ubuntu:latest","ubuntu:precise"],"Size":26366127,"VirtualSize":231071128} ``` ## docker コンテナに ssh で繋ぐ docker run で起動したコンテナで sshd を立てて ssh で繋ぐ まずは ubuntu:13.10 を起動して openssh-server をインストールする ```bash $ docker run -i -t ubuntu:13.10 /bin/bash root@b9da36681214:/# apt-get update root@b9da36681214:/# apt-get install openssh-server root@b9da36681214:/# mkdir /var/run/sshd root@b9da36681214:/# /usr/sbin/sshd root@b9da36681214:/# passwd root root@b9da36681214:/# exit ``` docker commit で openssh-server がインストールされたコンテナを image に変換する docker images で voluntas/ubuntu-sshd がある事を確認する ```bash $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b9da36681214 ubuntu:13.10 /bin/bash 4 minutes ago Exit 0 backstabbing_babbage $ docker commit b9da36681214 voluntas/ubuntu-sshd 7e9192a4e1293790d6ff1577485eff8d1915fae5b96174dc86e2124923b7530d $ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE voluntas/ubuntu-sshd latest 7e9192a4e129 38 seconds ago 225.5 MB ubuntu 13.10 9f676bd305a4 8 weeks ago 182.1 MB ubuntu saucy 9f676bd305a4 8 weeks ago 182.1 MB ubuntu 12.04 9cd978db300e 8 weeks ago 204.7 MB ``` docker run で voluntas/ubuntu-sshd コンテナを使って /usr/sbin/sshd をデーモンで起動する 22 番のポートをフォワードする ``` $ docker run -d -p 22 voluntas/ubuntu-sshd /usr/sbin/sshd -D cc529bb34bbe3134d47ee73f86c734c175d0ba28322e818654d4ec6327aca443 $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cc529bb34bbe voluntas/ubuntu-sshd:latest /usr/sbin/sshd -D 21 seconds ago Up 20 seconds 0.0.0.0:49154->22/tcp stoic_hawking b9da36681214 ubuntu:13.10 /bin/bash 6 minutes ago Exit 0 backstabbing_babbage ``` コンテナ ID cc529bb34bbe の 22 番ポートがローカルのどのポートにマッピングされているのかを確認する ``` $ docker port cc529bb34bbe 22 0.0.0.0:49154 ``` ``` $ ssh root@127.0.0.1 -p 49154 The authenticity of host '[127.0.0.1]:49154 ([127.0.0.1]:49154)' can't be established. RSA key fingerprint is 0b:2b:63:fe:ca:b9:ca:6f:e2:f9:0e:3f:29:7c:2d:2f. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '[127.0.0.1]:49154' (RSA) to the list of known hosts. root@127.0.0.1's password: Welcome to Ubuntu 13.10 (GNU/Linux 3.10.25-gentoo x86_64) * Documentation: https://help.ubuntu.com/ The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. root@cc529bb34bbe:~# ``` ## Tips - 終了したコンテナは /var/lib/docker/containers/ に残る ## デプロイ - [Running CoreOS 231.0.0 on EC2](https://coreos.com/docs/running-coreos/cloud-providers/ec2/) - [Running CoreOS 231.0.0 on Google Compute Engine](https://coreos.com/docs/running-coreos/cloud-providers/google-compute-engine/) ## 参考 - [Command Line Interface - Docker Documentation](https://docs.docker.io/en/latest/reference/commandline/cli/#run) - [Docker 虎の巻](https://gist.github.com/tcnksm/7700047) - [米Red Hatが「RHEL 6.5」を発表――Docker対応、仮想化KVMも強化 | SourceForge.JP Magazine] (http://sourceforge.jp/magazine/13/11/26/093600) - [Dockerと継続的インテグレーション](http://www.slideshare.net/techblogyahoo/140212-docker-continuousintegration) - [glidenote/vagrant-docker-digitalocean](https://github.com/glidenote/vagrant-docker-digitalocean) - [CoreOSでDocker入門 - $web->{note};](http://n8.hatenablog.com/entry/2013/08/15/164643) - [dockerのカスタムベースイメージを作成する - @znz blog](http://blog.n-z.jp/blog/2013-12-13-docker-custom-base-image.html) - [2014/01/17 Docker公式リポジトリでTrustedビルドimageを作成する手順 - 清水川Web](http://www.freia.jp/taka/blog/create-docker-trusted-build-image-on-the-docker-official-repository/index.html) - [GCE + Dockerハンズオン - Google ドライブ](https://docs.google.com/a/shiguredo.jp/presentation/d/1EgNM6DTtctEwFlUl_7iBbRRr36LIo14FnCaQQBzt-Cc/preview) - [vagrant-coreos-docker-apache-example/Dockerfile](https://github.com/paulwib/vagrant-coreos-docker-apache-example/blob/master/Dockerfile) - [Docker.io](http://www.slideshare.net/ladislavprskavec/dockerio-28554888) - [jpetazzo/pipework Software-Defined Networking tools for LXC (LinuX Containers)](https://github.com/jpetazzo/pipework) - [docker - manage docker containers — Ansible Documentation](http://docs.ansible.com/docker_module.html) - [RubyからDockerコンテナを作ってsshで操作できるようにする](http://kurochan-note.hatenablog.jp/entry/2014/02/09/235615) ### ようへいの日々精進 自分がやりたい事ほとんどやっててとても参考になる - [docker-registry と Amazon S3 で作る自分だけのコンテナ倉庫(リポジトリ) - ようへいの日々精進](http://inokara.hateblo.jp/entry/2014/01/26/102059) - [CoreOS を使って docker を体験した - ようへいの日々精進](http://inokara.hateblo.jp/entry/2013/09/27/074428) - [Dockerfile の書き方「私的」なベストプラクティス - ようへいの日々精進](http://inokara.hateblo.jp/entry/2013/12/28/121828) - [Dockerfile の書き方「私的」なベストプラクティス(2)〜サービスの起動について〜 - ようへいの日々精進](http://inokara.hateblo.jp/entry/2013/12/28/145849) - [docker のオリジナルコンテナイメージを作ってみた(2) - ようへいの日々精進](http://inokara.hateblo.jp/entry/2013/10/06/130251) - [docker memo - ようへいの日々精進](http://inokara.hateblo.jp/entry/2013/10/04/153221) - [docker API を使ってみる - ようへいの日々精進](http://inokara.hateblo.jp/entry/2013/09/30/234806) - [docker のコンテナイメージに ssh でアクセスしてみる - ようへいの日々精進](http://inokara.hateblo.jp/entry/2013/09/29/090500) - [オリジナルのコンテナイメージを index.docker.io に登録してみる - ようへいの日々精進](http://inokara.hateblo.jp/entry/2013/09/29/002159) - [docker のオリジナルコンテナイメージを作ってみた - ようへいの日々精進](http://inokara.hateblo.jp/entry/2013/09/28/150947) |
|
| 697位 |
|
|||
|
21:58:24 |
|
|
[追記]気づいたらいつの間にか50ストックを超えていました。みなさんありがとうございます。[/追記]
どうも、Opera Nextに(色んな意味で)驚きを隠せないあらっきぃです。 大抵の場合は`Array#indexOf`で事足りるんだけど、時々高速に配列に要素が存在するか調べたくなることがあります。 例えばこんな風に、要素数`100000`の配列があったとします。 ```javascript var arr = (function(len) { var i, arr = []; for(i = 0; i < len; i++) arr.push(~~(Math.random() * len)); return arr; })(1000000); ``` この配列に`999983`(`1000000`以下の最大の素数)が含まれているか調べるには、通常こう書きます。 ```javascript var search_num = 999983; if(arr.indexOf(search_num) !== -1) { //要素があった場合 } else { //要素が無かった場合 } ``` (CoffeeScriptの`in`演算子も`Array#indexOf`を呼びますね) ですが、この`Array#indexOf`は単純なリニアサーチ(線形探索)なので、最悪要素数に比例した時間がかかります。なので、複数回呼ぶ場合は速度においてネックになります。 ソートされていればバイナリサーチでもいいのですが、あいにくこの配列はソートされていそうにもありませんし(ボゴソート的確率でソートされていることはありますが…)、バイナリサーチを書くのは正直面倒です。 そこで、JavaScriptの全てとも言えるオブジェクトを使って、こんな風に書いてみることにしました。 ```javascript var arrTable = arr.reduce(function(m, a, i) { m[a] = (m[a] || []).concat(i); return m; }, {}); if(searchNum in arrTable) { //要素があった場合 } else { //要素が無かった場合 } ``` はい、ハッシュ法です(まあJavaScriptのオブジェクトをハッシュ法で実装しなきゃいけないという法律があるわけでもないので、もしかしたら二分探索かもしれませんが…)。 戯言は無視しましょう。 この方法を使うと、ハッシュテーブルを作るのには要素数に比例した時間がかかりますが、探索時間自体は(理論上)定数時間で終わることになります。 つまり、`Array#indexOf`を使っていた時には五回の探索で`5000000`のコストが掛かっていたわけですが、ハッシュ法だと`1000005`になるわけです。約五分の一です。すごい。 しかも、 ```javascript console.log(arrTable[searchNum]); ``` のようにすれば、配列中の要素のある位置が全て求まるように実装しました。便利ですね。 最後に、この探索をコンポーネントにしておきたいと思います。 ```javascript:search.js var Search = (function() { /* constructor */ function Search(arr){ if(!(this instanceof Search)) return new Search(arr); this._arr = arr; this._createTable(); } var /* member */ proto = Search.prototype = { _arr: null, _table: null, }; /* private */ proto._createTable = function() { this._table = this._arr.reduce(function(m, a, i) { m[a] = (m[a] || []).concat(i); return m; }, {}); }; /* public */ proto.has = function(a) { return a in this._table; }; proto.index = function(a) { return this._table[a] || []; }; proto.search = function(a) { return this.index(a).reduce(function(m, i) { m.push(this._arr[i]); return m; }, []); }; return Search; })(); ``` [追記]ちなみに、コメント欄でも触れられていますが、JavaScriptのオブジェクトのキーは文字列のみなのでオブジェクトに対してこの方法を使うことはできません。[/追記] |
|
| 698位 |
|
|||
|
07:53:51 |
(Libarts Inc. 所属) |
|
```ruby:version ruby: '1.9.2' rails: '3.2.3' ``` ## heroku ```bash: $ heroku create app-name --stack cedar ``` ```ruby:Gemfile group :production do gem 'pg' gem 'thin' end ``` でgem追加。 ```bash: bundle ``` ```:Procfile web: bundle exec thin start -p $PORT ``` をアプリのrootフォルダに追加。 ```ruby:config/environments/production.rb # config.assets.compile = false を config.assets.compile = true ``` ## Bootstrapを使う ```ruby:Gemfile gem 'twitter-bootstrap-rails' ``` ```bash: $ bundle $ rails g bootstrap:install $ rails g bootstrap:layout application fixed # fixed layoutの場合 ``` 設定を反映するためにサーバの再起動をする ## パスワードにbcrypt-rubyを使って、サインインしたユーザだけが見れるようにする まずはUser modelを作る ```bash: $ rails g model User account:string password_digest:string $ rake db:migrate ``` password_digestの方は変更したらだめ。 ```ruby:models/user.rb has_secure_password attr_accessible :password, :password_confirmation ``` を追加。 フォーム作ってもいいけど、ちょっと話ずれるので、consoleで話済ませます。 ```bash: $ rails c ``` ```irb: > User.create!(:account => "shimojik", :password => "kenta", :password_confirmation => "kenta") ``` セッションコントローラの作成 ```bash: $ rails g controller sessions ``` ルーティング ```ruby:config/routes.rb resources :sessions ``` コントローラ ```ruby:controller/session_controller.rb def index render "new" end def create user = User.find_by_account(params[:account]) if user && user.authenticate(params[:password]) session[:user_id] = user.id redirect_to root_path else flash.now.alert = "Invalid" render "new" end end def destroy session[:user_id] = nil redirect_to root_path end ``` サインイン用フォーム ```erb:views/sessions/new.html.erb <%= form_tag sessions_path do %> <div class="field"> <%= label_tag :account, "アカウント" %> <%= text_field_tag :account, params[:account] %> </div> <div class="field"> <%= label_tag :password, "パスワード" %> <%= password_field_tag :password, params[:password] %> </div> <div class="actions"> <%= submit_tag "ログイン" %> </div> <% end %> ```` current_userメソッドの追加 ```ruby:controllers/application_controller.rb helper_method :current_user private def current_user @current_user ||= User.find(session[:user_id]) if session[:user_id] end ``` ルーティング追加 ```ruby:config/routes.rb match "/signin" => "sessions#new", :as => :signin match "/signout" => "sessions#destroy", :as => :signout ``` ```ruby: def authorize if session[:user_id] @current_user = User.find(session[:user_id]) session.delete(:user_id) unless @current_user end end ``` ## 名前空間を使う(管理者ページなど) ```config/routes.rb namespace :admin do # ルーティング end ``` ```bash: $ mkdir app/controllers/admin ``` adminにおけるスーパークラスを作る ```ruby:controllers/admin/base.rb class Admin::Base < ApplicationController # before_filter などはここに入れるといい end ``` コントローラgenerate ```bash: $ rails g controller admin/home --skip-assets ``` ApplicationControllerじゃなくて、Admin::Baseをスーパークラスに設定 ```ruby:controllers/admin/home_controllers.rb class Admin::HomeController < Admin::Base def index end end ``` viewsはapp/views/adminフォルダの中に書いていけばいい。 (今回の場合はapp/views/admin/home/index.html.erbなど) adminのページかどうかはコントローラで判断 ```ruby: controller.kind_of?(Admin::Base) ``` ## 特定のコントローラやアクションに特定のレイアウトを適応 ```ruby:samples_controller.rb layout 'admin' # このcontroller全体 def index render(:layout => "check") # このactionのみ end ``` ## herokuでmail送信 ```bash: $ heroku addons:add sendgrid:starter ``` ```ruby:config/initializers/mail.rb ActionMailer::Base.smtp_settings = { :address => 'smtp.sendgrid.net', :port => '587', :authentication => :plain, :user_name => ENV['SENDGRID_USERNAME'], :password => ENV['SENDGRID_PASSWORD'], :domain => 'heroku.com' } ActionMailer::Base.delivery_method = :smtp ``` ## Mailer Mailerをgenerate ```bash: $ rails g mailer NoticeMailer sendmail ``` ```ruby:mailers/send_mailer.rb default :from => 'shimojik@example.com' def sendmail(user) @user = user mail to: :user.email, subject: "タイトル" end ``` ```ruby:app/views/notice_mailer/sendmail.text.erb # erb形式で内容を書く。 # mailers/sendmail.rbに書いたインスタンス変数は使える ``` あとは、アクションに追加。 ```ruby:controllers/some_controller.rb def add_user user = User.find(params[:id]) @mail = NoticeMailer.sendmail(user).deliver render text: "メールを送信しました" end ``` 他にもいろいろ在るので、追加していきます。みなさんのも教えて下さい:) |
|
| 699位 |
|
|||
|
15:41:01 |
(Increments株式会社 所属) |
|
複数人で作業しているとき、自分以外の誰かがremoteブランチを削除したのが自分のローカル環境に反映されないことがあると思うんですが、下のコマンドを実行してやればキレイにできます。
```sh # remoteブランチを単純参照 git remote show origin # remoteブランチでは削除されているが、ローカルに参照が残っているブランチを表示 git remote prune --dry-run origin # すでに削除されているremoteブランチのローカル参照を削除してきれいにする git remote prune origin ``` |
|
| 700位 |
|
|||
|
02:19:30 |
|
|
# javascriptで文字コード変換
## 概要 javascriptの文字列はUTF-16で String#charCodeAt(i) で取得出来る数値は2byte(0x0000から0xffff)になる。 escape("あ") は UTF-16がそのままエスケープされ "%u3042" になるが encodeURI("あ"),encodeURIComponent("あ") などではUTF-8に変換されて "%E3%81%82" になる。 ```js var str = "文字列をUTF-8に"; var utf8str = unescape(encodeURIComponent(str)); var utf16str = decodeURIComponent(escape(utf8str)); if (str == utf16str) {alert(true);} //=> true ``` とすることで UTF-16<=>UTF-8 の変換は出来るが、その他の文字コードは変換テーブルを用意する必要がある。 Shift_JIS(MS932), EUC-JP, UTF-8, JIS(iso-2022-jp)に対応したライブラリを2つ見つけたので検証する。 * Encoding.js - Converts character encoding. http://polygon-planet-log.blogspot.jp/2012/04/javascript.html * Escape Codec Library: ecl.js (Ver.041208) http://www.junoe.jp/downloads/itoh/enc_js.shtml または http://www.vector.co.jp/soft/other/java/se342855.html また、検証を進める中でecl.jsのJIS漢字テーブルを使った文字コード変換ライブラリを自作してしまったのでこれも合わせて検証する。文字列を一度数値配列にしてから変換するのでecl_array.jsとした。 * ecl_array.js https://github.com/wealandwoe/ecl_array.js (2014-09-19追記)さらに * inexorabletash/text-encoding - Polyfill for the Encoding Living Standard's API. https://github.com/inexorabletash/text-encoding というライブラリについて検証を追加した ## 検証 ### 各ライブラリの特徴 ecl.jsは各文字コードへURLエスケープする為のライブラリであって、直接文字コード変換する機能はない。 よって UNICODE(Javascript文字列) から SJIS へ変換する場合は ```js var str = "UNICODE文字列をシフトJISへ"; var sjis = UnescapeUnicode(EscapeSJIS(str)); var str2 = UnescapeSJIS(EscapeUnicode(sjis)); if (str==str2){alert("true");} //=> true ``` のようにする必要がある。 ``` Javascript文字列→SJISに変換してURLエスケープ→URLアンエスケープ(SJIS文字列) ``` このエスケープ・アンエスケープ処理を挟んでいる分ecl.jsは不利である。 encoding.jsの文字コード変換は配列を渡し配列で返るので文字列から配列に変換する関数は別途用意する必要がある。 ```js // 文字列を配列に変換 var str2array = function(str) { var array = [],i,il=str.length; for(i=0;i<il;i++) array.push(str.charCodeAt(i)); return array; }; var str = "あいうえお", array = str2array( str ), sjis_array = Encoding.convert(array, "SJIS", "UNICODE"), sjis = Encoding.codeToString( sjis_array ); // 配列から文字列変換する関数は用意されている alert(sjis); ``` text-encodingは日本語以外の様々なエンコーディングにも対応している。Encoding Living Standard( https://encoding.spec.whatwg.org/ )に沿った実装がされているようだ。以下のように使う。 ```js var encoder = new TextEncoder("utf-8"), // utf-8のエンコーダ(文字列→Uint8Array変換)を作成 u8array = encoder.encode("あいうえお"), // => Uint8Array[240, 128, 128, 128, 240, 128, 128, 128, 240, 128, 128, 128, 240, 128, 128, 128, 240, 128, 128, 128] decoder = new TextDecoder("utf-8"), // utf-8のデコーダ(Uint8Array→文字列)を作成 decodedText = decoder.decode(u8array); // => "あいうえお" alert(decodedText); //utf-8,utf-16以外のencodeにはオプションが必要 new TextEncoder("sjis", {NONSTANDARD_allowLegacyEncoding:true}).encode("ほげ"); // => Uint8Array[130, 216, 130, 176] //decodeはOK new TextDecoder("euc-jp").decode(new Uint8Array([164,226,164,178])); // => "もげ" ``` ちなみにTextEncoderおよびTextDecoderクラスはFirefoxでは実装済みなのでtext-encodingは不要。 ただしFirefoxのTextEncoderはエンコーディングにutf-8,utf-16le,utf-16be以外を指定するとエラーになる。 詳しくはMDNを参照 https://developer.mozilla.org/ja/docs/Web/API/TextEncoder ファイルサイズはecl.jsが22KBとコンパクトなのに対し, encoding.jsは262KB js minifyしても212KBと少し大きい。 text-encodingは対応する文字コードが多いのでサイズも大きく600KB以上。 ecl.jsのJIS漢字テーブル(JCT11280)は独特な圧縮がされている上、全体的にサイズを小さくしようと凝縮された書き方がされている。はっきり言うと読みにくい、がjavascriptの文法の勉強になった。 encoding.jsはUTF8-JISの変換・逆変換テーブルがありこれが数値の配列で書かれているのでサイズが大きいがとてもわかりやすい。 自作のecl.jsベースの文字コード変換ライブラリecl_array.jsは、ecl.jsが1つのエスケープ関数で行っている処理(javascript文字列=Unicodeから各文字コードへ変換しエスケープする)をインターセプトして数値の配列で取り出すように設計した。文字コードはecl.jsで扱えるSJIS,EUCJP,JIS7,JIS8,UTF8,UTF7,UTF16LEに加えUTF16BE,MUTF7(modified utf-7),Base64を追加した。 ```js var str = "あいうえお"; // 文字列を16bit数値の列に変換 var array = ECL.charset.Unicode.parse(str); //=> [12354, 12356, 12358, 12360, 12362] ECL.convert_array(array, "SJIS"); //=> [130, 160, 130, 162, 130, 164, 130, 166, 130, 168] ECL.convert_array(array, "EUCJP"); //=> [164, 162, 164, 164, 164, 166, 164, 168, 164, 170] // 配列からJS文字列(Unicode)に戻す場合は ECL.charset.Unicode.stringify( array ) ECL.charset.Unicode.stringify( array ); //=> "あいうえお" // UTF-8文字列からSJIS文字列へ var utf8str = unescape(encodeURIComponent("あいうえお")); var sjisstr = ECL.convert(utf8str, 'SJIS', 'UTF8'); sjisstr == "\x82\xA0\x82\xA2\x82\xA4\x82\xA6\x82\xA8"; //=> true /* 例: ecl_array.js と CryptoJS を使いSJISテキストの暗号文・Base64・SHA1ハッシュを作る CryptoJS : https://code.google.com/p/crypto-js/ */ var msg = "あいうえお"; var sjis = ECL.convert(msg, 'SJIS'); var sjis_wordarray = CryptoJS.enc.Latin1.parse( sjis ); // CryptoJSではWordArrayオブジェクトを作りデータをやりとりするのが楽 var encrypted = CryptoJS.AES.encrypt(sjis_wordarray, "Secret Passphrase"); //=> [object] var base64 = sjis_wordarray.toString(CryptoJS.enc.Base64); //=> "gqCCooKkgqaCqA==" var sha1 = CryptoJS.SHA1(sjis_wordarray).toString(CryptoJS.enc.Hex); //=> "2c332e42c55ba9293827672e85f16f1cda65518e" ``` ## 実験 4KBのテキストを1000回文字コード変換した時の処理秒数(ms) U : UNICODE, S : SJIS, 8 : UTF8, E : EUC-JP, J : JIS |ライブラリ | U->U | U->S | U->8 | U->E | U->J | |------------|-----:|-----:|-----:|-----:|------:| |encoding.js | | **495** | 336 | **507** | **503** | |ecl.js | | 1551 | 1483 | 1549 | 1756 | |ecl_array.js| | 616 | **289** | 599 | 624 | |native | | | 344 | | | |ライブラリ | S->U | S->S | S->8 | S->E | S->J | |------------|-----:|-----:|-----:|-----:|------:| |encoding.js | **278** | | **203** | **214** | **229** | |ecl.js | 1516 | | 2860 | 2829 | 3213 | |ecl_array.js| 294 | | 310 | 624 | 661 | |ライブラリ | 8->U | 8->S | 8->8 | 8->E | 8->J | |------------|-----:|-----:|-----:|-----:|------:| |encoding.js | **195** | **338** | | **347** | **345** | |ecl.js | 1810 | 3329 | | 3269 | 3298 | |ecl_array.js| 314 | 616 | | 607 | 638 | |native | 323 | | | | | |ライブラリ | E->U | E->S | E->8 | E->E | E->J | |------------|-----:|-----:|-----:|-----:|------:| |encoding.js | **255** | **223** | **213** | | **230** | |ecl.js | 2444 | 3826 | 3741 | | 4008 | |ecl_array.js| 288 | 609 | 304 | | 634 | |ライブラリ | J->U | J->S | J->8 | J->E | J->J | |------------|-----:|-----:|-----:|-----:|------:| |encoding.js | **277** | **217** | **247** | **209** | | |ecl.js | 1786 | 3165 | 2981 | 3111 | | |ecl_array.js| 547 | 902 | 616 | 853 | | * nativeは unescape(encodeURIComponent(str))とdecodeURIComponent(escape(utf8str))のこと * 同文字コードへの変換(無変換)はスルーしているので変換速度には影響しない。 * ほぼ全てにおいて encoding.js > ecl_array.js > ecl.js の順に速いという結果になった。 * ecl.js はURLエスケープ、アンエスケープの影響で3~10倍程度遅い * ecl_array.js は UNICODEが絡む変換はそこそこ速いが、他はencoding.jsより2倍程度遅い。 * encoding.jsは各文字コード間を直接変換しているが、ecl_array.jsは一度UNICODE配列にしているのでUNICODEが絡まない変換が遅いと思われる。 * (2014-03-12追記) その後ecl_array.jsに手を入れ変換速度はencoding.jsと同程度になった * (2014-09-19追記) text-encodingは未検証。少し試した限りではかなり(50倍ほど)遅かった。 4KBのテキストを1000回文字変換+URLエスケープ/アンエスケープした時の処理秒数(ms) |ライブラリ|esc U|esc S|esc 8|esc E|esc J|unesc U|unesc S|unesc 8|unesc E|unesc J| |----------|----:|----:|----:|----:|----:|------:|------:|------:|------:|------:| |encoding.js|180|1524|1512|1194|1227|287|406|419|388|716| |ecl.js|689|1133|1389|1123|1366|461|649|948|1656|964| |ecl_array.js|**476**|**934**|775|**949**|**940**|**248**|**281**|313|**286**|**575**| |native|||**118**|||||**306**||| * JS文字列→UTF-8エスケープならばnativeが最速 * ecl.js のエスケープは遅い。ecl_array.js はエスケープ・アンエスケープ処理を変更したら速くなった(正規表現による置換をやめた) * encoding.jsもアンエスケープは速い。ただUNICODEエスケープは内部でencodeURIComponentを使用しているためUTF-8へ変換されている ## 考察 text-encodingはサイズが大きく変換も遅いが対応する文字コードが多く便利。 日本語の文字コード変換をしたい場合は encoding.js か ecl_array.js を使うのがよいだろう。 Firefox19以降限定なら外部ライブラリなしでTextEncoder/TextDecoderが使える。 ecl.jsは昔からある有名なライブラリだがこのコンパクトさが素晴らしい。JIS漢字11280字34KB程をASCII印字可能文字とデコーダ含めて13KBに納めている。エンコーダが無くJIS X 0213等への対応が出来ないのが残念。 |
|
| 701位 |
|
|||
|
11:46:19 |
(SEAOS Inc. 所属) |
|
<h2> <span class="mw-headline" id=".E4.BA.8B.E5.89.8D.E6.BA.96.E5.82.99"> 事前準備 </span></h2>
<ul><li> OpenSSL インストール </li><li> Apache+mod_ssl or Apache-SSL インストール </li></ul> <h2> <span class="mw-headline" id=".E4.BD.9C.E6.A5.AD.E6.89.8B.E9.A0.86.28CA.E6.A7.8B.E7.AF.89.E7.B7.A8.29"> 作業手順(CA構築編) </span></h2> <h3> <span class="mw-headline" id=".E4.BA.8B.E5.89.8D.E8.A8.AD.E5.AE.9A.28openssl.conf.E4.BF.AE.E6.AD.A3.E7.AD.89.29"> 事前設定(openssl.conf修正等) </span></h3> <pre>/etc/ssl/openssl.conf </pre> <p>を修正します。最初にCA用ディレクトリを指定します。 </p> <pre>dir = /etc/ssl/hogeCA </pre> <p>/etc/ssl/hogeCA の配下に certs,private,crl,newcerts のディレクトリを作成します。 </p> <pre>mkdir -p /etc/ssl/hogeCA/certs mkdir -p /etc/ssl/hogeCA/private mkdir -p /etc/ssl/hogeCA/crl mkdir -p /etc/ssl/hogeCA/newcerts </pre> <p>privateには秘密鍵が置かれるので以下のようにします。 </p> <pre>chmod 700 private </pre> <p>シリアルを初期化します。 </p> <pre>echo "01" > /etc/ssl/hogeCA/serial </pre> <p>証明書データベースを初期化します。 </p> <pre>touch /etc/ssl/hogeCA/index.txt </pre> <h3> <span class="mw-headline" id="CA.E8.A8.BC.E6.98.8E.E6.9B.B8.2F.E7.A7.98.E5.AF.86.E9.8D.B5.E4.BD.9C.E6.88.90"> CA証明書/秘密鍵作成 </span></h3> <p>自己署名済みの証明書と秘密鍵を作成します。-days 1825 はCA証明書の有効期限です。365日*5年としています。 適当に変えてください。 </p> <pre>cd /etc/ssl/hogeCA openssl req -new -x509 -newkey rsa:2048 -out cacert.pem -keyout private/cakey.pem -days 1825 </pre> <p>パスフレーズを聞かれます。CA局のマスターパスワードみたいなモノなので、忘れない文字列を入れてください。 </p> <pre>Enter PEM pass phrase: *** </pre> <p>証明書情報が聞かれます。正しく入れてください。 ベリサインのサイトを参考にすると良いと思われます。 <a href="http://www.verisign.co.jp/server/help/csr/capache_new.html" class="external free" rel="nofollow">http://www.verisign.co.jp/server/help/csr/capache_new.html</a> </p> <pre>Country Name: JP State or Province Name: Kanagawa Locality Name: hogehoge Organization Name: foo bar Organizational Unit Name: (なんかあれば) Common Name: (サーバのFQDN/www.hoge.comとか) Email Address: (あれば) </pre> <p>作成が終わると以下のファイルができあがります。 </p> <pre>/etc/ssl/hogeCA/cacert.pem : 自己署名済みCA証明書 /etc/ssl/hogeCA/private/cakey.pem : CA証明書の秘密鍵 </pre> <p>CA証明書の秘密鍵は他人にみられるとこまるので、こうします。 </p> <pre>chown root:root /etc/ssl/hogeCA/private/cakey.pem chmod 600 /etc/ssl/hogeCA/private/cakey.pem </pre> <p>証明書が正しく出来たかを確認するには、以下のコマンドを発行します。 </p> <pre>openssl x509 -in /etc/ssl/hogeCA/cacert.pem -text </pre> <h3> <span class="mw-headline" id=".E3.83.96.E3.83.A9.E3.82.A6.E3.82.B6.E3.81.ABCA.E8.A8.BC.E6.98.8E.E6.9B.B8.E3.82.92.E7.B5.84.E3.81.BF.E8.BE.BC.E3.82.80"> ブラウザにCA証明書を組み込む </span></h3> <p>これをしないと、アクセスするたびに警告が発生します。 以下のコマンドでブラウザに取り込めるDER形式にエンコードします。 </p> <pre>openssl x509 -inform PEM -outform DER -in /etc/ssl/hogeCA/cacert.pem -out /etc/ssl/hogeCA/hogeCAcert.der </pre> <p>hogeCAcert.der をブラウザからダウンロード出来るようにします。 </p> <pre>cp /etc/ssl/hogeCA/hogeCAcert.der /var/www/ </pre> <p>また、*.derファイルのMIMEタイプを登録する必要があります。 </p> <pre>- /ver/www/.htaccess - AddType application/x-x509-ca-cert der - ここまで - </pre> <p>IE6.0の場合ですが、<a href="http://www.hoge.com/hogeCAcert.der" class="external free" rel="nofollow">http://www.hoge.com/hogeCAcert.der</a> とかアクセスすると、 ファイルのダウンロードが表示されてファイル種別:セキュリティ証明書になっています。 ここで、「開く」を選択すると「証明書」ダイアログが表示されるので「証明書のインストール」を選んでください。 以下の質問は「次へ」としてください。最後に「ルート証明書ストア」と出たら「はい」を選択すれば自己認証CAの 証明書がインストールされます。 インストールは、Opera7,Mozilla Firebird/0.6 でも確認済み </p> <h2> <span class="mw-headline" id=".E4.BD.9C.E6.A5.AD.E6.89.8B.E9.A0.86.28.E3.82.B5.E3.83.BC.E3.83.90.E8.AA.8D.E8.A8.BC.E7.B7.A8.29"> 作業手順(サーバ認証編) </span></h2> <p>作成した自己認証CA局を使って、目的のサイト自体の証明書を発行します。 ここからちょっと判りにくくなります。自分の立場(演じる役)を明確にさせておきましょう。 ,役,説明 ,CA管理者,ベリサインみたいな役 ,サーバ管理者,CAにサーバが発行する証明書を署名して欲しい役 !鍵ペアと証明書要求(CSR)作成(サーバ管理者) 判りやすさを最優先にする意味で、以下のようなディレクトリを作成しました。 </p> <pre>/etc/ssl/www.hoge.com </pre> <p>鍵ペアと証明書要求(CSR)ファイルを作成します。 </p> <pre>openssl req -new -keyout /etc/ssl/www.hoge.com/hoge_key.pem -out /etc/ssl/www.hoge.com/hoge_csr.pem </pre> <p>証明書情報が聞かれます。 </p> <pre>Country Name: JP State or Province Name: Kanagawa Locality Name: hogehoge Organization Name: foo bar Organizational Unit Name: (なんかあれば) Common Name: (CA局のFQDN/www.hoge.comとか) Email Address: (あれば) A challenge password: (パスワード) </pre> <p>作成が終わると以下のファイルができあがります。 </p> <pre>/etc/ssl/www.hoge.com/hoge_csr.pem : サーバ証明書要求(CSR) /etc/ssl/www.hoge.com/hoge_key.pem : サーバ秘密鍵 </pre> <h3> <span class="mw-headline" id=".E8.87.AA.E5.B7.B1.E8.AA.8D.E8.A8.BCCA.E5.B1.80.E3.81.A7.E7.BD.B2.E5.90.8D.28CA.E7.AE.A1.E7.90.86.E8.80.85.29"> 自己認証CA局で署名(CA管理者) </span></h3> <p>では、サーバ証明書要求(CSR)を先ほど作成した自己認証CA局で署名しましょう。 </p> <pre>openssl ca -out /etc/ssl/www.hoge.com/hoge_cert.pem -infiles /etc/ssl/www.hoge.com/hoge_csr.pem </pre> <p>を実行すると、CA局は「本当に署名していいの?」と確認します。迷わず[y]を選んでください。 </p> <pre>Enter PEM pass phrase:(CA局パスフレーズ) Check that the request matches the signature Signature ok The Subjects Distinguished Name is as follows countryName :PRINTABLE:(サーバ証明書情報) stateOrProvinceName :PRINTABLE:(サーバ証明書情報) localityName :PRINTABLE:(サーバ証明書情報) organizationName :PRINTABLE:(サーバ証明書情報) commonName :PRINTABLE:(サーバ証明書情報) emailAddress :IA5STRING:(サーバ証明書情報) Certificate is to be certified until 時間 GMT (365 days) </pre> <p>その後、自己認証CA局で管理する?(みたいな事)を聞かれるので[y]を選んでください。 これをしないと、あとからバージョンアップが出来ない!? </p> <pre>1 out of 1 certificate requests certified, commit? [y/n] </pre> <p>証明書が正しく出来たかを確認するには、以下のコマンドを発行します。 </p> <pre>openssl x509 -in /etc/ssl/www.hoge.com/hoge_cert.pem -text </pre> <p>また、現在出来上がったファイルはこんな感じとなります。 </p> <pre>/etc/ssl/www.hoge.com/hoge_csr.pem : サーバ証明書要求(CSR) ←もういらない!? /etc/ssl/www.hoge.com/hoge_key.pem : サーバ秘密鍵 /etc/ssl/www.hoge.com/hoge_cert.pem : サーバ証明書(署名済み) </pre> <h3> <span class="mw-headline" id=".E8.A8.BC.E6.98.8E.E6.9B.B8.E3.81.A8.E7.A7.98.E5.AF.86.E9.8D.B5.E3.82.92.E3.82.B5.E3.83.BC.E3.83.90.E3.81.AB.E7.B5.84.E3.81.BF.E8.BE.BC.E3.81.BF.E3.81.BE.E3.81.99"> 証明書と秘密鍵をサーバに組み込みます </span></h3> <p>Apache+mod_sslでもApache-SSLでも、こんな感じで登録すればオッケーです。 </p> <pre>SSLCertificateFile /etc/ssl/www.hoge.com/hoge_cert.pem SSLCertificateKeyFile /etc/ssl/www.hoge.com/hoge_key.pem </pre> <h3> <span class="mw-headline" id=".E3.83.88.E3.83.94.E3.83.83.E3.82.AF.E3.82.B9"> トピックス </span></h3> <ul><li> Apache+mod_ssl/Apache-SSLの起動時パスフレーズを削除する方法です。 </li></ul> <p>サーバ再起動時にいちいちパスフレーズを入力する必要が無くなります。 パスフレーズは、サーバ秘密鍵に書かれているのでこんな感じでRSAキーを書き込みます。 </p> <pre>openssl rsa -in /etc/ssl/www.hoge.com/hoge_key.pem -out /etc/ssl/www.hoge.com/hoge_key.pem.nopass </pre> <p>Apache+mod_ssl/Apache-SSLの設定を、このファイル名に変更してください。 </p> <pre>SSLCertificateKeyFile /etc/ssl/www.hoge.com/hoge_key.pem.nopass </pre> <ul><li> 発行後、期限切れのサーバ証明書を更新するには </li></ul> <p>同一サイトに対するサーバ証明書を発行するには、一度古くなったサーバ証明書を、CAから削除する必要があります。 </p> <pre>openssl ca -revoke /etc/ssl/www.hoge.com/hoge_cert.pem </pre> <p>その後、www.hoge.com 以下のファイル群を待避させる。 </p> <pre>mkdir /etc/ssl/www.hoge.com/2004bk/ mv /etc/ssl/www.hoge.com/* 2004bk </pre> <p>最後に、"作業手順(サーバ認証編)"を最初から行えば、サーバ証明書の更新が行えます。 </p> <ul><li> 自己認証CA局でより汎用的に署名するには </li></ul> <p>デフォルト設定だと、国名・地域・会社名が署名してもらいたい証明書と同一でないと弾くポリシーになっています。 このポリシーを変更するには /etc/ssl/openssl.conf を以下のようにします。 </p> <pre># For the CA policy [ policy_match ] countryName = supplied stateOrProvinceName = supplied organizationName = supplied organizationalUnitName = optional commonName = supplied emailAddress = optional </pre> <ul><li> 参考サイト <ul><li> <a href="http://ash.jp/sec/openssl_ca.html" class="external text" rel="nofollow">OpenSSLコマンドの使い方</a> </li><li> <a href="http://www.fc-lab.com/network/server/pki/openssl.html" class="external text" rel="nofollow">OpenSSLを使ったCAの構築</a> </li><li> <a href="http://moca.wide.ad.jp/notes/ca_doc/openssl.html" class="external text" rel="nofollow">CA 構築のための OpenSSL の設定</a> </li></ul> |
|
| 702位 |
|
|||
|
21:33:44 |
|
|
## はじめに 論文などの文章を書くとき、texを使う・・でも、軽いメモ書きとかミーティングログとか、毎週の報告書で書くにはちょっと手軽じゃない。一方、markdownは簡単で良いけど、ビュワーを通して見ないと整形されない。ソフトウェアのreadmeで使うには良いけど、普段のログをみんなで共有するとかドキュメントとして残すにはもう一歩足りない。 Word?なにそれ美味しいの?ってかそもそも、有料の時点で終わってる。(Office使いの人ごめんなさい。) 普段からログや文章を残す人・残したい人はツールやまとめる書式選びで日々悩んでいることと思います(僕だけか・・)。そんなあなたに普段の日常的な記録・ログ・報告の類はMarkdown記法でまとめておいて、必要に応じて、pdf化する方法を紹介します。なお、今回はtexフォーマットにしてから、pdf化する手順で紹介しますが、スタイルが質素なままで良ければ、そのままpdf化しても、html化しても構いません。 注意:方法は結構ギークなので、好きな方のみ採用してください。 ## MarkdownとPandocを用いた簡易Latex環境 標題は難しい感じがしますが、やり方は至って簡単です。Pandocとはドキュメント変換ツールで、markdown、html、Latexを相互変換する便利なツールです。出力側はwordやpdfファイルをサポートしているので、簡単な書式で書いた文章の体裁を整えるにはうってつけのツールといえるでしょう。今回はこのツールを使って、texファイルを作り、pdf化するまでの手順を紹介します。なお、インストール方法以外はおそらくwindows, linuxでも共通なので、他のOSを使っている人でも同じような作業で導入できると思います。 ### インストール homebrewを使ってインストールする方法もありますが、時間がかかるので、mac版のインストーラを使ってインストールするのが便利です。 * http://johnmacfarlane.net/pandoc/installing.html 上記のページから、dmgファイルをダウンロードしてインストールしてください。もちろん、linux用のtarball, windowsのインストーラも提供されています。 ### ドキュメント変換 ドキュメントの変換は以下のようなコマンドで行います。 ``` $ pandoc readme.md -o readme.tex ``` readme.mdが入力、readme.texが出力です。入力したreadme.mdと出力されたreadme.texをそれぞれ以下に示します。 ```readme.md # はじめに これはテストです。書式はgithubのmarkdownを採用しています。 # いろいろな書式 ## subsection的な * 箇条書きも対応してほしい * itemizeとか毎回書くのまじでしんどい * 入れ子とかもサポートしてくれると良いよね ### subsubsection的な 1. もちろん、数字のサポートも必須でしょ * 違うアイテマイズの入れ子ももちろんサポートするよね # リンク関係 [リンクはどうなるの?](http://localhost)  # 文字書式 * これはイタリック体です* * _これもイタリック体です_ * これはイタリック体になりません * **これは太字です** * __これも太字です__ > 引用はどうなるんだろう # 表はどうなる |カラム1|カラム2| |------|------| |hoge|geho| ``` 上記が以下のように変換されます。 ```readme.tex \section{はじめに}\label{はじめに} これはテストです。書式はgithubのmarkdownを採用しています。 \section{いろいろな書式}\label{いろいろな書式} \subsection{subsection的な}\label{subsection的な} \begin{itemize} \itemsep1pt\parskip0pt\parsep0pt \item 箇条書きも対応してほしい \item itemizeとか毎回書くのまじでしんどい \begin{itemize} \itemsep1pt\parskip0pt\parsep0pt \item 入れ子とかもサポートしてくれると良いよね \end{itemize} \end{itemize} \subsubsection{subsubsection的な}\label{subsubsection的な} \begin{enumerate} \def\labelenumi{\arabic{enumi}.} \itemsep1pt\parskip0pt\parsep0pt \item もちろん、数字のサポートも必須でしょ \begin{itemize} \itemsep1pt\parskip0pt\parsep0pt \item 違うアイテマイズの入れ子ももちろんサポートするよね \end{itemize} \end{enumerate} \section{リンク関係}\label{リンク関係} \href{http://localhost}{リンクはどうなるの?} \begin{figure}[htbp] \centering \includegraphics{sample.png} \caption{画像とかはどうなるの?} \end{figure} \section{文字書式}\label{文字書式} \begin{itemize} \itemsep1pt\parskip0pt\parsep0pt \item これはイタリック体です* \item \emph{これもイタリック体です} \item これはイタリック体になりません \item \textbf{これは太字です} \item \textbf{これも太字です} \end{itemize} \begin{quote} 引用はどうなるんだろう \end{quote} \section{表はどうなる}\label{表はどうなる} \begin{longtable}[c]{@{}ll@{}} \hline\noalign{\medskip} カラム1 & カラム2 \\\noalign{\medskip} \hline\noalign{\medskip} hoge & geho \\\noalign{\medskip} \hline \end{longtable} ``` ほとんどのMarkdownの書式をtex変換してくれていることがわかると思います。ここまでできるなら、markdownで書いてtex変換するほうが楽だと言えるでしょう。 なお、出力されるのは、texでいう\begin{document}の後に続く部分です。ドキュメントのスタイルなどは、別途、元となるtexファイルにまとめておき、中で上記のtexファイルをincludeするようにしておくと、毎回書き直す必要がなく便利だと思います。 ```manuscript.tex \documentclass[11pt,a4paper,twocolumn]{jarticle} % \usepackage{amsmath,amssymb} \usepackage{bm} \usepackage{graphicx} \usepackage{ascmac} \usepackage[dvipdfm]{hyperref} \title {Weekly Report} \author {Yamamoto Tarou} \begin {document} \maketitle \input {readme} \end{document} ``` 注意:使っているパッケージやドキュメントクラスは用途に応じて、使い分けてください。今回はjarticleの2カラム版のドキュメントを生成するように書いています。 後は、latexコマンドを使って、ビルドするだけです。 ``` $ platex manuscript.tex $ dvipdfmx manuscript.dvi ``` --- ``` $ pdflatex manuscript.tex ``` なお、文中の数式などもそのまま反映されることが確認済みです。もう少し頑張れば、論文などの執筆にも使える強力なツールになりそうです。 ついでに、論文のpdfをつくるときによく使うMakefileの中身をさらす。tableもなんとかしたいけど、これは次の課題。 ```Makefile # -*- makefile -*- TEX=platex DVI2PDF=dvipdfmx SOURCES = \ manuscript.tex \ $(NULL) MDSCRIPT = \ contents.md \ $(NULL) all: convertmd dvi2pdf clean convertmd: $(MDSCRIPT) @cat $^ \ | sed s/.png/.eps/g \ | pandoc -t latex \ | sed 's/includegraphics/includegraphics[width=1.0\\columnwidth]/g' \ | sed 's/\[htbp\]/\[t\]/g' \ > $(MDSCRIPT:.md=.tex) .tex.dvi: convertmd @${TEX} $< @${TEX} $< dvi2pdf: $(SOURCES:.tex=.dvi) @${DVI2PDF} $^ clean: @rm -f *.dvi *.aux *.log ``` 図はtexではepsの方がよく、markdown上で確認するときはpngがよいので、両方出力するようにして、sedで変換するようにしている.また、図の位置はトップのみにしたいので、[htbp]から[t]に、graphicsの広さは、2カラムの文章が多いため、1.0\columnwidthとしている。 ## Macで使う人に便利なツール Macの人限定ですが、Markdownの編集にはkobitoが便利です。 * [Kobito](http://kobito.qiita.com/) オープンにしてよい技術文章はQiitaで公開することもできるので、公開する場合は、Qiitaに内部で発表するにはTexでといった使い分けができます。また、編集した内容をDropboxなどで管理することで、複数のmac間でリソースを管理することもできます。 ## 将来的には プレゼンなどもこの形式で作れたら、なお便利でしょうね。でも、pandocを使えばできそうなので、試してみて成功したら次回紹介します。 ## 参考文献 * [GithubのMarkdown書式](http://qiita.com/Qiita/items/c686397e4a0f4f11683d) * [Kobito+Dropbox](http://qiita.com/yamakuda/items/5fdd269af54f83c6c64e) |
|
| 703位 |
|
|||
|
14:52:30 |
(Akatsuki Inc. 所属) |
|
##saharaとは OSの途中の状態を記憶しておき、好きな時にそこまでロールバックできる。 ##注意点 Vagrant1.1には公式には未対応の様子。 別途対応版を公開してくださっている方がいるので、そちらを利用させて頂く。 Vagrantの必須プラグインSaharaをVagrant 1.1に対応させました | Ryuzee.com http://www.ryuzee.com/contents/blog/6555 ###インストール ```sh $ git clone https://github.com/ryuzee/sahara.git $ cd sahara $ bundle install $ vagrant plugin install sahara ``` ###確認 ```sh $ vagrant plugin list sahara (0.0.15) ``` ###利用する ```sh # sandboxモード開始 $ vagrant sandbox on # 変更を決定する $ vagrant sandbox commit # 変更を破棄し、ロールバックする $ vagrant sandbox rollback # sandboxモード終了(commitしていないものは破棄される) $ vagrant sandbox off # 現在sandboxモードかどうかの確認 $ vagrant sandbox status ``` 便利! ##追記(2013/10/14) sandbox on, sandbox commitは非常に時間がかかる。 一度Vagrantを停止してから行うと少しましになる。 ```sh $ vagrant halt $ vagrant sandbox commit ``` ## Vagrantに慣れたら サーバ構成管理ツールのChefで、Vagrantの魅力を最大限に引き出しましょう! Chefの基本 http://qiita.com/kidachi_/items/9d569b8673e70ef93f0e knife-soloによるChefの実行 http://qiita.com/kidachi_/items/b222fb2892e6108c46d5 |
|
| 704位 |
|
|||
|
05:35:13 |
(リブライズ合同会社 所属) |
|
スタイルシートの中で画像を多数呼出していると、HTTPリクエストが大量発生してページの読み込みが遅くなります。このような場合、CSS Spriteを使って回避することが一般的ですが、Data URIを使うと運用はもっと簡単です。  ## CSSファイルへの埋込 例えばOSSCafeの場合、[サイトのCSS](http://www.osscafe.net/style/images.css)内で16ほどの画像ファイルを読込んでいます。 ```style/images.css body { background-image:url(images/body.png) } body>header { background-image: url(images/header.png) } body>header div.center>h1 { background-image:url(images/logo.png) } ...(略) ``` このCSSファイル内の画像ファイルを、Data URIに置き換えると、 ```style/images.css?inline body { background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK8AAACvABQqw0mAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTM5jWRgMAAAQRdEVYdFhNTDpjb20uYWRvYmUueG1wADw/eHBhY2tldCBiZWdpbj0iICAgIiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDQuMS1jMDM0IDQ2LjI3Mjk3NiwgU2F0IEphbiAyNyAyMDA3IDIyOjExOjQxICAgICAgICAiPgogICA8cmRmOlJE ...(略) ``` のように、画像部分がBASE64エンコードで埋め込まれた形になります。 ## 埋め込みの自動化 ("?inline"を付けたときだけ) ただ、これを手動で実行するのは面倒です。ここでは、CSSファイルの読み込み時に["?inline"とつけた場合](http://www.osscafe.net/style/images.css?inline)のみ、自動的に埋込むことにします。HTML内の表記としてはこんな感じ。 ```index.html <link rel="stylesheet" type="text/css" href="images.css?inline" /> ``` ちなみに、想定しているファイル構成は次の通り。 * index.html * style/ * **.htaccess** * **images**/ - *CSSから参照する画像を格納するディレクトリ* * **images.css** - *画像指定のみのCSS* * **other.css** - *画像指定以外のCSS* * **embed-images.php** - *自動変換スクリプト* .htaccessにてmod_rewriteを使って、"?inline"をつけた場合だけ、PHPのスクリプトを介するよう設定します。後半の```FileMatch```の部分は、スクリプトの直接起動を制限するためのものです。 ```style/.htaccess RewriteEngine On RewriteCond %{REQUEST_FILENAME} -f RewriteCond %{QUERY_STRING} inline RewriteRule \.css$ embed-images.php [QSA,L] <FilesMatch "embed-images.php"> Order Deny,Allow Deny from All Allow from env=REDIRECT_STATUS </FilesMatch> ``` ##Data-URIに置換え CSSファイル内の画像ファイルを、自動的にData URIに置き換えるPHPスクリプト(embed-images.php)を作成します。といっても、該当するCSSファイルを読込んで、画像ファイル名の部分をData URIに置きかえているだけ。 ```style/embed-images.php <?php $mime_types = array('png'=>'image/png', 'jpg'=>'image/jpeg', 'gif'=>'image/gif'); header('Content-type: text/css'); echo preg_replace_callback( '/url\((.*?\.(' . implode('|', array_keys($mime_types)) . '))\)/im', function ($matches) use ($mime_types) { $url = trim($matches[1], '\'\"'); return file_exists($url) ? 'url(data:' . $mime_types[strtolower($matches[2])] . ';base64,' . base64_encode(file_get_contents($url)) . ')' : $matches[0]; }, file_get_contents($_SERVER['DOCUMENT_ROOT'] . str_replace('?inline', '', $_SERVER['REQUEST_URI'])) ); ``` ##メリット / デメリット 冒頭に書いたように、HTTPリクエストが減って、ページの読み込みが高速になるはずです。このような構成を取ることで、運用面でも次のメリットがあります。 * ?inline を外せば、通常のCSSにすぐ戻せる * HTTPサーバがローカルに無い場合でも、元のCSSが表示される 後者は、デザイナがCSSを変更する場合に、ローカルファイルとしてそのまま扱える点で有利です。 Data URIにした場合、BASE64エンコードのため、画像のバイナリデータと比べてファイルサイズが大きくなります。このこと自体は、デメリットなのですが、リクエスト数を減らす方がどちらかというと重要です。また、HTTPサーバからの送出時にgzip圧縮するのであれば、サイズはバイナリとほぼ変わらなくなります。 ## Appendix ### ソースのダウンロード GitHubにも上げておきました。https://github.com/cognitom/wagon ### IEどうするのん? IEは、バージョン8以降でData URIに対応です。なので、IE7以下をケアする場合は、IEのバージョンで条件分岐。IE7以下で"?inline"を外せばOKです。このあたり、自由にできるのはCompassの"inline-image"より使いやすいところ。 ```index.html <!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="images.css" /><![endif]--> <!--[if gte IE 8]><!--><link rel="stylesheet" type="text/css" href="images.css?inline" /><!--<![endif]--> ``` ### 他の方法 - Sass Compass/Sass を使っているのであれば、```inline-image```という機能を使って、同様のことが属性単位で可能です。[詳しくはこちら](http://compass-style.org/reference/compass/helpers/inline-data/#inline-image) ```sass body background-image:inline-image(images/body.png) ``` ### 他の方法 - gulp 追記: gulpを使う方法がオススメです。 - [gulp-base64](https://github.com/Wenqer/gulp-base64): CSSに含まれる画像のパスをbase64にエンコード。一定サイズ以下のみエンコードすることも可能。 |
|
| 705位 |
|
|||
|
16:13:57 |
(Hematite llc 所属) |
|
goのnilは直感的ではない、これは強烈にハマりそう。
# 型を持つnil nilと一口に書くが、実際には型がある。 ## nilとnilが等価でないように見える nilが型情報を持つので、nil == nilがtrueになるとは限らない。 trueとなるためには、右辺と左辺の「nil」の型が一致しているという条件が必要。 ```go package main func main() { var x *int32 = nil var y *int64 = nil equals(x, y) return } func equals(x, y interface{}) { println(x == y) } ``` ```output > false ``` より凶悪なのが以下のようなパターンで、equals()内ではxがinterface{}として扱われているので、ここでnilを書くと左辺もinterface{}型のnilとして扱われる。 型が一致しないため、この結果もfalseとなる。 ```go package main func main() { var x *int32 = nil isnil(x) return } func isnil(x interface{}) { println(x == nil) } ``` ```output > false ``` ## nilはキャスト出来る 型があることを実感できる事実として、nilは型キャストすることが出来る。先のコードでfalseとなっていた例も、等価式のnilをキャストしてやればtrueを返せる。 ```go package main func main() { var x *int32 = nil isnil(x) return } func isnil(x interface{}) { println(x == (*int32)(nil)) } ``` ```output > true ``` つまり、オブジェクトをinterface{}として扱っている場合、等価演算子のみを用いてもnilであることを確実に知ることはできない。 ## 確実に「nil」を検出する nilを検出するにはreflect.ValueのIsNil()を併用しなければならない。 ただし、reflect.ValueOf(nil)の場合はZeroValueが返却されるのでIsNil()が呼べない。なので、xがnilであるかの検査は先に行わなければならない。 # nilの型を意識する話なので補足しておくと、ここでreflect.ValueOf()はinterface{}型を引数として要求する。したがって、(interface{})(nil)の場合にZeroValueが返却される。 簡単に書けば以下のような感じ。 ちなみに、xがstructなどだとIsNil()がpanicになるので、実際には注意しなければいけない。 ```go func isnil(x interface{}) { println( (x == nil) || reflect.ValueOf(x).IsNil() ) } ``` # 型かと思う振る舞いをするnil 先のようにnilは型情報を持つが、nilが型そのもののように振舞っているように見えるケースがある。それが型スイッチ構文を使う場合で、以下のようにcaseに型と一緒にnilを含めることが出来る。 このとき、nilのケースにはinterface型のnil(つまりinterface{}(nil))の場合にのみ入る。 ```go package main func main() { var x *int32 = nil typeswitch(x) return } func typeswitch(hoge interface{}) { switch hoge.(type) { case nil: println("hoge is nil") case *int32: println("hoge is *int32") case *int64: println("hoge is *int64") default: println("unknown") } return } ``` ``` > hoge is *int32 ``` 「hoge is nil」と出すためには、main()の中で宣言しているxをinterface{}型にしておかなければならない。これは[Language Specification](http://golang.org/ref/spec#Switch_statements)の「Type switches」にわかりやすい説明がある。 # わかりづらい goは比較的直感的に使える言語だと思うけれど、nilに関しては直感性が無く辛い。言語仕様を読めば、まぁソレっぽい事はちゃんと書いてあるんだけど・・・ また何か面白い挙動を見つけたら追記する。 |
|
| 706位 |
|
|||
|
21:37:49 |
(fablic.inc 所属) |
|
先日ちょっとしたMacアプリを作ったのですが、iOSアプリとの共通点や違いについて簡単にまとめてみます。
僕は業務や個人でふだんiOSアプリは作っているのですが、Macアプリは今回が初めてでした。全体としては、もちろんMac特有のお約束もあるにはあるのですが、iOSに馴れた人ならわりとすぐMacの開発が始められるという印象です。 今回作ったアプリはメニューバーに常駐するフィードリーダーで、Google Reader終了のタイミングで作りました。[見た通り](http://standmag.co)、非常にシンプルなものです。 更新された記事がレイアウトされるメインウィンドウが1枚、設定ウィンドウが1枚、インターフェースはそれくらいで、あとは記事の取得と保存にバックエンドで動くクロールマネージャがあります。 購読フィードが多い場合、クロールマネージャは適宜分割して読まれるブログを重みづけしつつ、バックグラウンドでクロールしていきます。ある程度新着がたまったら、メニューアイコンが点灯して通知してくれます。 ### 共通点 なりたちとしてiOSがMac OSのサブセットなので、言語としてはもちろんSDKを構成するクラス群も多くが共通です。双方のSDKを比べると、NS接頭子のクラスはおそらくMac OS SDKからiOS SDKに移入されたもので、そのまま同じものが使えます。NSString、NSArrayなど。 UIKitはiOS固有のフレームワークで、たとえばUIViewはMac OSだとNSViewになります。新しいだけにiOSの方が微妙にリファインされている感じですが、ビューのヒエラルキーやframeでの座標指定などは同じです。 blocksやARC、オブジェクトリテラルなど、新しめの記法もiOSに限らず同様に使えます。 データモデルはCoreDataがあり、これも共通です。 なので、モデル層のライブラリなどは結構Macにも流用できます。たとえばhttp通信のAFNetworkingなども、Macで同じように使えました。 ### 相違点 共通点は非常に多いのですが、Macならではの点ももちろんあります。今回気づいた範囲では、次のようなところです。 #### ウィンドウとメニュー デスクトップアプリなので、ウィンドウとメニューバーがあります。どちらもインターフェースビルダーで作れます。ウィンドウはその上にビューを配置してアプリのプレゼンテーションを作り、メニューにはコントローラに書いたアクションを接続してやります。iOSでもおなじみの操作で、あまり迷いません。  細かいところでは、ボタンの文字色を設定するところが見当たらず、ないはずないと思ってけっこう探してしまいました。MacではAttributedStringを使ってやるようです。 #### コントローラ AppDelegateは同じですが、その先のViewControllerにあたるクラスはありません。コントローラが必要なところはNSObjectをベースに作るというやり方で、画面遷移に対応してコントローラが並んでいくiOSアプリとちょっと異なります。 今回はウィンドウ毎に作りましたが、アプリ全体で一つでも可能で、かなり自由です。コントローラを中心にUIとアクションを結んでいくやり方は同じですね。 #### Cocoa Binding Macアプリならではという点では、Cocoa bindingというのが強力でした。これはModel - View - Controller の結合部、つなぎ目を抽象化する一種の規約というかメカニズムなんですが、その抽象化の結果、コードなしでDBのデータをテーブルビューに表示する、といったことができるようになっています。 使える場面は限られていますが、コードを書かずにデータの表示や更新ができてしまうというのはちょっと新体験で感動します。  上の画像は、これだけだとちょっと分かりづらいですが、Cocoa Bindingを使って購読ブログの一覧をテーブルビューに表示しています。ビュー上で編集を行うと、結果がそのままDBまで反映されます。 #### 動作確認と配布 あとは開発の周辺ですが、ちょっと作ってみて配布するというのがiOSアプリより簡単です。XcodeでRunすれば開発しているMacでアプリが動作しますし、バイナリをzipして送れば他のマシンでも動かせるし、App Storeに上げなければ審査もありません(むしろこれは、iOSの面倒くささに慣らされてしまっているのかもしれない・・)。 他方、シミュレータがないためOSのバージョン違いの確認などは面倒です。10.8と10.7でAuto Layoutの表示が微妙に違うのがわかり、旧いairを引っ張りだしてLionを入れ直したりしました。 ### おまけ ということで、iOSからMacアプリを作ってみたときに感じた共通点・相違点をまとめてみました。スマートフォンとデスクトップという違いはありますが、開発の流れとしては見た目ほどの違いはないというのが印象です。 Macユーザーは増えていますし、以前はややマイナーなイメージのあったMacアプリケーション開発も、今後はけっこう一般的になってくるかもしれません。今回のフィードリーダーもそうですが、ちょっとしたユーティリティなどを自分用に作るだけでも便利です。 おまけとして、Macのメニューバーにアイコンを出すだけの、常駐型アプリのスケルトンを上げておきます。テンプレとしてよかったらご利用ください。 メニューバーアプリのスケルトン https://github.com/inonb/MacMenuApp フィードリーダー Stand http://standmag.co |
|
| 707位 |
|
|||
|
13:30:00 |
|
|
「ブックマークレットで jQuery を使う魔法の 210 文字」という記事を [ブログ](http://www.otchy.net/20130617/bookmarklet-on-jquery-with-magical-210chars/) に書いたのですが、ここ Qiita ではその技術的な解説をしてみようと思います。
やりたい事はそのものずばり、**ブックマークレットの中で jQuery を使う**という事です。 ちょっとした処理の自動化や簡単なツールなど、よくブックマークレットを書いて活用しているのですが、ブックマークレットだと素の JavaScript を書かなければならず、DOM 操作が含まれる事をやろうとすると、jQuery に慣れた身には面倒くささが先に立ってしまいます。 ならば、**ブックマークレットで jQuery を使えるようにすればいい!**というのが今回の趣旨になります。 まず方針として、どんなページでブックマークを起動してもちゃんと jQuery が使えるようにしたいです。ですので、jQuery 本体は自前で調達しなければなりません。ただ、いくらミニマイズしたところで、jQuery 本体のサイズは 84KB / 8万文字超。さすがにこれをブックマークに押し込めるには無理があります。 そして、どんなページでもちゃんと動かせるようにするためには、元々のページに影響を与えてはいけません。元々 jQuery がロード済みのページであってもバージョンが古いかも知れないし、そもそも jQuery がロードされていないページだっていっぱいあります。 ですので**戦略のポイントは 3 つ**です。 * CDN 上の jQuery をロードしてその jQuery を使う * 元のページに jQuery が含まれていてもそれは上書きしない * グローバルに何も残さない それをコードで表現するとこんな感じになります。 ```js: (function(func) { var scr = document.createElement("script"); scr.src = "//ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"; scr.onload = function() { func(jQuery.noConflict(true)); }; document.body.appendChild(scr); })(function($) { console.log($().jquery); }); ``` 変数 `scr` は、CDN 上のスクリプトをロードするための `script` タグです。CDN としては Google のものを使う事にしました。本家の CDN は `https` 対応していないので、https ページで警告が出るのを避けられません。`src` の記述からこのようにプロトコルを省くことで、ページ自体のプロトコルに合わせたソースからロードすることが出来ます。 そして、`scr.onload` で CDN ロード完了時のアクションを指定してから、`document.body` に `scr` を追加します。 > この辺り、本来的な**正しい** JavaScript としては `scr.setAttribute` や、`scr.addEventListener` を使うべきところです。 > ただ今回はブックマークレットということでなるべく記述を短くしたいので、あえてレガシーな (しかしまだ有効な) 書き方をしています。 CDN からの jQuery のロードが完了すると、onload イベントが発生します。 そのタイミングで本来実行したかった `func` の引数に jQuery オブジェクトを渡して実行します。 ここでポイントになるのが `jQuery.noConflict` です。 デフォルトの `jQuery.noConflict()` ではグローバルの `$` ショートカットをロード以前に戻し、`jQuery.noConflict(true)` とすることで、グローバルの `jQuery` もロード以前に戻します。返り値はロードした新しい `jQuery` オブジェクトそのものです。 こうすることで、`jQuery` 以外のライブラリが `$` を使っていた場合でも、他のバージョンの `jQuery` がロード済みだった場合でも競合することが無くなります。 ついでに、グローバルの汚染も一切無くすことが出来ます。 さあ、ここまでで挙動自体は OK です。次にブックマークレットとして使いやすくするよう、コードをある程度短くします。 ```js: (function(f, s){ s = document.createElement("script"); s.src = "//ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"; s.onload = function() { f(jQuery.noConflict(true)) }; document.body.appendChild(s) })(function($){ console.log($().jquery); }); ``` …といっても、変数名を 1 文字にしたのと、`function` スコープの変数を `var` を使わずに定義するため、引数に入れたくらいです。細かいところだと、無くても動く `function` 内最後の `;` は取っています。 `document` の 1 文字化も試しましたが、使われる回数が少なすぎてかえって文字数が増えてしまいました。`createElement` や `appendChild` が長いので、繰り返し使うようだったらこれも 1 文字化するところですが、これらも 1 回ずつしか使われないのでやりません。 あとは余計なホワイトスペースを取り除くとこうなります。 ```js: (function(f,s){s=document.createElement("script");s.src="//ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js";s.onload=function(){f(jQuery.noConflict(true))};document.body.appendChild(s)})(function($){}) ``` これで 210 文字。これ以上短くするのは、このロジックのままだと、出来ても数文字程度かな?という気がするので、いったん満足してます。 > 個人的には `%20` を含まないブックマークレットに美学を感じています。 CDN の URL を `https ` 固定にして `bit.ly` などで短縮してしまうという大技でだいぶ減らせるので、どうしてもというときは検討してみてもいいですね。 ちなみにこのコードは、jQuery 以外のライブラリでもちょっと改造するだけで汎用的に使えそうです。 ### 追記 (最短を目指したバージョン) はてなブックマークコメントでヒントをもらった、`document` の引数化でマイナス 3 文字。それに加えて URL の短縮を使って、163 文字まで減らしてみました。 `bit.ly` のエイリアス `j.mp` を使う点と、bit.ly は `https` もサポートしてるので URL からプロトコルを省略できる点も重要ですね。 ```js: (function(d,f,s){s=d.createElement("script");s.src="//j.mp/1bPoAXq";s.onload=function(){f(jQuery.noConflict(true))};d.body.appendChild(s)})(document,function($){}) ``` これ以上はもう短くならない…かな? ### 追記2 (最短を目指したバージョン2) true を !0 に置換できることに気づいたので、161 文字! ```js: (function(d,f,s){s=d.createElement("script");s.src="//j.mp/1bPoAXq";s.onload=function(){f(jQuery.noConflict(!0))};d.body.appendChild(s)})(document,function($){}) ``` ### 追記3 (最短を目指したバージョン3) コメントのヒントのおかげで、ついに 159 文字を達成。 ```js: !function(d,f,s){s=d.createElement("script");s.src="//j.mp/1bPoAXq";s.onload=function(){f(jQuery.noConflict(1))};d.body.appendChild(s)}(document,function($){}) ``` |
|
| 708位 |
|
|||
|
15:20:12 |
(CyberAgent 所属) |
|
# はじめに
仕事などで触っている時間が長いのがエディター、それとコマンドシェルです。 今回はそんな触っている時間が長いZshをかわいくする方法を書いていきます。 なお、この記事は以下の記事を参考にしています。 [可愛いzshの作り方 - プログラムモグモグ](http://d.hatena.ne.jp/itchyny/20110629/1309355617) # 完成形 以下の画像の通りとなります。かわいいですね。頑張っている様子が見受けられます。 ちなみに、顔文字のモチーフは社内IRCに住み着いているunazu_kunというbotが元となっています。  # .zshrcの設定 ```shell:.zshrc # 色設定 autoload -U colors; colors # もしかして機能 setopt correct # PCRE 互換の正規表現を使う setopt re_match_pcre # プロンプトが表示されるたびにプロンプト文字列を評価、置換する setopt prompt_subst # プロンプト指定 PROMPT=" [%n] %{${fg[yellow]}%}%~%{${reset_color}%} %(?.%{$fg[green]%}.%{$fg[blue]%})%(?!(*'-') <!(*;-;%)? <)%{${reset_color}%} " # プロンプト指定(コマンドの続き) PROMPT2='[%n]> ' # もしかして時のプロンプト指定 SPROMPT="%{$fg[red]%}%{$suggest%}(*'~'%)? < もしかして %B%r%b %{$fg[red]%}かな? [そう!(y), 違う!(n),a,e]:${reset_color} " ``` |
|
| 709位 |
|
|||
|
17:14:35 |
|
|
# 簡単アニメーション!Pixi.jsを触ってみる!<br>〜(1)テキストを動かしてみる〜
突然ですが、 **Pixi.js** を触ってみることにしました。 (前フリ無しw) ## Pixi.jsとは? Pixi.jsは、Goodboy Digital社が配布している2D描画用のjavascriptライブラリです。 下記サイトで配布されています。 <a href="http://www.pixijs.com/"><img src="http://img.dfm.lrv.jp/pixi1/pixilogo.png"></a> [MITライセンス](http://opensource.org/licenses/MIT)に基づくオープンソースソフトウエアとして開発が進められています。 このライブラリを使って作ったアニメーションは、WebGLを自動的に使ってくれるようです。 (非対応のデバイスではCanvasを使う)。 難しそうなWebGLを覚えなくて使えるのはうれしいです。 [pixi_examples]: http://pixijs.github.io/examples/ [pixi.jsのexampleページ][pixi_examples]でデモが紹介されていますので、いくつか見てみましょう。(スクリーンショット画像クリックで本家pixijs.comサイトのexampleページに飛びます) **Example4 [Lot o' Sprites]** どや!ってな感じに、たくさんのスプライトを表示するデモ。 <a href="http://www.goodboydigital.com/pixijs/examples/4/"><img src="http://img.dfm.lrv.jp/pixi1/scs1.png"></a> **Example14 [Masking]** パンダ〜! <a href="http://www.goodboydigital.com/pixijs/examples/14/"><img src="http://img.dfm.lrv.jp/pixi1/scs2.png"></a> [pixi.js exampleページ][pixi_examples]には、他にもたくさんのサンプルがあります。気になる方はチェックしてみてくださいね〜 それでは、さっそく使ってみます。 ## Pixi.jsのインストール [Pixi.js](http://www.pixijs.com/)か[Pixi.jsのgithubリポジトリ](https://github.com/GoodBoyDigital/pixi.js)からダウンロードしてください。 ドキュメントなども、一式入手することができます。 ダウンロードしたpixi.js-master.zipを展開すると、下記のようにファイルがたくさんでてきます。  `pixi.js-master/bin/pixi.js`か`pixi.js-master/bin/pixi.dev.js`をロードすればPixi.jsの機能を利用することができます。 今回は、実装を確かめながら進めることになると思いますので、pixi.dev.jsの方を使ってみます。 ```js <script src="pixi.dev.js"></script> ``` さて、これでPixi.jsを使う準備ができました。いよいよ何か動くものを作ってみようと思います。 ## Hello World!(テキストを動かす) 今回は **テキストを表示させて動かす** ところまで挑戦してみます。 ### おおまかな流れ Pixi.jsでのアニメーションは基本的に下記のような流れとなります。 1. オブジェクト(画像スプライトやテキストなど)、ステージ、レンダラーを作る 2. ステージにオブジェクトを乗せる 3. レンダラーのViewをHTMLの要素に追加する 4. アニメーション処理を作る。この中で1コマで行う処理を書く。オブジェクトを動かしたり。最終的にレンダラーを使ってステージを描画する処理を含める 5. アニメーション処理を繰り返し実行 自分で理解するために、絵に描いてみました。  。。。見ても良くわかりませんね。。。。すいません。。 気を取り直して実際にコードを見てみましょう。短いコードですので全部載せてみます。 ```index.html <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <script src="pixi.dev.js"></script> </head> <body> <p>pixi.jsのhelloWorldです</p> <div id="pixiview"></div> <script> var width = 600; var height = 400; // ステージを作る var stage = new PIXI.Stage(0x000000); // レンダラーを作る var renderer = PIXI.autoDetectRenderer(width, height); // レンダラーのviewをDOMに追加する document.getElementById("pixiview").appendChild(renderer.view); // テキストオブジェクトを作る var word = "Hello World!"; var style = {font:'bold 60pt Arial', fill:'white'}; var textobj = new PIXI.Text(word, style); textobj.position.x = 60; textobj.position.y = height / 2; // テキストオブジェクトをステージに乗せる stage.addChild(textobj); // アニメーション関数を定義する function animate(){ requestAnimFrame(animate); // 次の描画タイミングでanimateを呼び出す textobj.rotation += 0.01; // テキストを回転する renderer.render(stage); // 描画する } // 次のアニメーションフレームでanimate()を呼び出してもらう requestAnimFrame(animate); </script> </body> </html> ``` [動作サンプル](http://img.dfm.lrv.jp/pixi1/sample/) それでは、最初から見ていきます。 ### 1. オブジェクト、ステージ、レンダラーの作成 #### ステージの作成(PIXI.Stage()) ステージは `PIXI.Stage()` で生成します。 ```js var stage = new PIXI.Stage(0x000000); ``` パラメータに指定しているのは背景色になります。 0x000000で黒、0xFF0000で赤になります。 このステージに、様々なオブジェクトを登録することで、にぎやかなアニメーションを作成していくことができます。 >2014.07.04修正 >以前はパラメータに文字列を渡した内容で記載しておりましたが、コメント欄で数値指定が正しいと教えて頂きましたので、記載を訂正しました。 >syo-sa1982さん、ありがとうございました! #### レンダラーの作成(PIXI.autoDetectRenderer()) 次に、レンダラーです。レンダラーは `PIXI.autoDetectRenderer()` で生成します。 ```js var width = 600; var height = 400; var renderer = PIXI.autoDetectRenderer(width, height); ``` 指定しているのは、レンダラーが扱う画面領域の縦・横サイズです。pixel数で指定します。 #### オブジェクトの作成 最後にオブジェクトですが、Pixi.jsでは様々なオブジェクトをステージに登録することができます。基本的なオブジェクトには下記のようなものがあります。 |オブジェクト|生成するメソッド| |:---------|:------------| |アウトラインフォントのテキスト|Text()| |ビットマップフォントのテキスト|BitmapText() |画像ファイルから読み込むイメージ|ImageLoader()| |円 |Circle()| |楕円 |Ellipse()| |長方形|Rectangle()| | : | : | Pixi.jsでは、これらのオブジェクトを複数組み合わせてステージに配置することができます。 今回は、文字列1つだけというシンプルなアニメーションを作りますので、この中から`PIXI.Text()`だけを利用します。 ```js var word = "Hello World!"; // 文字列を指定 var style = {font:'bold 60pt Arial', fill:'white'}; // 文字サイズや色など var textobj = new PIXI.Text(word, style); // テキストオブジェクトの生成 textobj.position.x = 60; // 表示位置(x) textobj.position.y = height / 2; // 表示位置(y) ``` 見たまんまですが、`new PIXI.Text(文字列,スタイル)` でテキストオブジェクトを生成しています。あとは、`position`プロパティで、ステージのX:0, Y:0からの相対座標でステージ上での表示位置を指定しています。 文字列やスタイルをいろいろと変更して遊んでみてください。 ### 2. オブジェクトのステージへの配置(stage.addChild()) オブジェクトのステージへの登録は、ステージオブジェクトの`addChild()`メソッドで行います。 ```js stage.addChild(オブジェクト); ``` 今回は1つだけですが、複数作成した場合、作成したオブジェクトを全てaddChild()でステージに登録するようにしてください。沢山登録すれば、どんどんにぎやかになりますよ。 ### 3. レンダラーViewのHTMLへの追加 いよいよ、対象となるHTMLの要素にレンダラーのViewを紐付けます。 サンプルでは、`<div id="pixiview'>` という要素に追加していますので、下記のようなコードになっています。 ```js document.getElementById("pixiview").appendChild(renderer.view); ``` bodyに直接追加する場合、 ```js document.body.appendChild(renderer.view); ``` と書くこともできます。 ### 4. アニメーションの作成(1コマの動き処理の作成と、繰り返し実行) さて、いよいよアニメーションを作ります。 実は、今までやってきたことは「段取り」にすぎません。 Pixi.jsを使って **モノを動かす** 部分は、これから作る処理の中で行います。 アニメーションは下記のように作ります。 1. 1コマ分の「動き」の処理を関数として作成 2. 作成した1コマ分の「動き」処理を繰り返し実行 それでは、それぞれの処理の内容を見ていきましょう。 #### 1コマ分の「動き」処理の関数をつくる 今回テキストを動かすために作った関数`animate()`はのコードを見てみましょう。 ```js function animate(){ requestAnimFrame(animate); // ①次の描画タイミングでanimateを呼び出す // ↓オブジェクトを動かす処理 textobj.rotation += 0.01; // ②テキストを回転する // ↑オブジェクトを動かす処理 renderer.render(stage); // ③描画する } ``` ここで、①と③の部分はお決まりの部分です。①の`requestAnimFrame()`については後述します。③は描画更新処理です。 ②の部分がオブジェクトを動かす処理となり、オブジェクトの数や種類、動かし方によって処理内容が変わる部分です。 上記例では、ただ回転するだけの処理となっていますが、例えば下記のように変更すれば横に動くようになります。 ```js // ↓オブジェクトを動かす処理 textobj.position.x += 2; // ②テキストを横移動させる if(textobj.position.x > 600){ textobj.position.x = -450; } // ↑オブジェクトを動かす処理 ``` <a href="http://img.dfm.lrv.jp/pixi1/sample2/"><img src="http://img.dfm.lrv.jp/pixi1/sample2.png"></a> 横に動くように変更した動作サンプル 是非いろいろ変更してみて、動きを試してみてください! #### 1コマ分の「動き」処理を繰り返し実行 さて、1コマ分の動きの処理をおこなう関数`animate()`ができました。 この関数を`requestAnimFrame()`に登録することで、次回ブラウザが画面リフレッシュするタイミングで呼び出してくれます。 ```js requestAnimFrame(animate); ``` ただし、`requestAnimFrame()`で登録された関数が呼び出されるのは1度だけです。 アニメーションを実現するには、繰り返し呼び出してもらう必要がありますね。 先ほど作成した1コマ分の動きの処理をおこなう関数`animate()`の中でも`requestAnimFrame()`を呼び出していたのはこのためです。 このようにすることで、画面リフレッシュのタイミングに同期して`animate()`が常に呼び出されるようになり、チラつきの無いスムーズなアニメーションにすることができます。 > **メモ:requestAnimFrame()?** > > 名前でピーンと来た方は、その通りです。 > > requestAnimFrame()は、`requestAnimationFrame()`というHTML5の標準APIのPolyFill 実装で、pixi.js内で提供されています。 > pixi.js内で`requestAnimationFrame()`も同様にベンダーPrefixが解決されていますので、requestAnimationFrame()と書いても大丈夫です。 > ## まとめ。次回は 最後までおつきあい頂きありがとうございます。 Pixi.jsの`Hello World!` どうでしたか? テキストが動いただけで、まだ「簡単アニメーション!」に至っておらず恐縮ですが、Pixi.jsの可能性を少しでも感じて頂けたら幸いです。 次回は、なんと! **画像を動かす!** に挑戦してみたいと思います!(ベタですね。その次どーすんだ?) というわけで、次もよろしくお願いします。 それでは! |
|
| 710位 |
|
|||
|
00:28:07 |
|
|
こんにちは。haranicleです。
今回は、きれいなソースコードを生産したい「きれいめ系プログラマ」な皆さんのために、 コードフォーマッタの設定とコメントの記述を助けてくれるXcodeのプラグインを紹介します。 **※Xcode5.1で使用する場合は、以下のように設定してください。(2014/04/06追記)** [iOSモテコーデ術Xcode5.1対応](http://blog.haranicle.net/motecode-xcode5_1/ "iOSモテコーデ術Xcode5.1対応") # コードフォーマッタ 開発メンバが多かったり、期間が長いプロジェクトだとコードフォーマットがバラバラでイケてないソースコードが生産されがちです。 モテなソースコードを生産するために、コードフォーマットを決めて自動で整形してあげましょう。 今回は以下を実現します。 - Xcode5でビルドに成功したタイミングで、自動的にコードフォーマッタを実行できる。 - プロジェクト毎に異なるコードフォーマットを設定できる。 ※本章は下記ページをベースに記載しています。 [Xcode で コードフォーマッター Uncrustify を使う - FUKULOG開発者ブログ](http://fukulog.github.io/blog/2013/05/22/using-uncrustify-in-xcode/ "Xcode で コードフォーマッター Uncrustify を使う - FUKULOG開発者ブログ") ## 前提条件 - Homebrewがインストール済みであること。 - Xcode5とCommand Line Toolsがインストール済みであること。 - 試しにフォーマッタを実行してみるXcodeのプロジェクト(今回はMoteSampleという名前のSingle View Applicationフォーマットのプロジェクト)を作成していること。 以下の説明は、これらの条件を満たしていることが前提です。 ## Uncrustifyのインストール Uncrustifyをインストールします。 Xcodeのプラグイン版もありますが、今回は、ビルド時に自動実行させるためにHomebrew版を使用します。 以下のコマンドをターミナルで実行してください。 ```: $ brew install uncrustify ``` ## Uncrustifyを実行するスクリプトの作成 `~/bin/`配下(フォルダがない場合は新規に作成する)に`uncrustify`という名前のファイルを作成し、`uncrustify`に以下を記述します。 ```ruby:uncrustify #!/usr/bin/ruby base_path = ENV['PWD'] puts "running uncrustify for xcode project path: #{base_path}" if base_path != nil paths = `find "#{base_path}" -name "*.m" -o -name "*.h" -o -name "*.mm" -o -name "*.c"` if paths.kind_of?(String) then paths = paths.split(/\r?\n/).map {|line| line.chomp} end paths = paths.collect do |path| path.gsub(/(^[^\n]+?)(\n)/, '"\\1"') end paths = paths.join(" ") result = `/usr/local/bin/uncrustify -c "#{base_path}"/.uncrustifyconfig --no-backup #{paths}`; puts result else puts "Invalid base path..." end ``` (2013/12/15修正 paths = `find... の結果がArrayではなく改行区切りのStringで取得されてしまった時にコードフォーマッタを実行できないバグを修正しました。) ## スクリプトに実行権限を追加 以下のコマンドをターミナルで実行して、スクリプトに実行権限を付与します。 ```: $ chmod +x ~/bin/uncrustify ``` ## Uncrustifyのフォーマットの作成 MoteSampleフォルダ(プロジェクトフォルダのルート)内に`.uncrustifyconfig`というファイルを作成し、下記ページのフォーマットを記述します(各パラメータについて日本語解説がついてて素敵です)。 [uncrustify - 意識の高さからかコードフォーマッター設定を公開 - Qiita [キータ]](http://qiita.com/ayakix/items/3f05da9541b8e130e39f "uncrustify - 意識の高さからかコードフォーマッター設定を公開 - Qiita [キータ]") ## Xcodeの設定 Xcode>Preferences...>Behaviors>Build>Succeedsと順に選択し、Runのチェックボックスを✓します。 次にRunの横のドロップダウンからChoose Script...をクリックし、`~/bin/uncrustify`を選択してOpenをクリックします。 これでXcodeでビルド成功時に自動でフォーマッタが走るようになりました。 Command Line Toolsがインストールされていないと実行されないので注意してください。 (僕はココで若干ハマりました...) ## 動作確認 MoteSampleのViewController.mに以下を記述します。 ```objc:ViewController.m -(BOOL)isMoteru:(NSInteger)age faceSize:(CGSize)faceSize money:(NSInteger)money{return NO;} ``` ⌘+Bでビルドし、ビルド成功のタイミングで以下のように整形されれば成功です。 ```objc:ViewController.m - (BOOL)isMoteru:(NSInteger)age faceSize:(CGSize)faceSize money:(NSInteger)money { return NO; } ``` `.uncrustifyconfig`をチームで共有すれば、全員が同じコードフォーマットのソースコードを生産できますね。 # VVDocumenter Uncrustifyはインデントなどの整形は出来ますが、変数名やコメントの記述方法については整形できません。 変数名は仕方ないとして、せめてコメントのフォーマットは統一したい! 折角コメント書くんだからAppledocを使ってAPIドキュメントを作りたい! そんなきれいめ系プログラマなあなたにおすすめしたいのがVVDocumenterです。 VVDocumenterはXcodeで"///"と入力すると、自動的にJavadocライクなコメントを生成してくれるXcodeのプラグインです。 ## 前提条件 - Xcode5がインストール済みであること。 - 試しにVVDocumenterを実行してみるXcodeのプロジェクト(今回はMoteSampleという名前のSingle View Applicationフォーマットのプロジェクト)を作成していること。 以下の説明は、これらの条件を満たしていることが前提です。 ## VVDocumenterのインストール ### ダウンロード 下記URLからVVDocumenter-Xcodeを取得します。 git cloneするなりダウンロードするなりお好きにどうぞ。 [onevcat/VVDocumenter-Xcode · GitHub](https://github.com/onevcat/VVDocumenter-Xcode "onevcat/VVDocumenter-Xcode · GitHub") ### インストール 1. ダウンロードしてきたファイルの中のVVDocumenter-Xcode.xcodeprojをXcodeで開きます。 2. ⌘+Bでビルドし、Buld Succeededと表示されればインストール成功です。 3. Xcodeを再起動します。 ## 動作確認 MoteSampleを開き適当なメソッドの前にカーソルを合わせ、"///"を入力します。 Javadocライクなコメントが挿入されたら成功です。 @paramや@returnを自動で挿入してくれるので便利ですね。 ## おまけ ###Xcodeでリファレンス表示も! VVDocumenterでコメントを書いておくとXcodeでメソッド名を⌥+クリックでサクッとリファレンスを見ることができます。 こんな感じでソースコードを書いておいて。  メソッド名を⌥+クリックでリファレンス表示!  これだけでも、インストールする価値がありますね。 ###Appledocも! 上記ソースコードからAppledocでAPIドキュメントを生成するとこんな感じになります。  #おわりに コードフォーマッタとVVDocumenterを設定したのに全然モテない!おかしい! |
|
| 711位 |
|
|||
|
00:02:46 |
|
|
JQueryとの連携方法でいつもとまどう点をまとめとこう。というよりも、jQueryからRailsのアクションを呼ぶ方法。初心者にはなかなか流れが掴みにくいです。 用途は限定されるかもしれないですが、たとえばselectの変更をjQueryで検知したときに、RailsでActionを叩いてsessionの値を書き換えるときなどは次のようにする。 まず、流れ。 1)jQueryでselectの変更を検知する 2)jQuery内からURLを指定してAjax通信する 3)2)のURLがActionを叩くようにルーティングを設定する 4)2)のURLに従って、html.erbファイルを設置する 5)後は好きなようにAction内で処理をする ```sample.html.erb <select id="year_select" name="y" style="width:100px;"> <option value="2011">2011</option> <option value="2012">2012</option> <option value="2013" selected="selected">2013</option> <option value="2014">2014</option> </select> ``` 例えば上のようなviewがあったとします。 \#year_selectの変更を検知するjQueryが下記。 ```sample.js $("select#year_select").change(function(){ $.ajax({ url: "projects/change_session_year", type: "GET", data: {year : $(":selected").attr("value"), id: 1, mode: 'hoge', type: 'entry' }, dataType: "html", success: function(data) { alert("success"); }, error: function(data) { alert("errror"); } }); }); ``` $.ajax はAjax通信するための構文。 url: 通信先のURLを指定。ルートからの指定でいいはず data: 通信先に渡すパラメータ。Actionからはもちろんparams[]で取得できる success,error 通信成功、失敗時の処理 慣れないとわかりにくいけど、通信後、戻り値がfunction(data)として渡されます。このdataをいじくってテンプレートを書き換えるというのが王道の使い方だと思いますが、ここではテンプレートはいじりません。 で、projects/change_session_year というURLでルーティングを設定します。 ```routes.rb get "projects/change_session_year" ``` 次、要注意です。これを忘れたがために嵌って時間ロスるすことがよくあります。 ルーティングに対応するテンプレートファイルを設置する!この例であれば、 projects/change_session_year.html.erb を忘れずに設置してください!中身は空でいいと思います。 $.ajaxで通信した際、このテンプレートが無いとエラーが返ってしまいます。普通のRailsの処理であれば、恐らく、missing_tamplateエラーが出るのですぐにわかるんだけど、Ajaxの場合は何も言わず失敗するので、気づくのに時間がかかるんです。 後は、projectsコントローラーにchange_session_yearアクションメソッドを用意して、好きなように処理を入れればOKです。 ```projects_controller.rb def change_session_year session[:year] = 2013 end ``` 肝は4)のテンプレートを忘れないかどうか、じゃないでしょうか。 |
|
| 712位 |
|
|||
|
21:35:00 |
(株式会社キュリオシティソフトウェア 所属) |
|
拙作のiPhoneアプリShareAlbum (http://bit.ly/sharealbum_jp) ではOAuthを使ってInstagramやTumblrなど様々なWebサービスと連携しています。このTipsではOAuth認可・認証のWebサービスと連携するアプリケーションの実装についてGoogleのライブラリによる説明を書いています。  OAuth連携することでこんなアプリケーションも簡単に作れますよということです # OAuth2.0について まずはOAuth2.0についてiOSアプリ開発者が知るべきことは下記の通りです * 基本的にAPIの認可のためにアクセストークンを取得する * アクセストークンはAPIのパラメータにセットしてリクエストを行う * アクセストークン取得までのフローは複数あり、主に認可コードフローやインプリシットグラントフローが使われる * iOSからはUIWebViewを用いてログイン画面を表示する  UIWebViewでログイン画面を出すとそのOAuthサービスのデザインのまま表示されます ## フローについて * 認可コードフロー + 別名サーバサイドWebアプリケーションフロー、エクスプリシットグラントフローとも呼ばれます * インプリシットグラントフロー + 別名クライアントサイドWebアプリケーションフローとも呼ばれます * リソース所有者パスワードクレデンシャルフロー * クライアントクレデンシャルフロー 次にこれらフローについて概要をまとめてみました ## 認可コードフローの概要 OAuthサービスとアプリケーションが共通のclient_secretを保持しておき、それを使って認可コードを取得、これとアクセストークンを交換します。 Webサービスの場合、ブラウザは認可コードについて保持することはありますが、アクセストークンを保持していないのでブラウザ履歴や,referer,jsなどからアクセスされるリスクが低くなります。リフレッシュトークンを使ってアクセストークンをリフレッシュすることが許されています。 #### 特徴まとめ * 認可コードフローでは認可コードと引き換えにアクセストークンを手に入れる * 認可コードはクエリパラメータとしてリダイレクトURLに付けられ送られてくる * アクセストークンはそのリダイレクトURLにアクセスした際にレスポンスとして受け取る #### 使われるケース + 長期に渡るアクセスやクライアントアプリがWebアプリケーションサーバの場合 ## インプリシットグラントフローの概要 iPhoneやAndoridなどクライアントアプリケーションでclient_secretを秘匿できないケースのために考えられたフローです。認可コードが不要ですが、そのためアクセストークンの期限が切れた場合に、ID/パスワードによるフローをユーザーに行わせたいため、リフレッシュトークンを利用させることはOAuthの仕様上定義されていません。 #### 使われるケース + iPhone,AndroidやJS、Flashなどclient_secretを保持できない(リバースエンジニアリングされると見られる)アプリケーション ## リソース所有者パスワードクレデンシャルの概要 ユーザ名とパスワードを直接access_tokenと交換できます。APIプロバイダ自身(つまりサービス元)が開発したモバイルアプリケーションなど信用できるクライアントのみで使われます。 #### 使われるケース + OAuthサービス元自身が作成するモバイルアプリケーション ## クライアントクレデンシャルの概要 特定のユーザでなくストレージサービスやDBサービスのAPIアクセス方法です。 #### 使われるケース + Webサービスとストレージ系サービス間の認可がすでに委譲済みな場合 # OAuthサービスへのアプリケーション登録例 ## InstagramのOAuthについて Instagramはデベロッパー用の管理画面が有り、そこから利用するアプリ情報を登録します。 http://instagram.com/developer/clients/manage/ アプリケーションの新規登録画面は次の通り  それぞれ入力するのは次の項目になります ・アプリケーション名 (ユーザに表示される) ・アプリケーションについての詳細(ユーザに表示される) ・ウェブサイトURL(ユーザに表示される) ・リダイレクトURL(認可コードをレスポンスパラメータとして付与されるURL) アプリケーション登録で重要なのは最後のリダイレクトURLです。 通常、リダイレクトURLはInstagramのアクセストークンを使いたいWebサービスのURLとします。 InstagramのID/パスワード入力画面からそのURLへクエリパラメータとして認可コードを付けてリダイレクトされWebサービスに戻ってくることで、OAuthの認可処理が完了しアクセストークンの利用ができるようになります。 しかしiOSアプリはWebアプリではないため、リダイレクトされるサイトはありません。iOSアプリ側はOAuthサーバーからのリダイレクト要求をキャンセルし、認可コードだけを取得するようにします。そのため、この連携アプリ登録時に入力するリダイレクトURLはダミーでも構いません。 # iOSアプリ側の仕様について ID/パスワードの入力はUIWebViewを使うことができますが、UIWebView内の通信ではレスポンスとしてステータスコードが取得できません。 そのため、OAuthサーバーに登録したリダイレクトURLをあらかじめiOSアプリ側で保存しておき、リダイレクト処理が実行されようとする際に、リダイレクトされようとするURLとあらかじめ保存していたURLとを比較することによってUIWebViewのリダイレクトの通信をキャンセルし、必要があればNSURLConnectionで別途通信を行いレスポンスを取得します。 ## 実装例 具体的な実装例はGoogleのOAuthライブラリであるGTMOAuth2ViewControllerTouch.mから、UIWebViewがOAuthサービス側からのリダイレクト命令をうけた際に、UIWebViewのdelegateであるwebView:shouldStartLoadWithRequest:navigationType:が実行されるため、リダイレクトしようとする際に処理を挟んでリダイレクトをキャンセルしているのがわかると思います ```objectivec:GTMOAuth2ViewControllerTouch.m - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { if (!hasDoneFinalRedirect_) { //まだアクセストークンを取得するためのリダイレクトがされていなければ //リクエストからリダイレクトさせるかを判定させてアクセストークンを取得させる hasDoneFinalRedirect_ = [signIn_ requestRedirectedToRequest:request]; if (hasDoneFinalRedirect_) { //UIWebViewのリクエストをキャンセルさせる(この時点でアクセストークンは取得済み) return NO; } } return YES; } ``` [signIn_ requestRedirectedToRequest:request];のrequestRedirectedToRquest:メソッドについて掘り下げましょう。この中では保持していたURLとリダイレクトされようとしたURLを比較しています。 ```objectivec:GTMOAuth2SignIn.m // リダイレクトURLとして指定したコールバックURLなら // このメソッドはアクセストークンを取得しYESを返す - (BOOL)requestRedirectedToRequest:(NSURLRequest *)redirectedRequest { //あらかじめ保持しているリダイレクトURLをNSStringとしてローカルに保持 NSString *redirectURI = self.authentication.redirectURI; if (redirectURI == nil) return NO; //ここではGoogle特有のRedirectURIと同じかどうかをチェックしている。 //今回の話とは関係ない NSString *standardURI = [[self class] nativeClientRedirectURI]; if (standardURI != nil && [redirectURI isEqual:standardURI]) return NO; //あらかじめ保持していたリダイレクトURLをNSURLにしておく(比較のため) NSURL *redirectURL = [NSURL URLWithString:redirectURI]; //リクエストしようとしたURLをNSURLにしておく(比較のため) NSURL *requestURL = [redirectedRequest URL]; //リクエストしようとしたURLをhostとパスに分ける、分けて比較するのは //about:blankを指定した際に分ける必要があったから? NSString *requestHost = [requestURL host]; NSString *requestPath = [requestURL path]; //リクエストしようとしたURLが保持していたURLと同じならコールバックURLとしてYESにする BOOL isCallback; // if (requestHost && requestPath) { //ここで比較 isCallback = [[redirectURL host] isEqual:[requestURL host]] && [[redirectURL path] isEqual:[requestURL path]]; } else if (requestURL) { // handle "about:blank" isCallback = [redirectURL isEqual:requestURL]; } else { isCallback = NO; } if (!isCallback) { // tell the caller that this request is nothing interesting return NO; } // we've reached the callback URL // try to get the access code if (!self.hasHandledCallback) { //クエリパラメータをリクエストしようとしたオブジェクトから取得 NSString *responseStr = [[redirectedRequest URL] query]; // [self.authentication setKeysForResponseString:responseStr]; #if DEBUG NSAssert([self.authentication.code length] > 0 || [self.authentication.errorString length] > 0, @"response lacks auth code or error"); #endif //アクセストークンを取得するリクエストを実行 [self authCodeObtained]; } // tell the delegate that we did handle this request return YES; } ``` ちなみに、クエリにある認可コードはURLエンコードされているので、UTF8でデコードしてあげています ```objectivec: + (NSString *)unencodedOAuthParameterForString:(NSString *)str { NSString *plainStr = [str stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; return plainStr; } ``` # 最後に OAuthの説明においてあやふやな部分や、間違っている箇所があればコメントもしくは編集リクエストなどでご指摘頂ければと思います # 参考 デジタル・アイデンティティ技術最新動向(2):RFCとなった「OAuth 2.0」――その要点は? (1/2) - @IT http://www.atmarkit.co.jp/ait/articles/1209/10/news105.html |
|
| 713位 |
|
|||
|
11:04:36 |
(mixi 所属) |
|
PCやスマホとかのWebサイトに対してA/Bテストやる方法は結構いろいろありますね。
有名どころだと [optimizely](https://www.optimizely.com/) とか、最近だと [planBCD](https://planb.cd/) とか。 iPhoneとかAndroidとか、ネイティブアプリとかたくさん作っている時代ですし、同じことはどうやってるのか。 ストアにリリースした後に検証・分析できるようなフレームワークやサービスがすでにあるのか。 ちょっと調べてみました。  # 調べ始め さくっとググってひっかかったものは - Quora - [How do companies practically A/B Test new versions of native mobile apps that are already in production?](http://www.quora.com/A-B-Testing/How-do-companies-practically-A-B-Test-new-versions-of-native-mobile-apps-that-are-already-in-production) - Qiita - [SkyLabを利用したiOSアプリケーションのABテスト ](http://qiita.com/okitsutakatomo/items/90a96a4ef2276251cd56) - 海外の記事 - [Roundup: A/B Testing Solutions](http://architects.dzone.com/articles/roundup-ab-testing-solutions) おおまかに下記の2種類がありそうでした。 - **GitHubとかで公開されているもの** - **サービスになってるもの** 以上の情報をもとに更にググって下記のようにまとめてみました。 # GitHubで公開されてるもの clutch.ioのように、もともとサービスになっていたものがオープンソースになったのが有名かもですね。 あとは、スタートアップ企業や個人が公開しているフレームワークがいくつかありました。 ### clutch.io https://github.com/clutchio - iOS/AndroidのSDK、Pythonで書かれたサーバー側のソースも公開されている。 - clutch.io がやっていたサービス - http://www.crunchbase.com/company/clutch-io - 2012年8月13日、Twitterが買収し、現在はオープンソース化されている。 ### Switchboard https://github.com/KeepSafe/Switchboard - iOS/Android のSDK、PHPのサーバー側のソースも公開されている - KeepSafeっていうスタートアップが公開しているiOS/Androidのフレームワーク - KeepSafeについて - http://www.crunchbase.com/company/keepsafe - Seed, 2/2012 $700k - Series A, 7/2013 2 $3.4M ### MSActiveConfig https://github.com/mindsnacks/MSActiveConfig - cocoapodsにドキュメントがある → [MSActiveConfig](http://cocoadocs.org/docsets/MSActiveConfig/1.0.1/) - MindSnacksっていうスタートアップが公開しているiOSのフレームワーク - MindSnacksについて - http://www.crunchbase.com/company/mindsnacks - USA - FUNDING TOTAL $7.7M - Venture Round, 3/2011, $1.2M - Venture Round, 8/2012, $6.5M ### Conductrics API Wrapper https://github.com/conductrics/conductrics-ios - [Conductrics](http://conductrics.com/)っていうA/Bテストのサービスを利用するためのObjective-Cのラッパーらしい。 - bandit-styleな最適化とか動的なターゲティングできるよって宣伝してる。 - Conductricsについて - http://www.crunchbase.com/company/conductrics - USA - FUNDING TOTAL $210k - Seed, 11/2012, $210k ### iOS A/B Split Test Library https://github.com/chrismaddern/iOS-Split-A-B-Test-Library - A/Bテストとか、遠隔的にアプリ内の変数を定義したりするiOSのライブラリ。 - [サーバーサイドはこっち](https://github.com/chrismaddern/A-B-Split-Test-Server) - [Venmo](http://www.crunchbase.com/company/venmo)というスタートアップの中のエンジニアさん([Chris Maddern氏](http://chris.cm/about-chris-maddern/))が作ってるらしい。 ### SkyLab https://github.com/mattt/SkyLab - Herokuの mattt氏 が作った iOS向けのライブラリ。この方は[AFNetworking](https://github.com/AFNetworking/AFNetworking)とかでも有名ですね。 - 初めて起動する際に、特定のパラメータに応じて挙動が決まるようになる。ロギングとかは開発者がよしなにやる。 ### GroundControl https://github.com/mattt/GroundControl - これも mattt氏 が作った iOS向けのライブラリ。 - ABテストではないけど、iOSなアプリをRemote Configurationするもの。 - .plist をサーバーから取ってくるやつ。sinatraで書かれた超シンプルなサーバー側のサンプルコード付き。 ちなみに - 調べてたら [Helios](http://helios.io/)の一部のようなものだった - Heliosについては [naoya氏 のエントリ](http://d.hatena.ne.jp/naoya/20130912/1378963649) が詳しい # サービスになってるもの 下記にサービスのURLと一緒にCrunchBaseのリンクも一緒に載せてみました。 Artisanを除けば、ここ1〜2年以内のスタートアップばかりの気もします。 なんか思ったよりもたくさんあったし、どこでも似たようなことができる感じです。 ただ、ブラウザ上でアプリのUI編集したら、リアルタイムで端末に反映されるとか、 正直「そんなことまでやるのかー」と思うものもいくつかありました。 ### Artisan  http://useartisan.com/ http://www.crunchbase.com/company/artisan-mobile - Note - re-store なしでリアルタイムにA/Bテストできるよっていってる。 - あと、rapid ui optimization っていってブラウザ上でボタン配置とかラベル変えて publish すると端末にも反映されてすぐ試せる的なものもあり。 - USA - FUNDING TOTAL $7M - Seed, 4/2012 $1.5M - Series A, 6/2013 $5.5M ### Apptimize  http://apptimize.com/ http://www.crunchbase.com/company/apptimize - Note - これもまたブラウザ上から編集するとリアルタイムで端末にも反映されるよ!ってやつ。 - 最近エントリに上がってた → http://techcrunch.com/2014/01/22/apptimize-seed-funding-visual-editor/ - USA - FUNDING TOTAL $2.1M - Seed, 6/2013 - Seed, 1/2014 $2.1M ### Leanplum  https://www.leanplum.com/ http://www.crunchbase.com/company/leanplum - USA - FUNDING TOTAL $825k - Seed, 8/2012 - Angel, 2/2013 $825k ### Vessel  https://www.vessel.io/ http://www.crunchbase.com/company/vessel - USA - 10/2/2012~ ### Pathmap  https://www.pathmapp.com/ http://www.crunchbase.com/company/pathmapp - USA - 12/2012~ - [Kristopher B. Jones](http://www.crunchbase.com/person/kristopher-b-jones)氏がファウンダー兼CEOらしい。他にもたくさん事業作ってるっぽい。そのうちのひとつ。 ### Appiterate  http://appiterate.com/ http://www.crunchbase.com/company/appiterate-com - Note - これもブラウザからボタンの配置をDrang&Dropで移動したり色やテキストを変えると、端末にも反映されるよ!ってやつ。 - USA - 1/5/2013~ ### Splitforce  https://splitforce.com/ http://www.crunchbase.com/company/splitforce - USA - 6/2013~ ### Arise.io  http://arise.io/ http://www.crunchbase.com/company/arise-io - France - 7/2013~ ### 他にも - http://appguru.io/ - Hercules とか Heatmaps というモバイルアプリ向けのサービスがあるみたいだけど、どこが作ってるかはよくわからんかった - [Swrve](http://www.crunchbase.com/company/swrve-new-media) - モバイル・アプリ向けに IN-APP REVENUE を最大化するためにA/Bテストだけじゃなくていろいろやってるらしい。[参考](http://www.swrve.com/product/overview) - 具体的なサービスやお値段は要相談みたいな感じでよくわからんかった。 - [Optimimo](http://www.crunchbase.com/company/optimimo) - サービスはあるはずなんだけど、なんか 504 がかえってきてた。。 ### Kii Could (追記) MBaaS でお馴染みの [Kii Cloud](http://jp-cloud.kii.com/) さんでもA/Bテストをサポートしているそうです。 - http://docs.kii.com/ja/guides/ab-testing/ - SDK - http://community-jp.kii.com/t/ios-storage-sdk-v2-1-14/67 - http://community-jp.kii.com/t/android-storage-sdk-v2-1-19/66 # 調べて思ったことなど ### 全体として OptimizelyやTestflightなんかはググると日本語のエントリも複数見つかって、日本でも結構使われてるんだなという印象があります。一方で、上記のようなネイティブアプリ向けの A/B testing サービスとかは、ググってもなかなか日本語のエントリはひっかからなかったので、日本では今のところあんまり需要ないのかな?という印象でした。 しかし、GitHubで公開されているものに関しては、使ってみたってエントリはいくつか見つかりました。 - [Clutch A/B TestingによるiOSアプリ向けA/Bテストソリューションのご提案](http://qiita.com/laiso/items/b6b1497b9365f57de787) - [SkyLabを利用したiOSアプリケーションのABテスト](http://qiita.com/okitsutakatomo/items/90a96a4ef2276251cd56) なので、ちょっと気にしているエンジニアさんとか結構いるのかもしれません。海外ではいくつかのスタートアップ企業だったり個人がライブラリを公開したりしてるので、日本でも「あるよ!」ってところが実はあるのかもしれません。 一方、サービスになってるものに関しては、まだサービスが出始め?の雰囲気が少なからずあるので、利用するにしろ、どこが良いのかまだ判断つきにくい感じなのかなとも思います。 ### これからどうなるのか 2014年になって、optimizely から [Native app iOS a/b testing product を kickstart してほしい](http://www.linkedin.com/jobs2/view/11259845)っていう LinkedIn の求人エントリがあったのを見かけたり、Facebook が [Airlockっていう Mobile App A/B Testing Framework](https://code.facebook.com/posts/520580318041111/airlock-facebook-s-mobile-a-b-testing-framework/) あるよって言ってます。 なので、これからまた出てきたり淘汰されたりもあるかもしれません。 とにかく、海の向こうではネイティブアプリ向けの A/B testing サービスやフレームワークがいくつかありました。 今後ニーズは高まる気もします。しかし、サービスとしてはマネタイズとかどんなもんなの?って感じもあります。なんとなく、向こうで定着したサービスだったりノウハウとかが来年あたりに日本でも利用されていくのかもしません。 日本じゃどっか作ったりしてないのかな。 あるいは、マネタイズの見通しがたつのであれば、clutch.io などのオープンソースなコードをサクッとフォークして、粛々と日本語のドキュメントを揃えて新規サービスとしてローンチする狡猾な人が居てもいいのかも? **追記 2014年 1月31日 金曜日 14時24分 JST** タイムリーにも、日本発の MBaaS を運営している [Kii Cloud](http://jp-cloud.kii.com/) の方から Twitter で「ちょうどうちでもA/Bテストをサポートしましたよ」って教えてもらいました。 - https://twitter.com/KiiCloudJP/status/429033084873220097 - https://twitter.com/KiiCloudJP/status/429075492595982336 ---- 以上、チラシの裏でしたー! |
|
| 714位 |
|
|||
|
02:06:25 |
|
|
今年一年の機械学習を素人的に振り返ってみるとでぃーぷらーにんぐがすごかったなー、みたいなミーハーな感想がまず思い浮かぶわけなんだけども、実際のところ今ホットな研究は何なんだろうということで、泣く子も黙る機械学習の代表的な国際会議、NIPSとICMLの過去3年分の採択論文を著者にフォーカスしてみることにした。
1st authorの重要度をそれ以外の著者よりも重くしてスコアづけした。 (複数人の著者がいる場合は1st authorを0.8として、残りの0.2を他の著者に分配、1人の場合は1とする) 参考: [IR研究者をスコアリングしてみた - 睡眠不足?!](http://d.hatena.ne.jp/sleepy_yoshi/20090215/p1) [NLP(自然言語処理)研究者をスコアリングしてみた](http://d.hatena.ne.jp/beatinaniwa/20090221/1235221833) 以下各々の自己紹介ページを参照しつつ、該当会議での1st author論文タイトルを引用して、スコアの上から順番にみていきます。名寄せミスなどによる集計ミスがあったらごめんなさい。 ##Anima Anandkumar http://newport.eecs.uci.edu/anandkumar/ > * Spectral Methods for Learning Multivariate Latent Tree Structure (NIPS 2011) > * High-Dimensional Graphical Model Selection: Tractable Graph Families and Necessary Conditions (NIPS 2011) > * A Spectral Algorithm for Latent Dirichlet Allocation (NIPS 2012) > * Latent Graphical Model Selection: Efficient Methods for Locally Tree-like Graphs (NIPS 2012) > * Learning Mixtures of Tree Graphical Models (NIPS 2012) > * When are Overcomplete Topic Models Identifiable? Uniqueness of Tensor Tucker Decompositions with Structured Sparsity (NIPS 2013) > * Learning Linear Bayesian Networks with Latent Variables (ICML 2013) カルフォルニア大学アーバイン校の准教授。 グラフィカルモデルや潜在変数モデルの高次元学習が専門らしい。 ダントツのスコアトップでした。毎年NIPSやICMLに投稿していて、かつそのほとんどがfirst authorなのがすごい・・・ ##Nicolò Cesa-Bianchi http://homes.di.unimi.it/~cesabian/ > * Efficient Online Learning via Randomized Rounding (NIPS 2011) > * A Linear Time Active Learning Algorithm for Link Classification (NIPS 2012) > * Mirror Descent Meets Fixed Share (and feels no regret) (NIPS 2012) > * A Gang of Bandits (NIPS 2013) > * Online Learning with Switching Costs and Other Adaptive Adversaries (NIPS 2013) ミラノ大学の教授。 学習理論からゲーム理論、はたまたバイオインフォマティクスとカバー範囲がかなり広い。 個人的には教授なのにfirst authorとしてガンガン論文を通しているのが、驚きで新鮮だった。 ##Alekh Agarwal http://research.microsoft.com/en-us/um/people/alekha/ > * Stochastic convex optimization with bandit feedback (NIPS 2011) > * Noisy matrix decomposition via convex relaxation: Optimal rates in high dimensions (ICML 2011) > * Distributed Delayed Stochastic Optimization (NIPS 2011) > * Stochastic optimization and sparse statistical recovery: Optimal algorithms for high dimensions (NIPS 2012) > * Selective sampling algorithms for cost-sensitive multiclass prediction (ICML 2013) マイクロソフトリサーチのポスドク研究者。 確率的な最適化に関する論文が目立つ。単なる理論で終わらさずにそれが実際のデータセットに対し計算可能であるか、学習と計算のトレードオフについて日々思いを馳せている。 ##Francis Bach http://www.di.ens.fr/~fbach/ > * Non-Asymptotic Analysis of Stochastic Approximation Algorithms for Machine Learning (NIPS 2011) > * Shaping Level Sets with Submodular Functions (NIPS 2011) > * On the Equivalence between Herding and Conditional Gradient Algorithms (ICML 2012) > * Non-strongly-convex smooth stochastic approximation with convergence rate O(1/n) (NIPS 2013) INRIA (フランス国立情報学自動制御研究所) の研究者。 かつては機械学習界のスーパースターMichael Jordan先生の元で研究をしていたらしい。専門分野はグラフィカルモデルや凸最適化、信号処理など。 ちなみにINRIAは機械学習に強いだけでなく、OCamlやCoqもここで開発されたらしい。 ## Samory Kpotufe http://ttic.uchicago.edu/~samory/ > * k-NN Regression Adapts to Local Intrinsic Dimension (NIPS 2011) > * Pruning nearest neighbor cluster trees (ICML 2011) > * Gradient Weights help Nonparametric Regressors (NIPS 2012) > * Adaptivity to Local Smoothness and Dimension in Kernel Regression (NIPS 2013) > * Regression-tree Tuning in a Streaming Setting (NIPS 2013) Toyota Technological Institute Chicago (豊田工業大学シカゴ校) の研究者。 特にノンパラメトリックな手法や、高次元な統計的学習を専門とする。 (豊田工業大学シカゴ校なんてあったの恥ずかしながら初めて知った。。) ##Elad Hazan http://ie.technion.ac.il/~ehazan/ > * Beating SGD: Learning SVMs in Sublinear Time (NIPS 2011) > * Newtron: an Efficient Bandit algorithm for Online Multiclass Prediction (NIPS 2011) > * A Polylog Pivot Steps Simplex Algorithm for Classification (NIPS 2012) > * Projection-free Online Learning (ICML 2012) > * Linear Regression with Limited Observation (ICML 2012) イスラエル工科大学の准教授。 機械学習や最適化の根本的な問題に対して効率的なアルゴリズムを研究している。 特に理論よりの機械学習トップカンファレンスであるCOLTにも論文を通しており、イスラエル工科大学の響きと相まってなんかもうすごい印象。 ##Victor Gabillon http://chercheurs.lille.inria.fr/~gabillon/ > * Multi-Bandit Best Arm Identification (NIPS 2011) > * Classification-based Policy Iteration with a Critic (ICML 2011) > * Best Arm Identification: A Unified Approach to Fixed Budget and Fixed Confidence (NIPS 2012) > * Adaptive Submodular Maximization in Bandit Setting (NIPS 2013) > * Approximate Dynamic Programming Finally Performs Well in the Game of Tetris (NIPS 2013) 本ランキング初のPh. Dの学生(!) 強化学習を専門とする。前述のINRIAでも研究しているらしい。 該当NIPSの論文を見てもバンディットアルゴリズムに関連するものがほとんどである。 ##Richard Socher http://www.socher.org/ > * Unfolding Recursive Autoencoders for Paraphrase Detection (NIPS 2011) > * Parsing Natural Scenes and Natural Language with Recursive Neural Networks (ICML 2011) > * Recursive Deep Learning on 3D Point Clouds (NIPS 2012) > * Reasoning With Neural Tensor Networks for Knowledge Base Completion (NIPS 2013) > * Zero-Shot Learning Through Cross-Modal Transfer (NIPS 2013) お待たせしました、Richard Socherです。 現在StanfordのPh. Dコースに在籍(!) 自然言語処理界では知らぬものはおそらくいない、Chris Manning, Andrew Ngらの研究グループ。Deep learningを自然言語処理へと応用した第一人者の一人。 ACLやEMNLPなど自然言語処理のトップカンファレンスにも論文を通しまくっていて、プロフィール写真を見てもどこかサイヤ人っぽい。 ##Eunho Yang http://www.cs.utexas.edu/~eunho/ On the Use of Variational Inference for Learning Discrete Graphical Models (ICML 2011) Graphical Models via Generalized Linear Models (NIPS 2012) Conditional Random Fields via Univariate Exponential Families (NIPS 2013) Dirty Statistical Models (NIPS 2013) On Poisson Graphical Models (NIPS 2013) テキサス大学のPh. Dの学生(!) グラフィカルモデルに関する論文が目立つ。 #最後に バンディットアルゴリズム関連の強化学習の研究は割りとホットそうな印象を受ける。 前にQiitaでも[A/Bテストよりすごい?バンディットアルゴリズムとは一体何者か](http://qiita.com/yuku_t/items/6844aac6008911401b19)のように話題になっていたけど、そのときはA/Bテストの文脈で出てきていたためかなんとなくスルーしていた。 しかし今回のランキングでもバンディットアルゴリズム関連の研究はそこそこあって、しかもつい先日某検索ポータル会社で働いている友人もバンディットアルゴリズムをプッシュしていたので、これを気に最初から勉強しておきたい。 それとやはり自然言語処理におけるDeep Learningの応用はなんだかおもしろそうで、先日このアドベントカレンダーにも投稿していた[@kiyukuta](https://twitter.com/kiyukuta)の素晴らしい発表資料があるので、これを参照しつつ、このあたりの話題にもついていけるような基礎体力を来年はつけていたい気運である。 [自然言語処理まわりのDeep Learningを自分なりにまとめてみた — KiyuHub](http://kiyukuta.github.io/2013/09/28/casualdeeplearning4nlp.html) それでは、みなさま、良いクリスマスと良いお年を。 |
|
| 715位 |
|
|||
|
12:04:14 |
(LINE corp 所属) |
|
iOS Advend Calender 2013 iOS second stage 6日目担当の[@fm_tonakai](http://twitter.com/fm_tonakai)です。
普段はWebの会社でiOSアプリの開発を行っています。 初のQiita投稿で緊張しています。 今日はIn App Purchaseのレシートのローカル検証についてちょっと調べたので書きます! はじめに --------------------------- iOS7よりSKPaymentTransaction#transactionReceiptがDepricatedになりました。 代わりにNSBundle#appStoreReceiptURLが追加され、 Transactionの処理時にこのAPIを用いてレシート情報を取得することができます。 この情報を用いて今までのようにAppleに問い合わせしなくてもローカル内で レシートの検証が行えるようになりました。 **なりましたが、簡単にできるとは言っていない!** レシートの検証について --------------------------- 公式のドキュメントとして下記のドキュメントが出ています。 **[レシート検証プログラミングガイド ](https://developer.apple.com/jp/devcenter/ios/library/documentation/ValidateAppStoreReceipt.pdf)** https://developer.apple.com/jp/devcenter/ios/library/documentation/ValidateAppStoreReceipt.pdf 内容を追っていくと、レシートのデータを証明書で検証して、 中のPayloadを取得して解析してと非常にめんどくさいことを要求されています。 また、このドキュメント自体はヒントしか書いておらず、動くコードは書かれていません。 したがって、内容を理解して自分でコードを書く必要があります。 **無理です。** なぜこんなにめんどくさいことになったのか ----------- レシート検証プログラミングガイドによると以下のとおりに書いています。 >攻撃者は、アプリケーションのバイナリにパッチを当てる、または検証コードが依存しているオペ レーティングシステムの基本的なルーチンを改変することによって、検証コードの回避を試みること があります。このようなタイプの攻撃に対して耐性を得るには、次のものも含めたさまざまなコー ディングテクニックが必要になります。 >● 暗号チェック用のコードは、システムが提供するAPIを使用せずに、インラインで処理します。 システムでAPIを提供するとそこからハックされる危険があるということだそうです。 安全なレシートの検証を行うために、自前で解析しろとのことです。 レシートの解析をする --------- レシート検証プログラミングガイドのヒントだけでレシート検証を行うには ゆとりエンジニアには厳しいです。そこで既に実装されているものを探すと以下のものがあります。 rmaddy / VerifyStoreReceiptiOS https://github.com/rmaddy/VerifyStoreReceiptiOS これを用いるには以下の2つが必要です。 OpenSSL-for-iPhone https://github.com/x2on/OpenSSL-for-iPhone Apple Root 証明書 http://www.apple.com/certificateauthority/ 1.まずOpenSSL-for-iPhoneをgit cloneしてbuild-libssl.shを実行する ```bash git clone https://github.com/x2on/OpenSSL-for-iPhone cd OpenSSL-for-iPhone ./build-libssl.sh ``` build-libssl.shを見るとOpenSSLのソースをダウンロードして armv7,armv7s,arm64などでビルドしてくれているようです。 ビルドにはしばらく時間がかかります。 完了するとlibディレクトリにlibssl.aとlibcrypto.aが作成されます。 2.Apple Root 証明書をダウンロードする 3.プロジェクトにファイルを追加&設定 + OpenSSL-for-iPhoneで生成したlibssl.aとlibcrypto.a + Apple Root証明書(AppleIncRootCertificate.cer) + VeryfyStoreReceipt.h/m + Build SettingsのHeader Search Pathsに```path/to/OpenSSL-for-iPhone/include```を追加 4.BundleIDとVersionをハードコーディングする AppDelegate.mかどこかで以下のコードを書く ```objc NSString *const global_bundleVersion = @"1.0"; NSString *const global_bundleIdentifier = @"com.example.projectname"; ``` 5.レシート検証を行う -paymentQueue: updatedTransactions:内で ```objc NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; if (verifyReceiptAtPath(receiptURL.path)) { NSDictionary *receipt = dictionaryWithAppStoreReceipt(receiptURL.path); NSLog(@"%@", receipt); } ``` これでレシート検証できるかと思います。 VerifyStoreReceiptiOSで何をしているのか ------------ 解析のAPIを共通化すると先程も述べたとおりハックされる危険があります。 実際VerifyStoreReceiptiOSを入れてビルドすると このコードを使うとハックされる危険があるので 自分で実装することをおすすめするみたいな警告が出ます。 なので勉強のためコードの中身を読んでいくことにします。 必要そうな知識 ------------ とりあえず自分はPKCS#7とASN1が謎だったのでとりあえずググってみました。 ・PKCS#7 http://eternalwindows.jp/crypto/pkcs7/pkcs701.html 暗号化データや署名データを証明書と共に格納できる形式。RFC2315に詳細が記述されている。 とりあえずレシートデータと証明書を一緒に入れられる形式のデータだよという認識 ・ASN1 http://e-words.jp/w/ASN2E1.html データの構造を定義する言語の一つで、主にコンピュータ間の通信プロトコルを規定するために使われる。ASN.1自体はプロトコルの「ひな型」にあたる。 要はクラス定義のようなものか。 レシート内部の構造としてはこう図解されています。  全体がPKCS#7でレシートデータの入っているpayloadの部分がASN1で定義されたフォーマットで 入っているということでしょう。 実際に読んでみる ------------ レシート検証プログラミングガイドでは検証の手順として以下のようになっています。 > 1. レシートの位置を特定します。 > レシートが存在しない場合、検証は失敗します。 > 2. レシートがAppleによって適切に署名されていることを確認します。 > Appleによる署名がない場合、検証は失敗します。 > 3. レシートにあるバンドルIDが、Info.plistファイルにあると予想されるハードコーディングされた定数CFBundleIdentifierの値と一致することを確認します。 > 一致しない場合、検証は失敗します。 > 4. レシートにあるバージョン文字列が、Info.plistファイルにあると予想されるハードコーディングされたCFBundleShortVersionString定数の値と一致することを確認します。 > 一致しない場合、検証は失敗します。 > 5. “GUIDのハッシュ値の計算” (9 ページ)の説明に従って、GUIDのハッシュ値を計算します。 > 計算結果がレシートにあるハッシュ値と一致しない場合、検証は失敗します。 >すべてのテストに合格すると、検証は成功です。 **1. レシートの特定** これは簡単です。単純にappStoreReceiptURLの値がnilなら終了です。 **2.署名の確認** ここから本格的に検証が始まります。 検証はdictionaryWithAppStoreReceipt関数で行われています。 350行目までが署名の確認のコードのようです。 この辺のことについてはレシート検証プログラミングガイドの検証のヒントとほぼ同じです。 レシートデータをPKCS7として、AppleRoot証明書をX509で読み込み PKCS7_verify関数で検証しています。 この時最初にOpenSSL_add_all_digests();関数を呼び出さないと検証が失敗してしまうようです。 http://ataugeron.github.io/blog/blog/2013/09/23/app-store-receipt-validation-on-ios-7/ **3.4. バンドルIDとバージョン番号のハードコーディングした値がレシートと一致しているか確認する。** これはverifyReceiptAtPath関数の536行目からの最後のチェックです。(GUIDのチェックも行っています) この段階でレシートの内容を解析している必要があります。 バージョン番号なんかは毎回手で修正しないといけないのでしょうか。。。。 ハードコーディングする理由に関してはコメントにURLが書いています。 http://www.craftymind.com/2011/01/06/mac-app-store-hacked-how-developers-can-better-protect-themselves/ Info.plistの中身なんか簡単に書き換えられるから、そこから値を持ってくるのは危ないということのようです。 5.GUIDのハッシュ値の計算 GUIDのハッシュ値の計算はレシート検証プログラミングガイドでこのように書いています。 > GUIDのハッシュ値の計算 > OS Xでは、“OS XでのGUIDの取得” (13ページ)で説明された方法を使用してGUIDをフェッチします。 > iOSでは、UIDeviceのidentifierForVendorプロパティによって返された値を使用してGUIDを計算します。 > ハッシュ値を計算するには、最初にGUID値をオペーク値(タイプ4の属性)とバンドルIDに連結します。UTF-8文字列の解釈や正規化を一切行わずにレシートの生のバイトを使用します。次に連結されたこの一連のバイトに対してSHA-1のハッシュ値を計算します。 さらっと書かれています。。。。 GUIDのハッシュ値の計算は524行目から行っています。 iOSの場合はidentifierForVendorの値を使います。 ```objc unsigned char uuidBytes[16]; NSUUID *vendorUUID = [[UIDevice currentDevice] identifierForVendor]; [vendorUUID getUUIDBytes:uuidBytes]; NSMutableData *input = [NSMutableData data]; [input appendBytes:uuidBytes length:sizeof(uuidBytes)]; [input appendData:[receipt objectForKey:kReceiptOpaqueValue]]; [input appendData:[receipt objectForKey:kReceiptBundleIdentifierData]]; NSMutableData *hash = [NSMutableData dataWithLength:SHA_DIGEST_LENGTH]; SHA1([input bytes], [input length], [hash mutableBytes]); ``` レシート情報の解析 ------------ 署名の確認が終わった段階でペイロードをNSDictionaryに書き換える処理を 351行目から467行目までで行われています。 レシート検証プログラミングガイドではasn1cを使った生成コードを用いての解析の **"ヒント"** が書かれていますがこのコードでは直接解析をしています。 ```c //350行目 ASN1_OCTET_STRING *octets = p7->d.sign->contents->d.data; const uint8_t *p = octets->data; ``` ここでpayloadの始まりのポインタが取れるようです。 それからASN1_get_objectという関数で順繰り値を取っていくという感じになってます。 コードと上記図を見比べるとなんとなく何やってるかがわかる気がします。 解析に必要な情報はレシート検証プログラミングガイドの後半にレシートのフィールドと言う項目でまとめて書いてあります。 おわりに(結局自分でやってないけど) ------------ というわけで、VeryfyReceiptForiOSのコードを見てローカルレシート検証について確認しましたがこれを自力でやるにはなかなか酷な感じです。 とりあえず警告どおりそのまま使わず、関数を分割したり名前を変更したり等このコードを編集する形で利用すればいいのかなと思いました。(小並感) |
|
| 716位 |
|
|||
|
01:13:10 |
|
|
[連番の空ファイルを生成するワンライナー](http://qiita.com/stc1988/items/dd4420897d063028077b)でファイルを作成したものの、
ウン十万・ウン百万個以上のファイルを作成するとlsするだけでも時間がかかる。 ## 1. lsを使ってカウント ```bash $ ls -1 | wc -l ```` -1オプションで1行に1ファイル表示させ、`wc -l`で行数をカウントする 行数でなく単語数(ファイル数)でカウントしてもよい ```bash $ ls | wc -w ``` ## 2. findを使ってカウント ```bash $ find . -type f | wc -l ``` ## カウント速度を比較してみる 10万ファイルを作成し比較 > $ seq -f "file_%06g" 100000 | xargs touch $ time ls -1 | wc -l 100000 > real 0m0.357s user 0m0.320s sys 0m0.044s > $ time find . -type f |wc -l 100000 > real 0m0.162s user 0m0.020s sys 0m0.152s ## lsの方が遅いのか? lsはデフォルトでソートを実施するので時間がかかる。 そこで、`-U`オプションを用いるとソートを実施しないようにする ```bash $ ls -U1 | wc -l ``` カウント速度を計測 > $ time ls -U1 | wc -l 100000 > real 0m0.068s user 0m0.020s sys 0m0.052s findよりも早くなりました。 __(※13/10/02 追記)__ lsでサブディレクトリを考慮して厳密にカウントするなら、下記のようにしたほうがよいとコメントを頂きました。 ```bash ls -F | grep -v / | wc -l ``` |
|
| 717位 |
|
|||
|
19:55:08 |
(フリーランス 所属) |
|
本当はrails_adminをカスタマイズするより、自前で実装した方が最終的には幸せになれると思うんですが、そうもいかない事情もしばしばあったりして(主にスケジュール的な理由で)、後から泥臭い場当たり的な対処に奔走しなければならないこともあります。
そういう時のためのノウハウが貯まったので書いておきたいと思います。 ## deviseと連携させる これは公式にもちゃんと書いてて、超簡単。 ```rb:config/initializers/rails_admin.rb # 略 config.current_user_method { current_administrator } # 略 ``` ## テーブルごとにアクセス権限を限定する [cancan](https://github.com/ryanb/cancan "ryanb/cancan")と連携できます。まだまだ楽勝。 ```rb:config/initializers/rails_admin.rb # 略 config.authorize_with :cancan # or config.authorize_with :cancan, AdminAbility # 略 ``` ```rb:app/models/ability.rb class Ability include CanCan::Ability def initialize(model) if model.super_admin? can :manage, :all else can :manage, [NormalModel] end end end ``` 権限カラムを用意するなりRoleクラスを作って関連付けるなりして権限設定。 ## cssを上書きする Rails Engineなので、探索パスの前の方にscssを置いてやればそっちを読みにいく。 一番簡単なのは、`app/assets/stylesheets/rails_admin/custom/theming.css.scss`を作成して、そこに記述する。 既存の設定に影響を与えず、rails\_adminの一番後ろに追加されるので上書きしやすい。 がっつり書き換えたい時は、公式のwiki見てテーマを作ることになる。 ## 既存のdashboardを潰して、独自のトップ画面を表示する 同じく探索パスの前の方に配置すれば、テンプレートの差し替えが可能。 kaminariと同じ感じでカスタマイズが出来る。 `app/views/rails_admin/dashboard.html.haml`を作成して、そこに記述する。 ## フィールドレベルでアクセス権限を限定する この辺りから辛さが増してきますw とりあえず、表示だけでも隠す方法 ```rb:config/initializers/rails_admin.rb config.model 'SampleModel' do field :super_admin_only_column do visible do if bindings[:controller].try(:current_administrator).try(:super_admin?) true else false end end end end ``` 同種の設定が増えてきたら、`visible`のブロックを全部lambdaで囲んでしまって、フィールドの設定で`instance_eval`すれば多少すっきりします。 ## 入力フォームの下に出てる「必須」とか「オプション」とかの注意書きを変更する 簡単だけど、何弄れば変わるのか調べづらい。 ```rb:config/initializers/rails_admin.rb config.model 'SampleModel' do field :name do help "必須, 名前を入れてください" end end ``` ## サイドバーのナビゲーションメニューの並べ替え weightというパラメーターの小さい順に上から並ぶ。 ``` config.model 'SampleModelFirst' do weight 1 end config.model 'SampleModelSecond' do weight 2 end ``` これもモデル数が増えてくると面倒なので、`lambda`と`instance_eval`で工夫する。 ## カスタムアクションを定義する 公式ではgem作れ、みたいに書いてあるけど、別に普通にlib以下に書けば問題ありません。 そんな再利用性のあるカスタマイズしないしw ```lib/custom_action.rb require "rails_admin/config/actions" require "rails_admin/config/actions/base" module RailsAdmin module Config module Actions class CustomAction < RailsAdmin::Config::Actions::Base # アクションの登録 RailsAdmin::Config::Actions.register(self) # 編集とか削除とかのリンクと同じ箇所に追加する register_instance_option :member? do true end # 表示するかどうかの設定。特定のクラスだけ出すように設定する等。 register_instance_option :visible? do if bindings[:abstract_model].model == TargetModel true else false end end # font_awesomeのアイコン名。ここで指定したアイコンが勝手に表示される。 register_instance_option :link_icon do "icon-eye-open " end # pjaxを利用して画面遷移をするかどうか register_instance_option :pjax? do false end # controllerの実際の処理 # 実際には`(rails_adminのgemディレクトリ)/app/controllers/rails_admin/main_controller.rb`でclass_evalによって定義されたメソッドの中で、instance_evalされる事によって実行される register_instance_option :controller do Proc.new do case @object when Targetmodel redirect_to main_app.root_path else raise "object is not a TargetModel" end end end end end end end ``` rails_adminの設定ファイルにも追加が必要 ```rb:config/initializers/rails_admin.rb require 'lib/custom_action' # 略 config.actions do # root actions dashboard do statistics false end # collection actions index new export bulk_delete # member actions show edit delete show_in_app custom_action # クラス名をアンダースコア形式で追加する end # 略 ``` 実際にコントローラー内でどういう値が利用できるか、利用できるオプション設定は、ソースを読んで確認した方が良い。 以下のコードが参考になる。 - rails_admin/app/controllers/rails_admin/main_controller.rb - rails_admin/app/controllers/rails_admin/application_controller.rb - rails_admin/lib/rails_admin/config/actions/base.rb - rails_admin/lib/rails_admin/config/actions/show.rb - rails_admin/app/views/rails_admin/main/show.html.haml ## belongs_to関連のselectフォームのカスタマイズ (簡易版) 段々辛くなってきたぜ。 fieldの設定で、ある程度スコープを弄ることができます。 ```rb:config/initializers/rails_admin.rb config.model 'SampleModel' do field :parent_model do associated_collection_cache_all false # 事前にモデルを読み込まなくなる associated_collection_scope do sample_model = bindings[:object] Proc.new { |scope| # scoping all ParentModel scope = scope.limit(100) # 上限を増やす } end end end ``` 実際、これは何をしているかというと、ParentModelに対するrails\_adminのindexアクションを呼び出しています。 その結果を元にselectフィールドを更新しています。 indexアクションでは、main\_controllerに定義されている`list_entries`を呼び出してモデルを取得しています。 `list_entries`は基本的なスコープを構築します。 この時、さっき指定したassociated_collection\_scopeを構築するためのProcがinstance\_evalされてスコープが追加されます。 ただ、厄介な事にこの後に実際のデータを取得する`get_collection`という処理が入ります。 ここで入力パラメーターを元にソートするスコープが追加されるので、上記の設定でorder指定しても上手く動作してくれなかったりします。 面倒だったので、私はこの問題はスルーしました。 ここでは以下のコードが参考になります。 - rails_admin/app/controllers/rails_admin/main_controller.rb - rails_admin/app/controllers/rails_admin/application_controller.rb - rails_admin/lib/rails_admin/config/actions/index.rb - rails_admin/lib/rails_admin/config/fields/association.rb - rails_admin/lib/rails_admin/config/fields/types/belongs_to_association.rb この辺のコード読んでると、MVCとは何だったのか、みたいな気分になりますが仕方ないので諦めます。 ## belongs_to関連のselectフォームのカスタマイズ (カスタムフィールド版) カスタムフィールドと自前のコントローラーを組み合わせれば結構自由にカスタマイズが聞きます。 例えば、二つのモデルをまとめて検索して一つのセレクトボックスで表示するとか。 belongs_toの検索は`rails_admin/app/assets/javascripts/rails_admin/ra.filterling-select.js`によって実現されています。 実際の利用方法は`rails_admin/app/views/rails_admin/main/_form_filtering_select.html.haml`が参考になります。 このテンプレートロジック入りまくってるんですが、仕方ないですよねー(棒 読んでると辛くなってきます。 どうもrails_adminを拡張するポイントとしては、viewから色々キックするのが楽っぽいです。非常に不幸になりそうなんですが…。 このfilterling-selectを丸々自前で書いても良いのですが、流石に面倒なのでこれを活用します。 大エリアと中エリアがあってそれをまとめて設定するみたいなフォームを作る例で説明していきます。 まずは、フォームのビューを書きます。 メイン部分は`rails_admin/app/views/rails_admin/main/_form_filtering_select.html.haml`からパクってきます。 ```rb:app/views/rails_admin/main/_form_area_select.html.haml :ruby selected_id = form.object.new_record? ? nil : form.object.area_ids if selected_id large_and_middle_area = LargeAndMiddleArea.find_by_ids(selected_id) collection = large_and_middle_area ? [[large_and_middle_area.label, large_and_middle_area.id]] : [] else collection = [] end current_action = params[:action].in?(['create', 'new']) ? 'create' : 'update' js_option = { xhr: true, remote_source: main_app.admin_large_and_middle_areas_path, current_action: current_action, } = form.select field.method_name, collection, { selected: selected_id, include_blank: true }, field.html_attributes.reverse_merge({ data: { filteringselect: true, options: js_option.to_json }, placeholder: t('admin.misc.search') }) ``` ビューから思いっきりモデルのfindとか走ってますがスルーしてください。 他に良いやり方が思いつかないので、泣きながらやるしかありませんでした。 rails_adminに限っては、MVCなんてものは存在しません。 js_optionのremote_sourceに自前で用意するコントローラーへのパスを書いておきます。 rails_adminから呼ばれるので、`main_app`を忘れないように。 後は、rails_admin本体のコードを参考にフォームヘルパーを書けばオッケーです。 続いて、カスタムフィールドの定義が必要です。 ここのpartialオプションが非常に重要です。 ```rb:lib/rails_admin_area_select.rb module RailsAdmin module Config module Fields class AreaSelect < Base # フィールドタイプの登録 RailsAdmin::Config::Fields::Types::register(:area_select, self) # モデルに登録されている値から、表示用に整形した値を返す # 一覧画面に表示される register_instance_option :pretty_value do LargeAndMiddleArea.find_by_ids(value).try(:label) end # パラメーターによってソートできるかどうか register_instance_option :sortable do false end # テキスト入力によって関連モデルを検索できるかどうか register_instance_option :searchable do true end # このフィールドが表示するテンプレート # さっき作成した自前のテンプレートを指定する register_instance_option :partial do :form_area_select end register_instance_option :inline_add do false end register_instance_option :inline_edit do false end # ビューから参照するために自分で定義したメソッド def selected_id bindings[:object].send(:area_ids) end # ビューから参照するために自分で定義したメソッド def method_name :area_ids end # ビューから参照するために自分で定義したメソッド def multiple? false end end end end end ``` fieldのタイプが定義できたので、rails_adminの設定を書き換えます。 ```rb:config/initializers/rails_admin.rb require 'lib/rails_admin_area_select' # 略 config.model SampleModel do field :area_ids, :area_select end # 略 ``` 最後に、jsonを返すコントローラーを書きます。 ra.filterling-select.jsは、idとlabelという属性を持ったjsonの配列を返す事で動作します。 ```rb:app/controllers/admin/large_and_middle_areas_controller.rb class Admin::LargeAndMiddleAreasController < ApplicationController def index @large_and_middle_areas = LargeAndMiddleArea.find_all_by_name(params[:query]) end end ``` ```rb:app/views/admin/large_and_middle_areas/index.json.jbuilder json.array! @large_and_middle_areas do |large_and_middle_area| json.extract! large_and_middle_area, :id, :label end ``` これで、自前のコントローラーに対して検索処理をかけて関連モデルを選択し設定する事ができます。 今回の例に限らず、裏側でsolrで検索させる事も可能です。 かなり辛いので、こんなことする羽目になる前に、自前の管理画面をちゃんと実装した方が良いと思います。 今あるコードを捨てる勇気が欲しい…。 というわけで、ちょっとしたカスタマイズからカスタムアクション、カスタムフィールドの定義方法まで書きました。 なんかもう色々辛いし不毛なので必要にならない方が良いのですが…。 |
|
| 718位 |
|
|||
|
12:23:00 |
|
|
# はじめに Windows 7 で Vim 環境を構築した時の手順をまとめた。 GitHub で管理している .vimrc の導入方法についても書いた。 # Vim の導入 以下のページで配布されている Vim をダウンロード。 [Vim — KaoriYa](http://www.kaoriya.net/software/vim/) ダウンロードしたファイルを解凍し、 vim.exe を実行すれば Vim が起動する。 # 設定方法 Vim の設定は、 Linux の場合と同様にホームディレクトリ配下の .vimrc に書けば良い。 ## GitHub で管理している .vimrc の導入方法 まずは、 Windows PowerShell を管理者として実行する。 そして、以下のようなコマンドを実行し、 .vimrc へのシンボリックリンクを $HOME ディレクトリ配下に作成する。 PS C:\Users\hide> cmd /c mklink .vimrc "C:\Users\hide\dotfiles\.vimrc" また、 NeoBundle が必要であれば、以下のようなコマンドを実行し neobundle.vim へのシンボリックリンクも作成しておく。 PS C:\Users\hide> mkdir .vim PS C:\Users\hide> mkdir .vim/bundle PS C:\Users\hide> cmd /c mklink /D ".vim/bundle/neobundle.vim" "C:\Users\hide\dotfiles\neobundle.vim" # 参考 * [Vim — KaoriYa](http://www.kaoriya.net/software/vim/) * Windows 用の Vim を配布している * vim.org 等で配布されるオリジナルの Vim に、日本語を扱う上で便利な設定やスクリプトが追加されている * [vimrc, runtime の優先順位を理解して Windows と Linux で Vim の設定を共有する - teppeis blog](http://teppeis.hatenablog.com/entry/20080705/1215262928) * Vim の設定ファイルについて分かりやすく説明している * [Vimの.vimrcと.vimフォルダをDropboxを使ってWin/Macで共有する - アインシュタインの電話番号☎](http://blog.ruedap.com/entry/20110301/vim_vimrc_vimperator_dropbox_windows_mac_share) * Windows でのシンボリックリンクの作成方法について書いてある |
|
| 719位 |
|
|||
|
01:53:44 |
|
|
IRCクライアント、皆さんは何を使われてますか? 私は今まで mac上で [limechat](http://limechat.net/mac/ja.html) や [textual](http://www.codeux.com/textual/) といった GUI クライアントを使っていましたが、最近社内のイケてる同僚エンジニアより勧められた weechat を導入したところ色々と捗ったので、私の設定をご紹介しつつオススメしたいと思います。 ## [weechat](http://weechat.org/) とは? - モジュール式: 軽量のコアと選べるプラグイン - multi-protocols architecture (mainly IRC) - C 言語、Python、Perl、Ruby、Lua、Tcl、Scheme で拡張可能 - 詳しく文書化され、数種類の言語に翻訳されている - GPLv3 ライセンスのもとにリリースされるフリーソフトウェア - スクリプトを作成する大きなコミュニティを持つ活発なプロジェクト (本家サイトより転載) --- weechat は主に IRC用として使う CUI のチャットクライアントで、軽量かつ拡張性の高さが特徴の様です。 開発も活発な様で、2014/3/27現在 `ver0.4.3` がリリースされています。 とりあえず入れてみましょう! ## install ``` brew install weechat --with-ruby --with-python --with-perl ``` mac は brew で入ります。各言語で書かれたプラグインスクリプトを使いたいので、オプションを付けています。 正常にインストールできたら ``` weechat ``` で起動します。 ※ `weechat-curses` でも起動します(エイリアス)。  これが初期画面。味気ない画面ですが、これからカスタマイズしていきます。 今から書いて行く設定を適用していくと、最終的に  こんな感じの画面になります。 (私は iTerm2 + tmux 上で利用しています) ## サーバ設定 まずはサーバを設定します。 ``` /server add hoge irc.example.com/6667 ``` `hoge` 部分に任意のサーバ名を入れ、ホストを適宜入力してください。 必要であれば ``` -ssl -autoconnect -password=mypassword ``` といったオプションがありますので、付与してください。 ### 設定したサーバへの接続、切断 ``` # 接続 /connect hoge # 切断 /disconnect hoge ``` ### チャンネル接続、切断 ``` # 接続 /join #hoge # 切断 /close #hoge ``` ### チャンネル間の移動 weechat では、各チャンネル(に限らず1つの画面)を `buffer` と呼びます。 ``` # buffer の移動 /buffer #hoge ``` ### 設定を保存する 今から設定する内容は、保存しないと終了時に消えてしまいます。 設定をいじった場合は終了前に save しましょう。 ``` /save ``` ### weechat を終了する ``` /exit ``` ## チャンネルリストを表示する 現状ですと、自分がどのチャンネルを設定しているか等分かりにくいです。 現在自分が設定しているチャンネル(buffer)リストを表示するプラグインを入れます。 ``` /script install buffers ``` `/script install hoge` でプラグインスクリプトをインストールする事ができます。 この `buffers` というプラグインを入れると、先ほどのキャプチャ左端の様に設定したチャンネル一覧が常に表示される様になります。 `Alt + ↑ or ↓` で buffer の移動ができます。 ## チャンネルを素早く移動できるようにする ちまちまと `/buffer #hoge` と入力してチャンネルを移動するのは辛いものがありますので、便利プラグインを入れます。 ``` /script install go ``` この `go` というプラグインを入れると、 ``` /go ``` と入力してエンターを押した後、チャンネル名をタイプしていく毎にインクリメンタルサーチしてくれます。 `/go` の後にチャンネル名を入力しても移動できます。 ### キーバインドを設定する 更に以下の様に設定しておくと ``` /key bind ctrl-G /go ``` `ctrl + g` で go を起動できて捗ります。 ## ウィンドウを分割する ``` # 水平分割 /window splith ``` ``` # 垂直分割 /window splitv ``` 引数として数値を渡せば、`/window splith 60` など割合を指定して分割する事もできます。 先ほどのキャプチャでは4画面に分割しています。 ### ウィンドウを統合する ``` /window merge ``` で分割したウィンドウを統合する(元に戻す)事ができます。 オプションもいくつかあり、 ``` # 全部統合する /window merge all ``` ``` # ウィンドウ番号を指定して統合する /window merge -window 1 ``` 等があります。 ### ウィンドウ間を移動する ``` # 引数として up down left right を与えると、その方向へ移動できる /window {up|down|left|right} ``` ただこれだといちいちコマンド入力が面倒なので、ショートカットを登録します。 ``` /key bind ctrl-H /window left /key bind ctrl-J /window down /key bind ctrl-K /window up /key bind ctrl-L /window right ``` ここでは vim のキーバインドに合わせていますが、各位お好きな様に設定すると良いのではないでしょうか。 なお `HJKL` 部分は大文字で設定しますが、実際のキー入力は大文字にしなくても大丈夫です。 ### 入力バーを一カ所にまとめる ウィンドウを分割すると、各ウィンドウに入力バーが存在します。これがうざい感じなので、画面下部に1つ入力バーがある状態にします。 ``` /bar add rootinput root bottom 1 0 [buffer_name]+[input_prompt]+(away),[input_search],[input_paste],input_text ``` とした後 ``` /bar del input ``` でOKです。 もとに戻したい場合は ``` /bar del rootinput ``` と入力します。 ### アクティブなウィンドウだけハイライトする 現状だと、分割した全てのウィンドウがハイライトされている状態です。好みもあるかと思いますが、私の場合今どこのウィンドウを見ているかを忘れて誤爆する事がありますので、見ている(アクティブ)ウィンドウ以外をグレーアウトする設定にしています。 ``` /set weechat.look.color_inactive_window on ``` ### レイアウト設定を保存する ``` /layout store {name} ``` で現在のウィンドウとバッファを保存します。名前を付けて複数保存する事ができ、指定しない場合は `default` として保存されます。 また、保存した設定は ``` /layout apply {name} ``` で呼び出せます。 ## 全チャンネルの発言をモニタするバッファを追加する limechat 等の一般的な IRCクライアントには全チャンネルのタイムラインが表示されるウィンドウがあります。これも追加できます。 ``` /script install chanmon ``` これを入れると `chanmon` バッファが生成されますので、ウィンドウ分割して表示させとくと便利です。 ## ハイライト、通知周り キーワードハイライトを設定し、通知を行えなければ話しになりませんね。 ``` /set weechat.look.highlight "$nick,hoge,fuga,piyo" # 正規表現指定 /set weechat.look.highlight_regex .*\bこんにちは\b.* ``` さらに ``` /script install highmon ``` これを入れると、ハイライトを含む発言のみを集めた `highmon` バッファが作成されます。 [こちらのページ](http://phyks.me/2013/12/highmon_weechat.html) に書いてある設定を行うと、ハイライトを含む発言が入力バーの下にどんどん表示されていくのでチェック漏れが無くなって良いです。チェックしたら ``` /clear_highmon ``` でバッファをクリアできます。 これもキーバインド設定しとくと捗りますよ。 ``` /key bind ctrl-C /clear_highmon ``` これで `ctrl + c` で消去できます。 (再起動時に一気に再出力されるのがうざいですが、対策検討中..) ### Growl通知 mac の `notification_center` への通知プラグインもありますが、現状 Growl の方がデザイン等好みの設定をする事ができますのでこちらを利用しています。 利用する為には [gntp](https://github.com/kfdm/gntp/) という python のライブラリが必要なので、入れておきます。 ※ pip で入れるので、pipが無い場合は先にコンソールから `easy_install pip` で入れておきます。 ``` # コンソール上にて sudo pip install gntp ``` ``` # weechat入力バーにて /script install growl ``` Growl 環境設定のアプリケーション欄に WeeChat の表示が出ていればインストール成功です。 ## その他の設定 ### ログレベルの設定 ログはデフォルトだと何でも記録してしまい膨大な量になるので、絞っておいた方が良いです。 ``` 0: 記録しない 1: ユーザからのメッセージ (チャンネルまたはプライベート) 2: ニックネームの変更 (自身と他のユーザ) 3: 任意のサーバメッセージ (参加/退出/終了メッセージを除く) 4: 参加/退出/終了メッセージ 9: すべて記録する /set logger.level.irc 2 ``` ### join / part / quit を表示しない ``` /filter add irc_smart * irc_smart_filter * ``` ### URL をクリックで開く(iTerm) iTerm2 の `Preferences` > `Pointer` メニューの下部にある、`⌘-Click Opens Filename/URL` にチェックを入れる事で、`Command + Option + Click` で URL をブラウザで開く事ができます。  ## その他便利なプラグイン `/script` でインストールできるスクリプトプラグインの一覧を表示し(ここに表示されていないプラグインも沢山ある様です)、インストール等の操作が可能です。  左から、 `i` がインストール済み `a` が autoload 設定済み `r` が running 状態である 事を意味しています。 カーソルを移動して選択した状態でキー操作する事で、様々な操作ができます。(例えば `/script install hoge` 等と入力しなくても、選択して `i` をタイプすればインストールされる) ### プラグインインストール時に注意する事 エラーが発生した場合等に気づける様に、`weechat` バッファを表示しておくのをお勧めします。このバッファには各操作のログや、`/help hogehoge` を入力した際の出力内容が流れます。  上は `/help chanmon` を実行した結果 例えばプラグインのインストールに成功したが、必要なライブラリが不足していた為起動に失敗した場合等は `i` `a` は表示されているが `r` が表示されていない状態になるので、そういった時に `weechat` バッファでエラー状況を確認します。 ### 発言者ごとにテキストを色分けする ``` /script install colorize_lines ``` を入れると  みたいに発言者ごとに色分けされます。 ### grep検索 ``` /script install grep ``` バッファとログファイルから正規表現で grep 検索できるようになります。 ### url hinter http://tkengo.github.io/blog/2014/04/15/introduction-weechat-url-hinter/ こちらで紹介されている `url_hinter` プラグインを導入すると、マウスを使用せずに URL をブラウザで開く事ができ、とても便利です。 ※ 4/16現在公式プラグインとして申請中との事です ### url短縮 ``` /script install shortenurl ``` isgd または tinyurl で URL を短縮してくれます。 ログファイルが膨大な場合に weechat を再起動すると、リクエスト飛ばしまくって起動に物凄く時間かかりますので、このプラグインは autoload 設定しない方が良さそうです。 ### urlのみ集める ``` /script install urlbuf ``` 発言から URL のみを集めた `urlbuf` バッファが作られます。 これもログファイルが膨大な場合の再起動時に時間がかかる要因となるので、 autoload 設定しない方が良さげ。 ### 発言をカラフルにする ``` /script install prism ``` このプラグインを入れると発言にランダムなレインボーカラーを付与する事ができます。以下の様な種類があります。 ``` /prism hoge ```  ``` /prism -e hoge ```  `eye-destroying colors` モードw ``` /prism -b hoge ```  テキストのリバースもできます。無駄に楽しい。 ### マインスイーパーがやりたい ``` /script install minesweeper ``` ## 困っている事 - 画像URLが貼られた時にプレビューを表示したい ## その他 ### ユーザーズガイド weechat は [ユーザーズガイド](http://www.weechat.org/files/doc/stable/weechat_user.ja.html) が日本語で詳細に書かれている為、これを読めば大抵の事は解決すると思います。 ### スクリプトプラグイン制作 様々な言語でスクリプトプラグインを作る事が出来るので、自分で書いてみるのも楽しそうです。 [スクリプト制作ガイド](http://www.weechat.org/files/doc/stable/weechat_scripting.ja.html) も日本語で読めるので便利! ### 参考サイト - http://blog.tnmt.info/2012/02/08/weechat-growl-for-windows/ - http://blog.glidenote.com/blog/2012/02/11/weechat-plugins/ - http://inputxoutput.com/howto-weechat/ - https://gist.github.com/studio3104/6899792 ## まとめ CUI チャットクライアント weechat の導入から便利な設定までをご紹介しました。 Limechat 等の GUI クライアントに比べると初期設定など手がかかる部分もありますが、自分用にカスタマイズしていく楽しさもあり、ただの IRC クライアントでは無く環境の様な感じで使えるので大変気に入ってます。 もっと便利な使い方、プラグイン情報などありましたらコメント頂けると有り難いです! 良い IRC 生活を :) |
|
| 720位 |
|
|||
|
17:17:20 |
|
|
リモートから特定のブランチを指定してcloneする方法
``` git clone -b ブランチ名 https://リポジトリのアドレス ``` |
|
| 721位 |
|
|||
|
18:56:09 |
|
|
## はじめに
久々にjavascriptを弄ることになり、おさらいで各種書籍を読み直しています。 この際自分の中での暗黙知を明文化しておきたいと思い、しっくり来ているところだけピックアップしてみました。 (ほぼ自己メモです。) ### for-inループ + プロトタイプ連鎖からきたプロパティを除外するためhasOwnPropertyを使う ```js:pattern00.js // for-inループ var man = { hands: 2, legs: 2, heads: 1 }; for (var i in man) { if (man.hasOwnProperty(i)) { // フィルタ console.log(i, ":", man[i]); } } ``` ### forループ + myarray.lengthに対してのキャッシュ(ループ毎に問い合わせるよりも相当速い) + 単独varパターン(var宣言は1つに、そして関数先頭で) + for末尾のカウンタインクリメントにi++は使わず、i += 1にする。(++,--の「過剰なトリック」を避けるため) ```js:pattern01.js function looper() { var i = 0, max, myarray = []; // ... for (i=0, max = myarray.length; i < max; i += 1){ // myarray[i]に対する処理 } } ``` ### switch + switchとcaseを揃える(波括弧のインデントルールの例外) + case内のコードはインデントする + caseの最後はbreak; で終わらせる + 意図的にbreakを省略して次のcaseに続けるのは避ける + switchの最後はdefault: で終わらせる ```js:pattern02.js var inspect_me = 0, result = ''; switch (inspect_me) { case 0: result = "zero"; break; case 1: result = "one;" break; default: result = "unknown"; } ``` ### インデント + 空白4個に揃える(JSLintのデフォルトなので) + インデントが空白かタブか、インデント幅は?等の話題は宗教戦争。正解は無い。(が必ず統一すること) + ifやforの中に文が1つしかなくても、波括弧は省略しない(文1カッコ無しだと、後々うっかり追加でインデント狂う副作用があるため) ```js:pattern03.js function outer(a, b) { var c = 1, d = 2, inner; if (a > b) { inner = function() { return { r : c - d }; }; } else { inner = function() { return { r : c + d }; }; } } ``` ### 命名規則 + コンストラクタの頭文字は大文字 ` var adam = new Person();` ` function MyConstructor() {...}` + 関数名はキャメルケース ` myFunction()` + 変数名は小文字アンダースコア区切り(関数名と見分ける事ができるため) ` var favorite_bands = "hoge";` + 定数は大文字、単語区切りはアンダースコア ` var MAX_WIDTH = 800;` ### コメント + 習慣にするのが難しいけれど最も重要なのは、コメントを最新に保つこと (古くなったコメントは誤解を生み、コメントが無い方がよっぽどましな場合もある) + 以下はJSDocの例 ```js:pattern04.js // JSDoc記法 /** * 文字列を反転させる * * @param {String} 反転させたい文字列 * @return {String} 反転された文字列 */ var reverse = function(input) { // ... return output; } ``` + YUIDocのデモは[こちら](http://jspatterns.com/book/2/) 以下にも転記 ```js:pattern05.js /** * My JavaScript application * * @module myapp */ var MYAPP = {}; /** * A math utility * @namespace MYAPP * @class math_stuff */ MYAPP.math_stuff = { /** * Sums two numbers * * @method sum * @param {Number} a First number * @param {Number} b The second number * @return {Number} The sum of the two inputs */ sum : function(a, b) { return a + b; }, /** * Multiplies two numbers * * @method multi * @param {Number} a First number * @param {Number} b The second number * @return {Number} The two inputs multiplied */ multi : function(a, b) { return a * b; } }; /** * Constructs Person objects * @class Person * @constructor * @namespace MYAPP * @param {String} first First name * @param {String} last Last name */ MYAPP.Person = function(first, last) { /** * Name of the person * @property first_name * @type String */ this.first_name = first; /** * Last (family) name of the person * @property last_name * @type String */ this.last_name = last; }; /** * Returns the name of the person object * * @method getName * @return {String} The name of the person */ MYAPP.Person.prototype.getName = function() { return this.first_name + ' ' + this.last_name; }; ``` ### コンストラクタ + コンストラクタは単なる関数ですが、newを使って呼び出すことができる。 + newを付け忘れて呼ぶと構文エラーにも実行時エラーにもならないが、thisがグローバルオブジェクトを指してしまう。 #### コンストラクタ関数を*new*を使って呼び出す時、関数内部では以下のようになります + 空のオブジェクトが作成され、変数thisで参照される。thisはこの関数のプロトタイプを継承する。 + thisが参照するオブジェクトにプロパティとメソッドが追加される。 + thisが参照する新しく作られたオブジェクトは、関数の最後で暗黙に返される。 ```js:pattern06.js var Person = function(name) { this.name = name; this.say = function() { return "I am " + this.name; }; }; ``` ####上記例ではthisにメソッド say()を追加しているが、メモリが無駄なのでプロトタイプに追加すべき ```js:pattern07.js Person.prototype.say = function () { return "I am " + this.name; }; ``` |
|
| 722位 |
|
|||
|
18:26:09 |
|
|
## Revealってなに?
iOS用のRuntime inspector。 要はSafariのWeb Inspectorのアプリ版のようなもの。 通常の2D表示だけでなく、3D表示も出来て多段レイヤーの重なり方も表示してくれる超スグレモノ。 Reveal http://revealapp.com/ ## インストール - Revealを起動して、[Help]-[Show Reveal Library in Finder]を選択 - [Reveal.framework]のディレクトリをXcodeの任意プロジェクトにドラッグして追加 - Xcodeプロジェクト内で[CFNetwork.framework]と[QuartzCore.framework]を追加 - XcodeプロジェクトのBuild Settings -> Linking -> Other Linker Flags に「-ObjC」を追加 ※CocoaPodsは2013-06-04現在未対応。近いうちに対応予定らしい。  ## 実行 - Xcode上からアプリを[Run] - Reveal上部のプルダウンから起動中のアプリを選択  これだけ。 今までNSLogデバッグしていたような箇所もGUIで弄くり回せるので、超捗る。 Web Inspectorと同様に、動的にラベルの文字列や高さなども変えられる。 しかもデバッグしてる実機端末上にもちゃんと反映される(!)  今までNSLogデバッグしていたような箇所もGUIで弄くり回せるので、超捗る。 Xcode本体に取り込んでくれないかなあ。 |
|
| 723位 |
|
|||
|
03:00:12 |
(Picos LLC. 所属) |
|
## 概要 2013/3/26にTwitterで縦書の俳句を投稿出来るアプリ「[俳句ったー](http://bit.ly/Haikutter)」をリリースしました。 開発のきっかけはTwitterの公式ウェブサイトが改行コードに対応しこれは**ネタアプリチャンス**だと思い作りました。 > 3/14(木) 公式ウェブサイトが改行コードに対応 3/15(金) 帰りがけに「ねぇねぇ、[@yoshiakist](http://qiita.com/users/yoshiakist)くん、デザインやらない?」と話す 3/16(土) [@yoshiakist](http://qiita.com/users/yoshiakist)「デザイン出来ましたよw」 3/17(日) 寝坊して昼くらいからコーディング 翌0:30 **Submit!!** # つれー実質8時間くらいしかコーディングしてないわー してないわー という格段に開発速度を早めているXCodeの機能を紹介します。 ## Storyboard メインのStoryboardです。主要な画面遷移になります。  図のように、ViewController同士の画面遷移と画像設定などの各Viewのプロパティが書けるので、UI周りのコードはほぼ書いてません。 一見変わった画面遷移っぽいですがビューのアニメーションの都合でこういう構成にしました。真ん中のInput View Controllerが起点となって画面遷移が行われます。 ## Xib 俳句のプレビュー画面です。  Viewのみが必要な場合はXibを使います。こちらもStoryboardと同じくUIのコードはほぼ書いてません。文字が適当に打ってあるのは体裁を調整するためです。 なぜここだけStoryboard外でXibなのかは、入力画面からプレビューへの遷移を紙をめくったようなアニメーションにしたかったからです。(アニメーションを気にしなければStoryboardだけで済みました。) ```objectivec: /* 入力画面からプレビューへの画面遷移はこんな感じのコードです */ UINib *nib = [UINib nibWithNibName:@"Preview" bundle:nil]; self.preview = [[nib instantiateWithOwner:self options:0] objectAtIndex:0]; // -- いろいろ設定などする --- [UIView transitionFromView:self.view toView:self.preview duration:0.6f options:UIViewAnimationOptionTransitionCurlUp completion:nil]; ``` nibファイルからXibで設定したUIのViewが取り出せるというイメージです。 ## ContainerView ViewControllerに子のViewControllerを持たせられる素晴らしい機能です。 上のStoryboardの画像で確認出来るように(一番左のContainer)、柔軟な画面設計が実現します。 ## その他 俳句ったーに限った話ですが、Twitterの投稿は`Social.framework`の`SLComposeViewController`に丸投げでアカウント認証やTwitterAPIを気にせず済みますので早いです。 注意したところは、縦書を全角スペースで体裁整えているので俳句の字余りによってはTwitterの投稿制限である140文字を意外とすぐ超えてしまいます。 そして`SLComposeViewController`の`setInitialText:`は140文字を超える文字を渡すことが出来ない(空の文字列になる)ので、渡す前に確認する必要があります。 ```objectivec: /* 元のコードからは改変してますがツイートの投稿画面表示はこんな感じです */ if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter] && text.length <= 140) { SLComposeViewController *vc = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter]; [vc setInitialText:text]; [self presentViewController:vc animated:YES completion:nil]; } ``` ## 参考サイト [初めての Storyboard in iOS 5 Part 1](http://www.raywenderlich.com/ja/25696/初めての-storyboard-in-ios-5-part-1) [iOS5では使えない…Xcode4.5の新機能、StoryboardでのContainer Viewが便利!](http://www.zero4racer.com/blog/942) ## まとめ - Storyboard、Xibを使用することによりUI周りのコードは9割くらい削減出来ます。レイアウトの変更も容易ですし、何よりも**UIに関することはここを見ればいい**ということが明確になるのがいろいろ捗ります。 - Sotryboard、XibでautoresizingMaskを適切に設定していれば、3.5inch、4inchの画面サイズもだいたいは対応出来ると思います。(俳句ったーの場合完全に対応出来るパターンです。) - ContainerViewを使えば更にStoryboardの自由度がアップ。 - Storyboard、Xib、ContainerViewなど新しい機能はそれなりに学習コストがかかりますが、対象のアプリが使用出来るiOSのバージョンだったら使わない理由はないと個人的には思います。 なんかUI周りの話しかしませんでしたが、それだけUI周りをコードで書くのは時間がかかるしメンテも大変だなぁって話でした。 |
|
| 724位 |
|
|||
|
12:12:09 |
|
|
gulpのタスクは基本的に非同期で実行されます。例えば以下のコードでは、ファイルコピーが完了する前に "done" と表示される可能性があります。 ```javascript gulp.task('copy', function() { // ファイルをコピー gulp.src('src/file').pipe(gulp.dest('dest')); }); // copyに依存するタスク gulp.task('done', ['copy'], function() { // ファイルコピー完了「前」に実行される!! console.log("copy done"); }); ``` gulpが高速に動作する理由の一つはこの非同期性ですが、どうしても同期的に処理したい場合(すなわち特定のタスクの完了を待ってから別なタスクを実行したい場合)もあると思います。 この記事では同期的にタスクを実行する方法として、[gulp API docs](https://github.com/gulpjs/gulp/blob/master/docs/API.md#async-task-support)に記載されているもの、そしてその発展形を紹介します。 ## タスク内の非同期処理が一つだけの場合 ### Streamをreturnする まずは1番お手軽な方法から。タスクの最後にStreamをreturnするだけです。タスク内の非同期処理が一組だけの場合はこれで十分でしょう。 ```javascript gulp.task('copy', function() { // ファイルをコピーしてそのStreamをreturnする return gulp.src('src/file').pipe(gulp.dest('dest')); }); // copyに依存するタスク gulp.task('done', ['copy'], function() { // ファイルコピー完了後に実行される console.log("copy done"); }); ``` ### タスク完了callbackを利用する ```gulp.task(name, function(callback) {});``` のようにタスクがcallback引数を受け取る場合、callbackが実行されるまでgulpは次のタスクの実行を控えます。この機能と「Streamの'end'イベント」を組み合わせても、同期的なタスク実行を実現できます。 (Streamの'end'イベントについては[node.jsのドキュメント](http://nodejs.org/api/stream.html#stream_stream)に詳しく書かれています。gulpのStreamもnode.jsのStreamを継承しています。) ```javascript gulp.task('copy', function(callback) { // ファイルをコピーし'end'イベントをハンドル // 'end'イベントはファイルコピー完了時に実行される gulp.src('src/file').pipe(gulp.dest('dest')).on('end', function() { // callbackを実行してgulpにタスク完了を通知 callback(); }); }); // copyに依存するタスク gulp.task('done', ['copy'], function() { // ファイルコピー完了後に実行される console.log("copy done"); }); ``` ## タスク内の非同期処理が複数の場合 タスク内に非同期処理が複数ある場合は少し工夫が必要です。基本は前述の「タスク完了callbackを利用する方法」と同じですが、'end'イベントハンドラの中で直接callbackを実行するのではなく、 **待つべき非同期処理の回数分だけ呼び出された後に、callbackを実行する** そんな処理を実行します。 言葉で説明するとややこしいですが実装は簡単です。 ```javascript gulp.task('copy', function(callback) { var wait_max = 3; // 完了を待つべき非同期処理の数 var wait_count = 0; // すでに完了した非同期処理の数 // 'end'イベントハンドラから呼び出される処理 function onEnd() { if (wait_max === ++wait_count) { // callbackを実行してgulpにタスク完了を通知 callback(); } } // ファイルをコピー gulp.src('src/file1').pipe(gulp.dest('dest1')).on('end', function() { onEnd(); }); gulp.src('src/file2').pipe(gulp.dest('dest2')).on('end', function() { onEnd(); }); gulp.src('src/file3').pipe(gulp.dest('dest3')).on('end', function() { onEnd(); }); }); // copyに依存するタスク gulp.task('done', ['copy'], function() { // ファイルコピー完了後に実行される console.log("copy done"); }); ``` また、このような処理が何箇所にも出てくる場合は、以下のように再利用できるコードを書いておくと良いでしょう(もちろん他の実装方法もあると思います。例えば[underscore](http://underscorejs.org/)の[after](http://underscorejs.org/#after)を利用しても同等の事が可能でしょう)。 ```javascript /* 関数を遅延実行(deferred)するオブジェクト -- untilで指定したeventを全て待ち合わせてからexecで指定した関数を実行する。 */ var Defer = function() { var wait_max = 0, wait_count = 0, callback = null; function onEventEnd() { if (max === ++count) { callback && callback(); } } this.until = function(ev) { max++; ev.on('end', onEventEnd); }; this.exec = function(cb) { callback = cb; }; }; ``` このDeferを使えば前述の例を以下のように書けます。 ```javascript gulp.task('copy', function(callback) { var d = new Defer(); // ファイルをコピー d.until( gulp.src('src/file1').pipe(gulp.dest('dest1'))); d.until( gulp.src('src/file2').pipe(gulp.dest('dest2'))); d.until( gulp.src('src/file3').pipe(gulp.dest('dest3'))); d.exec(function() { // callbackを実行してgulpにタスク完了を通知 callback(); }); }); // copyに依存するタスク gulp.task('done', ['copy'], function() { // ファイルコピー完了後に実行される console.log("copy done"); }); ``` |
|
| 725位 |
|
|||
|
22:19:21 |
|
|
# はじめに
4月まで残すところ2ヶ月と迫り、新卒などを対象とした新人研修の準備が始まっている頃かと思います。 新人研修の中でgitを教える際に、筆者は[tig](http://jonas.nitro.dk/tig/)の活用をおすすめしています。 講師の立場からすれば、短い時間に高効率でgitの本質を伝えることができます。 研修生の立場では、tigを利用して簡単で直感的にgitリポジトリを閲覧・操作することができます。 tigを使うとどうしてそうなるのか、いくつかの理由を以下に紹介します。 # セットアップが簡単ですぐ使い始められる tigは依存関係が少なくポータブルな実装でありインストールが簡単です。 会社から提供する開発サーバーであっても、社員ひとりひとりが所有するPCやMacであっても、 yumやbrewなどのパッケージマネージャから少ない手順でインストールすることができます。 一例: [CentOS6.4にgit tigインストールと使い方 - Qiita [キータ]](http://qiita.com/s_tomoyuki/items/9557970f44847d5bd854) (パッケージがない環境であっても、make && make installも簡単です) 準備に手間取らないツールは、研修講師としても研修生としても都合がよいものだと思います。 # 起動も操作も簡単 起動は `tig` コマンドだけで済みます。(引数も渡せますが最初は知らなくても問題ないです) UNIXコマンドの多くはコマンドライン引数の確認・習熟が使いこなしの鍵です。 git関連コマンドも例外ではありませんが、初学コストが高いことは否めません。 研修講師はgitコマンドの紹介と引数の意味の説明に時間を割く必要があります。 研修生は長いコマンドを間違いなく打ち込む作業を行う必要があります。 研修生が学ぶべき本質はコマンドの記法ではなく、そのコマンドが何をしているかです。 tigを起動すればgitリポジトリを操作していることが自明になります。 その操作についても、基本の操作体系はviに近いところがありますが、複雑なキーストロークは無いため癖の少ないシンプルな構成です。限られた新人研修の時間でも操作方法の学習に割く時間は少なくできるでしょう。 しかも、不慣れなgitコマンドの操作に対して、tigによる操作はキー1つの押下で1つのアクションが実行されるため、高速です。gitリポジトリを短時間で効率よくブラウズしていくことでコードリーディング効率の助けにもなるでしょう。 参考: [git - やけに丁寧なtigの設定ガイド(キーバインド概論編) - Qiita [キータ]](http://qiita.com/yoshikazusawa/items/3eaa6db78fa348d38bfe) # gitの概念が容易に把握できるビューの構成 tigは以下のビューを切り替えてgitリポジトリを操作します。 * コミットログを表示・操作するmainビュー * ブランチを表示・操作するbranchビュー * コミット間の差分を表示できるdiffビュー * ファイルの内容を確認できるblobビュー * 変更のステージ・アンステージを操作できるstatusビュー これはgitの中心の概念を学ぶ際に直感的です。ブランチを選択し差分をコミットし修正の履歴が生まれるという基本を、branchビューでチェックアウト → statusビューで差分のステージ&コミット → mainビューでコミットログの確認 と流れを追って説明できます。 参考: * [git - tigの画面をアニメGIFで紹介してみる(ブラウズ編) - Qiita [キータ]](http://qiita.com/yoshikazusawa/items/93653a6aa2013b3c3396) * [git - tigの画面をアニメGIFで紹介してみる(ファイル編集・差分の削除・revert編) - Qiita [キータ]](http://qiita.com/yoshikazusawa/items/d0df1327dcc9797bb91d) # TUIツールなためリモート接続環境でもローカルでも同じように使えるポータビリティ 社内の開発環境はそれぞれ異なるかと思います。 Webサイト開発事業であれば開発サーバーを立ててログインして利用することが多いかもしれませんし、 iOS/Androidのネイティブアプリケーション開発事業であれば、たいていは手元の開発機で完結するかもしれません。 あるいは、それらの両方を行う複合的で多様な開発環境かもしれません。 tigはTUI(ターミナル上のテキストで表現されたGUI)ツールであるため、リモートでもローカルでもターミナル上で同じように動きます。 そのため、もし複数の事業を行っており、部署ごとにリモート環境だったりローカル環境だったりする会社であっても、tigを使えなくなることは無いかと思いますので、tigの使い方を覚えて無駄になりづらいです。 # スタンドアロンであるため宗教戦争に巻き込まれない エディタやIDEのプラグインを通じてgitを操作する開発者は多いと思います。 筆者も普段の開発環境ではエディタにVimを使い、gitのプラグインである fugitive.vim を愛用しています。 Emacsユーザーの間ではmagitが好評を博していると聞きます。 一方で、エディタやIDEに何を利用するかは個人の趣向が反映され、多種多様な広がりがあります。 そのため全社的に同じ開発ツールで揃っていることは稀かと思います。 エディタやIDEに結合したgitプラグインは、導入方法も、使い方も、備えている機能も千差万別なので、 使いこなせるようになれば便利な半面、新人研修という局面で紹介するには不向きです。 tigはエディタやIDE等とは独立したスタンドアロンのツールです。 他のツールの好みを邪魔することなく、難なくだれでも利用できるツールとして扱えます。 ※ リモート環境を考慮する必要がなければGUIのSourceTreeを採用するのも良い選択だと筆者は思います # 必要十分なデフォルト設定に加えて高いカスタマイズ性も備える底の深さ tigは敷居が低いだけでなく、高度なカスタマイズを行い長く使える自由度も備えています。 デフォルト設定の基本操作を獲得した後は独自のコマンドをキーにバインドするなどして、 さらに使いやすいツールに仕上げていくことが簡単にできます。 gitの学習とは直接は結びつかないですが、 いわゆるdotfiles系の学習の題材にすることもできると思います。 参考: * [やけに丁寧なtigの設定ガイド(表示制御編) - Qiita [キータ]](http://qiita.com/yoshikazusawa/items/d99e8b5d6f6a45aa6e2e) * [tigカスタムキーバインド紹介(mainビューとbranchビュー編) - Qiita [キータ]](http://qiita.com/yoshikazusawa/items/c3eb0e299e6d96850c75) * [tigとGitHubを連携させる幾つかのカスタムキーバインド - Qiita [キータ]](http://qiita.com/yoshikazusawa/items/a8812e2dc736f7f967bb) # まとめ 以上、gitの新人研修で筆者がtigをおすすめする理由を紹介してみました。 gitの大事な概念を伝えることに集中させてくれる有用なツールとなると思いますので、 新人研修担当者の方や講師の方はぜひ検討されてみてはいかがでしょうか。 あわせて読みたい: [git - 日本語で読める!tigに関する有用な記事リンク集 - Qiita [キータ]](http://qiita.com/yoshikazusawa/items/6af83ab1cf4ad51cce2a) |
|
| 726位 |
|
|||
|
12:17:43 |
(Recruit Lifestyle Co. Ltd. 所属) |
|
前回の続きです。 前回はこちら! [最も簡単にiBeaconの電波を発信する方法](http://qiita.com/Morikuma_Works/items/a0dd3cfcd1eef8dbd492) さあiBeaconのアプリを作ってみよう! となった時に、一番困るのがiBeaconを発信するアプリを作成しても、 電波はもちろん見えないため、本当に機能しているかどうかがわからないところです。 自分で実装?めんどいな…っていうかiPhone2台も持ってないしなぁ…… ここの周りにiBeacon形式のBluetooth飛んでないかなぁ…… というわけで、Macを使った最も簡単にiBeaconの電波を「受信」する方法をまとめたいと思います。 え?どこかで聞いたことがあるくだりだって?気にしない気にしない。 #環境 * Bluetooth Low Energy(BLE, Bluetooth4.0)が搭載されたMac製品 * Macbook Pro, Macbook Pro Retina, Macbook Airなどなど * 最近の製品なら大抵OK * node.js v0.10.24 #準備 Macでnode.jsとnpmを使えるようにする必要があります。 http://nodejs.org/ から"INSTALL"をクリックしpkgファイルをダウンロードし、インストールします。 その後、無ければ適当にディレクトリを作成しましょう。 ここでは`~/ibeacon`を作成します。 ```Bash mkdir ~/ibeacon ``` これで準備は完了です! #iBeaconを受信してみる 今回はbleaconを使います。 まず、bleaconをnpm installしましょう。 ```Bash cd ~/ibeacon npm install bleacon ``` その後、適当にjsを書きます。下記をコピペでも構いません。 ```js:receive.js Bleacon = require('bleacon'); Bleacon.startScanning(); Bleacon.on('discover', function(bleacon) { console.dir(bleacon); }); ``` これで準備は完了です。 実際に実行してみましょう。 ```Bash node ~/ibeacon/beacon.js ``` これで特にエラーがでなければ電波を待ち構えている状態です。 あなたが作成した発信アプリを起動してみましょう!するとこんな感じでずらずらでてくるはずです。 ```js { uuid: 'cce55e10ea7f472faa05102fd7aeced8', major: 36, minor: 5, measuredPower: -57, rssi: -34, accuracy: 0.22223443385817412, proximity: 'immediate' } { uuid: 'cce55e10ea7f472faa05102fd7aeced8', major: 36, minor: 5, measuredPower: -57, rssi: -32, accuracy: 0.19498999948233425, proximity: 'immediate' } { uuid: 'cce55e10ea7f472faa05102fd7aeced8', major: 36, minor: 5, measuredPower: -57, rssi: -33, accuracy: 0.20816698139465403, proximity: 'immediate' } { uuid: 'cce55e10ea7f472faa05102fd7aeced8', major: 36, minor: 5, measuredPower: -57, rssi: -31, accuracy: 0.18264712128403449, proximity: 'immediate' } ... ``` 取れる情報は、 * UUID: ビーコンに設定されているUUID * major: ビーコンに設定されているmajor * minor: ビーコンに設定されているminor * measuredPower: 1mでのRSSI強度 * rssi: RSSI強度 * accuracy: measuredPowerとrssiから計算したビーコンとの距離(あてにならない) * proximity: ビーコンとの近さ。 * unknown(不明), immediate(すぐそこ), near(近い), far(遠い)で表現 とまあ、iOSで使用できる値はほぼ取得できます。 上記のままだと、iBeaconのフォーマットに従ってAdvertiseされているBluetoothすべてを表示します。 自分の発信アプリだけの挙動が見たい場合はUUID、major、minorを指定して、それだけを受信することもできます。 ```Javascript Bleacon.startScanning(uuid); //UUIDが一致するものすべて Bleacon.startScanning(uuid, major); //UUIDとmajorが一致するものすべて Bleacon.startScanning(uuid, major, minor); //UUIDとmajorとminorが一致するものすべて ``` とても簡単! |
|
| 727位 |
|
|||
|
20:25:42 |
|
|
モバイルデバイス全盛の昨今、サイトのデザインだけでなく動きも対応しなくては!というケースがあります(私はありました)。
そんなわけで、JavaScriptのタッチ対応ライブラリについて評価を行ってみました。ライブラリの一覧については[こちらのサイト](http://www.queness.com/post/11755/11-multi-touch-and-touch-events-javascript-libraries)が詳しく、ここから対応イベント・更新頻度を基に評価対象を表題の3つに絞り、その動作を比較してみました。 各ライブラリによるタッチの挙動は、以下で比較可能です(おまけでTouchyも入ってます)。 **[Gesture Detector](http://jsfiddle.net/icoxfog417/uQBvP/embedded/result/)** 今スマホやタブレット的な何かでこの記事を読まれているあなた!はぜひ実際に動かしてみて下さい。 私はAndroid4.0デバイスしか持ってないので、xxで動かんぞ、ということがあるかもしれないです。そんな時はコメントなどで連絡いただけたらうれしいです。また、コードは[こちら](http://jsfiddle.net/icoxfog417/uQBvP/)。 ## 結論 **結論としてはHammer.jsをお勧めしたい。**ただ、1つに絞るならという話で、QuoJSも遜色ない。 デバイス・ブラウザの数は今後も増えていくはずで、そうなるとこうしたライブラリを選択する際「メンテナンスの継続性」は一番考慮すべきポイントと思う。そうなると、開発者以外の人の手で機能拡張プラグインなどがリリースされているHammer.jsにやはり軍配かなと。 以下は、簡単な所感。 | ライブラリ | 所感 | |:-----------|:------------|:------------| | [Hammer.js](http://eightmedia.github.io/hammer.js/) |対応イベントは豊富で精度もいい。カスタムジェスチャー・プラグインなどを追加可| | [QuoJS](http://quojs.tapquo.com/) | 必要十分なイベントを十分な精度で検知してくれる。優秀。 | |[TouchSwipe](http://labs.rampinteractive.co.uk/touchSwipe/demos/) | 不安な実装だが、指の数やジェスチャーの時間など結構細かいパラメーターをくれる。| **<他ライブラリについて>** [jQuery.pep.js](http://pep.briangonzalez.org/) ドラッグ&ドロップ特化だが、特化している分惚れ惚れするほど動きが滑らか。ぜひリンクからサンプルを試してもらいたい。ドラッグ&ドロップの実装が必要なら、これ+不足分を上記のライブラリでカバー、というスタイルをおすすめしたい。 [Touchy](http://touchyjs.org/) Exampleが面白い。タッチイベント全般というよりは特定のジェスチャー(FlickやWheel)に特化している感じで、そうしたジェスチャーがまさに必要なら上記のようなライブラリで対応するより滑らかに動いてくれるはず。ただ、mouseイベントに一切反応してくれないのでPCサイトと共用したいならちと面倒。 [jQuery UI Touch Punch](http://touchpunch.furf.com/) 一言で言えば動かない。jQuery UIをタッチ対応させるというコンセプトは非常に良くjQuery touch で検索しても結構ヒットするのだが、Exampleは動かずissueも放置されている。もう更新は止まっている? 以下、詳細。 ## Hammer.js 単体として使うだけでなく、jQuery Pluginとしても導入できる。ジェスチャー検知時のオプションも豊富で、環境によってのチューニングもできそう。また、カスタムのジェスチャーも実装可能。ただ、rotateについてはQuoJSのようにLeft/Rightがないので注意。 機能拡張用プラグインも幾つかありより高度なタッチ機能を実装していきたいなら発展性があるHammer.jsは魅力だと思う。 ## QuoJS 対応しているイベント、検知精度、共に十分。ライブラリはjQueryライクな実装で使いやすく、jQueryでサポート外となってしまったbrowserなどが取れるのもモバイル対応時にはありがたい。 ただ、タッチイベント対応時に必須なpreventDefaultを行う機能がライブラリ標準でないので、これは自分で行っておく必要がある(いつか対応される気もするが)。これをやっておかないと悲惨なのでその点は要注意。 ## TouchSwipe ExampleのPinch In と Outが逆な気がしたり(ソースを見るかぎりほんとに逆?)、動作が不自然だったりと不安なオーラを放っている・・・が、イベント発生時取得できる情報が多いためカスタマイズの幅は広いと思う(独自ジェスチャーを作るなど)。 |
|
| 728位 |
|
|||
|
23:58:42 |
|
|
# 忙しい人向け
``` export JAVA_HOME=`/usr/libexec/java_home -v 9` #java9が使いたいとき export JAVA_HOME=`/usr/libexec/java_home -v 1.8` #java8が使いたいとき export JAVA_HOME=`/usr/libexec/java_home -v 1.7` #java7が使いたいとき export JAVA_HOME=`/usr/libexec/java_home -v 1.6` #java6が使いたいとき ``` # 解説 - バージョンが異なる複数のjava appの開発を行うケースなどで、同じMacのローカル環境でjava/javacのバージョンを切り替えて使いたいケースがあります。 - xcode toolsには `java_home` という複数のバージョンのjavaそれぞれのhome directoryのpathを表示してくれるコマンドがあるので、これを使うとそれが可能になります。 [java_home(1)のman](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/java_home.1.html) - なので前提条件としてXcode Command Line Toolsが必要ですが、普段macで開発してる人なら入れてると思うので(これがないとbrewもportsも使えないですし)問題ないかと思います - このコマンドの実態は `/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home` ですが、 `/usr/libexec/java_home` にsymbolic linkが貼られます - オプション無しで実行すると最新のJDKのバージョンのhome directoryのpathを表示してくれます(バージョン数降順がdefault orderのようです多分) ```shell-session $ /usr/libexec/java_home /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home ``` - manページにも記載がありますが、 `-v` オプションで表示したいJDKのバージョンを指定することができます。 ```shell-session $ /usr/libexec/java_home -v 1.8 /Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home ``` - なお、java9だけはなぜか `-v 9` と指定しないとエラーが出るので注意が必要です。 ```shell-session $ /usr/libexec/java_home -v 1.9 Unable to find any JVMs matching version "1.9". /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home $ /usr/libexec/java_home -v 9 /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home $ /usr/libexec/java_home -v 8 Unable to find any JVMs matching version "8". /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home ``` ## 例:java8が使いたい時のJAVA_HOME指定 ```sh export JAVA_HOME=`/usr/libexec/java_home -v 1.8` # JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home ``` ## 例:java6が使いたい時のJAVA_HOME指定 ```sh export JAVA_HOME=`/usr/libexec/java_home -v 1.6` # JAVA_HOME=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home ``` ## 補足 インストールされていないバージョンを指定した場合は最新のバージョンが指定される?ようです(詳細未確認) ```sh $ /usr/libexec/java_home -v 1.7 # Unable to find any JVMs matching version "1.7". # /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home ``` ```sh $ /usr/libexec/java_home -v 1.4 # Unable to find any JVMs matching version "1.4". # /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home ``` |
|
| 729位 |
|
|||
|
23:10:15 |
(pixiv Inc. 所属) |
|
[tmuxのすすめ | catatsuyのBlog](http://blog.catatsuy.org/a/243)
以前にこの記事を書きましたが,現在の私の設定とは一部乖離しているので追記します # ESC キーの効きを良くする tmux では ESC キーの反応が遅いです 私はたまに vim も使うので ESC の反応が悪いのは少し困ります ```.tmux.conf set -s escape-time 0 ``` # マウスを使えるようにする マウスのスクロールを使えるようにします ```.tmux.conf set-window-option -g mode-mouse on ``` # Solarized を使って色をいい感じにする [Solarized - Ethan Schoonover](http://ethanschoonover.com/solarized) これを使うと色がいい感じになります dircolors なども用意されていて大変良いです `source-file` などを使って外部ファイルとして読み込むのもよいですが,`.tmux.conf` に関しては大して長くもないのでコピペしてしまっても良いと思います [solarized/tmux at master · altercation/solarized](https://github.com/altercation/solarized/tree/master/tmux) これの好きな色を選択しましょう ちなみに dircolors も設定したい場合は zsh ならば [.zshrcを色んな環境で共有する方法を考えてみた - Qiita [キータ]](http://qiita.com/catatsuy/items/00ebf78f56960b6d43c2#2-5) が参考になるかと思います # tmux のコピーについて [Ubuntu - tmux上のコピペをうまく設定する方法 - Qiita [キータ]](http://qiita.com/catatsuy/items/71b0f4932f00c6711ef5) 以前こんな記事も書いたのですが,これではリモートのサーバー上で tmux を立ち上げた時に無力です 実は Mac の場合,iTerm2 の開発版が OSC52/PASTE64 に対応しているので tmux のコピーとクリップボードの同期が取れます [iTerm2のクリップボードインテグレーション(OSC 52/PASTE64)のつかいかた - Qiita [キータ]](http://qiita.com/kefir_/items/1f635fe66b778932e278) この通りやれば,ローカルだろうとリモートだろうとクリップボードに同期が取れますので `tmux-copy` なるコマンドを用意する必要はありません `reattach-to-user-namespace` 自体は `pbcopy/pbpaste` を使う際に必要ですので設定しておくと良いでしょう 残念ながら Ubuntu に関しては gnome-terminal が対応していないためこの機能を現在使うことはできません # tmux の設定 現在の私の設定です ```.tmux.conf # Prefix set-option -g prefix C-z # 日本語環境なら必須?? setw -g utf8 on set -g status-utf8 on # status set -g status-interval 10 # KeyBindings # pane unbind 1 bind 1 break-pane bind 2 split-window -v bind 3 split-window -h bind C-r source-file ~/.tmux.conf bind C-k kill-pane bind k kill-window unbind & bind -r ^[ copy-mode bind -r ^] paste-buffer set -s escape-time 0 # shell set-option -g default-shell /bin/zsh set-option -g default-command /bin/zsh set-window-option -g mode-mouse on #### COLOUR (Solarized dark) #### cf: https://github.com/altercation/solarized/blob/master/tmux/tmuxcolors-dark.conf # default statusbar colors set-option -g status-bg black #base02 set-option -g status-fg yellow #yellow set-option -g status-attr default # default window title colors set-window-option -g window-status-fg brightblue #base0 set-window-option -g window-status-bg default #set-window-option -g window-status-attr dim # active window title colors set-window-option -g window-status-current-fg brightred #orange set-window-option -g window-status-current-bg default #set-window-option -g window-status-current-attr bright # pane border set-option -g pane-border-fg black #base02 set-option -g pane-active-border-fg brightgreen #base01 # message text set-option -g message-bg black #base02 set-option -g message-fg brightred #orange # pane number display set-option -g display-panes-active-colour blue #blue set-option -g display-panes-colour brightred #orange ``` 参考になれば幸いです |
|
| 730位 |
|
|||
|
07:07:01 |
|
|
技術書を自分のお金で買うようになってから、かれこれ十数年。
ダメな技術書が放つ独特のニオイがやっとわかるようになってきたので、書いてみました。 以下、主観を多分に含みます。「あー、あるある」と思いながら軽く読んでくださいね。 (逆バージョンの「良書のみつけかた」は、近日公開予定でございます。) ダメな技術書の「あるある」 ========================= 誤字・脱字が多い ---------------------- 推敲に時間を割いてないことの証拠である。よって誤字・脱字が多い本は技術書としてもクソ本である確率が高い。 正誤表・サポートページがない ------------------------------------------- 多少の誤字・脱字は仕方がないとしても、それを Web で補う気すらない著者がいる。そういう方の書いた本は、えてしてクソ本であることが多い。 レビュアーの名前が献辞に載ってない ------------------------------------------------- レビューを経ていないと、著者のひとりよがりな内容だったり、技術的に正しくない内容があったりするので要注意だ。(※レビューなしで良書を出してる方もいるので、これだけでクソ本判定してはいけない。) 監訳者がいない or いるけど監訳してない ----------------------------------------------------------------- 翻訳書の場合、その道に詳しい人に監訳をしてもらわずに出版されてしまった本がある。監訳者がいたとしてもほとんど仕事をしていないような場合がある。ひどい場合だと翻訳会社にベロンと丸投げしてそのまま出してしまったような本すらある。それらのどれであったとしても、訳文がしっちゃかめっちゃかになるのは避けられない。 この場合、原書が良書であったとしてもクソ本と化す。ただ単にクソ本を出すことでも大罪なのだが、翻訳独占権によって他の良識ある人が別訳を出すことができなくなってしまうので、その業は深い。 サンプルコードがバグバグ or 動かない -------------------------------------------------- こういうのに遭遇するたびに、コードは実際に動かしてチェックしてくれよ、と思う。 サンプルコードに脆弱性がある ------------------------------------------ [高木先生に首を刈られる危険性](http://takagi-hiromitsu.jp/diary/20051227.html)を知らずに出版してしまっている時点でヤバい。 著者のプロフィールが長い or カッコ良すぎる ----------------------------------------------------------- プロフィールがやたら長いと、クソ本率が高まる。海外大学卒とか、何かの資格を当時最年少で取得したとか、ナントカ協会の理事だとか、そういうのをひけらかしている場合なぜか技術的な内容もクソであることが多い。バズワード (「クラウド」とか「BYOD」とか) がそこに含まれていると、クソ本率がMAXになる。不思議だ。 やたらと多い参考文献 --------------------------------- 参考文献が何ページにもわたってずらずらと列挙されている本は、何故かクソ本であることが多い。 RFC・I-D・本・Webページ・言語仕様・リファレンス、その他やたらめったらとリストアップされている (しかも本文中との関係が示されていない) 本があって、内容をみたら案の定クソであった。 無意味な付録 ----------------------- 本文と関係がない無意味なものを、そのままベロンと巻末に採録している本はクソ本率高し。PHPの言語仕様を採録している本があった。内容をみたら案の定クソであった。GPLの翻訳全文を無意味に採録している本があった。内容をみたら案の定クソであった。 危険人物 ------------------------------- クソ本ばかりのくせに何冊も出版している著者がいる。なぜそんなことが可能なのかよく分からないけど、本当にそんな人がいるんだよ! 買う前にググって、著者がネタ人物扱いされていないかどうかよくよくチェックするべき。 油断してはいけない…… ========================= 以下はダメとはいえないまでも、注意しておいたほうが良い場合。 古い ----------------- 技術書は経年劣化が激しい。 ものすごくおおざっぱに言うと、2005年以前のJavaの本、2009年以前の Ruby・Ruby on Rails の本、2006年以前の JavaScript の本、2005年以前の MySQL の本は概ね使えないと思っておいたほうが良いと思う。 もちろんそのなかにも良書はあるので、よくよく確かめてから買ってほしい。 「○○公式」「○○認定」 ------------------------------ 「公式と書いてあるから」と安心して買ってしまうとひどい目に遭ったりする。よくよく考えてみれば公式だろうと認定を受けていようとダメな本はダメな本だし、良い本は良い本である。 内容の良さにはあまり関係がない。 ポケットリファレンスの類 ------------------------------------- 個人的にはイマイチ。買ったけど結局使わないことが多い。 クックブックの類 -------------------------- 同じく。好きな人もいるみたいなので、相性の問題かも。 余談 ========= 誤字・脱字がやたらと多いことで有名な、とある方がいる。そのお方、著書について Amazon のレビューで誤字・脱字についてボロクソに書かれたら、自身のブログで「誤字・脱字が多少あったとしても技術書なんだから別に良いだろ、普通わかるだろ」みたいなことを言って開き直っていた。多少ってレベルじゃねーよ、とツッコミはさておき、開き直ってる時点で書き手として論外でしょ、と思った。 実はつい最近、似たようなことを言ってる人を他にも見つけた。良書とクソ本の区別がつきやすくなるので助かるなぁと思った。 もちろん、その本には正誤表が無かった。 |
|
| 731位 |
|
|||
|
17:09:36 |
(PixelGrid Inc. / html5j 所属) |
|
```js:
console.log('しゅつりょく'); console.log(obj); ``` こんな感じで書くのが普通ですが、引数として出力する変数を取る書き方がいくつかあります。 ```js: var str = "もじれつ"; var num = 555; var flo = 1.7320508075; console.log('Stringの出力 : %s', str); console.log('Numberの出力 : %d', num); console.log('Floatの出力 : %f', flo); console.log('複数出力 : %s %d %f',str,num,flo); ``` ### 出力結果 ``` Stringの出力 : もじれつ Numberの出力 : 555 Floatの出力 : 1.7320508075 複数出力 : もじれつ 555 1.7320508075 ``` floatの桁数指定とかはまだ実装されてないみたいです。 --- 他にもハイパーリンクとして出力、スタイルを指定して出力なんかもあります。 ```js: var url = "http://qiita.com/" var sty = 'color:#fff;background:#000;'; console.log('Linkの出力 : %o', url); console.log('%c:スタイルを指定して出力', sty); ``` ### 出力結果 ``` Linkの出力 : "http://qiita.com/" ※ console上でクリックすると飛べるようになります :スタイルを指定して出力 ※ console出力すると実際には黒背景に白文字になります ``` Cライク?な書き方ができるのがおもしろい。 console.log()で文字列連結して変数の出力するときとか、ちょっと楽かも? --- ※追記 `%O`指定の出力について書きました。 [console.log()でオブジェクトを出力するときにラベルをつける方法](http://qiita.com/nakajmg/items/9c6ad7e1e01e1204d430) --- 参考URL:[Chrome Devtools Cheatsheet](http://anti-code.com/devtools-cheatsheet/#console-api) |
|
| 732位 |
|
|||
|
07:07:07 |
|
|
今月からようやくModern Objective-Cに着手(かなり今さらだけど・・)
記述がかなりシンプルだとは聞いていて、情報もたくさん挙がっていたのだけど、困惑したのでメモ。メンバー変数とか@synthesizeとか宣言不要でコードがスッキリして嬉しい。基本的には@propertyだけ宣言すれば良いみたい。また@properyもいままでヘッダファイルでのみ宣言していたけど、プライベートなものは実装ファイルの無名カテゴリ内に宣言すればよいみたい(パブリックプロパティは従来通りヘッダファイルに記述)。以下、プライベートプロパティを宣言する時の例。 <a href="http://b.ruyaka.com/2013/11/16/objective-c-modern-property-notation/#modern-objective-c-property"><img src="http://b.ruyaka.com/wp-content/uploads/2013/11/modern-objective-c-property.png" alt="Modern Objective-cのプロパティ記述"></a> 一応、メンバー変数とか本当に定義されているのか確認。 <a href="http://b.ruyaka.com/2013/11/16/objective-c-modern-property-notation/#modern-objective-c-property-check"><img src="http://b.ruyaka.com/wp-content/uploads/2013/11/modern-objective-c-property-check.jpg" alt="Modern Objective-cのプロパティ記述の確認"></a> もう"Legacy" Objective-Cでは書けないな。 ### 参考サイト 参考にさせていただいたサイトです。 * [Objective-Cプログラミング言語 - プロパティの宣と実装](https://developer.apple.com/jp/devcenter/ios/library/documentation/ObjC.pdf) by Apple > 歴史的には、インターフェイスにはクラスのインスタンス変数宣言が必要でした。(中略)インスタンス変数は実装詳細であり、通常、クラス自身の外からアクセスされることはありません。さらに、実装ブロック内に宣言すること、あるいは宣言済みプロパティから自動生成させることも可能です。したがって通常は、インスタンス変数宣言をパブリックインターフェイスで行うべきではないので、波括弧も省略してください。 公式ドキュメントです。いままでインターフェイスで宣言してました。公式で実装ブロック内での宣言やプロパティでの自動生成でも可能で、括弧も省略してよいみたい。 * [Xcode4.4 Modern Objective-C Syntaxでコードをきれいにする方法](http://www.zero4racer.com/blog/798) by Zero4Racer (2012/7/25) > コンパイラが@synthesizeを補完してくれる様になったため、基本的な状況では、@synthesizeを書かなくてよくなりました。また、synthesizeされる相手>のivarも、同名の変数ではなくて、アンダーバーを最初に付けた物がデフォルトとなります。これまで議論されて来たことがappleのコンパイラによって、これ>からは定義されているため、安心ですね。ちなみに、これは以前からですが、(strong)というのも,デフォルトがstrongなので、書かなくてよくなりました。 * [プロパティに対応するインスタンス変数の命名規則について](http://www.awaresoft.jp/ios-dev/item/115-ivar-naming-convention.html) by Awaresoft (2012/3/18) >1.プロパティに対応するインスタンス変数はヘッダファイルで宣言する必要はまずない。(iOSのバージョンに関係なく) > 2.プロパティに対応するインスタンス変数はsynthesizeで常に別名をつけるべき。 > 3.インスタンス変数名はプロパティ名の前に_をつけたものにする。 > 4.インスタンス変数にアクセスするときは、クラス内からでも常にアクセッサメソッドを通して行うようにする。(setter/getter内やinit, dealloc以外) 参照 : [Modern Objective-Cでのプロパティ記述](http://b.ruyaka.com/wp-content/uploads/2013/11/modern-objective-c-property-check1.jpg) 追記1 : 文章の補足と参考サイトを追加しました。 |
|
| 733位 |
|
|||
|
21:53:07 |
(Canon Inc. 所属) |
|
Windows/Linuxで両方で動作する成果物を想定。
有償のツールは理解が得られる方が稀なので除外。 # 仕様書 ## 外部仕様 Word/Excelが手軽だけど差分が追いにくい。 Markdown+PandocかSphinxでPDF提出がいいかな? * [Pandoc - About pandoc](http://johnmacfarlane.net/pandoc/) * [Sphinx-Users.jp :: ドキュメンテーションツール スフィンクス Sphinx-users.jp](http://sphinx-users.jp/) ## 内部仕様 きちんと書いてあればDoxygenで十分だと思う。 Cしか対応していないみたいだけどdocuriumの方がgitとの親和性が高くて(tag付された結果をまとめて解析してくれるみたい)出力結果も今風にできてる。 * [Doxygen](http://www.doxygen.jp/) * [github/docurium](https://github.com/github/docurium) ## インセプションデッキ 作っておくと上司/部下/協力メンバで方針を合わせやすい。 * [ネスケラボ » インセプションデッキ](http://blog.nextscape.net/index.php/research/agile/inceptiondeck) # 開発 ## ソースコード規約・整形 規約は組織ごと、プロジェクトごとに決まりがあるのでそれに従う。読みにくいコードを書く人がプロジェクトに含まれる場合は[リーダブルコード](http://www.oreilly.co.jp/books/9784873115658/)を経費で買って読ませる。それでも直らなかったら直るまでレビューし続けるかソースコードを触れないように遠ざける。 整形はuncrustifyが痒いところに手が届いてよさそう。設定ファイルを作りこんで使いまわすのが良い。 * [Uncrustify - Source Code Beautifier for C-like languages](http://uncrustify.sourceforge.net/) ## 文字コード VisualStudioがBOMありじゃないと動かないのでUTF-8(BOMあり)一択。 最近のgccだとBOMありでも動くらしいけど、古いgccの場合はビルドスクリプトにBOMの削除ルーチンを仕掛けておけば良い。 * [Add/Remove BOM — Gist](https://gist.github.com/386410) (Ruby版) * [tune/bom_manage](https://github.com/tune/bom_manage) (Bat + nkf版) ## コンパイラ * VisualStudio * Eclipse + CDT * gcc * llvm-clang * etc... 指定されたものを使う。 警告は最大限出すようにし(/W4 or -W -Wall)、地道に警告が出ないよう対処していく。 警告数が0になった段階で警告をエラーとして扱うようにする。 VisualStudioは独自のコンパイラしか使えないのかと思ってたけどMakefileも扱えるらしい([青の部隊 505小隊 ULZ - VisualStudio攻略](http://www6.atwiki.jp/we_hate_sunshine/pages/77.html#id_6e4e76b6))。 アサインされたプロジェクトでVisualStudioしか使ってなくても VS -> VS+Makefile -> Eclipse+CDT+Makefile -> ... と段階を踏むことでマルチ環境でビルドできるように持っていけるかも。移植性があるように実装されてないとだめだけど。 好みの問題だけど、コンパイル結果の出力は色付けしたほうがいいかも。エラーはエラーらしく真っ赤に表示されるべし。 * [johannes/colorgcc](https://github.com/johannes/colorgcc) * [窓の杜 - 【REVIEW】「Visual Studio」のビルド・デバッグ出力を色分けする拡張機能「VSColorOutput」](http://www.forest.impress.co.jp/docs/review/20120327_520592.html) ## Version Control System Subversion以前のVCSは時代遅れ。 gitのリポジトリ管理はRhodeCodeがおすすめ。インストールがgitlabとかと比べて簡単そうだし、コードレビューも流行りのプルリクエスト形式でできるようになるし、誰でもリポジトリ作り放題! * [Git](http://git-scm.com/) * [RhodeCode](http://rhodecode.org/) 運用方法はさておいて、ビルドが壊れるコミット/テストが通らないコミットは許さないようにしたい。 git+hookスクリプト+Jenkinsで可能なのはわかるんだけど、どうしたら手軽に実現できるのかが分からない。 Gitのhookスクリプトはbleis-tiftさんのがよさそう。 * [bleis-tift/Git-Hooks](https://github.com/bleis-tift/Git-Hooks) .gitignoreはgithubから持ってくるのがベター * [github/gitignore](https://github.com/github/gitignore) ## Issue Tracking System RedmineでもJIRA(有償)でも。Tracはマルチプロジェクトがデフォルトで扱えないのとリリーススピードがいまいち遅いので割引。コミットはチケット番号と必ず関連付けるようにする。 * [Overview - Redmine](http://www.redmine.org/) * [課題管理、プロジェクト管理ソフトウェア | Atlassian](http://www.atlassian.com/ja/software/jira/overview) Pivotal Trackerクローンの[fulcrum](https://github.com/malclocke/fulcrum)でもいいかも。 ## Continuous Integration JenkinsでOK。 最初はローカルPCで動かして、慣れてきたらサーバを用意してくるので十分。 ビルドスクリプトはJenkinsに保存できるけど、バッチファイル/シェルスクリプトを用意してJenkinsから叩くほうがビルドスクリプトの修正履歴も管理できていいらしい。 VisualStudioの場合はDebugとReleaseは最低限、gcc+makeの場合もうっかり壊れやすいオプションの組み合わせはテストする。 * [Welcome to Jenkins CI! | Jenkins CI](http://jenkins-ci.org/) ## ソースコード検索 milkodeが鉄板 * [メインページ - Milkode](http://milkode.ongaeshi.me/wiki/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8) # 性能・品質評価 ## メトリクス集計 SourceMonitor以外にいいのがない… 定期的にメトリクスを取る方法を模索中。 * [SourceMonitor V3.3](http://www.campwoodsw.com/sourcemonitor.html) 見るべきメトリクスは下記の3つ。 1. 総行数 2. ネストの数 3. 関数の複雑度 総行数はプロジェクトの規模感を知るため。 ネストの数が多いと綺麗にかけていない可能性大。 関数の複雑度が高いところからリファクタリングを行うこと。どんなに複雑な処理でも30ぐらいまでは改善可能だと思う。 ## カバレッジ Linuxのgcov以外に選択肢がない… orz 解析結果の可視化はlcovじゃなくて[gcovr](https://software.sandia.gov/trac/fast/wiki/gcovr)がよさそう。Coverture形式の出力を経ることでJenkinsでもきれいに表示させることができる。 * [Gcov - Using the GNU Compiler Collection (GCC)](http://gcc.gnu.org/onlinedocs/gcc-4.5.2/gcc/Gcov.html#Gcov) * [gcovr – FAST](https://software.sandia.gov/trac/fast/wiki/gcovr) ## 静的解析 脆弱性やバグを見つけるタイプ、C++だとcppcheckがあるけど、C向けには鉄板ツールが無い気がする。いくつか見つけたけど実戦投入できてないので効果は不明。 * [cppcheck | Free Development software downloads at SourceForge.net](http://sourceforge.net/projects/cppcheck/) * [オープンソースの静的解析ツール AdLint(アドリント) | オージス総研](http://www.ogis-ri.co.jp/product/1199335_6798.html) * [Splint Home Page](http://lclint.cs.virginia.edu/) * C言語の静的解析ができるが、標準でも厳しすぎる印象。 * [Clang Static Analyzer](http://clang-analyzer.llvm.org/) * 実戦投入したことが無いので効果が不明。 コードの重複を判定するのに重きをおいた静的解析ツール。こっちも実戦投入してないので詳細不明。 * [CCFinder ホームページ](http://www.ccfinder.net/ccfinderxos-j.html) * ソースコードの重複解析。実戦投入したことが無いので効果が不明。 * [PMD](http://pmd.sourceforge.net/) * 一緒についてくるCPDでC/C++でも重複調査ができるらしい(参考:[重複コードを撃退!DRY原則をC言語で実践するためのCPDを調べた。 | Futurismo](http://hmi-me.ciao.jp/wordpress/archives/516))。 * [Duplo | Free Development software downloads at SourceForge.net](http://sourceforge.net/projects/duplo/) * ソースコードの重複解析。実戦投入したことが無いので効果が不明。 メモリアクセス系のチェックはvalgrindが鉄板だけどLinuxじゃないと動かない。 * [Valgrind Home](http://valgrind.org/) ## バグ密度/バグ予測 Gitを使っていればGoogle謹製ツールのbugspotsの恩恵に預かれる、Subversion版もあるけど。 * [igrigorik/bugspots](https://github.com/igrigorik/bugspots) * [takanorig/bugspots-svn](https://github.com/takanorig/bugspots-svn) ## パフォーマンス gprofしか知らない… * [Man page of GPROF](http://linuxjm.sourceforge.jp/html/GNU_binutils/man1/gprof.1.html) # テスト ## 単体テスト *単体テストはLogicをテストする。* 手動でデバッガ止めるなんてアホ、xUnit Framework以外に選択肢はない。 どこでも使える無難なのはcUnit/CppUnitだけどあまり評判は良くないみたい。GoogleTestが今なら使えるか? 今現在xUnit Frameworkの仕組みが入ってないなら一番簡単な箇所(例えば2つの値を交換するとか)の関数に対して1つだけテストを足せば良い。1つテストを書くほうがテストを増やしていくよりもずっと難しい。 * [CUnit Home](http://cunit.sourceforge.net/) * [CppUnit - C++ port of JUnit | Free Development software downloads at SourceForge.net](http://sourceforge.net/projects/cppunit/) * [googletest - Google C++ Testing Framework - Google Project Hosting](http://code.google.com/p/googletest/) ## 結合テスト・システムテスト・受け入れテスト *結合テスト・システムテスト・受け入れテストはFeatureをテストする。* ツールはBDD(Behavior Driven Development)用のフレームワークを使うのがいいかも。 * [Are there any good open source BDD tools for C/C++? - Stack Overflow](http://stackoverflow.com/questions/193965/are-there-any-good-open-source-bdd-tools-for-c-c) * [arnaudbrejeon/cspec](https://github.com/arnaudbrejeon/cspec) swigを使ってRuby製のツール(CucumberとかCapybara)を使うことはできないか? # その他/評価待ち * [Sonar](http://www.sonarsource.org/) * pom.xmlがさっぱりわからない… orz * [GitStats | Free Development software downloads at SourceForge.net](http://sourceforge.net/projects/gitstats/) * Gitのコミット履歴から活動状況を集計してくれる。導入は簡単だけどどう使っていいかが分からない。 * [gperftools - Fast, multi-threaded malloc() and nifty performance analysis tools - Google Project Hosting](http://code.google.com/p/gperftools/) * [blackducksw/ohcount](https://github.com/blackducksw/ohcount) * ソースコードの行数カウンタ、SourceMonitorで十分か? |
|
| 734位 |
|
|||
|
19:22:24 |
|
|
完成品はこんな感じ
[] (http://miyakeryo.com/?p=186) メッセージアプリとかでよくあるやつなんだけど、画像でもいいんだけどセリフの長さによってサイズ変えたりしたいから、描いたほうが楽だなってことで簡単なんで描いちゃいましょう。 コードは主要部分はこんな感じ ```objc //描画するぜ! - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextBubblePath(context, CGRectMake(60.5, 40.5, 170, 70)); CGContextStrokePath(context); } //角度→ラジアン変換 #if !defined(RADIANS) #define RADIANS(D) (D * M_PI / 180) #endif //吹き出しを描く void CGContextBubblePath(CGContextRef context, CGRect rect) { CGFloat rad = 10; //角の半径 CGFloat qx = 10; // くちばしの長さ CGFloat qy = 20; // くちばしの高さ CGFloat cqy = 4; // 上くちばしカーブの基準点の高さ CGFloat lx = CGRectGetMinX(rect)+qx; //左 CGFloat rx = CGRectGetMaxX(rect); //右 CGFloat ty = CGRectGetMinY(rect); //上 CGFloat by = CGRectGetMaxY(rect); //下 CGContextBeginPath(context); CGContextMoveToPoint(context, lx, ty+rad); //左上 CGContextAddArc(context, lx+rad, ty+rad, rad, RADIANS(180), RADIANS(270), 0); //左上のカーブ CGContextAddArc(context, rx-rad, ty+rad, rad, RADIANS(270), RADIANS(360), 0); //右上のカーブ CGContextAddArc(context, rx-rad, by-rad, rad, RADIANS(0), RADIANS(90), 0); //右下のカーブ CGContextAddArc(context, lx+rad, by-rad, rad, RADIANS(90), RADIANS(125), 0); //くちばしの付け根(下の凹み) CGContextAddQuadCurveToPoint(context, lx, by, lx-qx, by); //くちばしの先端 CGContextAddQuadCurveToPoint(context, lx, by-cqy, lx, by-qy); //くちばしの付け根(上) CGContextClosePath(context); //左上の点まで閉じる } ``` 意外と短い。 でもこれで吹き出しの形が描けます。 ちょっとだけ説明。 まず、左上のカーブ。 ``` CGContextAddArc(context, lx+rad, ty+rad, rad, RADIANS(180), RADIANS(270), 0); ``` 点(lx+rad, ty+rad)を中心にして、半径radの円を180度から270度まで時計回りで描くよ。ってことです。 これで左上のカーブが描けます。 ※ 注意 原点が左上なので、0度が右、90度が下になります。 180度から270度ってことは、左から上ってことです。 次の行で上の直線を描かずに、すぐに右上のカーブを描いてるのは勝手に間は直線で結ばれるからです。 勝手に結ばれるのが気になるなら、こんな感じで直線を描いてもいいです。 ``` CGContextAddLineToPoint(context, rx-rad, ty); ``` 左下のカーブは、円を下から35度だけ描いて止めてます。 次に、くちばしの先端。 ``` CGContextAddQuadCurveToPoint(context, lx, by, lx-qx, by); ``` 現在の点から、点(lx-qx, by)まで線を引くんだけど、点(lx, by)で線を引っ張って曲げる。ってことです。 厳密には違うかもしれないけど、まあそんなとこです。 だから、もっと強く曲げたかったら、点(lx, by)を点(lx+1, by+1)とかにしたらいいんです。 最後に、一番上。吹き出しの大きさを指定してるところ。 ``` CGRectMake(60.5, 40.5, 170, 70) ``` 0.5とか中途半端な座標を使ってるのは、よくあるTips。 1ptの線を描くとき座標の中心に描こうとして線がぼやっとするから。 Retinaだったら問題ないんだけどね。 はい。おわりです。 完成品では、Clipしてグラデーションで塗ったり、縁取りしたり、影つけたりしてるんだけど、 気になったらGitHubにあげてるんでみてねー。 [BubbleDraw - GitHub](https://github.com/miyakeryo/objc-ryo/tree/master/BubbleDraw/BubbleDraw) |
|
| 735位 |
|
|||
|
19:28:14 |
|
|
はじめに ---- 最近[ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa)というものを知りました。これはリアクティブプログラミングというパラダイムのCocoa実装のようです。リアクティブプログラミングそのものは[なぜリアクティブプログラミングは重要か。](http://d.hatena.ne.jp/pokarim/20101226)を読むと、分かったような分からないようなもやもやした状態になります。 もやもやした状態は、自分で実験していく事で分かるかもしれません。なので手を動かしてReactiveCocoaをかじってみます。 試しに作るもの ---- [ReactiveCocoa : NSHipster](http://nshipster.com/reactivecocoa/)のサンプルを見つつ、教科書的なユーザ作成画面を作ってみます。 - username email password passwordVerification のTextField - createボタンは以下の条件を満たしたら押せる(ルール1) - username,emailが空でない - passwordとpasswordVerificationが同じ - passwordは8文字以上 - passwordとpasswordVerificationが違う場合とpasswordが8文字以下の場合は、Labelに警告が表示される(ルール2)  Target/Action方式で実装してみる ---- まずは、作り慣れたTarget/Actionの方法で実装してみます。ViewControllerに、ボタンのenableを操作するupdateButtonEnable:メソッドと、Labelを書き換えるupdatePasswordMessage:メソッドを実装します。また、それぞれのTextFieldにaddTargetしています。 ```objective-c: - (void)viewDidLoad { [super viewDidLoad]; // ルール1 [self.usernameField addTarget:self action:@selector(updateButtonEnable:) forControlEvents:UIControlEventEditingChanged]; [self.emailField addTarget:self action:@selector(updateButtonEnable:) forControlEvents:UIControlEventEditingChanged]; [self.passwordField addTarget:self action:@selector(updateButtonEnable:) forControlEvents:UIControlEventEditingChanged]; [self.passwordVerificationField addTarget:self action:@selector(updateButtonEnable:) forControlEvents:UIControlEventEditingChanged]; // ルール2 [self.passwordField addTarget:self action:@selector(updatePasswordMessage:) forControlEvents:UIControlEventEditingChanged]; [self.passwordVerificationField addTarget:self action:@selector(updatePasswordMessage:) forControlEvents:UIControlEventEditingChanged]; } // ルール1 createボタンのenable条件 -(void)updateButtonEnable:(UITextField *)field { NSLog( @"%@,%@,%@,%@", self.usernameField.text, self.emailField.text, self.passwordField.text, self.passwordVerificationField.text); self.createButton.enabled = [self.usernameField.text length] > 0 && [self.emailField.text length] > 0 && [self.passwordField.text length] >= 8 && [self.passwordField.text isEqual:self.passwordVerificationField.text]; } // ルール2 passwordの条件 -(void)updatePasswordMessage:(UITextField *)field { NSString *message = @""; if( [self.passwordField.text length] < 8){ message = @"need 8 char."; } if( ![self.passwordField.text isEqualToString:self.passwordVerificationField.text] ){ message = @"need same password"; } self.passwordMessageLabel.text = message; } ``` 絵にするとこんな感じです。  ReactiveCocoa方式で実装してみる ---- 次はReactiveCocoaで実装します。Target/Action方式の時に作ったメソッドを、ReactiveCocoaではRACSignalオブジェクトとして実装します。またaddTargetする代わりにRAC()マクロを使ってバインドします。 ```objective-c - (void)viewDidLoad { [super viewDidLoad]; // ルール1 createボタンのenable条件 RACSignal *formValidSignal = [RACSignal combineLatest:@[ self.usernameField.rac_textSignal, self.emailField.rac_textSignal, self.passwordField.rac_textSignal, self.passwordVerificationField.rac_textSignal] reduce:^(NSString *username, NSString *email, NSString *password, NSString *passwordVerification) { NSLog( @"%@,%@,%@,%@", username, email, password, passwordVerification); return @([username length] > 0 && [email length] > 0 && [password length] >= 8 && [password isEqual:passwordVerification]); }]; RAC(self.createButton,enabled) = formValidSignal; // ルール2 passwordの条件 RACSignal *passwordValidSignal = [RACSignal combineLatest:@[ self.passwordField.rac_textSignal, self.passwordVerificationField.rac_textSignal] reduce:^(NSString *password, NSString *passwordVerification) { NSString *message = @""; if( [password length] < 8){ message = @"need 8 char."; } if( ![password isEqualToString:passwordVerification] ){ message = @"need same password"; } return message; }]; RAC(self.passwordMessageLabel,text) = passwordValidSignal; } ``` 絵にすると、Target/Action方式とほぼ同じです。  Target/Action方式とReactiveCocoa方式を見比べる ---- ReactiveCocoa方式では、UITextFieldにrac_textSignaleというプロパティが生えています。またRACSignalのreduce:の戻り値が、RAC()マクロでバインドされたプロパティに値をセットするようです。 ルールの実装が、Target/Action方式では「メソッドに実装」されており、ReactiveCocoaでは「オブジェクトに実装」されているように見えます。 Target/Action方式では、ルールにメソッドの中から直接UIのオブジェクトにアクセスしています。これは「ルール部分とUI部分が癒着している」ようにも見えます。ルールが複雑になると、ここが肥大化して行くのだろうなぁと思います。 一方ReactiveCocoa方式では、ルールがRACSignalとして独立しており、UIとの分離が行われています。ただソースコードの煩雑さが増しています。手放しで喜べるものでもないですね。 RACSignalの連鎖 ---- RACSignalは連鎖する事が出来ます。 ここまでのサンプルでは、ルール1と2に、「パスワードが8文字以上」「パスワードが同じか」のロジックが重複して書かれています。これをRACSignalの連鎖を使って分離します。 RACSignalで拾える「状態変化」は、KVOやUIのイベントNSNotification等いろいろ拾えるようですが、RACSignalそのものも拾えます。なので「パスワードが8文字以上かどうか」というRACSignalと、「パスワードが同じかどうか」というRACSignalを作って、連鎖させてみます。 ややこしいのでまずは絵を見せます。  ```objective-c: - (void)viewDidLoad { [super viewDidLoad]; RACSignal *samePasswordSignal = [RACSignal combineLatest:@[self.passwordField.rac_textSignal, self.passwordVerificationField.rac_textSignal] reduce:^(NSString *password, NSString *passwordVerification){ return @([password isEqualToString:passwordVerification]); }]; RACSignal *passwordLengthSignal = [RACSignal combineLatest:@[self.passwordField.rac_textSignal] reduce:^(NSString *password){ return @([password length]>=8); }]; // ルール1 createボタンのenable条件 RACSignal *formValidSignal = [RACSignal combineLatest: @[self.usernameField.rac_textSignal, self.emailField.rac_textSignal, samePasswordSignal, passwordLengthSignal] reduce:^(NSString *username, NSString *email, NSNumber *passwordsame, NSNumber *passwordLength) { NSLog( @"%@,%@,%@,%@", username, email, passwordsame, passwordLength); return @([username length] > 0 && [email length] > 0 && [passwordsame boolValue] && [passwordLength boolValue]); }]; RAC(self.createButton,enabled) = formValidSignal; // ルール2 passwordの条件 RACSignal *passwordValidSignal = [RACSignal combineLatest:@[samePasswordSignal, passwordLengthSignal] reduce:^(NSNumber *same, NSNumber *length){ NSString *message = @""; if( ![length boolValue]){ message = @"need 8 char."; } if( ![same boolValue] ){ message = @"need same password"; } return message; }]; RAC(self.passwordMessageLabel,text) = passwordValidSignal; } ``` RACSignalの生成時に、combineLatestに別のRACSignalを設定しておけば、そのRACSignalにも反応するため、Signal→Signal→ と連鎖させる事が出来ます。 BOOL型で値を返そうとしてもNSBoolといったオブジェクトが無いので、NSNumblerのboolValueを使っています。 この、Signalが連鎖していく所は、通常のObjective-Cで書く場合に実装しにくい所だと思います。また、Signalの連鎖をfilterで止めたりする事も出来るようです。まだ自分はそこまで理解できてませんが。 さいごに ---- ReactiveCocoaは「リアクティブプログラミング」というパラダイムの1実装のようです。このパラダイムは「オブジェクト指向プログラミング」や「関数型プログラミング」と同じレベルの概念で、正直難しくて分かっていません。ですが「関係性を記述する」という考え方はとても魅力的です。参照:[2010-12-26 なぜリアクティブプログラミングは重要か。](http://d.hatena.ne.jp/pokarim/20101226) ReactiveCocoaを「便利なライブラリ」として見た場合、「状態の変化をトリガーに、他の状態を書き換える」ために使えます。これはMacOS用アプリのCocoaBindingに似ていると思います。 またSignalの連鎖は、普通に作ったときにはあまり思いつかないアイデアです。UIの「ビジネスロジック」部分が複雑でややこしくなる場合、このアイデアでルールを分解して組み合わせる、という使い方が出来ると思います。 一方でReactiveCocoaは、使うには「複雑」なライブラリだと思います。これは元々の「リアクティブプログラミング」というパラダイムを「ライブラリ」としてObjective-Cに組み込んでいるので、Objective-Cと馴染んでおらず、ソースコードの複雑さが増しているように思います。 今現在は、ReactiveCocoaは「使い所が難しい」というイメージで落ち着いています。 参考にしたページ --- - リアクティブプログラミング - [2010-12-26 なぜリアクティブプログラミングは重要か。](http://d.hatena.ne.jp/pokarim/20101226) - 上記blogの中の、この絵が象徴的です。 ~~この絵の「関数」が、RACSignalなんだと自分は理解しています。~~ - コメントで指摘もらいました。この絵の中の「振る舞い」がRACSignalに該当しているそうです。 -  - ReactiveCocoa - https://github.com/ReactiveCocoa/ReactiveCocoa - [Function Reactive Programming Framework - Reactive Cocoa | Cocoaの日々情報局](http://cocoadays-info.blogspot.jp/2013/02/function-reactive-programming-framework.html) - [ReactiveCocoaについて - qiita](http://qiita.com/somtd@github/items/8409ddd6d0927c04c1dd) - [今回作成したコード](https://github.com/taktamur/RACSample) |
|
| 736位 |
|
|||
|
21:04:25 |
|
|
とあるプロジェクトでiTunes関係のサービスを作るので、APIを触ってみる。 > iTunes Store Web Service Search API を使ってアプリ情報を取得する > http://jmblog.jp/archives/798 こちらの記事によると、Appleが標準で提供しているAPIのドキュメントは **古い** らしい。 ## Search API ### URI 現在のAPIのURIは、 http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/wa/wsSearch?[parameters] こうなっている。 ### Parameter パラメーターについては、[Appleのドキュメント](http://www.apple.com/itunes/affiliates/resources/documentation/itunes-store-web-service-search-api.html#searching)に書いてある。 #### term 検索キーワード。URLエンコード必要。必須キー。 ### country iTunes Storeの国を指定する。日本だったら `JP` 。必須。 ### entity iTunesで検索できるもの(曲や映画やアプリ)を絞り込むキー。 必須ではない。 ``` movie : movieArtist, movie music : musicArtist, musicTrack, album, musicVideo, mix, song podcast : podcastAuthor, podcast musicVideo : musicArtist, musicVideo audiobook : audiobookAuthor, audiobook shortFilm : shortFilmArtist, shortFilm tvShow : tvEpisode, tvSeason software : software, iPadSoftware, macSoftware ebook : ebook all : movie, album, allArtist, podcast, musicVideo, mix, audiobook, tvSeason, allTrack ``` この中の一つだけ指定するだけでも、大分検索結果が絞れる。 ### callback JSONP。 javascriptでcallback functionを使う場合指定する。 必須ではない。 使えるものはこんなところ。他にもあるけどAppleのドキュメント読んで。 ### Return ``` http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/wa/wsSearch?term=dinka&country=JP&entity=musicTrack ``` `term` を **dinka** 、 `country` を **JP** 、 `entity` を **musicTrack** にして検索してみる。ちなみにdinkaはアメリカのプログレッシブ・ハウスのクイーン。 返ってきたデータ : ``` { "results": [ { "primaryGenreName": "Dance", "currency": "JPY", "country": "JPN", "trackTimeMillis": 187500, "trackNumber": 15, "trackCount": 20, "discNumber": 1, "discCount": 1, "trackExplicitness": "notExplicit", "collectionExplicitness": "notExplicit", "releaseDate": "2013-01-02T08:00:00Z", "trackPrice": 200, "collectionPrice": 1050, "artworkUrl100": "http://a1.mzstatic.com/us/r1000/062/Music/v4/90/3f/c8/903fc88b-1552-48a3-f7d8-8f690e4bb94f/2400fixEDMitunes.100x100-75.jpg", "trackName": "Radiate (feat. Julie Thompson)", "collectionName": "Manhattan Records Presents \"A Night Out\" - EDM Party Music (Mixed By DJ Kent)", "artistName": "Dinka", "trackId": 584640299, "collectionId": 584639988, "artistId": 268011176, "kind": "song", "wrapperType": "track", "collectionCensoredName": "Manhattan Records Presents \"A Night Out\" - EDM Party Music (Mixed By DJ Kent)", "trackCensoredName": "Radiate (feat. Julie Thompson)", "artistViewUrl": "https://itunes.apple.com/jp/artist/dinka/id268011176?uo=4", "collectionViewUrl": "https://itunes.apple.com/jp/album/radiate-feat.-julie-thompson/id584639988?i=584640299&uo=4", "trackViewUrl": "https://itunes.apple.com/jp/album/radiate-feat.-julie-thompson/id584639988?i=584640299&uo=4", "previewUrl": "http://a1762.phobos.apple.com/us/r1000/086/Music/v4/5c/ff/97/5cff97e9-2f86-9e7c-9dd3-da289c9bd712/mzaf_1805109549567810261.aac.m4a", "artworkUrl30": "http://a3.mzstatic.com/us/r1000/062/Music/v4/90/3f/c8/903fc88b-1552-48a3-f7d8-8f690e4bb94f/2400fixEDMitunes.30x30-50.jpg", "artworkUrl60": "http://a2.mzstatic.com/us/r1000/062/Music/v4/90/3f/c8/903fc88b-1552-48a3-f7d8-8f690e4bb94f/2400fixEDMitunes.60x60-50.jpg" }, { "primaryGenreName": "Dance", "currency": "JPY", "country": "JPN", "trackTimeMillis": 490382, "trackNumber": 14, "trackCount": 40, "discNumber": 1, "discCount": 1, "trackExplicitness": "notExplicit", "collectionExplicitness": "notExplicit", "releaseDate": "2010-07-23T07:00:00Z", "trackPrice": 150, "collectionPrice": 1500, "artworkUrl100": "http://a2.mzstatic.com/us/r1000/004/Music/c4/90/f9/mzi.kizpeple.100x100-75.jpg", "trackName": "Elements (EDX's 5un5hine Remix)", "collectionName": "Ibiza House Tunes 2010", "artistName": "Dinka", "trackId": 381728466, "collectionId": 381728372, "artistId": 268011176, "kind": "song", "wrapperType": "track", "collectionCensoredName": "Ibiza House Tunes 2010", "trackCensoredName": "Elements (EDX's 5un5hine Remix)", "artistViewUrl": "https://itunes.apple.com/jp/artist/dinka/id268011176?uo=4", "collectionViewUrl": "https://itunes.apple.com/jp/album/elements-edxs-5un5hine-remix/id381728372?i=381728466&uo=4", "trackViewUrl": "https://itunes.apple.com/jp/album/elements-edxs-5un5hine-remix/id381728372?i=381728466&uo=4", "previewUrl": "http://a601.phobos.apple.com/us/r1000/072/Music/v4/7b/96/fa/7b96fa0b-6c99-6402-3517-6b495db76ef8/mzaf_7307640393574451724.aac.m4a", "artworkUrl30": "http://a1.mzstatic.com/us/r1000/004/Music/c4/90/f9/mzi.kizpeple.30x30-50.jpg", "artworkUrl60": "http://a3.mzstatic.com/us/r1000/004/Music/c4/90/f9/mzi.kizpeple.60x60-50.jpg" }, (ずっと続く) ``` ### Analyze return data ``` {results: [JSONオブジェクト] } ``` 結果は `results` というキーにJSONオブジェクトの配列で返される。 返されるJSONオブジェクトの中身 : ``` { "primaryGenreName": "Dance", "currency": "JPY", "country": "JPN", "trackTimeMillis": 187500, "trackNumber": 15, "trackCount": 20, "discNumber": 1, "discCount": 1, "trackExplicitness": "notExplicit", "collectionExplicitness": "notExplicit", "releaseDate": "2013-01-02T08:00:00Z", "trackPrice": 200, "collectionPrice": 1050, "artworkUrl100": "http://a1.mzstatic.com/us/r1000/062/Music/v4/90/3f/c8/903fc88b-1552-48a3-f7d8-8f690e4bb94f/2400fixEDMitunes.100x100-75.jpg", "trackName": "Radiate (feat. Julie Thompson)", "collectionName": "Manhattan Records Presents \"A Night Out\" - EDM Party Music (Mixed By DJ Kent)", "artistName": "Dinka", "trackId": 584640299, "collectionId": 584639988, "artistId": 268011176, "kind": "song", "wrapperType": "track", "collectionCensoredName": "Manhattan Records Presents \"A Night Out\" - EDM Party Music (Mixed By DJ Kent)", "trackCensoredName": "Radiate (feat. Julie Thompson)", "artistViewUrl": "https://itunes.apple.com/jp/artist/dinka/id268011176?uo=4", "collectionViewUrl": "https://itunes.apple.com/jp/album/radiate-feat.-julie-thompson/id584639988?i=584640299&uo=4", "trackViewUrl": "https://itunes.apple.com/jp/album/radiate-feat.-julie-thompson/id584639988?i=584640299&uo=4", "previewUrl": "http://a1762.phobos.apple.com/us/r1000/086/Music/v4/5c/ff/97/5cff97e9-2f86-9e7c-9dd3-da289c9bd712/mzaf_1805109549567810261.aac.m4a", "artworkUrl30": "http://a3.mzstatic.com/us/r1000/062/Music/v4/90/3f/c8/903fc88b-1552-48a3-f7d8-8f690e4bb94f/2400fixEDMitunes.30x30-50.jpg", "artworkUrl60": "http://a2.mzstatic.com/us/r1000/062/Music/v4/90/3f/c8/903fc88b-1552-48a3-f7d8-8f690e4bb94f/2400fixEDMitunes.60x60-50.jpg" } ``` 全部を説明するのは面倒なので一部だけ説明する。 ### primaryGenreName ジャンルの名前。 ### trackPrice 楽曲の金額 ### artworkUrl100, artworkUrl130, artworkUrl160 楽曲のアートワークのURLとサイズ。 ### collectionName アルバムやオーディオブックの名前。 ### trackName 楽曲の名前。 ### trackViewUrl 楽曲のiTunesストアのURL。 ### trackId iTunesで管理してる楽曲のID。 だいたいこのようなかたちで検索結果が返される。 ## Lookup API 楽曲の **trackId** がわかると、iTunes Storeの **Lookup API** が使用可能になる。 ### URI ``` http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/wa/wsLookup?[parameters] ``` ### Parameters だいたいSearch APIと同じだが、 **Lookup APiのパラメーターには、かならず `id` パラメーターが必要となる*** `id` は上記のSearchAPIの結果だとtrackIdを持ってくる。 ### Return `id` パラメーターに `trackId` のデータを入れ、 `country` データも入れる。 ``` http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/wa/wsLookup?id=584640299&country=JP ``` 返ってくるデータ : ``` { "primaryGenreName": "Dance", "currency": "JPY", "country": "JPN", "trackTimeMillis": 187500, "trackNumber": 15, "trackCount": 20, "discNumber": 1, "discCount": 1, "trackExplicitness": "notExplicit", "collectionExplicitness": "notExplicit", "releaseDate": "2013-01-02T08:00:00Z", "trackPrice": 200, "collectionPrice": 1050, "artworkUrl100": "http://a1.mzstatic.com/us/r1000/062/Music/v4/90/3f/c8/903fc88b-1552-48a3-f7d8-8f690e4bb94f/2400fixEDMitunes.100x100-75.jpg", "trackName": "Radiate (feat. Julie Thompson)", "collectionName": "Manhattan Records Presents \"A Night Out\" - EDM Party Music (Mixed By DJ Kent)", "artistName": "Dinka", "trackId": 584640299, "collectionId": 584639988, "artistId": 268011176, "kind": "song", "wrapperType": "track", "collectionCensoredName": "Manhattan Records Presents \"A Night Out\" - EDM Party Music (Mixed By DJ Kent)", "trackCensoredName": "Radiate (feat. Julie Thompson)", "artistViewUrl": "https://itunes.apple.com/jp/artist/dinka/id268011176?uo=4", "collectionViewUrl": "https://itunes.apple.com/jp/album/radiate-feat.-julie-thompson/id584639988?i=584640299&uo=4", "trackViewUrl": "https://itunes.apple.com/jp/album/radiate-feat.-julie-thompson/id584639988?i=584640299&uo=4", "previewUrl": "http://a1762.phobos.apple.com/us/r1000/086/Music/v4/5c/ff/97/5cff97e9-2f86-9e7c-9dd3-da289c9bd712/mzaf_1805109549567810261.aac.m4a", "artworkUrl30": "http://a3.mzstatic.com/us/r1000/062/Music/v4/90/3f/c8/903fc88b-1552-48a3-f7d8-8f690e4bb94f/2400fixEDMitunes.30x30-50.jpg", "artworkUrl60": "http://a2.mzstatic.com/us/r1000/062/Music/v4/90/3f/c8/903fc88b-1552-48a3-f7d8-8f690e4bb94f/2400fixEDMitunes.60x60-50.jpg" } ``` ちなみに、このLookup APIも `results` キーにJSONオブジェクトが入っている配列という形で返ってくる。その中身が上。 中身を見ると、SearchAPIで返ってきたデータとほぼ同じ。 ## 重要なこと これらのAPIで返ってくるデータに `trackViewUrl` とか、URLに関するデータがあるが、このURLを **そのまま開くとWeb上でtrackを見ることができる** が、 **`itunes://`** というプロトコルをつけると、 **iTunes上で表示される。*** これでひと通りiTunesAPIの説明(もといメモ)は終わり。 |
|
| 737位 |
|
|||
|
00:00:12 |
|
|
こんにちは、[iOS Advent Calendar / Aug.](http://qiita.com/advent-calendar/ios)28日目担当の[@mtgto](http://twitter.com/mtgto)です。
今日はObjective-Cのソースコードからドキュメントを生成するappledocというツールについて書こうと思います。Javadocや[doxygen](http://www.doxygen.jp/)のObjective-C版といったところでしょうか。 まずはappledocを見たことのない人のために、実例を見ていただきましょう。 [AFNetworkingのドキュメント](http://afnetworking.org/Documentation/) 一見するとApple謹製のドキュメントのようなデザインですが、実はこれ今回ご紹介するappledocによりソースコードのコメントから自動生成されたものなのです。 ## サンプル まずはどのように書けばいいのかということで、自分のプロジェクトの中からドキュメント化してあるメソッドのサンプルを持って来ました。 Javadocなどに親しみのある人にはほとんど調べることなく書くことができると思います。 ```objective-c /** * XMLノードから初期化を行います。 * * @param node XMLノード * @return nodeが不正な場合はnilを返します。 */ + (Channel*)channelWithXMLNode:(GDataXMLNode*)node; ``` 引数や戻り値の説明以外のところ、段落やリスト、引用などは[markdown風の記述を行うことができます](http://gentlebytes.com/appledoc-docs-comments/#layout)。 # インストール appledocはCUIのプログラムです。ソースコードからビルドする場合は、githubからソースコードをcloneしてくる必要があります。 Xcodeのプロジェクトになっているので、自分でビルドしてもよいですが、ビルド&インストールを行う便利なシェルスクリプトが用意されているので、そちらを使用したほうが簡単です。 ```sh git clone git://github.com/tomaz/appledoc.git cd appledoc sudo sh install-appledoc.sh ``` [Homebrew](http://mxcl.github.com/homebrew/)にもappledocのパッケージがあります。 ```sh brew install appledoc ``` [Jenkins](http://jenkins-ci.org/)などによる自動ドキュメント生成ができるとかっこいいんですが、appledoc自体がObjective-C + Cocoa Foundationを使って書かれているので、Mac以外で動くようにするのはちょっと大変そうです。 # 機能 appledocによってできることは大きく次の2つがあります。 - HTMLの生成 - Xcodeヘルプの作成 ## HTMLの生成 先ほど見たAFNetworkingの例のように、HTMLを生成することができます。ブラウザから見れるのでOSSなどのドキュメントとしてウェブサイトで公開するとよさそうです。 ```sh appledoc --project-name プロジェクトの名前 --project-company プロジェクトの会社名 --create-html --no-create-docset --output HTMLを生成するディレクトリ ソースコードの場所 ``` 例として、`~/projects/TestProject`に置かれているプロジェクト"TestProject"のHTML版ドキュメントを~/docs/TestProject"に生成するなら次のようなコマンドになります。 ```sh appledoc --project-name TestProject --project-company TestCompany --create-html --no-create-docset --output ~/docs/TestProject ~/projects/TestProject ``` ## Xcodeヘルプの作成 次にXcode内で使用できる形式のヘルプの作成を行う方法です。 ```sh appledoc --project-name プロジェクトの名前 --project-company プロジェクトの会社名 --no-create-html --create-docset --output docsetを生成するディレクトリ ソースコードの場所 ``` 成功すると`docset`という拡張子でヘルプが生成されます。 ここで`--install-docset`という引数を追加するとXcodeに`docset`がインストールされます。先ほどと同じように"TestProject"のヘルプをXcodeにインストールしてみましょう。 docsetを保存する必要はないので、作業用ディレクトリとして`/tmp`を指定しています。 ```sh appledoc --project-name TestProject --project-company TestCompany --no-create-html --create-docset --output /tmp ~/projects/TestProject ``` docsetのインストールに成功すると`~/Library/Developer/Shared/Documentation/DocSets`に*.docsetというファイルが生成されています。中身はパッケージになっており、Info.plistやHTMLが含まれていることがわかります。 インストール後、Xcodeのヘルプとして使用するためにはXcodeを再起動しなければならないようです(起動時にドキュメントのチェックをしている?)。また起動中に`~/Library/Developer/Shared/Documentation/DocSets`のファイルを削除するとXcodeが強制終了するようです。 再起動後にOrganizerのDocumentationタブを開くと、"TestProject Documentation"というドキュメントが追加されており、Organizerからのドキュメント検索やプロジェクト内でのQuick Helpからドキュメントを参照を行うことができます。やったね。 # plistによるコマンドの記述 先ほどのコマンドラインからの実行ではどうしても引数の数が多くなり、コマンドも長くなってしまいます。 appledocではAppledocSettings.plistというファイルがあればそこから実行引数を取得して実行してくれる機能がついています。 plistをサポートしている、このへんもCUIながらCocoaアプリである所以なのかもしれません。 ```xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>--project-name</key> <string>TestProject</string> <key>--project-company</key> <string>TestCompany</string> <key>--company-id</key> <string>com.example</string> <key>--output</key> <string>/tmp</string> <key>--input</key> <array> <string>TestProject</string> </array> <key>--create-docset</key> <true/> <key>--install-docset</key> <true/> <key>--keep-undocumented-objects</key> <true/> <key>--keep-undocumented-members</key> <true/> <key>--ignore</key> <array> <string>lib</string> <string>Test.m</string> <string>Test.h</string> </array> <key>--exit-threshold</key> <integer>1</integer> </dict> </plist> ``` ## 無視ファイルの指定 `--ignore`に指定している文字列は各ファイル、各ディレクトリの名前の**接尾語**になっているかどうかだけを見ます。なので、上の例だと - libフォルダ以下のすべてのファイル - *Test.m - *Test.h がドキュメント化の対象から外されます。 詳しくは[Ignore pathsの説明 (英語)](http://gentlebytes.com/appledoc-docs-examples-basic/#ignore-paths)をご覧ください。 ## 終了コード appledocは未設定ですと警告が一つでも発生すると終了コードが1になります。 完全にドキュメントされてないファイルがあるとそれだけで警告が出てしまうため、Jenkinsやシェルスクリプトから実行しているときに警告だけで終了コードが0にならないと不便になる時があります。 上記のplistでも設定していますが、`--exit-threshold`で指定した値以下の終了コードは0に丸めることができます。 エラーコードの詳細については、[Controlling appledoc build resultsの説明 (英語)](http://gentlebytes.com/appledoc-docs-examples-xcode/#exit-code-threshold)をご覧ください。 # 注意 appledocを使っていて出くわした(回避不能な?)トラブルを2つ紹介します。 - Objective-Cには名前空間がないため、Xcodeは同じ名前のクラスが複数ドキュメントにあるとどれが選択されるかわからないようです(最初に見つかったものを選んでいるのでしょうか?)。 - たまに解析エラーになるファイルがあるようです。そのときにはappledocの無視ファイルに登録するなどして回避しましょう。 # 終わりに 3日前のコードは自分で書いたものであってもすぐには読めません。 githubなどに置かれる他の人の書いたコードであればもっと時間がかかるでしょう。 そんなときにプロジェクトのドキュメントがあれば、 - 「このメソッドはこういう仕様なんだな」 - 「このクラスはこういう目的で作られてるんだな」 というのが、まあ、少しはわかりやすくなるかもしれません;-p ソースコードを腐らせないためにもドキュメント化をしてみませんか? # 動作確認環境 - MacBook Air (2011) - Mac OS X 10.8 - Xcode 4.4.1 - appledoc 2.0.5 (build 789) # 参考にしたページ - [github - tomaz/appledoc](https://github.com/tomaz/appledoc) - [Gentle Bytes (appledocの作者のウェブサイト)](http://gentlebytes.com/) - [appledocの記法](http://gentlebytes.com/appledoc-docs-comments/) - [Safx - appledocを使ってObjective-CソースをXcode Organizerで表示できるようにする](http://safx-dev.blogspot.jp/2012/06/appledocobjective-cxcode-organizer.html) - [Safx - appledocのドキュメント作成を自動で行うようにする](http://safx-dev.blogspot.jp/2012/06/appledoc.html) |
|
| 738位 |
|
|||
|
00:02:41 |
|
|
## ことの始まり
最近になって、自宅サーバ最高だぜ!から、24時間部屋の中でなるファンの爆音が辛い歳になってきて、さてさて、パブリッククラウドを使うようになった自分ではありますが、AWSってほんとなんでも揃っているデパートですごいなと実感してきました。一昔前は、かなり否定派だったのですが、いざつかってみると、こんなに簡単にいろいろできちゃうものなのか?と本当に感動・・ といいつつ、実際、私はまともに利用していないのですが、知り合いの会社さんから、「こんなんなってます・・」という相談を持ちかけられて、見せてもらったところ、結構びっくりしました。 それは、利用料金・・  うげぇ・・・。企業から見ればそうでもない額なのかもしれないけど、私から見れば、一日でお給料がすっ飛びそうなお値段・・ そんでもってこれを限りなくコストをゼロな構成することができるか、ほぼ趣味で考えてみました。 ## S3の料金体系 調べようと思いましたが、わかりずらいので(汗) [S3の料金体系が分かりにくいと聞かれたので纏めた](http://qiita.com/kawaz/items/07d67a851fd49c1c183e) を参考にさせていただきました。kawazさん曰く、 > 一番高いインターネットへのデータ送信だけ考えておけばよいと思います まさしくこれです。データ送信のところをなんとかすれば、コストを下げれるのではないかと思いいろいろと試したことをメモします。 ## 知り合いさんのサービスについて いろいろな理由で教えてくれませんでした(汗)じゃあ書くな! ざっと聞いた感じでは、S3から何かを持ってきて表示させているものなのかなー?くらいです。そしてここまで料金がかさむってことは、その何かをリクエストのたびに提供しているってこと?(あたりまえか) たとえばですが、10MBのファイルを10人の人が、クライアントからリクエストすれば、10MB X 10人で、100MBのネットワーク転送量になりますね。それが、100人だったら、1000人だったら、100000だったら?って考えれば、いろいろ納得な感じで、「あぁ、いろんな意味ですごいなS3」ってなるのが納得いただけるかと思います。 ・・余談はさておき じゃあ、さっそく策について考えてみたいと思います ## データ送信量を抑えるために ### CloudFront を利用すれば? 色々な方から提案をいただきましたこのCloudFront 詳しくは[アマゾンのサイト](http://aws.amazon.com/jp/cloudfront/)を見ていただくとして、 サイトの説明を要約すると `Amazon CloudFront はコンテンツ配信ネットワークです。これは他のアマゾン ウェブ サービスを統合し、開発者や事業主に、短い待ち時間と高速なデータ転送速度、および契約なしに、エンドユーザーにコンテンツを配信する簡単な方法を提供します。 ` ということなのですね。ふむふむつまり、CDNかぁ。。そうか!CDNってコンテンツデリバリーサービスの略だったのか!と初めて知ったところで、ん?まてよ?たしかに説明を見る限りでは、むちゃむちゃすごいのですが、前述の > 一番高いインターネットへのデータ送信だけ考えておけばよいと思います を考えると、どんだけ早く配信できたとしても、ネットワーク転送料はかわらないじゃん。つまり、コスト削減の近道ではない。。 なので、CloudFrontは、もっと規模が大きくなって収益が見込めるときに使ったほうが良いと判断しましたので提案からは除外しました。 ### AWSやめてVPSとかオンプレミスにすれば? 色んな意味でぶん殴られそうな気がしましたのでこの提案はやめました。そりゃそうですよね。便利だからAWS使っているんですもん。ちょっと触った僕もそう思いましたし。 ### CDNを自作する? どうやらここに落ち着きそうな気がしてきました。そして、これをどのように実現するのかを自分なりに考えてみました。つまりこの苦労話を今後書くわけです ## 自作CDNを作ろう そもそもCDNが何の略かもしれない私が考えてるわけなので、本来のCDNとは大幅にずれた話になりそうな気がしますので、色々な意味でこの話についてのつっこみは無しでお願いします。たぶん説明されても私には何のことがわからない人だと思いますので。 ### ネットワーク転送量で課金されないクラウドを選択 ネットワーク転送量がコストのネックになっている部分であれば、つまりは、ネットワーク転送量で課金されないところを選択すればいいんじゃないのか?つまり、先ほど自己否定していた、VPSとかオンプレミス(データセンター)・・とは言っても私には、データセンターを紹介する力量はないので、必然的に、VPSを選択することになります。 ### VPS サービス会社選定 どこも評判の良いVPS事業者ですが、その中でも私が良いと思ったのは ### [sakura internet](http://www.sakura.ad.jp/) と ### [Conoha by GMO](https://px.a8.net/svt/ejp?a8mat=2NX10F+D5BMPE+50+4YOM2Q) でした。 どちらも通信速度が安定しておりますし、サービスも良さそうです。 また、Conoha は萌えます。 ### ミドルウェア選定 静的コンテンツをキャッシュするミドルウェアは何がいいのかな?ということで、プロキシーサーバをどれにするか迷いました。あ、リバースプロキシ的な使い方をすることが前提で。です。 ### Squid 大昔に自宅サーバでこれを使っていましたが、改めて見ると設定めんどくせーー。と感じましたのでこちらは見送りました。 ### Varnish cache 名前もなんとなくかっこいいし、設定もそんなに難しくなさそうだったのですが、いかんぜん情報があまりあがっていなく、ちょっとどう提案してよいのかわからなかったため、こちらも、見送りました。 ### Nginx ん、なんかかっこよい名前だし、最近人気がありますね。これにしようと・・明確な基準もないまま、Nginxで考えることにしました ## 自作CDN 構成 こうすれば、コストを抑えられるのでは的な考えで作ってみたのが以下の構成です  とまあ、なんか事故レベルでセンスのない構成図になってしまいましたが 要約すると * 初回アクセスは、自作CDNから取得しようと試みるも、キャッシュがないので、S3から持ってきたものを返す -> ここで、自作CDNにキャッシュされる * 2回目以降同様のファイルにアクセス要求がある場合は、キャッシュから取得する、つまり、S3へのアクセスはない * 複数 nginx があるのは、DNSラウンドロビンでアクセスを分散させるため です。 この構成が、ばちっと決まれば、 `10MB X 10人で、100MBのネットワーク転送量になりますね。それが、100人だったら、1000人だったら、100000だったら?って考えれば、いろいろ納得な感じで、「あぁ、いろんな意味ですごいなS3」ってなるのが納得いただけるかと思います` の話でたとえると、S3のアクセスは10MB X 3人の 30MBで済んでしまうわけです。数値的には・・ってだけですが。。 さてさて・・・[結果はこちら](http://qiita.com/hit/items/bbaa15be6e800b8b2882)にのせましたのでぜひご覧くださいませ |
|
| 739位 |
|
|||
|
02:46:20 |
(みんせつ 所属) |
|
```
この記事は2014年01月頃に書いた記事のため現在では内容が古く、正確でないかもしれません。 ``` Dockerを使うと、隔離された環境でアプリケーションを動かすことができます。 今回は実際の開発中のアプリケーションの代わりに、Sentryをデプロイしてみます。 # はじめに **Docker** [Docker, Inc](http://blog.docker.io/)が公開している、Linuxコンテナを使って隔離された環境を構築できるソフトウェア - [仮想環境構築に docker を使う - apatheia.info](http://apatheia.info/blog/2013/06/17/docker/) - [Rebuild: 14: DevOps with Docker, chef and serverspec (naoya, mizzy)](http://rebuild.fm/14/) **Sentry** Pythonで書かれたエラーログギングツール。サービスとして利用することもできるが、自分で構築することもできる。Djangoベース 今回は実際のアプリケーションを想定してgithubから最新版のソースコードをチェックアウトします。 - https://getsentry.com/ # 環境 | | | 備考 | |:-----------|:-----------|:--: | |ホスト(Vagrant + VirtualBox) | Ubuntu 12.04 LTS| http://files.vagrantup.com/precise64.box | |Docker |0.7.2, build 28b162e | | ユーザー名: `ka2n` ホームディレクトリ: `/home/ka2n/` # 構成 Web <-> Host:80 <-> nginx <-> Docker container:4900-4990 Dockerを利用してSentryのコンテナを走らせ、nginx経由で外部に公開します。Sentryが保存するデータはホスト側に保持します。 # セットアップ ## Dockerのインストール [ドキュメント](http://docs.docker.io/en/latest/installation/ubuntulinux/#ubuntu-precise)通りにインストールします。 Ubuntu12.04のカーネルだと不具合があるため、13.04のカーネルに更新 ```bash # カーネルを更新 sudo apt-get update sudo apt-get install -y linux-image-generic-lts-raring linux-headers-generic-lts-raring # 再起動 sudo reboot ``` Docker自体のインストールはすごく楽で、ワンライナーでまとめて実行してくれます。 中身はaptのソースを追加して`apt-get install`しているだけ。 ```bash sudo apt-get install -y curl # curlがなければ curl -s https://get.docker.io/ubuntu/ | sudo sh ``` インストールされたか確認 ```bash sudo docker run -i -t ubuntu /bin/bash # ubuntuコンテナで/bin/bashが立ち上がる。`exit`で閉じる ``` ## イメージファイルを作成 次にDockerイメージをビルドするため、2つのDockerfileを作ります。 - ベース: 言語やライブラリをセットアップした環境 - アプリケーション: ベースにさらにgihtubから最新のコードをチェックアウトしたコンテナ ### Dockerfile ```docker:~/sentry/base/Dockerfile # ubuntuイメージからスタート FROM ubuntu RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list RUN apt-get update RUN apt-get upgrade -y # よく使うコマンドを追加 RUN apt-get install -y curl sudo git-core vim RUN apt-get install -y build-essential gcc ###### Python ##### RUN apt-get -y -q install python python-dev # setuptoolsをインストール ADD https://bitbucket.org/pypa/setuptools/raw/0.8/ez_setup.py /tmp/ez_setup.py RUN python /tmp/ez_setup.py # pipをインストール ADD https://raw.github.com/pypa/pip/master/contrib/get-pip.py /tmp/get-pip.py RUN python /tmp/get-pip.py ``` ```docker:~/sentry/app/Dockerfile # baseイメージからスタート FROM ka2n/base ###### アプリケーションをセットアップ ##### # アプリケーションのコードをコンテナの/appに追加 ADD /src /app # インストール(`bundle install`とか`npm install`とか) RUN pip install /app # 設定ファイルを追加 ADD /conf/sentry.conf.py /sentry.conf.py ##### Docker #### EXPOSE 9000 # Sentryが起動するHTTPサーバのポート ENTRYPOINT ["sentry", "--config=/sentry.conf.py"] CMD ["start"] ``` ## アプリケーションの設定 Sentry用の設定ファイルを作ります。 ポイントとしては、環境変数から設定値を書き込むように変更してあります。 ```python:~/sentry/conf/sentry.conf.py import os.path import os CONF_ROOT = os.path.dirname(__file__) DB_NAME = os.environ.get('SENTRY_DB', os.path.join(CONF_ROOT, 'sentry.db')) DB_USER = os.environ.get('SENTRY_DB_USER', '') DB_PASS = os.environ.get('SENTRY_DB_PASS', '') DB_HOST = os.environ.get('SENTRY_DB_HOST', '127.0.0.1') DB_PORT = os.environ.get('SENTRY_DB_PORT', '') DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': DB_NAME, 'USER': DB_USER, 'PASSWORD': DB_PASS, 'HOST': DB_HOST, 'PORT': DB_PORT, } } SENTRY_PUBLIC = False SENTRY_ALLOW_REGISTRATION = False ################ ## Web Server ## ################ # You MUST configure the absolute URI root for Sentry: SENTRY_URL_PREFIX = os.environ.get('SENTRY_URL_PREFIX', 'http://sentry.example.com') ALLOWED_HOSTS = ["*"] SENTRY_WEB_HOST = '0.0.0.0' SENTRY_WEB_PORT = os.environ.get('SENTRY_PORT', 9000) SENTRY_WEB_OPTIONS = { 'workers': 3, # the number of gunicorn workers 'limit_request_line': 0, # required for raven-js 'secure_scheme_headers': {'X-FORWARDED-PROTO': 'https'}, } ########### ## etc. ## ########### SECRET_KEY = '<Your Secret Key>' ``` ちなみにこれはテスト用途の設定のため、実運用では別途設定値を追加する必要があります。 ## 操作用スクリプト イメージのビルドと実行をするために`build.sh`と`run.sh`を作ります。 ### ~/sentry/build.sh ```bash:build.sh #!/bin/bash APP_IMAGE_NAME=ka2n/sentry # アプリケーションを更新 pushd ./app/src git fetch origin git reset --hard origin/master popd # アプリケーションイメージをビルド docker build -t=$APP_IMAGE_NAME ./app ``` 最新のソースコードのチェックアウトと、インストールまで行ったイメージを作成します。 ### ~/sentry/run.sh ```bash:run.sh #!/bin/bash APP_IMG_NAME=ka2n/sentry APP_VHOST=sentry.example.com # 同じタグで実行中のコンテナをリストアップ CURRENT_CONTAINERS=`docker ps | grep $APP_IMG_NAME | awk '{print $1}'` echo "[Running containers]" echo "$CURRENT_CONTAINERS" # ファイル永続化用のディレクトリを作成 mkdir -p /var/app/sentry # 新しいコンテナを起動する echo "[Launch new container]" docker run \ -d \ -v /var/app/sentry:/sentry/state \ -e SENTRY_DB=/sentry/state/sentry.db \ -e SENTRY_URL_PREFIX="http://$APP_VHOST" \ -e SENTRY_PORT=9000 \ -p 9000 \ $APP_IMG_NAME # コンテナの情報を取得 NEW_ID=`docker ps -l -q` NEW_ADDR=`docker port $NEW_ID 9000` NEW_PORT=${NEW_ADDR#0.0.0.0:} NEW_IP=`docker inspect --format="{{ .NetworkSettings.IPAddress }}" $NEW_ID` echo "[New container info]" echo "CONTAINER ID: ${NEW_ID}" echo "IP: ${NEW_IP}" sleep 1 # NGINX用の設定ファイルを生成 echo "[Write new nginx config]" cat <<EOF > ./nginx-app.conf upstream container-sentry { server 127.0.0.1:$NEW_PORT; } server { listen 80; server_name $APP_VHOST; proxy_set_header Host \$http_host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_redirect off; location / { proxy_set_header Host \$host; proxy_pass http://container-sentry; break; } } EOF # NGINXをリロード service nginx reload # 古いコンテナをシャットダウン echo "[Shutting down old containers]" if [ -n "$CURRENT_CONTAINERS" ]; then docker kill $CURRENT_CONTAINERS fi echo "[Done]" ``` 新しいコンテナを立ち上げます。 データベースなどの情報は環境変数を通じてこの時に渡します。 今回はsqliteを使用するので、データベースファイルを保存する永続化用のディレクトリパスを環境変数`SENTRY_DB`に指定しています。 また、コンテナを破棄 起動するたびにホストへフォーワードされるポート番号が変化するのでnginxの設定を更新する必要があります。 このシェルスクリプトはコンテナを立ち上げた後、nginx用の設定を`~/sentry/nginx-app.conf`に出力します。 ## ファイル構成 最終的なファイル構成は以下のようになりました。 ```bash ~/sentry ├── state # 永続化するデータを保存するディレクトリ ├── nginx-app.conf # 自動生成されるNGINX用の設定ファイル ├── app │ ├── Dockerfile │ ├── conf │ │ └── sentry.conf.py # アプリケーション設定 │ └── src # アプリケーション本体 ├── base │ └── Dockerfile ├── build.sh # ソースコードのアップデート, イメージのビルド └── run.sh # コンテナ実行、nginxへの登録, 古いコンテナの破棄 ``` ## Webサーバの設定 ホスト側にインストールされているNGINXの`nginx.conf`の`http`ディレクティブ内に以下の設定を追記します ```nginx http { # … include /home/ka2n/sentry/nginx-*.conf; } ``` ## アプリケーションのビルド ベースイメージをビルドします。 初回とベースのDockerfileを更新した場合のみこのビルドから実行します。 ```bash sudo docker build -t=ka2n/base ~/sentry/base ``` 次にベースイメージを元にしてアプリケーションイメージをビルドします。 ```bash cd ~/sentry sudo ./build.sh ``` ## アプリケーションの実行 ```bash cd ~/sentry sudo ./run.sh ``` 古いコンテナが破棄され、新しいコンテナが起動します。 データベースの接続情報などはこの時点で初めてアプリケーションに注入されます。 アプリケーションのソースコードが更新された場合でも`build.sh` -> `run.sh`を実行するだけでコンテナごと新しいものに差し替えることができます。 ## まとめ Dockerを使うことで、herokuなどのPaaSに移行したり逆にherokuからDockerへと移行したりと、可搬性の高いアプリケーションにすることができます。 - http://www.docker.io/ |
|
| 740位 |
|
|||
|
23:26:11 |
|
|
## 簡単に
* アニメーションとかゲームみたいに描画のパフォーマンスが必要なもの、凝ったグラフィックを描画したい場合はSurfaceView使え。 * メインスレッド以外でも描画可能だけど、スレッド間での排他処理とかは気をつけてね。 * Surfaceへのアクセスは`SurfaceHolder`インターフェイス経由で行うよ。 ## 普通のViewとSurfaceViewの違い | - | 普通のView | SurfaceView | |:--|:--|:--| | 描画処理 | メインスレッドのみ | バックグラウンドスレッドでも可能 | | 使い道 | 静的なコンテンツ(意味深)に最適 | 常に更新されるような描画内容に最適 | ## 疑問 ### Surfaceってなに? 画面に描画される内容の生のバッファ(のハンドル) ### SurfaceHolderは? `Surface`のピクセルを実際にいじったり、`Surface`の変化を監視する人のためのインターフェイス。`SurfaceView`には`getHolder()`メソッドが用意されていて、その`SurfaceView`のホルダーのインスタンスを取得できる。 ``` public MySurfaceView(Context context) { super(context); SurfaceHolder holder = getHolder(); // でもこのタイミングではまだSurfaceの準備ができてない } ``` ## その前に、知っておかなければ行けないAndroidのグラフィック描画のための4要素 ### ビットマップ 実際にスクリーンに転送される最終的なピクセルの配列。 ### キャンバス(Canvas) 「ここに青い四角を描く」「ここに白い丸を描く」というような命令をビットマップの形にするための機構。 ### プリミティブ 四角(Rect)とか文字列(Text)とかのこと。 ### ペイント(Paint) 色とかスタイルとか、プリミティブを描画する時の属性的な情報。 ## SurfaceViewの基本的な使い方 バックグラウンドスレッドで描画処理できるのが肝だけど、基本を理解するにはとりあえずメインスレッドで一回だけ描画するような使い方をしてみるのがわかりやすい。 基本的には、`SurfaceView`を継承したサブクラスを用意して、その中で独自の描画の処理を書いてく。 ``` public class MySurfaceView extends SurfaceView ``` `SurfaceView`は自身の`Surface`の状態変化についても監視する必要がある(途中でサイズが変わったら、それに応じて描画内容も調整したりとか)ので、実際には`SurfaceHolder`からのコールバックを受け取るための`SurfaceHolder.Callback`をimplementすることになる。 ``` public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback ``` `SurfaceView`のコンストラクタの中で、ホルダーのコールバックに自身をセットしておけば、`Surface`の準備ができたタイミング(実際に描画できるようになったタイミング)等でコールバックが呼ばれるようになる。 ``` public MySurfaceView(Context context) { super(context); getHolder().addCallback(this); } ``` `SurfaceHolder.Callback`で実装しなければいけないメソッドは下記の三つ。各メソッドが呼ばれるタイミングは名前の通り、`Surface`が作られた時/変化があった時/破棄された時。 ``` public void surfaceCreated(SurfaceHolder holder) {} public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} public void surfaceDestroyed(SurfaceHolder holder) {} ``` `surfaceCreated`あるいは`surfaceChanged`が呼ばれたタイミングでは、実際に`Surface`に描画する準備ができている。都合良く引数で`SurfaceHolder`のインスタンスまで渡してくれている。 ``` public void surfaceCreated(SurfaceHolder holder) { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.BLUE); paint.setStyle(Style.FILL); Canvas canvas = holder.lockCanvas(); canvas.drawColor(Color.BLACK); canvas.drawCircle(100, 200, 50, paint); holder.unlockCanvasAndPost(canvas); } // https://www.linux.com/learn/tutorials/707993-how-to-draw-2d-object-in-android-with-a-canvas の内容を少し改変して使用 ``` `SurfaceHolder`からは`Canvas`を取得できるので、そこに対して描画命令を出していく。`lockCanvas()`と`unlockCanvasAndPost()`はマルチスレッド用の排他処理だけど、Canvasの取得と描画内容の反映のために必然的にこのペアを使うことになるので、強く意識しなくても大丈夫かも。 ## 注意事項・おまけ * SurfaceViewの上に普通のUI要素(ボタンとか)を重ねてもOKだけど、パフォーマンスは悪化するよ。 * OpenGLで描画したい場合は`GLSurfaceView`というサブクラスがあるよ。 ## 参考 * http://developer.android.com/reference/android/view/SurfaceView.html * http://developer.android.com/reference/android/view/Surface.html * http://developer.android.com/reference/android/view/SurfaceHolder.html * http://developer.android.com/reference/android/graphics/Canvas.html * https://www.linux.com/learn/tutorials/707993-how-to-draw-2d-object-in-android-with-a-canvas |
|
| 741位 |
|
|||
|
00:25:26 |
(株式会社メルカリ 所属) |
|
`/* ~ //*/` の形式で書いておくと、コメントにしたりコメントを外したりが簡単に切り替えられて便利。
## コメントにしたいとき。 ```javascript /* hogehoge(); fugafuga(); //*/ ``` ## コメントを外したいとき。 先頭の`/*`を`//*`にするだけで切り替わる。スラッシュ一文字を消したりつけたりするだけ。 ```javascript //* hogehoge(); fugafuga(); //*/ ``` |
|
| 742位 |
|
|||
|
00:33:55 |
(Increments 所属) |
|
アプリのビルド設定をいくつか用意して簡単に切り替えたい、という状況がときどきあります。たとえば、開発用サーバに接続するデバッグアプリと本番サーバに接続するデバッグアプリを使い分けたい場合です。
そんなときは Xcode のスキームを使うと便利です。今回は Sample.app を作成し、普通のデバッグビルドに加えて、コンパイル時に `MY_DEBUG` マクロが定義されるデバッグビルドを実行できるようにしてみます。 ## 1. プロジェクトのコンフィグを作る コンフィグは、ビルドに関する様々な設定(ターゲット OS や使用するコンパイラ、リンカオプションなど)をまとめたものです。既存のデバッグ用コンフィグを複製して新しいコンフィグを用意してみましょう。 Project Navigator (Xcode の左ペインのファイルツリー)からプロジェクトを選択します。 Info タブを開くとプロジェクトの情報が表示されます。 Configurations の "Debug" を複製して名前を "Debug (MY_DEBUG)" とします。 [](https://bitbucket.org/uasi/img/raw/master/xcode-scheme/xcode-1.png) ## 2. ビルド設定にマクロの定義を追加する Build Settings タブを開き、 Preprocessor Macros の設定を探します。先ほど作った "Debug (MY_DEBUG)" に `MY_DEBUG` マクロを追加します。 [](https://bitbucket.org/uasi/img/raw/master/xcode-scheme/xcode-2.png) ## 3. 新しいスキームを作りビルド設定を変える 新しいスキームを作り、デバッグビルドに "Debug (MY_DEBUG)" コンフィグを使うように設定してみましょう。 ツールバーの Scheme ボタンをクリックして New Scheme... を選びます。名前を "Sample (MY_DEBUG)" とします(補足:スキーム名にカンマなどを含めるとビルドに失敗することがあります。カッコ以外の記号は使わないほうが無難です)。 Scheme ボタンから Edit Scheme… を選び、 "Sample (MY_DEBUG)" を編集します。左のリストから Run Sample.app を選び、 Info タブの Build Configuration を "Debug (MY_DEBUG)" に変更します。 [](https://bitbucket.org/uasi/img/raw/master/xcode-scheme/xcode-3.png) ## 4. スキームを共有する Scheme ボタンから Manage Schemes… を選び、共有したいスキームの Shared チェックボックスをオンにします。チェックボックスがオンのスキームはファイルに書き出され、他の開発者と共有できるようになります。ファイルは `Sample.xcodeproj/xcshareddata/xcschemes/${スキーム名}.xcscheme` にあるので、バージョン管理システムに追加しておきましょう。 [](https://bitbucket.org/uasi/img/raw/master/xcode-scheme/xcode-4.png) ## 5. できあがり Scheme ボタンから "Sample" か "Sample (MY_DEBUG)" を選ぶと `MY_DEBUG` マクロの有無が切り替わります。 コンフィグを変えるだけでなく、デバッガを使い分けたりビルドの前処理スクリプトを走らせたりと、色々なスキームを作ってみると便利かもしれません。 |
|
| 743位 |
|
|||
|
19:25:07 |
|
|
formのバリデーション作成用の正規表現判定
###数字の場合 全て数値(全角) ``` /^[0-9]+$/ ``` 全て数値(半角) ``` /^[0-9]+$/ ``` 全て数値(全角,半角) ``` /^[0-90-9]+$/ ``` 全て全角数値(マイナス、小数点) ``` /^[ー]?[0-9]+(\.[0-9]+)?$/ ``` 全て半角数値(マイナス、小数点) ``` /^[-]?[0-9]+(\.[0-9]+)?$/ ``` ###文字の場合 半角アルファベット(小文字) ``` /^[a-z]+$/ ``` 半角アルファベット(大文字) ``` /^[A-Z]+$/ ``` 半角アルファベット(大文字・小文字) ``` /^[a-zA-Z]+$/ ``` 半角アルファベット(小文字・数値) ``` /^[a-z0-9]+$/ ``` 半角アルファベット(大文字・数値) ``` /^[A-Z0-9]+$/ ``` 半角アルファベット(大文字・小文字・数値) ``` /^[a-zA-Z0-9]+$/ ``` 全角ひらがな ``` /^[ぁ-ん]+$/ ``` 全角カタカナ ``` /^[ァ-ン]+$/ ``` 全角ひらがな、カタカナ ``` /^[ぁ-んァ-ン]+$/ ``` 半角カナ ``` /^[ァ-ン゙゚]+$/ ``` 漢字 ``` /^[一-龥]+$/ ``` 全角ひらがな、漢字 ``` /^[一-龥ぁ-ん]/ ``` 全角ひらがな、全角カタカナ、漢字 ``` /^[ぁ-んァ-ン一-龥]/ ``` ###e-mailの場合 ``` /^\S+@\S+\.\S+$/ ``` ###URL ``` /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w-.\/?%&=]*)?/ ``` ###電話番号の場合 電話番号(ハイフンなし10桁) ``` /^\d{10}$/ ``` 携帯番号(ハイフンなし11桁) ``` /^\d{11}$/ ``` 携帯番号(ハイフンなし10桁or11桁) ``` /^\d{10}$|^\d{11}$/ ``` ###クレジットの場合 クレジットカード(VISA,Master,Diners,Discover,Amex対応) ``` /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6011[0-9]{12}|3(?:0[0-5]|[68][0-9])[0-9]{11}|3[47][0-9]{13})$/ ``` ###郵便番号の場合 郵便番号(ハイフンあり3桁・5桁・7桁) ``` /^\d{3}[-]\d{4}$|^\d{3}[-]\d{2}$|^\d{3}$/ ``` 郵便番号(ハイフンあり5桁) ``` /^\d{3}[-]\d{2}$/ ``` 郵便番号(ハイフンあり7桁) ``` /^\d{3}[-]\d{4}$/ ``` 郵便番号(ハイフンなし3桁) ``` /^\d{3}$/ ``` 郵便番号(ハイフンなし5桁) ``` /^\d{5}$/ ``` 郵便番号(ハイフンなし7桁) ``` /^\d{7}$/ ``` 郵便番号(ハイフンあり・なし両方) ``` /^\d{3}[-]\d{4}$|^\d{3}[-]\d{2}$|^\d{3}$|^\d{5}$|^\d{7}$/ ``` |
|
| 744位 |
|
|||
|
10:11:17 |
(Ponksoft 所属) |
|
遅ればせながらNode.js始めまして、チャットアプリを作ってみました。
なるべくソースが短くなるようにしてみました。 server.jsとindex.htmlをWebサーバの同じ場所に置いて `node server` で起動します。 ブラウザからサーバの3000番ポートを開いてチャットが出来ます。例`http://example.com:3000` ```js:server.js var html = require('fs').readFileSync('index.html'); var http = require('http').createServer(function(req, res) { res.writeHead(200, {'Content-Type': 'text/html'}); res.end(html); }); var io = require('socket.io')(http); http.listen(3000); io.on('connection', function(socket) { socket.on('msg', function(data) { io.emit('msg', data); }); }); ``` ```html:index.html <!doctype html> <meta charset="utf-8"> <title>chat</title> <script src="/socket.io/socket.io.js"></script> <script src="//code.jquery.com/jquery-2.1.3.min.js"></script> <form><input></form><div></div> <script> var socket = io(); $('form').submit(function() { socket.emit('msg', $('input').val()); $('input').val(''); return false; }); socket.on('msg', function(data) { $('div').prepend(data + '<br>'); }); </script> ``` ##注意点 socket.ioモジュールの読み込みに失敗するときは以下のコマンドでインストールしてください。 ``` npm install socket.io ``` 外部公開する場合はサニタイズしてください。index.htmlの `$('div').prepend(data + '<br>');` の前に `data = $('<div/>').text(data).html();` と一行加えるとタグを無効化できます。 (追記2013/12/8 Thanks to @LightSpeedC さん) `readFileSync` メソッドの代わりに以下の様に `createReadStream` と `stream.Readable.pipe` を使うとブロックされずにパイプで繋いでファイルを流すことができます。 `require('fs').createReadStream('index.html').pipe(res);` (追記終わり) |
|
| 745位 |
|
|||
|
11:21:37 |
|
|
git-completion.bash を導入する。 [git/contrib/completion/git-completion.bash at master · git/git](https://github.com/git/git/blob/master/contrib/completion/git-completion.bash) # 導入方法 + [git/contrib/completion/git-completion.bash](https://github.com/git/git/blob/master/contrib/completion/git-completion.bash) をダウンロードして適当な場所に配置(例えば ~/.git-completion.bash) + ~/.bashrc に `source ~/.git-completion.bash` の一行を加える これだけ |
|
| 746位 |
|
|||
|
17:07:42 |
|
|
業務で iOSアプリの開発をしていると、品質保証のためのテストや第三者検証のために、事前にデバイス登録をした機器にだけインストールできる AdHoc配布用の ipaファイルをテストチームにリリースすることが多いと思います。この ipaファイルは、AdHoc専用の証明書を使ってデジタル署名を行って作成します。
一方、実際に AppStoreに出す時は、Store用の証明書で署名を付けることになります。 AdHoc用の ipaを使ってテストを回し、これが全部終わって出荷可能の判断がくだされた後、Store用にビルドをしなおして Storeに上げる、という運用をしていることもあるでしょう。 もちろんこの場合も「全く同じソースコードベース」で「全く同じビルド設定」でビルドをして、Provisioning Profile と署名用証明書だけが異なるようにすれば理論的には問題ありません。ソースコードだけでいえば、Subversion や Git などのSCMを使っていれば、同じコードベースからのビルドであることはほぼ保証できるでしょう。 ビルド設定についてはどうでしょう。異なる署名でビルドする為にビルド構成(Debug, Release の他に Release_For_Storeを作るとか)を増やしたり、対応するSchemeを増やしたりすることになります。しかしこの方法だと、ビルド設定を変える必要が生じた時に忘れずに Release と Release_For_Store の両方に同じ変更を加える必要がでてきたりして、 **人為ミスが入りこむ余地が生じ、メンテナンスリスクが存在**します。 そうではなく、 **Store用の署名をした ipaをベースに、署名だけを AdHoc用に付け替える**ことができれば、テスト対象にしたipaと、実際に Storeにアップロードする **ipaの実行バイナリ同一性**を担保できます。 ここでは、その方法を説明してみます。 ## Step1:Store用署名をした ipaファイルを zip展開する ```bash % unzip TheApplication.ForStore.ipa ``` ipa ファイルは実際にはzip圧縮されたデータですので、まずはこれを展開します。展開すると `Payload` という名前のディレクトリが出来るはずです。このディレクトリの中に、実際のアプリケーションパッケージ(ここでは `Payload/TheApplication.app`)があり、署名はそのパッケージ全体に対して付されています。 ## Step2:アプリケーションパッケージの中の Provisioning Profileを置き換える ```bash % cp ForAdHoc.mobileprovision Payload/TheApplication.app/embedded.mobileprovision ``` アプリケーションパッケージの中にはビルド時に参照した Provisioning Profileも格納されています。署名を付け替えるのであれば、この Provisioning Profileに紐付けられた証明書と整合してなければ最終的に実機でアプリを起動できません。よって、署名を付け替える前に AdHoc配布用の Provisioning Profileのファイルで上書き置換をする必要があります。ファイル名は `embedded.mobileprovision`という名前決め打ちです。 ## Step3:署名をし直す ```bash % codesign --force --sign 'iPhone Distribution: MyCompany ForAdHoc (XXXXXX)' --resource-rules 'Payload/TheAppliation.app/ResourceRules.plist' 'Payload/TheApplication.app' ``` `codesign`コマンドを使って署名をし直しましょう。--force オプションはすでに署名がされていても、それを消して署名を上書きするためのオプションで、今回は必須です。--resource-rules オプションは署名の際に参照されるルールファイルの指定で、これもアプリケーションパッケージの中に格納されています。 なお、署名に使う証明書の識別子を --sign オプションの引数に渡しますが、当該マシンに入っていて、署名に使うことができる証明書(証明書に対応する秘密鍵も存在している状態のもの)の識別子リストは、下記のコマンドで一覧を得られます。(今回の例では仮に `iPhone Distribution: MyCompany ForAdHoc (XXXXXX)`という識別子を想定しています) また、`security`コマンドではなく、キーチェーンアクセスからGUI上で確認することもできます。 ```bash % security find-identity -p codesigning -v ``` ## Step4:圧縮&アーカイブして AdHoc用 ipaファイルにする ```bash % zip -ry TheApplication.ForAdHoc.ipa Payload ``` `Payload`ディレクトリを zip圧縮して AdHoc用ipaを作ります。ダイナミックリンクライブラリなどによっては パッケージの中に相対参照のシンボリックリンクファイルが存在する可能性があるので、 zipコマンドの -y オプションを念の為に付けています。自分のアプリでは不要であればこのオプションは付けなくてもよいでしょう。 * * * 署名付け替えのステップは以上です。 私の現場では、これをさらに Jenkinsに組みこみ、普段は AdHoc用の ipaだけが生成されるが、任意のタイミングで Store用/AdHoc用両方を生成できる仕組みも構築しています。 Jenkinsのパラメータ付きビルドの仕組みを使って、必要な時だけ署名用証明書の秘密鍵をアップロードすることで実現しているのですが、これについてもいずれ機会があれば書いてみたいと思います。 |
|
| 747位 |
|
|||
|
17:03:03 |
|
|
* [クラスの落とし穴1 - プロパティの初期化](http://qiita.com/cocottejs/items/f7cb629ad17de04bf2fc) * クラスの落とし穴2 - メソッドとクロージャ(この投稿) * [クラスの落とし穴3 - 継承](http://qiita.com/cocottejs/items/e75f751c7aa8a7361aab) * [クラスの落とし穴4 - プライベート変数の実装](http://qiita.com/cocottejs/items/35e0edef71d8c0fc3348) # はじめに [前回](http://qiita.com/cocottejs/items/f7cb629ad17de04bf2fc)は、プロパティをコンストラクタで **初期化しなかった**場合に起きる落とし穴を投稿しました。 今回は、メソッドをコンストラクタで **初期化した**場合に起きてしまう落とし穴を投稿します。 プロパティとメソッドでコンストラクタ初期化するかしないか推奨の実装方法に差があるのは、javascriptの興味深いところです。 # クラス定義 まずは前回のおさらいで、クラス定義を次のように書くようにしていました ```javascript: // クラスを定義 function Klass1 () { // プロパティ this.name = 'foo'; this.hobbies = []; }; // メソッド Klass1.prototype.setName = function setName (value) { this.name = value; }; Klass1.prototype.addHobby = function addHobby (value) { this.hobbies.push(value); }; ``` これを次のように変更しても動作は同じになります。 ```javascript: // クラスを定義 function Klass2 () { // プロパティ this.name = 'foo'; this.hobbies = []; // メソッド this.setName = function setName (value) { this.name = value; }; this.addHobby = function addHobby (value) { this.hobbies.push(value); }; }; ``` # prototypeの役割 上の二つのクラス定義(Klass1, Klass2)から作成されたそれぞれのインスタンスの内容を出力してみましょう ```javascript: var inst1 = new Klass1(); var inst2 = new Klass2(); console.log(inst1); // -> {name: 'foo',hobbies: []} console.log(inst2); // -> {name: 'foo',hobbies: [], setName: [Function: setName], addHobby: [Function: addHobby]} ``` 最初のクラス定義ではメソッドはインスタンスのプロパティではありません。 二番目のクラス定義ではメソッドもプロパティです。 prototypeは、共通の処理を複数のインスタンスから使用することができます。 そのため、最初の方がメモリの節約になる事は分かると思います。 よってこの例では、後者よりも前者の方が優れています。 # クロージャの活用 メモリの節約はできますが、インスタンスに定義することでクロージャを使用する事ができるのは便利です。 jsにすこし慣れた頃に活用したくなって次の様にしてみたくなります。 ```javascript: // クラスを定義 function Klass (name) { // メソッド this.sayName = function sayName () { console.log('私は' + name + 'です。'); }; }; var inst = new Klass('foo'); inst.sayName(); ``` しかし、これはやはりこのようにすべきです。 ```javascript: // クラスを定義 function Klass (name) { // プロパティ this.name = name; }; // メソッド Klass.prototype.sayName = function sayName () { console.log('私は' + this.name + 'です。'); }; var inst = new Klass('foo'); inst.sayName(); ``` 前者のインスタンスに直接メソッドを追加する利点はなんでしょうか? それはnameをprivate変数にしてカプセル化に成功している事があげられます。 しかしそれでもクロージャを安易に多用すべきものではありません。 次の項でクロージャによってメモリリークを引き起こす例を挙げます。 # メモリリークの危険地帯 コンストラクタでは引数に複雑なオブジェクトが渡される事があります。 じつはその際にクロージャはメモリリークの温床になります。 例えば、次の例で確認できます。 ```javascript: // クラスを定義 function Klass (status) { // メソッド this.sayName = function sayName () { console.log('私は' + status.name + 'です。'); }; }; // とりあえず全ステータスを管理するオブジェクト var allStatus = { s1: { name: 'foo', blob: '非常に大きいデータ。例えばBLOBなど' } }; // インスタンス化 var inst = new Klass(allStatus.s1); inst.sayName(); // メモリの開放を期待 delete allStatus.s1; ``` 上記では最後の行でallStatusからs1のプロパティをdeleteしたことで、GCに回収される事を期待しています。 クラスの定義部分は無視して、`var allStatus`以下をみるだけではそのように動作しても良さそうです。 しかしコンストラクタにGC対象を渡している先でクロージャにより捕捉され、 **回収される事はありません。** しかもクロージャで使用したいのはnameの変数だけですが、 **インスタンスが存在する限りblobもメモリに常駐します。** ここでは、blobはallStatus初期化時に設定されていますが、そうとは限りません。 インスタンスが作成された後でもblobの追加は起こりえます。 そうなるとますますメモリリークを調査する事が難しくなります。 コンストラクタの引数がオブジェクトの場合は、クロージャを使用する際に注意する必要がある事がよくわかると思います。 この短いサンプルでも、定義部分の前半とステータスの処理の後半を別々に見ていると、メモリリークの危険性が存在する事は分かりません。 今回はたかが2つのメンバをもつオブジェクトですが、実際にはもっと複雑なオブジェクト例えば循環参照をもつなどのオブジェクトの場合は、多くの不要になったオブジェクトを抱えたままの状態で動作する事になります。 メソッドなどの即時でない関数のスコープの外側にオブジェクト型の変数が存在する状況は **メモリリークの危険地帯** である事を認識してください # プライベート変数を実装 それではnameだけ安全にprivate変数として使用するにはどうすれば良いのでしょうか? それは、次のようにする必要があります。 ```javascript: // クラスを定義 function Klass (status) { // プライベート変数 var name = status.name; // メソッド this.sayName = sayNameCreator(name); }; // nameをクロージャで捕捉したsayNameを返す function sayNameCreator (name) { return function sayName () { console.log('私は' + name + 'です。'); }; }; ``` コンストラクタからnameだけを引数にしたクロージャを活用します。 この場合は、statusがGCの対象になります。 また、nameはプリミティブな変数のため、オブジェクトをそのままprivare変数にするよりメモリリークの危険性がぐっと減りました。 # クロージャ関数のカプセル化 しかしこれでは、sayNameCreatorがKlassと同じスコープに変数として存在してしまいます。 sayNameCreatorをクラス定義にカプセル化できないでしょうか? その為には、更に関数スコープで包括する必要がありそうです。 ここでは即時無名関数を使用する方法を紹介します。 引数はまたnameだけとしましょう ```js:klass.js // クラスを定義 var Klass = (function () { function Klass (name) { // メソッド this.sayName = sayNameCreator(name); }; function sayNameCreator (name) { return function sayName () { console.log('私は' + name + 'です。'); }; }; // クラス定義を返す return Klass; })(); ``` sayNameCreatorは関数内のローカル関数になりました。 そして、Klassはreturnによって外部から参照出来ます。 # Node.jsではカプセル化は不要 最近は、上記のようにクラス定義を関数スコープに閉じ込めて記述している例が多いので、あまり理解せずにクラス定義はこのようにするものだと思ってしまっているかもしれません。 そのため、いつでもカプセル化をしておけば良いと思いがちですが、node.jsなどCommonJSに準拠している場合は、カプセル化はまったくの不要です。 ファイル内ではすべてローカル変数になるため、クラスとして定義された関数は`module.exports`に代入するだけです。 # 変更ができるprivate変数 じつは気づいているかもしれませんが、先のprivate変数なのにクラスからも値を変更できない欠点があります。 では、`setName`メソッドを追加してprivate変数を変更するにはどうすれば良いでしょうか? それは、private変数をオブジェクトに変更します。 ```javascript: // クラスを定義 var Klass = (function () { function Klass (status) { // プライベート変数 var pr = { name: status.name }; // メソッド this.sayName = sayNameCreator(pr); this.setName = setNameCreator(pr); }; function sayNameCreator (pr) { return function sayName () { console.log('私は' + pr.name + 'です。'); }; }; function setNameCreator (pr) { return function setName (name) { pr.name = name; }; }; // クラス定義を返す return Klass; })(); ``` せっかくオブジェクトからメモリリークの起きにくいよう文字列のみカプセル化したのに、またオブジェクトにしたら意味がないと思うかもしれません。しかし状況は以前と全く異なります。プライベート変数を再構築する事でコントロール出来ています。 そのためクラスで必要としない余分な参照が削除されています。 #jsにprivate変数は必要なのか? > ここからは知識の共有というよりは概念やコーディングルールといった内容のため、絶対これが正解といったものではありません。 とここまで、コンストラクタ内で(メソッドの初期化による)クロージャを使用しないでprivate変数を作成する方法を考えてきました。 **しかし** クラス定義で必ずしもprivate変数が必要になるかと思うと個人的にはそうでもないのでは?と思っています 今回は簡単な例でしたので、比較的追っかけるのが楽でしたが、複雑なコードではprivate変数をウォッチするのは大変です。 そのため、クロージャ使用を最低限にしメモリリークの危険を少なくするように心がけた方が良いと思います。。。が コレばかりはオブジェクト指向支持者との宗教戦争になるのでなんとも判断しにくい部分として「わからない」を答えにしてまとめたいと思います。 もしprivate変数を無理に作成しないでコーディング規約のみ処理するのであれば、次のようにします。 結局最初のクラス定義と変わらないように見えますが、どこが違うでしょうか? ```javascript: // クラスを定義 function Klass (name) { // _が最後についた変数は仮想private変数です this.name_ = name; }; // メソッド Klass.prototype.sayName = function sayName () { console.log('私は' + this.name_ + 'です。'); }; ``` ただ変数名の後ろに`_`をつけただけです。 ふざけているようですが。コレによって外部から操作しないという目印にします。 実際にこの方法はよく見かけます。 もちろん`_`でなくてもかまいせんし、位置も前でも後ろでもかまいません。 jsはプロトタイプベースの言語です。忠実にOOを実装することをこだわるのではなく、時にはこういった割り切りも必要です。 また、ECMAScript5環境下ではObject.definePropertyを使用する事でよりprivate変数ぽくなります。 (実際にはアクセスできます) ```javascript: // クラスを定義 function Klass (name) { // _が最後についた変数は仮想private変数です Object.defineProperty(this, 'name_', {value: name, writable: true}); }; // メソッド Klass.prototype.sayName = function sayName () { console.log('私は' + this.name_ + 'です。'); }; var inst = new Klass('foo'); console.log(inst); // {}となり_nameは列挙されない console.log(inst.name_); // 'foo' ``` > クロージャはprivate変数を定義する為に安易に利用しない方がいい場合があるという内容でした。 > それでもクロージャは強力な機能です。クラス定義よりもっと活用する場面があります。それについてはまた次回。 # メモリリークについて 本当のメモリリークと今回あげたメモリリークは厳密に言えば違うのですが、解説のためにわかりやすくしています。 実はコンストラクタ内の関数のスコープで捕捉されたオブジェクトは、インスタンスが破棄されればGCの対象になります。 しかし、javascriptでは一度作成されたインスタンスが確実に破棄されたかを確認しにくいという問題があります。 `delete`キーワードがありますが、それは参照を切るだけで解放の対象にすることを意味しません。 Cのfreeのようなものが存在しません。 別の場所で参照が存在し、それに気がつかないのであればメモリリークと同じです。 そして、クロージャを使用すると無駄に参照が残っていることがあることも多いのも事実です。 # さいごに いろいろと説明しましたが、実は覚えておくことは多くありません。 **「コンストラクタ内にfunctionのキーワードを含まない」** これだけ覚えておくだけで、不要な参照をずいぶんとなくすことができます。 |
|
| 748位 |
|
|||
|
00:43:11 |
|
|
PHPは最近の他の言語とは違ってシングルスレッドなので、一回のリクエストで複数の処理を同時に実行したりといったことは通常はできません。
非同期処理はできず、常に上から順に処理を進めていきます。 どうしてもやりたいなら`` `php hoge.php &` ``などと別プロセスで動かすといった手段しかありませんでした。 http://d.hatena.ne.jp/milktea_cg7/20130529/1369821459 ところでなにやらpthreadsとかいうモジュールを見つけたので使ってみます。 http://www.php.net/manual/ja/book.pthreads.php LinuxであればPECLからインストールしましょう。 Windowsではバイナリが落ちてたので拾ってきます。 http://pecl.php.net/package/pthreads https://github.com/krakjoe/pthreads php_pthreads.dllをエクステンションのディレクトリに、pthreadVC2.dllは環境変数Pathが通っているところに置きます。 そしてphp.iniに以下を記述して再起動。 `extension=php_pthreads.dll` これでpthreadsが使用可能になります。 phpinfo()にpthreadsという項目が追加されるのを確認しましょう。 それではさっそく実行。 ```php <?php class TestThread extends Thread{ /** * @Overide * startしたときに呼ばれる */ public function run(){ // 時間のかかる処理 sleep(10); // このスレッドのIDを確認 print($this->getThreadId()); // これを呼び出したスレッドのIDを確認 print($this->getCreatorId()); } } // TestThreadを別スレッドで起動 $testThread1 = new TestThread(); $testThread1->start(); $testThread2 = new TestThread(); $testThread2->start(); $testThread3 = new TestThread(); $testThread3->start(); $testThread4 = new TestThread(); $testThread4->start(); $testThread5 = new TestThread(); $testThread5->start(); ``` 10秒かかる処理が5個あるということで一見50秒かかりそうですが、実際は10秒で処理が完了します。 TestThread::start()は、TestThread::run()を呼び出しますが、その処理の終了を待たずにすぐに次に進みます。 これでスレッド処理が簡単に行えます。 ```php <?php class TestThread extends Thread{ public function run(){ sleep(10); } } // TestThreadを起動 $testThread = new TestThread(); $testThread->start(); // Thread::run()がまだ走っているか → true var_dump($testThread->isRunning()); // Thread::run()が終了するまで待機 $testThread->join(); // Thread::run()がまだ走っているか → false var_dump($testThread->isRunning()); ``` join()はスレッドが終了するまで待機してくれます。 これで並列処理なんかも簡単に書けるようになりましたね。 まあスレッドが必要になるような処理をPHPで書くなよという話ではありますが。 |
|
| 749位 |
|
|||
|
21:26:33 |
|
|
デフォルトのファイルベースでのセッション管理と比べて
複数のWEBサーバ間でセッション共有してくれたり、今更感はあるけど 幸せになれそうなのでやってみます。 ちなみにNginx+FastCGI環境です。 # 導入 ## インストール PHPがインストールされている前提です。 PHPのモジュールにはmemcachedを使用します。memcache(激似)というものもありますが、[有志の比較](http://d.hatena.ne.jp/masayuki14/20120710)によると現状こちらの方が早いとの事なので。絶賛開発合戦中なので将来的にはわかりませんが。 ```bash # yum install memcached php-pecl-memcached ``` ## 起動 ```bash # chkconfig memcached on # service memcached start ``` ## PHP設定変更 スクリプト側で変更も可能なのでそういう用途にする場合は飛ばしてください。 設定ファイルを変更すればアプリの修正が不要だったり他の開発者さんに意識せずにmemcachedを使ってもらう事が可能ですね。(切替時に一旦セッションが破棄されるとは思いますが) ファイルへのセッション保存設定をコメントアウトし、 新たにmemcachedを使用する設定を追加します。 ```bash # vi /etc/php.ini ; session.save_handler = files // コメントアウト ; session.save_path = "/var/lib/php/session" // コメントアウト # vi /etc/php.d/memcached.ini session.save_handler = memcached // コメントアウトをはずす session.save_path = "localhost:11211" // 追記 ``` WEBサーバが増えた場合はカンマ区切り追記していきます。 session.save_path = "localhost:11211,otherserver.com:11211" ちなみに自分の環境はphp-fpmなのでmemcache.iniの部分と手順が違いました。 ```bash # vi /etc/php-fpm.d/www.conf ; php_value[session.save_handler] = files //コメントアウト ; php_value[session.save_path] = /var/lib/php/session //コメントアウト php_value[session.save_handler] = memcached //追記 php_value[session.save_path] = "localhost:11211" //追記 # service php-fpm reload ``` これでデフォルトでセッション管理がmemcachedになります。 ## PHPでmemcached使用設定 上記デフォルト設定を行わなかった場合は以下の様にスクリプト上で設定を変更します。 ```php $session_save_path = 'localhost:11211'; if (ini_set('session.save_handler', 'memcached') && ini_set('session.save_path', $session_save_path)){ session_start(); } ``` ## 確認 memcachedと同時にインストールされているmemcached-toolで現在のcacheデータを確認できます。 ``` # memcached-tool localhost:11211 dump ``` また、`/var/lib/php/session/`を監視するなり、memcachedを停止して挙動を見て正しく動作しているか確認してみるのも良いでしょう。 もしうまく動作していない場合、セッション開始時にPHPのエラーログが吐かれるはずなので確認してみましょう。 ## ポートを開放 ロードバランシングする際はmemcachedが使用する11211番ポートを他のWEBサーバに対して開放する事 # 実用編 ## キャッシュサイズを変更する memcachedのキャッシュ領域はデフォルトで64MB確保されます。サービス運用でこのサイズでは足りない場合等はこのサイズを変更する必要がありますね。 まずは現在の設定を確認する方法です。 ファイルを1つアップするだけでapcの様にGUIでキャッシュ状況を表示できるようなツールも公開されています。 [memcache.php stats like apc.php](http://livebookmark.net/journal/2008/05/21/memcachephp-stats-like-apcphp/) here is the the source code.の部分からダウンロードして、以下の部分をサーバに合わせて修正してアップするだけとお手頃です。 ```php: define('ADMIN_USERNAME','memcached'); // Admin Username define('ADMIN_PASSWORD','password'); // Admin Password $MEMCACHE_SERVERS[] = 'mymemcache-server1:11211'; ```  わざわざこんな面倒な事したくなーいって方は以下の様にtelnetでも確認できます。 ``` # telnet localhost 11211 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. stats // これを入力します quit // これで終了します ``` すると現在の動作状態が出力されます。 limit_maxbytesの部分が現在のキャッシュサイズですね。byte単位で表記されています。 設定を変更するには ``` # vi /etc/sysconfig/memcached PORT="11211" USER="memcached" MAXCONN="1024" CACHESIZE="64" //ここを変更します(MB) OPTIONS="" # service memcached restart ``` ## 有効期限を変更する memcachedを使っている場合ってガベージコレクションで削除されないから肥大化しない? みたいな疑問もあるかと思いますが、そこは安心memcachedの有効期限で管理されます。 この有効期限、セッションではphp.iniのsession.gc_maxlifetimeが使用されるようになっています。 デフォルトだと1440秒(24分)です。 検証はしてませんが通常のガベージコレクションだと100アクセスに1回呼ばれる形になる部分はmemcachedだとないのでは?と思うので少し短い気もしますね。 変更したい場合はsession_start前に以下の様に設定を変更しましょう。 ``` ini_set('session.gc_maxlifetime', 3600); ``` ## セッション管理以外で使用 セッション管理以外でもmemcachedを使いたいぜ!って事もあるかと思います。 set()で値をキャッシュ、get()で取得します。 ```php: $m = new Memcached(); $m->addServer('localhost', 11211); $m->set('foo','var',60); // 60秒からfooというキーでvarという値をキャッシュします var_dump($m->get('foo')); $m->add('hoge','fuga',60); // set同様に値をキャッシュします $m->add('hoge','piyo',60); // addは同一キーが存在する場合失敗するので次のvar_dumpではfugaのままです var_dump($m->get('hoge')); $m->flush(); // 全てのキャッシュデータを削除します var_dump($m->get('foo')); // 削除されているためFALSEが返ります ``` add()とset()は似ていますが、既に同じキーがキャッシュされている場合add()は失敗します。 さてここで問題が一つ発生します。flush()をしてしまうと、なんとセッションまで消えてしまうではないですか。 というわけでセッション以外のキャッシュのみ削除するという処理も書いておきます。 getAllKeys()で全てのキーを取得し、セッションデータに付与される接頭語を判別、なければ削除という動きです。接頭語は[マニュアル](http://php.net/manual/ja/memcached.sessions.php)にある通り' memc.sess.key.'となりますゆえ、これを先頭一致で判別します。 ```php: $m = new Memcached(); $cachekeys = $m->getAllKeys(); foreach ($cachekeys as $key) { $prefix = 'memc.sess.key.'; if (strpos($key, $prefix, 0) !== 0) { $m->delete($key); } } ``` なのでセッションと他のデータを同時にmemcachedで管理する場合は、キーにprefixを付けていく事を意識した方が良さそうですね。 |
|
| 750位 |
|
|||
|
14:40:48 |
|
|
# vimfiler とは ファイル操作をサポートする Vim のプラグイン。 GitHub で公開されている。 [Shougo/vimfiler.vim](https://github.com/Shougo/vimfiler.vim) # 導入方法 vimfiler は unite.vim の機能を利用するので、まずは unite.vim をインストールする必要がある。 unite.vim インストール後、 NeoBundle を導入済みの場合はまずは以下を .vimrc に記述する。 NeoBundle 'Shougo/vimfiler' そして、ノーマルモードで以下のコマンドを実行すると、 vimfiler をインストールできる。 :NeoBundleInstall # 基本操作 以下のコマンドを実行すると、 vimfiler を起動できる。 :VimFiler 起動すると、カレントディレクトリのディレクトリとファイルが表示される。 j, k でカーソルを上下に移動でき、選択中のファイルは e で開ける。 q で vimfiler を終了できる。 | キー | 操作 | |----------|-------| j | カーソルを下に移動 k | カーソルを上に移動 h | 親ディレクトリへ移動 l | 子ディレクトリへ移動 q | VimFiler を停止 ~ | ホームディレクトリへ移動 M | マスクモード e | 選択中のファイルを開く N | 新規ファイルを作成 K | 新規ディレクトリを作成 D | ファイル削除 yy | 選択中のファイルのフルパスをコピー gs | セーフモードのオン/オフの切り替え Ctrl + l | 画面の再描画 . | 隠しファイルの表示/非表示の切り替え vimfiler は default ではセーフモードで起動し、ファイルの削除や作成はできないようになっている。 ファイルの作成などをするには、 `gs` と入力しセーフモードを解除する必要がある。 また、直近のファイル操作が vimfiler の画面に反映されていない時は、 `Ctrl + l` で画面が再描画するといよい。 ## マスクモード M でマスクモードに入る。 マスクモードでは、 unite.vim のインターフェースでファイルの絞り込みができる。 # 設定(.vimrc) 以下は、私の vimfiler 用の設定。 プラグインをインストールする以外、何の設定も書いていない。 ```vim:.vimrc " VimFiler NeoBundle 'Shougo/vimfiler' ``` # 参考 * [VimFilerはじめました | Scimpr Blog](http://blog.scimpr.com/2013/03/06/vimfiler%E3%81%AF%E3%81%98%E3%82%81%E3%81%BE%E3%81%97%E3%81%9F/) * 導入方法、基本操作についてとても分かりやすく書いてある * [vimfiler のデフォルトキーバインドまとめ(機能別) - Alone Like a Rhinoceros Horn](http://d.hatena.ne.jp/h1mesuke/20100611/p1) * キーバインドが分かりやすくまとめてある * [vimfilerとUniteでVimをファイラとして使用する方法 | karakaram-blog](http://www.karakaram.com/vimfiler) * キーバインドが分かりやすくまとめてある |
|
| 751位 |
|
|||
|
01:32:09 |
|
|
# minttyとは
minttyは端末エミュレータです。PuTTYを基に開発されました。名前も似ていますね。[ちなみにMinTTYという名前だったこともあります。](http://en.wikipedia.org/wiki/Mintty) minttyとPuTTYを比較しますと、 * minttyのほうがPuTTYより更新が頻繁です(latest releaseが、[minttyは2013-4-7](https://code.google.com/p/mintty/wiki/Changes)で、[PuTTYは2011-12-10](http://www.chiark.greenend.org.uk/~sgtatham/putty/))。 * minttyのほうがかっこいい(と私は思います)。 そんなminttyを導入して遊んでみましょう。 - - - # Cygwinの導入 minttyの導入じゃないの?と思われるかもしれませんが、2011年の終わりにminttyはCygwinのデフォルトの端末となりました。なのでCygwinを導入すればminttyが使えます。[MSYS](http://www.mingw.org/wiki/MSYS)を使うという方法もありますが、せっかくなのでCygwinにしました。 さっそくhttp://www.cygwin.com/ からsetup.exeをダウンロードしてインストールしましょう。 導入パッケージについてはとりあえず次のものを選んでおけばよろしいと思います。 * git-svn * zsh * screen * wget * make * openssh * openssl * emacs * vim * g++ あとで追加できるのであまり悩まずさくっと進みましょう。デスクトップにアイコンができますので、実行すればお馴染みの黒い画面が出てきます。 #### 起動時に最大化 起動した時に自動でウィンドウを最大化してくれると嬉しいですよね。 デスクトップにできているCygwin Terminalのショートカットを右クリックし、プロパティを開いてリンク先に次のように書きます。 `C:\cygwin\bin\mintty.exe --window max -i /Cygwin-Terminal.ico -` これでWindows上にUNIXライクな環境ができました。いろいろ遊んでみましょう☆ミ 遊んでいるうちにおそらく # もうちょっとなんとかならんか と思う方が居られるかと思います。デフォルトのままでは今ひとつ使いづらいもので。なのでなんとかしましょう。 まずはログインシェルとホームディレクトリを変更しましょう。 そのためには/etc/passwdを書き換えます。 #### /etc/passwdってどこ Cygwinのルートディレクトリはインストールしたディレクトリ(デフォルトではC:\cygwin)です。なので/etc/passwdはC:\cygwin\etc\passwdにあります。 /etc/passwdの中から自分のログイン名がある行を見つけて、末尾を次のように書き換えます。 `/home/yourname:/bin/bash` → `/cygdrive/c/Users/yourname:/bin/zsh` ホームディレクトリは変更する前はC:\cygwin\home\yournameになっています。これはこれでいいという方も居るかもしれませんが私はC:\Users\yournameに変更してホームディレクトリをWindowsと統一しました。 #### cygdriveってなに ここにWidowsのドライブがマウントされています。`ls /cygdrive/c`とすると、 WindowsのCドライブが見えますね。 minttyを再起動すればログインシェルとホームディレクトリが変更されているのが確認できます。zshもそのままでは使いにくいので設定をいじったほうがいいですがその辺は皆さんにお任せして次に行きます。 - - - # minttyをかっこよくする タイトルバーを右クリックしてOptionsをクリックします。とりあえずTransparency をHighにしてウィンドウを透過させますとスケスケになってかっこよくなります(HighよりGlassのほうがかっこいいかもしれません。お好きな方を選びましょう)。フォントも変更しましょう。[Inconsolata](http://www.levien.com/type/myfonts/inconsolata.html)がオススメです。 [Top 10 programming fonts ](http://hivelogic.com/articles/top-10-programming-fonts)でも一位のイケメンなフォントです。Inconsolataは本家から取得しても良いですが、[Google Fonts](http://www.google.com/fonts/) にあるものはヒンティングの修正が行われており、Clear Type環境下でよりイケメンなレンダリングが期待できるので、こちらから取得するのがおすすめです。 また、Inconsolataには日本語フォントが含まれませんので、メイリオなどをフォントリンクさせるのがオススメです。 #### フォントリンクってなに ちょっとminttyと関係ない話になりますが、フォントリンクについて。 1. レジストリエディタを起動して、 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink に進みます。 2. 新規 -> 複数行文字列値を選択。[新しい値 #1] というキーが作成される。 3. [新しい値 #1] から [Inconsolata] に変更。 4. [Inconsolata] を右クリックし修正を選択。 5. 複数行文字列の編集ダイアログが開くので、値のデータウインドウに任意のフォントキー(日本語フォント)を入力しOKを押してダイアログを閉じる。(メイリオをリンクさせる場合、meiryo.ttc,Meiryo と入力)。 6. PCを再起動。 これで端末上で日本語もきれいに表示されます。なお、名前が知りたいフォントがあった場合はコントロールパネルを開いてフォントに進んで右クリック->プロパティで確認できます。メイリオ以外をリンクさせたい場合はそちらから。 また、Cygwinを導入しますと、端末上から簡単にレジストリを読めます。試しに`ls /proc/registry` と入力して確認してみると楽しいかもしれません。 あとは、 * Text->Localeをja_JP、Character setをUTF-8 * Keys->Backspace sends ^Hをチェックしてバックスペースキーでちゃんと消せるようにする * Mouse->Copy on select と Right click action のPasteをチェックしてPuTTYと同じように操作できるようにする。 などでしょうか。また、これらの設定はホームディレクトリの.minttyrcに保存されます。自分で設定するのはめんどくさいという方は次の内容をコピペするのが手っ取り早いと思います。 ``` BoldAsFont=no Transparency=high Font=Inconsolata FontIsBold=no FontHeight=10 CursorColour=255,0,0 CursorType=block Locale=ja_JP Charset=UTF-8 BackspaceSendsBS=yes RightClickAction=paste ClicksTargetApp=no CursorBlinks=yes FontSmoothing=default CopyOnSelect=yes ``` - - - # <del>apt-cygの導入</del> cyg-fastのほうがいいと思います。導入手順は[こちら](http://qiita.com/d9magai@github/items/ff867393b7c257135e70) 以上でひと通りの設定が終わりました。 mintty(Cygwin)で遊んでいますと「パッケージを新しくインストールしたいな」と思うようになります。Cygwinで新しいパッケージをインストールする場合、基本的にはsetup.exe を使ってGUIで操作します。これは分かりやすいといえば分かりやすいのですが、いちいち起動が面倒ですし、パッケージ選択画面は表示が重いですし、Search欄での検索はインクリメンタルサーチなので一文字入力するごとに待たされてしまいます。 そこでapt-getライクなパッケージ管理を行えるapt-cygを導入します。 ``` $ svn --force export http://apt-cyg.googlecode.com/svn/trunk/ /bin/ $ chmod +x /bin/apt-cyg ``` Google Codeよりapt-cygをダウンロードしてインストールして、実行権限をつけます。これでインストール終了です。 デフォルトでは mirror.mcs.anl.gov を見に行くようになっていますので、国内のリポジトリに設定しましょう。 ``` $ apt-cyg -m http://ftp.iij.ad.jp/pub/cygwin/ update ``` また、次のようにupdateがうまくいかない場合があります。 ``` $ apt-cyg update Working directory is / Mirror is http://ftp.iij.ad.jp/pub/cygwin/ mkdir: ディレクトリ `//http%3a%2f%2fftp.iij.ad.jp%2fpub%2fcygwin%2f' を作成できません: Read-only file system /usr/bin/apt-cyg: line 88: cd: //http%3a%2f%2fftp.iij.ad.jp%2fpub%2fcygwin%2f: No such file or directory ``` こんな時は次のコマンドでWorking directoryを適切に設定してあげましょう。 ``` $ apt-cyg -c /setup ``` #### apt-cygの使い方 `$ apt-cyg update` リポジトリの更新。 `$ apt-cyg find パッケージ名` パッケージを検索。 `$ apt-cyg install パッケージ名` パッケージをインストール。 ラクチンになりましたね。 #### apt-cygをちょっと修正 apt-cygは大文字と小文字を区別します。「ImageMagickをインストールしたいな」と思ったとしましょう。 「magick」で検索をかけます。 ``` $ apt-cyg find magick Working directory is /setup Mirror is http://ftp.iij.ad.jp/pub/cygwin/ Searching for installed packages matching magick: Searching for installable packages matching magick: ``` ありゃあ? 今度はmを大文字にして「Magick」で検索をかけます。 ``` $ apt-cyg find Magick Working directory is /setup Mirror is http://ftp.iij.ad.jp/pub/cygwin/ Searching for installed packages matching Magick: Searching for installable packages matching Magick: GraphicsMagick ImageMagick libGraphicsMagick-devel libGraphicsMagick3 libImageMagick1 libMagick-devel libMagick10 perl-Graphics-Magick perl-Image-Magick ``` 見つかりました。 大文字と小文字を区別せずに見つけてきてくれたほうが嬉しいですので、apt-cygを次のように書き換えます。  これでめでたく`$ apt-cyg find magick`で見つけてきてくれるようになりました。 |
|
| 752位 |
|
|||
|
12:43:50 |
|
|
## はじめに Webアプリケーションを作る手段はLAMPだけではありません。 ちょっとしたものであればnode.js + express + SQLite3を使えば驚くほど簡単に作ることができます。 ここでは例として簡易版twitterのようなミニブログを作ります。 ### 必要となる知識 - コマンドラインツールの扱い方 - HTML, JavaScript - SQL ### このエントリを読んでできること - node.jsを使って短時間でWebアプリケーションを作る ### このエントリを読んでもできないこと - 小規模でないWebサービスを作って運用する - WebSocketを使ってリアルタイムWebアプリケーションを作る ## アウトライン 1.node.jsをインストールする 2.アプリケーションを作って動かしてみる 3.フォームから受け取ったデータを表示する 4.SQLite3を使ってデータを永続化する ## 1.node.jsをインストールする 言語本体のバージョンとパッケージのバージョンとの間にはしばしば悩ましい問題があります。 実は今回用いるSQLite3用のパッケージもご他聞に漏れず、今のところ最新バージョンのnode.js v0.10.xでは動作しません。 しかし、既にnode.jsをインストールしてしまった人もご心配なく。 nvm(Node Version Manager)を使えば複数のバージョンのnode.jsを共存させることができるのです。 ### 1.1 nvmのインストール 下記からnvmをインストールします。 - [nvm](https://github.com/creationix/nvm) ### 1.2 node.js v0.8.xのインストール nvmを使ってnode.js v0.8.xをインストールします(*1)。 ```bash $ nvm install 0.8 ``` v0.8.xがインストールされたことを確認します。 ```bash $ node -v v0.8.x ``` > node.js > http://nodejs.org/ > > Node.js 日本ユーザグループ > http://nodejs.jp/ ## 2.アプリケーションを作って動かしてみる expressはnode.jsのWebアプリケーションフレームワークです。 expressを使えばコマンド1つでアプリケーションの雛形を作ることができます。 ### 2.1 アプリケーションディレクトリの作成 適当な場所にアプリケーションのディレクトリを作ります。 ここでは`miniblog_sample`とします。 ```bash $ mkdir miniblog_sample $ cd miniblog_sample ``` ### 2.2 expressのインストール expressをインストールします。 node.jsのパッケージのインストールにはnpmコマンドを使います。 ```bash $ npm install express ``` ### 2.3 アプリケーションの雛形を作って動かしてみる expressでアプリケーションの雛形を作ります(*2)。 ```bash $ ./node_modules/express/bin/express -e ../miniblog_sample destination is not empty, continue? y(Enter) $ npm install ``` node.jsサーバを起動します。 ```bash $ node app.js Express server listening on port 3000 ``` ブラウザで`http://localhost:3000`にアクセスしアプリケーションが動作していることを確認します。 確認したらCtrl+Cで終了します。 > express > http://expressjs.com/ > > express | 日本語ドキュメンテーション > http://hideyukisaito.github.io/expressjs-doc_ja/ > > ejs > https://github.com/visionmedia/ejs ## 3.フォームから受け取ったデータを表示する フォームから受け取ったデータの表示するためには、(1)テンプレートにフォームとデータ表示部を追加し、(2)サーバースクリプトに受け取ったフォームデータをテンプレートにセットする機能を追加します。 ### 3.1 テンプレート `views/index.ejs`を編集してフォームとデータ表示部を追加します(省略のためにbodyだけ載せています)。 ```html:views/index.ejs <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> <ul> <% if (content) { %> <li><%= content %></li> <% } %> </ul> <form action="/" method="post"> <input type="text" name="content" /> <button type="submit">submit</button> </form> </body> ``` ### 3.2 サーバースクリプト POST時にもテンプレートを表示するように`app.js`に追記します。 ```javascript:app.js app.get('/', routes.index); app.post('/', routes.index); // 追記 app.get('/users', user.list); ``` POSTされたフォームデータをテンプレートにセットするために`routes/index.js`を編集します。 ```javascript:routes/index.js exports.index = function(req, res){ res.render('index', { title: 'Miniblog Sample', content: req.body.content, }); }; ``` サーバーを起動して`http://localhost:3000`にアクセスし、フォームの送信内容が表示されることを確認します。 ## 4.SQLite3を使ってデータを永続化する フォームの投稿内容が表示されるようになりましたが、保存されないのでブラウザを更新すると消えてしまいます。 そこで、データベースの中でも比較的学習コストの小さいSQLite3を使って投稿内容が保存されるように改良しましょう。 ### 4.1 SQLite3とnode-sqlite3のインストール 下記サイトからSQLite3をインストールします。 - [SQLite3](http://www.sqlite.org/) node.jsからSQLite3を扱うパッケージnode-sqlite3をインストールします。 ```bash $ npm install sqlite3 ``` ### 4.2 スキーマ設計 SQlite3を起動してDBファイルを作成します。 ```bash $ sqlite3 db.sqlite3 ``` 投稿内容を保存するためのテーブルを作成します(*3)。 ```SQL CREATE TABLE messages (content TEXT); ``` テーブルが作成されたことを確認してSQLite3を終了します。 ```bash $ sqlite> .tables messages $ sqlite> .quit ``` ### 4.3 データの保存と参照 POSTされたフォームデータをDBに保存し、保存されているデータを参照するために`routes/index.js`を編集します。 ```javascript:routes/index.js var sqlite3 = require('sqlite3').verbose(); var db = new sqlite3.Database('db.sqlite3'); exports.index = function(req, res){ db.serialize(function(){ if (req.body.content) { db.run("INSERT INTO messages (content) VALUES (?)", req.body.content); } db.all("SELECT content FROM messages", function(err, rows){ if (!err) { res.render('index', { title: 'Miniblog Sample', messages: rows, }); } }); }); }; ``` 投稿内容の複数表示に対応するために`views/index.ejs`を編集します(省略のためにbodyだけ載せています)。 ```html:views/index.ejs <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> <ul> <% messages.forEach(function(message){ %> <li><%= message.content %></li> <% }) %> </ul> <form action="/" method="post"> <input type="text" name="content" /> <button type="submit">submit</button> </form> </body> ``` サーバーを起動して`http://localhost:3000`にアクセスし、投稿内容が保存されることを確認します。 > node-sqlite3 > https://github.com/developmentseed/node-sqlite3 ## おわりに おつかれさまでした。いかがでしたでしょうか? node.jsを使えば簡単にWebアプリケーションが作れるということが少しでも伝われば幸いです。 今回作成したサンプルのソースコードは下記GitHubからダウンロードいただけます。 - [GitHub](https://github.com/otoyo0122/miniblog_sample) > *1 node-sqlite3が現在node.js v0.8.xをサポートしているためです > *2 eオプションはテンプレートエンジンとしてejsを用いるためです > *3 運用前提ならインデックスやデータの定期削除も必要になるでしょう |
|
| 753位 |
|
|||
|
10:29:40 |
(Freelancer 所属) |
|
アプリ内で Google Maps を使用するための SDK、"Google Maps SDK for iOS" の導入手順です。
公式情報は [Google Developers](https://developers.google.com/maps/documentation/ios/start) にあります。  ##1. API Keyを取得 - [Google APIs Console](https://code.google.com/apis/console/) の Services タブで、Google Maps SDK for iOS を ON にする。 - API Access タブで、"Create new iOS key" ボタンをクリック。ポップアップで出てくるウィンドウに、Google Maps SDK for iOSを使用するアプリのBundle identifierを入力する。 ##2. SDK をプロジェクトに追加 - GoogleMaps SDK for iOS をダウンロード [Getting the Google Maps SDK for iOS](https://developers.google.com/maps/documentation/ios/start#getting_the_google_maps_sdk_for_ios) - GoogleMaps.framework をプロジェクトに追加する - Build Phasesタブの、Copy Bundle Resourcesに、GoogleMaps.framework フォルダ配下にある GoogleMaps.bundle を追加する ##3. 必要なフレームワークをプロジェクトに追加 - AVFoundation.framework - CoreData.framework - CoreLocation.framework - CoreText.framework - GLKit.framework - ImageIO.framework - libicucore.dylib - libstdc++.dylib - libz.dylib - OpenGLES.framework - QuartzCore.framework - SystemConfiguration.framework ##4. ビルド設定 - Architectures を `armv7` にする(もしくは Valid Architectures から `armv7s` を外す) - Other Linker Flags に `-ObjC` を追加 ##5. Info.plistの編集 Bundle identifier欄に、手順1でAPI Key取得時に入力したBundle identifierを入力する。 ##6. AppDelegate で初期化 - ヘッダをインポート ```` #import <GoogleMaps/GoogleMaps.h> ```` - `application:didFinishLaunchingWithOptions:` で、`provideAPIKey:`メソッドをコール(引数には手順1で取得したAPI Keyを渡す) ```` [GMSServices provideAPIKey:@"YOUR_API_KEY"]; ```` ##7. 地図を表示する ViewController の実装 - ヘッダをインポート ```` #import <GoogleMaps/GoogleMaps.h> ```` - プロパティ追加 ```` @property (nonatomic, weak) GMSMapView *mapView; ```` - GMSMapViewの初期化と諸々の設定 ```` - (void)loadView { GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.8683 longitude:151.2086 zoom:6]; self.mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; self.mapView.myLocationEnabled = YES; self.view = self.mapView; GMSMarkerOptions *options = [[GMSMarkerOptions alloc] init]; options.position = CLLocationCoordinate2DMake(-33.8683, 151.2086); options.title = @"Sydney"; options.snippet = @"Australia"; [self.mapView addMarkerWithOptions:options]; } ```` - 地図の描画を開始する ```` - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.mapView startRendering]; } ```` - 地図の描画を停止する ```` - (void)viewWillDisappear:(BOOL)animated { [self.mapView stopRendering]; [super viewWillDisappear:animated]; } ```` |
|
| 754位 |
|
|||
|
10:07:19 |
|
|
**社内共有用**
# dockerに関する情報共有 [Docker](https://www.docker.io/)の社内プレゼン・ハンズオンやるやるいっててやれていないのでQiitaを使って一部代用。 # docker image を自作するには 自作する方法はあるのだけれど、 今時点だとMac OS Xな環境で行う場合どうすれば楽にできるんだろうか、 って思ったので個人的にメモとしてまとめておく。 今回boot2dockerを利用するため、 以下のブログエントリを参考しました。大変ありがたかったです。 m(_ _)m。 [boot2dockerでdockerを試す](http://blog.n-z.jp/blog/2014-02-23-boot2docker.html) # 環境構築 ## boot2dockerを使おう [boot2dockerでdockerを試す](http://blog.n-z.jp/blog/2014-02-23-boot2docker.html) の通りインストールすればよいので割愛 homebrewでインストールをして 起動準備をして、起動を行う。 boot2dockerインストール時に合わせてdockerもインストールされる。 ```bash > brew install boot2docker > boot2docker init > boot2docker start ``` ## docker hostの設定をする homebrewでインストールを行い、 [DOCKER_HOST](https://github.com/dotcloud/docker/pull/3303)環境変数に boot2dockerのゲストマシンのエンドポイントを指定 ```bash > brew install homebrew > export DOKCER_HOST='tcp://localhost:4243' ``` これで準備完了 ## dockerの動作確認 docker pull ubuntuを試す ``` > docker pull ubuntu ``` で、docker imageをpullすることが出来れば boot2docker, dockerの設定は成功。  # Dockerfileの構築 ## ディレクトリ・ファイルの構成 僕は以下の形で スクリプトや設定ファイルを収めたディレクトリ、 Dockerfileを配置している。  各ディレクトリに何を置いて、 そしてDockerfileに何を書くのかを簡単に書いていく。 ## まずはDockerfile書いてdocker imageをビルドだ Dockerfileに以下のコマンドを書いていく。 ```bash FROM centos MAINTAINER Keiji Matsuzaki <futoase@gmail.com> ``` MAINTAINERの部分は適宜自分の名前・メールアドレスに書き換えること。 [docker indexに表示される](https://index.docker.io/u/futoase/docker-growthforecast/)。  この状態で、```docker build``` を実行する。  docker公式の **centos** docker imageを利用し、 docker imageが構築される。 この場合、特に何もしないから公式のdocker imageと同じものができあがる。 ## 作りたてのdocker imageを起動する 作りたてのdocker imageのIMAGE IDを取得する。 ``` > docker images -q | head -1 9f3172f57d27 ``` 取得したdocker imageのIMAGE IDを利用し、 ```docker run``` コマンドを実行する。 ```bash > docker run -i -t $(docker images -q | head -1) /bin/bash bash-4.1# ``` -i でインタラクティブモード、 -t で仮想端末を有効にする。 そして最後に渡している/bin/bash は、 docker containerで実行するコマンドだ。 以上のオプションを渡すことにより、 入力待ち状態にすることができた。 またdocker containerを作ることもできた! ただこのままだとdocker containerから抜けた瞬間に docker containerが消えてしまうけれど。 # docker templateを書いていこう 簡素なdocker templateを書いて、 試しに動作させてみよう 以下の要件(真顔)を満たす感じで。 - 利用するdocker imageは **docker official imageにあるcentos** とする - reverse proxyとして **nginx** を利用する - 予め用意しておいたウェブアプリを動作させる - 各サービスを起動させるスクリプトを```docker run```時に実行させるようにする - docker containerの外からは80番ポートでアクセスできるようにする ## Dockerfile Dockerfileを以下のように書く。 ```bash:Dockerfile FROM centos MAINTAINER Keiji Matsuzaki <futoase@gmail.com> # setup remi repository RUN wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm RUN wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm RUN curl -O http://rpms.famillecollet.com/RPM-GPG-KEY-remi; rpm --import RPM-GPG-KEY-remi RUN rpm -Uvh remi-release-6*.rpm epel-release-6*.rpm RUN yum -y update && yum -y upgrade # setup nginx yum repository ADD ./templates/nginx.repo /etc/yum.repos.d/nginx.repo RUN yum -y update # setup tools RUN yum -y groupinstall --enablerepo=epel,remi "Development Tools" # install nginx RUN yum -y install --enablerepo=nginx nginx # mkdir /tmp/download RUN mkdir -p /tmp/download # setup ruby-install RUN wget -O /tmp/download/ruby-install-0.4.0.tar.gz https://github.com/postmodern/ruby-install/archive/v0.4.0.tar.gz RUN cd /tmp/download && tar -xvzf ruby-install-0.4.0.tar.gz RUN cd /tmp/download/ruby-install-0.4.0 && make install # cleanup RUN rm -rf /tmp/download # install ruby 2.1.1 RUN ruby-install ruby 2.1.1 ENV PATH /opt/rubies/ruby-2.1.1/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin RUN gem install bundle --no-ri --no-rdoc # install sinatra-hello-world RUN adduser -m app RUN mkdir -p /var/app && chown app:app /var/app RUN su app -c 'git clone https://github.com/Substanz/sinatra-hello-world.git /var/app/sinatra-hello-world' RUN su app -c 'cd /var/app/sinatra-hello-world && bundle ins --path vendor/bundle' # add startup.sh ADD ./scripts/startup.sh /var/app/startup.sh RUN chown app:app /var/app/startup.sh RUN chmod +x /var/app/startup.sh CMD ["/var/app/startup.sh"] ``` ### Dockerfileがやってること 1. remi yum リポジトリの登録 2. nginx yum リポジトリの登録 3. [ruby-install](https://github.com/postmodern/ruby-install) のインストール 4. [ruby v2.1.1](https://www.ruby-lang.org/ja/news/2014/02/24/ruby-2-1-1-is-released/)のインストール 5. [bundler](https://github.com/bundler/bundler)をインストール 6. 用意したサンプルアプリをgithubのリポジトリからcloneする 7. startup.shスクリプトをホストからdocker imageにコピー・実行権限付与 8. [CMD](http://docs.docker.io/en/latest/reference/builder/#cmd) により、detach した時に実行されるコマンドを登録(上記の場合、startup.shを利用) ### ネットワーク経由で手に入らないリソースについて **nginx.conf**, **nginx.repo**, **startup.sh** については ホスト側にあるtemplates, scriptsディレクトリから 必要なファイルをコピーしている。 - **nginx.repo** ファイルを利用して```yum nginx``` リポジトリを登録 - **nginx.conf** ファイルを利用して```nginx``` の設定を行う - **startup.sh** ファイルを利用して```git clone``` を行ったアプリケーションを起動する ## templatesディレクトリ 僕は、templatesディレクトリに マシンのミドルウェアを設定するためのファイルをおいている。 今回はnginxのyum リポジトリ設定ファイル、 リバースプロキシ設定ファイルを置いている。 ## scriptsディレクトリ scriptsディレクトリについては、 マシン起動時に実行するコマンドを書いた スクリプトファイルをまとめるようにしている。 今回は、docker run -d (detach mode)の時に 実行するスクリプトファイルを置いた。 ## docker imageを作成する。 ```bash > docker build . ``` ## boot2dockerのポートフォワードを指定 [dockerのdocument](https://docs.docker.io/en/latest/installation/mac/#forwarding-vm-port-range-to-host)に[VBoxManageコマンド](http://www.virtualbox.org/manual/ch08.html)を実行せよ、と書かれている。 boot2dockerをstopさせたあと、ポートフォワードの設定を行う ```bash > boot2docker stop > VBoxManage modifyvm "boot2docker-vm" --natpf1 "nginx,tcp,,8080,,8080"; ``` boot2docker マシンのIPアドレス設定やポートフォワードなどについては 既に本家にPRが上がっている。 [PR#93](https://github.com/boot2docker/boot2docker/pull/93/)がmergeされれば 便利になると思う... ## 動作確認 終わったら、boot2dockerを起動し、 docker containerを立ち上げ、 http://localhost:8080/ にアクセスする。 ```bash > boot2docker up > docker run -p 8080:8080 -d $(docker images -q | head -1) > open http://localhost:8080/ ``` Welcome to underground... が出ればOK  docker imageの作成手順については以上 :smile: # 他参考 - [Docker commands](http://docs.docker.io/en/latest/reference/commandline/) - [Dockerfile Reference](http://docs.docker.io/en/latest/reference/builder/) - [Docker Run Reference](http://docs.docker.io/en/latest/reference/run/) |
|
| 755位 |
|
|||
|
01:17:32 |
(コロプラ 所属) |
|
Cocoaフレームワークには、KVOと呼ばれるオブザーバの機能を標準で擁しています。 基本的な考え方は、KVO用に作られたメソッドを経由してプロパティを操作することで、適切にその変化をオブザーバに通知する、という仕組みです。 KVOに準拠するためにはプロパティのアクセサメソッドを適切に設定しないとなりませんが、通常のプロパティを使っている場合は特に問題なく準拠できていることになります。 ##KVOの登録 とあるプロパティの値の変化を監視する場合。 ```objc // HogeClassのインスタンスを生成。`fuga`というプロパティを持っている想定 HogeClass *hoge = [[HogeClass alloc] init]; [hoge addObserver:self forKeyPath:@"fuga" options:NSKeyValueObservingOptionNew context:nil]; ``` 監視対象オブジェクトの`addObserver:forKeyPath:options:context:`メソッドにメッセージを送信し、監視元(オブザーバ)として自分自身を登録しています。 この登録を行ったクラス側では、以下のメソッドを実装する必要があります。(値が変化した際に呼び出されるメソッドです) ```objc - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // 変化した値がなにかを判別 if ([keyPath isEqual:@"fuga"]) { // 処理したいキーだった場合は処理 } } ``` ちなみに渡される引数は以下の通りです。 | 引数 | 値 | 例 | |--------|--------|---------| | keyPath | 変化したキー | fuga | | object | 監視対象オブジェクト | hoge | | change | 変更された値 | fugaの値と変化した状態の情報 | | context | 任意のオブジェクト | nil | ###contextについて contextに渡す値はオブザーバに伝えたい任意の値です。 Appleのガイドラインから引用すると、 > オブジェクトをオブザーバとして登録するときに、コンテキストポインタを提供することもできま す。コンテキストポインタは、observeValueForKeyPath:ofObject:change:context:が呼び出さ れたときに、オブザーバに渡されます。コンテキストポインタとしては、C言語のポインタおよびオ ブジェクト参照を指定できます。コンテキストポインタは、監視の対象となる変化を識別する一意の 識別子として、あるいは、オブザーバに何らかのデータを渡すために使用できます。 この`context`を使用する際、ARC環境下では適切にキャストしないとコンパイルエラーになって使用できません。 具体的にはbridgeキャストを用いて以下のようにする必要があります。 ```objc self.context = @"hoge"; [hogeInstance addObserver:self forKeyPath:@"fuga" options:NSKeyValueObservingOptionNew context:(__bridge void *)self.context]; ``` ```objc // 通知を受け取ったとき NSString *hoge = (__bridge NSString *)context; ``` ちなみに`__bridge`キャストについては以下の記事が分かりやすいです。 [[iOS5] ARC : Autorelease, キャスト, 環境設定](http://blog.natsuapps.com/2011/11/ios5-arc-autorelease-bridge-xcode.html) ##KVOの削除 当然、登録した監視は外すことが出来ます。 ```objc [observedObject removeObserver:observer forKeyPath:@"fuga"]; ``` `observedObject`は監視対象オブジェクト、`observer`は監視元オブジェクトです。 (例では`observedObject`が`HogeClass`オブジェクト) ##手動変更通知 監視対象となるプロパティによっては、すべての通知が行われると処理が多くなる場合があります。 その場合は、該当プロパティを手動通知に切り替えてまとめて通知したり、といったことが可能になっています。 手動通知を行う場合は`automaticallyNotifiesObserversForKey:`メソッドを実行し、その中で自動通知か手動通知かを振り分けます。 ```objc + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey { BOOL automatic = NO; if ([theKey isEqualToString:@"fuga"]) { automatic = NO; } else { automatic = [super automaticallyNotifiesObserversForKey:theKey]; } return automatic; } ``` さらに、`willChangeValueForKey:`、`didChangeValueForKey:`メソッドも実装する必要があります。 ###手動通知を実装するアクセサメソッドの例 ```objc - (void)setFuga:(NSString *)theFuga { [self willChangeValueForKey:@"fuga"]; _fuga = theFuga; [self didChangeValueForKey:@"fuga"]; } ``` |
|
| 756位 |
|
|||
|
11:43:22 |
|
|
# はじめに
私はまだまだテスト初心者です. 単体テストすらろくに書いたことが無いので,皆様の知恵など色々お貸し頂ければと思います. 編集リクエストも遠慮無くどうぞ. ここに記載しているコードのライセンスは [NYSL](http://www.kmonos.net/nysl/) でどうぞ. # Espressoとは 2013.10にGoogleが公開したAndroid用のUIテスティングフレームワークですね. まだ公開されて間もないできたてホヤホヤの激熱テストツールです(エスプレッソだけに) 短いコードでアクションだったり評価したり,処理が終わるまで待ってテストしたりということを自動でやってくれます. # Espresso導入 ここからEspressoのjarを落として,そのままテストプロジェクトのlibsディレクトリに突っ込んで終わりです. https://code.google.com/p/android-test-kit/source/browse/#git%2Fbin%2Fespresso-standalone # Espresso前準備 ## AndroidManifest.xmlの<manifest>タグの中に下記コードを追加します. targetPackageのところはテスト対象となるアプリのパッケージ名を入力してください. android.permission.SET_ANIMATION_SCALE のパーミッションは,アニメーションの実行速度を変更するパーミッションです. 毎回開発者オプションから変更するのは面倒なので,テストコード実行前に変更を行い,テスト後に元に戻すようにします. ```xml:AndroidManifest.xml <uses-permission android:name="android.permission.SET_ANIMATION_SCALE" /> <instrumentation android:name="com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner" android:targetPackage="com.example.appli" /> ``` ## Instrumentation runnerを変更します 1. Eclipseのテストプロジェクトを右クリックして実行(run)→実行の構成(Run Configuration)を開きます. 1. 左ペインの Android JUnit Test から,目的のテストプロジェクトを選択します. (一覧に無い場合は左上のアイコンから新規作成を行います.) 1. テスト(Test)タブのRun all tests in the selected project, or packageにチェックを入れます. 1. 新規作成した場合は,名前を設定し,テスト対象のプロジェクトを選択します. 1. Instrumentation runner:で「com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner」を選択します. 1. 適用(Apply)をクリックして完了です. なお,どうやらGoogleInstrumentationTestRunnerへ変更すると,クラス単位やメソッド単位でのテストができなくなるようです. ## Activityテストのためのテンプレート * setUpメソッド ここはテストを開始する前に呼び出されるメソッドです. オーバーライドしたgetActivity()をコールしてActivityを取得します.このとき,Activityも作成されます. さらにTestUtils#toggleAnimationEnable()をコールます.(TestUtils.javaについては下記参照) * getActivityメソッド getActivity()をオーバーライドしていますが,これは呼び出すActivityへIntent情報などを渡したい場合にオーバーライドすれば良く,何も指定する必要が無い場合はオーバーライドしなくて構いません. 今回は IntentにBundleで情報を渡したいと仮定してオーバーライドしています. * tearDownメソッド tearDown()はテスト終了後に行う処理です. 今回はsetUp()で無効化したアニメーションをここで有効化(元にもど)します. * test*メソッド 後は通常のJUnit同様テストメソッドにシナリオを書いていくだけです. が,Activity起動~終了の1サイクルを1シナリオテストとして,複数のシナリオテストをしたい場合は,同じように別のTestクラスを作成していきます. ```java:ActivityTest.java package com.example.appli; import android.app.Activity; import android.content.Intent; import android.test.ActivityInstrumentationTestCase2; import com.example.appli.TestUtils; public class MainActivityTest extends ActivityInstrumentationTestCase2<TestActivity> { private static final String TAG = MainActivityTest.class.getSimpleName(); private Activity mActivity; public MainActivityTest() { super(TestActivity.class); } @Override public void setUp() throws Exception { super.setUp(); mActivity = getActivity(); TestUtils.toggleAnimationEnable(mActivity, TAG, false); } @Override public TestActivity getActivity() { Intent intent = new Intent(); intent.putExtra("key", "value"); setActivityIntent(intent); return super.getActivity(); } @Override protected void tearDown() throws Exception { TestUtils.toggleAnimationEnable(mActivity, TAG, true); super.tearDown(); } public void testStory() throws Exception { // TODO ここにテストを書く } } ``` ### TestUtils (コピペでOKです) ウィンドウアニメーションスケールとトランジションアニメーションスケールを変更します. 上記のMainActivityTest.javaではsetUp()とtearDown()から呼び出しています. https://code.google.com/p/android-test-kit/wiki/DisablingAnimations の情報を元に少しだけ手を加えています. ```java:TestUtils.java package com.example.appli; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import android.content.Context; import android.content.pm.PackageManager; import android.os.IBinder; import android.util.Log; public final class TestUtils { private static final int RESULT_ENABLED = 0; private static final int RESULT_DISABLED = 1; private static final int RESULT_NG = -1; /** * EspressoでUIテストを実行する場合は setUp メソッドで無効にし,tearDownで有効にしてください * * @param context * @param TAG * @param isEnable */ public static void toggleAnimationEnable(Context context, String TAG, boolean isEnable) { int permStatus = context .checkCallingOrSelfPermission("android.permission.SET_ANIMATION_SCALE"); if (permStatus == PackageManager.PERMISSION_GRANTED) { int result; if ((result = reflectivelyToggleAnimation(TAG, isEnable)) != RESULT_NG) { if (result == RESULT_ENABLED) { Log.i(TAG, "All animations enabled."); } else { Log.i(TAG, "All animations disabled."); } } else { Log.i(TAG, "Could not toggle animations."); } } else { Log.i(TAG, "Cannot disable animations due to lack of permission."); } } private static int reflectivelyToggleAnimation(String TAG, boolean isEnable) { try { Class<?> windowManagerStubClazz = Class.forName("android.view.IWindowManager$Stub"); Method asInterface = windowManagerStubClazz.getDeclaredMethod("asInterface", IBinder.class); Class<?> serviceManagerClazz = Class.forName("android.os.ServiceManager"); Method getService = serviceManagerClazz.getDeclaredMethod("getService", String.class); Class<?> windowManagerClazz = Class.forName("android.view.IWindowManager"); Method setAnimationScales = windowManagerClazz.getDeclaredMethod("setAnimationScales", float[].class); Method getAnimationScales = windowManagerClazz.getDeclaredMethod("getAnimationScales"); IBinder windowManagerBinder = (IBinder) getService.invoke(null, "window"); Object windowManagerObj = asInterface.invoke(null, windowManagerBinder); float[] currentScales = (float[]) getAnimationScales.invoke(windowManagerObj); for (int i = 0; i < currentScales.length; i++) { if (isEnable) { currentScales[i] = 1.0f; } else { currentScales[i] = 0.0f; } } setAnimationScales.invoke(windowManagerObj, currentScales); return isEnable ? RESULT_ENABLED : RESULT_DISABLED; } catch (ClassNotFoundException cnfe) { Log.w(TAG, "Cannot disable animations reflectively.", cnfe); } catch (NoSuchMethodException mnfe) { Log.w(TAG, "Cannot disable animations reflectively.", mnfe); } catch (SecurityException se) { Log.w(TAG, "Cannot disable animations reflectively.", se); } catch (InvocationTargetException ite) { Log.w(TAG, "Cannot disable animations reflectively.", ite); } catch (IllegalAccessException iae) { Log.w(TAG, "Cannot disable animations reflectively.", iae); } catch (RuntimeException re) { Log.w(TAG, "Cannot disable animations reflectively.", re); } return RESULT_NG; } private TestUtils() { // インスタンス化の禁止 } } ``` # Espresso UI テストの基本 やっと前準備が終わりました.あとはゴリゴリテストを書いていくだけです. 基本的なメソッドは下に列挙していきます. ## Espresso.onView(Matcher<View> matcher) Matcherを渡すと,一致したViewのViewInteractionを返します. このViewInteractionに対して,クリックとか値のチェックなどを指定できます. ## ViewMatchers.withId(int id) いつものfindViewByIdと同じく,ViewのIDを渡すと一致したViewのViewMatcherを返します.1 ```java:e.g. Espresso.onView(ViewMatchers.withId(R.id.button)) ``` のように記述します. ## ViewInteraction.perform(ViewAction...) ViewInteractionのメソッドです.引数で渡されたViewActionを実行します. 例えば,渡されたViewActionがViewActions.click()なら,そのViewをクリックします. ```java:e.g. Espresso.onView(ViewMatchers.withId(R.id.button)).perform(ViewActions.click()); ``` ## ViewInteraction.check(ViewAssertion) ViewInteractionのメソッドです.引数で渡されたViewAssertionでViewの状態を検査します. ```java:e.g. // R.id.button が表示されていることをチェックします. Espresso.onView(ViewMatchers.withId(R.id.button)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())); ``` ```java:e.g. // R.id.button が表示されていないことをチェックします. // notメソッドは org.hamcrest.Matchers.not Espresso.onView(ViewMatchers.withId(R.id.button)).check(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))); ``` ## ViewMatchers.isDisplayed() その名の通り,Viewが表示されているかを返すメソッドです. ## ViewMatchers.withText() Viewが持っているテキストが一致するかを返すメソッドです. ## Tips ViewMatchersやViewAssertions等,大量にstaticメソッドを利用するので,staticインポートしてしまった方がコードが短くなります. EclipseならViewMathers.withIdなど,staticメソッドにカーソルを合わせて Ctrl + Shift + Mで自動的にstaticインポートしてくれます. staticインポートすると,このようになります ```java:before Espresso.onView(ViewMatchers.withId(R.id.button)).check(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))); ``` ```java:after onView(withId(R.id.button)).check(matches(not(isDisplayed()))); ``` # 欲しい ViewMatcherやViewActionが無い場合 歴史が浅いためか,見た感じ欲しいViewMatherやViewActionが無かったりします. この場合は自分で書くようです. 例えば,前方一致でTextViewの値を検査したいとしますが,ViewMatchersにはそれらしきメソッドがありません. (というか,このくらいありそうな物ですが,なにか方法があれば教えてください) ## ViewMatcherを自作する というわけで,いくつかのViewMatcherを作ってみます. (あまり自信ないので,おかしなところ有ればご指摘ください) ```java:EspressoUtils.java package com.example.appli.espresso; import static org.hamcrest.Matchers.is; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; import com.google.android.apps.common.testing.ui.espresso.matcher.BoundedMatcher; public final class EspressoUtils { /** * TextViewの文字列を前方一致で検査 * * @param expectString * @return */ public static Matcher<View> startsWith(final String expectString) { final Matcher<String> textMatcher = is(expectString); return new BoundedMatcher<View, TextView>(TextView.class) { @Override protected boolean matchesSafely(TextView textView) { CharSequence text = textView.getText(); if (text == null) { return text == expectString; } return expectString.startsWith(text.toString()); } @Override public void describeTo(Description description) { description.appendText("start text : "); textMatcher.describeTo(description); } }; } /** * TextViewの文字列を後方一致で検査 * * @param expectString * @return */ public static Matcher<View> endsWith(final String expectString) { final Matcher<String> textMatcher = is(expectString); return new BoundedMatcher<View, TextView>(TextView.class) { @Override protected boolean matchesSafely(TextView textView) { CharSequence text = textView.getText(); if (text == null) { return text == expectString; } return expectString.endsWith(text.toString()); } @Override public void describeTo(Description description) { description.appendText("end text : "); textMatcher.describeTo(description); } }; } /** * ProgressBarのプログレスを前方一致で検査 * * @param expectString * @return */ public static Matcher<View> withProgress(final Integer expectProgress) { final Matcher<Integer> progressMatcher = is(expectProgress); return new BoundedMatcher<View, ProgressBar>(ProgressBar.class) { @Override protected boolean matchesSafely(ProgressBar progressBar) { return progressMatcher.matches(progressBar.getProgress()); } @Override public void describeTo(Description description) { description.appendText("with progress : "); progressMatcher.describeTo(description); } }; } private EspressoUtils() { } } ``` みたいな感じでしょうか. テキストの前方,後方一致と,ProgressBarのプログレスの値を検査するメソッドを作ってみました. なお,ButtonやEditTextはTextViewを,SeekBarやRatingBarはProgressBarを継承しているため,同じ検査メソッドで利用できます. # シナリオテスト いよいよシナリオテストですが,実際は上のコードを組み合わせて,Viewを操作して状態を確認して…の繰り返しです. 簡単に書いてみます. ここでは,MainActivityにEditTextとButtonを持ち,EditTextに入力した内容がSubActivityに送られて,そこのTextViewに文字列が表示される.ということで書いてみます. ## MainActivity ```java:MainActivity.java package com.example.test; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; public class MainActivity extends Activity { private static final int WC = LayoutParams.WRAP_CONTENT; private static final int MP = LayoutParams.MATCH_PARENT; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final LinearLayout rootLayout = new LinearLayout(this); rootLayout.setOrientation(LinearLayout.VERTICAL); rootLayout.setLayoutParams(new LayoutParams(MP, MP)); setContentView(rootLayout); // EditText final EditText editText = new EditText(this); editText.setId(R.id.editText); editText.setLayoutParams(new LayoutParams(MP, WC)); editText.setHint("'test'で始まる文字列か,'123'で終わる文字列か,'abc'を入力してください"); rootLayout.addView(editText); // Button final Button button = new Button(this); button.setId(R.id.button); button.setLayoutParams(new LayoutParams(WC, WC)); button.setText("OK"); rootLayout.addView(button); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String input = editText.getText().toString(); Intent intent = new Intent(); intent.putExtra("input", input); intent.setClassName(getApplicationContext(), "com.example.test.SubActivity"); startActivity(intent); } }); } } ``` ## SubActivity.java ```java:SubActivity.java package com.example.test; import android.app.Activity; import android.os.Bundle; import android.view.ViewGroup.LayoutParams; import android.widget.LinearLayout; import android.widget.TextView; public class SubActivity extends Activity { private static final int WC = LayoutParams.WRAP_CONTENT; private static final int MP = LayoutParams.MATCH_PARENT; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final LinearLayout rootLayout = new LinearLayout(this); rootLayout.setOrientation(LinearLayout.VERTICAL); rootLayout.setLayoutParams(new LayoutParams(MP, MP)); setContentView(rootLayout); Bundle bundle = getIntent().getExtras(); String text = bundle.getString("input"); // TextView final TextView textView = new TextView(this); textView.setId(R.id.text); textView.setLayoutParams(new LayoutParams(WC, WC)); textView.setText(text); rootLayout.addView(textView); } } ``` ## /res/values/view_ids.xml ```xml:view_ids.xml <?xml version="1.0" encoding="utf-8"?> <resources> <item name="editText" type="id"/> <item name="button" type="id"/> <item name="text" type="id"/> </resources> ``` ## MainActivityTest.java ```java:MainActivityTest.java package com.example.test; import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView; import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click; import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.typeText; import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.matches; import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId; import android.app.Activity; import android.content.Intent; import android.test.ActivityInstrumentationTestCase2; import com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers; import com.example.test.TestUtils; public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> { private static final String TAG = MainActivityTest.class.getSimpleName(); private Activity mActivity; public MainActivityTest() { super(MainActivity.class); } @Override public void setUp() throws Exception { super.setUp(); mActivity = getActivity(); TestUtils.toggleAnimationEnable(mActivity, TAG, false); } @Override protected void tearDown() throws Exception { TestUtils.toggleAnimationEnable(mActivity, TAG, true); super.tearDown(); } public void testStory() throws Exception { onView(withId(R.id.editText)).perform(typeText("123456")); onView(withId(R.id.button)).perform(click()); onView(withId(R.id.text)).check(matches(ViewMatchers.withText("123456"))); } } ``` これでプロジェクトファイルを右クリック→実行→Android JUnit Testを選択するとテストが実行されます. ちょっと速いですが,画面の動きを見ると,ちゃんとEditTextに文字列が入力され,次のSubActivityへ遷移して,そこのTextViewに入力された文字列が表示される事が分かります. もちろん,JUnitの検査バーはグリーンになるはずです. ところで,EditTextのヒントに入力文字列の条件が書かれています. コレを次のSubActivityのTextViewから取得してテストします…しようと思ったんですが,力尽きました. なので,みなさんぜひ挑戦してみてください. # Tips つかれたので,あとでまとめます…(あんまりないけど) Activity遷移後のActivityインスタンスのとりかたとか… |
|
| 757位 |
|
|||
|
18:04:55 |
|
|
つい先日、GitHubで管理していたテスト用中央ブランチに、チームメンバーが誤って```git push --force```してしまい、
一部の歴史が消失するという事件が起きました。 ぎゃあああ!なんばしよっとね!うっかりでしたじゃ済まんばい! とか思っていたらJenkinsの開発者みたいなスゴい人でもやらかしちゃうみたいです。 Jenkinsの開発者、間違えて一ヶ月前のローカルレポジトリをgit push --forceしてしまう http://cpplover.blogspot.jp/2013/11/jenkinsgit-push-force.html スゴい人でもやらかすんだから、平民の我々もそのうちやらかすに違いない。 緊急バグ修正などで慌てていたら尚更ですね。(というか自分が一番やりかねない) というわけで、何とか仕組みの上で防くことができればと思って仕掛けることにしました。 ----- 以下のスクリプトを、```.git/hooks/pre-push```というファイル名で保存。 push直前に起動し、大事なブランチへ```git push --force```しそうであればエラーを返して食い止めてくれます。 ``` #!/bin/sh # 守りたいbranch名を定義 PROTECTED_BRANCHES=( trunk master ) CURRENT_BRANCH=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,') PUSH_COMMAND=$(ps -ocommand= -p $PPID) IS_DESTRUCTIVE='force|delete|\-f' WILL_REMOVE_PROTECTED_BRANCH=':'$PROTECTED_BRANCH for i in "${PROTECTED_BRANCHES[@]}" do PROTECTED_BRANCH=$i # エラーメッセージを定義 MESSAGE="$PROTECTED_BRANCH には $PUSH_COMMAND してはダメです!先にgit fetch && git merge origin $PROTECTED_BRANCH すること。by pre-push hook" if [[ $PUSH_COMMAND =~ $IS_DESTRUCTIVE ]] && [ $CURRENT_BRANCH = $PROTECTED_BRANCH ]; then echo $MESSAGE exit 1 fi if [[ $PUSH_COMMAND =~ $IS_DESTRUCTIVE ]] && [[ $PUSH_COMMAND =~ $PROTECTED_BRANCH ]]; then echo $MESSAGE exit 1 fi if [[ $PUSH_COMMAND =~ $WILL_REMOVE_PROTECTED_BRANCH ]]; then echo $MESSAGE exit 1 fi done exit 0 ``` 参考 https://gist.github.com/pixelhandler/5718585 自前の開発環境に合わせて少々書き換えました。 ----- 本当はサーバサイドフックを利用して、特定ブランチへの破壊的なpushであれば云々、みたいな処理にしたかったのですが、 GitHub上で実現する方法がよくわからず、やむなくpre-pushを利用しました。 クライアントサイドフックなので、中央ブランチを完璧に守るためには、開発者全員の環境に仕掛ける必要があります。 あと、pre-pushはgitのバージョンによっては動かないそうです。 1.8.2以上であれば動作するはずです。(1.8.3環境で確認済み) |
|
| 758位 |
|
|||
|
15:51:47 |
|
|
Bashのスクリプト内で {} や () や [] などの記号の意味。 詳細はここの記事に出ている。 http://stackoverflow.com/questions/2188199/how-to-use-double-or-single-bracket-parentheses-curly-braces http://mywiki.wooledge.org/BashFAQ/031 ## bracket [] の意味 []はtestコマンドの略式。 if文の引数で使う事が多い。 ```bash if [ -e "file.txt" ]; then echo "File exists" fi ``` `[` の直後と `]`の直前には必ず半角スペースが必要となる。 例えば ```bash if [-e "file.txt" ]; then echo "File exists" fi ``` と書くと、`[-e: command not found` というエラーメッセージが出る。 ## double bracket [[ ]] bracket と基本的には同じ。 ~~ただし、こちらはbashのビルトインコマンド~~ (20150428追記) single bracketもbashの場合ビルトインコマンド。 double bracketの方が後に追加されたもので、bash,zsh,Korn shellでのみ使える。 tcshなどでは、`[[`は使えません。 ```bash if [[ -e "file.txt" ]]; then echo "File exists" fi ``` double bracketの方がsingle bracketよりも機能が充実しており、`&&`, `||`, Pattern matching, 正規表現などが使える。 ``` [[ 1 -lt 2 && 2 -lt 3 ]] [[ abc == a* ]] [[ a2 =~ a[0-9] ]] ``` ## Parentheses () subshellを起動してコマンドを実行する。 ```bash (cd /tmp; pwd) # => "/tmp" pwd # => current directory ``` ()の中で実行したコマンドは別プロセスで実行されるので、起動したスクリプト内には何も影響を与えない。 以下のようにサブシェルの実行結果をリダイレクトすることも可能 ```bash (cd /tmp; pwd) > pwd.txt ``` ## Braces {} 「変数の展開」と 「一連のコマンドをカレントシェルで実行する」の2種類の意味がある。 1. 変数の展開の例。このように変数名がどこまでなのか明示的に指定する。 ```bash VAR=1234 echo $VAR # => "1234" echo $VAR1234 # => "" echo ${VAR}1234 # => "12341234" ``` 2. 一連のコマンドをまとめて実行。 ```bash { time -p { sleep 1; sleep 2; echo "finished"; } > f.txt; } 2> time.txt ``` この例では f.txt に "finished"という文字列が書き込まれ、timeの実行結果がtime.txtに書き込まれる。 timeコマンドは `{ sleep ... }` の一連の処理の合計時間が書き込まれる。 #### 注意点 - `{ ... }` の中のコマンドは必ずセミコロンで終端する必要があることに注意。無いとsyntax errorになる。 - (Parenthesesを使ってサブシェルで実行する場合にはセミコロンは不要) - `{` `}` は両端に空白が必要。無いとsyntax errorになる。 - (Parenthesesの場合は不要) |
|
| 759位 |
|
|||
|
16:33:34 |
(ShouldBee 所属) |
|
# この記事の目的 6秒かかる直列処理を並行処理に改修し、3秒で終わるようにする # 準備 まずは、適当にフォルダを作る ``` mkdir ~/Desktop/goroutine cd ~/Desktop/goroutine/ ``` つぎに、main.goファイルを作る ``` touch main.go ``` # まず直列実行するプログラムを書く * 1秒待つコマンド * 2秒待つコマンド * 3秒待つコマンド 上記を順番に実行していくプログラムを `main.go` の中身を書く。 つまり、合計で6秒かかってしまう。 ```go:main.go package main import ( "log" "time" ) func main() { log.Print("started.") // 1秒かかるコマンド log.Print("sleep1 started.") time.Sleep(1 * time.Second) log.Print("sleep1 finished.") // 2秒かかるコマンド log.Print("sleep2 started.") time.Sleep(2 * time.Second) log.Print("sleep2 finished.") // 3秒かかるコマンド log.Print("sleep3 started.") time.Sleep(3 * time.Second) log.Print("sleep3 finished.") log.Print("all finished.") } ``` ともあれ、実行してみる ``` % go run main.go 2013/12/05 15:02:09 started. 2013/12/05 15:02:09 sleep1 started. 2013/12/05 15:02:10 sleep1 finished. 2013/12/05 15:02:10 sleep2 started. 2013/12/05 15:02:12 sleep2 finished. 2013/12/05 15:02:12 sleep3 started. 2013/12/05 15:02:15 sleep3 finished. 2013/12/05 15:02:15 all finished. ``` やはり、6秒かかった。 これを並行化したい。 # Goルーチンを使って並行化し、3秒で終わるようにする Go言語にはGoルーチンという処理の並行化を簡単にできる仕組みがある。クロージャーや関数に `go` をつけるだけなのでシンプル。 ただし、Goルーチンだけでは、並行化した処理が終わる前に、メインの処理が終わってしまう。つまり、待ってくれない。Goルーチンが終わるまで待つには、「チャネル」という仕組みを使う。 なお、各ルーチンの実行結果を、呼び出し元に戻すときにもチャネルを使う。今回は、特に実行結果は必要ないので、チャネルに適当な値を入れることにする。ここでは、とりあえずbool値にする。 以上を踏まえて、さきほどの `main.go` を改修する: ```go:main.go package main import ( "log" "time" ) func main() { log.Print("started.") // チャネル sleep1_finished := make(chan bool) sleep2_finished := make(chan bool) sleep3_finished := make(chan bool) go func() { // 1秒かかるコマンド log.Print("sleep1 started.") time.Sleep(1 * time.Second) log.Print("sleep1 finished.") sleep1_finished <- true }() go func() { // 2秒かかるコマンド log.Print("sleep2 started.") time.Sleep(2 * time.Second) log.Print("sleep2 finished.") sleep2_finished <- true }() go func() { // 3秒かかるコマンド log.Print("sleep3 started.") time.Sleep(3 * time.Second) log.Print("sleep3 finished.") sleep3_finished <- true }() // 終わるまで待つ <- sleep1_finished <- sleep2_finished <- sleep3_finished log.Print("all finished.") } ``` 実行してみよう ``` % go run main.go 2013/12/05 15:14:58 started. 2013/12/05 15:14:58 sleep1 started. 2013/12/05 15:14:58 sleep2 started. 2013/12/05 15:14:58 sleep3 started. 2013/12/05 15:14:59 sleep1 finished. 2013/12/05 15:15:00 sleep2 finished. 2013/12/05 15:15:01 sleep3 finished. 2013/12/05 15:15:01 all finished. ``` 並行化ができて、3秒で終わるようになった\(^o^)/ # チャネルが冗長なので1つにしたい… `sleep1_finished`、`sleep2_finished`、`sleep3_finished`の3つのチャネルを作ったが、もっとエレガントにチャネルしたい。 ```go:main.go package main import ( "log" "time" ) func main() { log.Print("started.") // チャネル finished := make(chan bool) go func() { // 1秒かかるコマンド log.Print("sleep1 started.") time.Sleep(1 * time.Second) log.Print("sleep1 finished.") finished <- true }() go func() { // 2秒かかるコマンド log.Print("sleep2 started.") time.Sleep(2 * time.Second) log.Print("sleep2 finished.") finished <- true }() go func() { // 3秒かかるコマンド log.Print("sleep3 started.") time.Sleep(3 * time.Second) log.Print("sleep3 finished.") finished <- true }() // 終わるまで待つ <-finished <-finished <-finished log.Print("all finished.") } ``` チャネルはひとつにできたが、待つところで3回待つ必要がある…。 10並行したら10回 `<-finished` を書かないといけないのは面倒だし、エンバグしそうなので、回数を指定したい。 # 「終わるまで待つ」ところを回数指定にする とりあえず無骨に `for` で3回 `<-finished` を実行すれば良いようだ ``` go:main.go package main import ( "log" "time" ) func main() { log.Print("started.") // チャネル finished := make(chan bool) go func() { // 1秒かかるコマンド log.Print("sleep1 started.") time.Sleep(1 * time.Second) log.Print("sleep1 finished.") finished <- true }() go func() { // 2秒かかるコマンド log.Print("sleep2 started.") time.Sleep(2 * time.Second) log.Print("sleep2 finished.") finished <- true }() go func() { // 3秒かかるコマンド log.Print("sleep3 started.") time.Sleep(3 * time.Second) log.Print("sleep3 finished.") finished <- true }() // 終わるまで待つ for i := 1; i <= 3; i++ { <-finished } log.Print("all finished.") } ``` 実行してみる: ``` % go run main.go 2013/12/05 15:32:40 started. 2013/12/05 15:32:40 sleep2 started. 2013/12/05 15:32:40 sleep1 started. 2013/12/05 15:32:40 sleep3 started. 2013/12/05 15:32:41 sleep1 finished. 2013/12/05 15:32:42 sleep2 finished. 2013/12/05 15:32:43 sleep3 finished. 2013/12/05 15:32:43 all finished. ``` # 回数指定じゃなくて、ルーチンの数だけ待ちたい `<-finsihed` を列挙するよりも、回数指定のほうが並行数の増減に対応しやすいが、ルーチンの数に応じて待つようにしたい。 どうやってやるかだが、クロージャーを配列にして、要素の数だけGoルーチンを開始し、要素の数だけ `<-finsihed` を実行するようにする。この変更を加えたコードが下記になる。 ```go:main.go package main import ( "log" "time" ) func main() { log.Print("started.") finished := make(chan bool) // 配列 funcs := []func(){ func() { // 1秒かかるコマンド log.Print("sleep1 started.") time.Sleep(1 * time.Second) log.Print("sleep1 finished.") finished <- true }, func() { // 2秒かかるコマンド log.Print("sleep2 started.") time.Sleep(2 * time.Second) log.Print("sleep2 finished.") finished <- true }, func() { // 3秒かかるコマンド log.Print("sleep3 started.") time.Sleep(3 * time.Second) log.Print("sleep3 finished.") finished <- true }, } // 並行化する for _, sleep := range funcs { go sleep() } // 終わるまで待つ for i := 0; i < len(funcs); i++ { <-finished } log.Print("all finished.") } ``` 実行してみる: ``` % go run main.go 2013/12/05 16:30:18 started. 2013/12/05 16:30:18 sleep1 started. 2013/12/05 16:30:18 sleep2 started. 2013/12/05 16:30:18 sleep3 started. 2013/12/05 16:30:19 sleep1 finished. 2013/12/05 16:30:20 sleep2 finished. 2013/12/05 16:30:21 sleep3 finished. 2013/12/05 16:30:21 all finished. ``` # まとめ 6秒かかる処理を、Goルーチンとチャネルを組み合わせて並行化し、3秒で終わるようになった。 # 課題 最後の `for` あたりをもっとシンプルにする方法はないものか? # UPDATE 2013/12/06 チャネルを使わずに待つ方法 調べてみたら [sync.WaitGroup] というモジュールがあることがわかった。これを使うとチャネルを宣言しなくても、処理を待つことができる。 ```go:main.go package main import ( "log" "sync" "time" ) func main() { log.Print("started.") // 配列 funcs := []func(){ func() { // 1秒かかるコマンド log.Print("sleep1 started.") time.Sleep(1 * time.Second) log.Print("sleep1 finished.") }, func() { // 2秒かかるコマンド log.Print("sleep2 started.") time.Sleep(2 * time.Second) log.Print("sleep2 finished.") }, func() { // 3秒かかるコマンド log.Print("sleep3 started.") time.Sleep(3 * time.Second) log.Print("sleep3 finished.") }, } var waitGroup sync.WaitGroup // 関数の数だけ並行化する for _, sleep := range funcs { waitGroup.Add(1) // 待つ数をインクリメント // Goルーチンに入る go func(function func()) { defer waitGroup.Done() // 待つ数をデクリメント function() }(sleep) } waitGroup.Wait() // 待つ数がゼロになるまで処理をブロックする log.Print("all finished.") } ``` 実行結果: ``` % go fmt main.go && go run main.go 2013/12/06 16:14:40 started. 2013/12/06 16:14:40 sleep1 started. 2013/12/06 16:14:40 sleep2 started. 2013/12/06 16:14:40 sleep3 started. 2013/12/06 16:14:41 sleep1 finished. 2013/12/06 16:14:42 sleep2 finished. 2013/12/06 16:14:43 sleep3 finished. 2013/12/06 16:14:43 all finished. ``` [sync.WaitGroup]: http://golang.org/pkg/sync/#WaitGroup |
|
| 760位 |
|
|||
|
15:17:21 |
|
|
Ruby で gem 管理に使われる Bundler. その Bundler で依存関係を解決するために使われるファイルには,
+ gemspec + Gemfile + Gemfile.lock の 3 つがあります.これら 3 つのファイルの区別を意識することはあまりありませんが,それぞれ異なる役割を担っています. # 3 つのファイルの役割 各ファイルの役割を簡単にまとめます. ## gemspec + gem の依存関係を記述します. ## Gemfile + 依存する gem の取得先を記述します. - 通常は取得先は `source` 行一行だけでよいはず. - GitHub リポジトリなどから edge バージョンを取得する場合は,その場所をここに書く. ## Gemfile.lock + 開発環境と運用環境とで同じ gem をインストールするために使います. - `bundle` などで自動で生成されます. - 依存 gem のバージョンと取得先が記録されます. # Gemfile.lock の扱いについて Gemfile.lock については,これをリポジトリに含めるかどうかが議論されるのですが,前節で述べたように,「開発環境と運用環境とで同じ gem をインストール」したいかどうかで決めればよいかと思います.すなわち, + 確実動作が期待されるアプリでは Gemfile.lock をリポジトリに含める. + デプロイの自由度を確保したいライブラリは Gemfile.lock をリポジトリに含めない. + (そうでないものは,開発環境=運用環境としたい気持ちの強さでどちらかに倒す) とするのがよいでしょう. # 参考 + http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ - この記事は↑を読んで考えたことをまとめたものです. - 元記事と異なる主張があるかもしれません. |
|
| 761位 |
|
|||
|
13:20:23 |
|
|
## ex) play framework 1.2.4 をインストールする場合 デフォルトでインストールされるバージョンの確認 ```sh: $ brew info play play 2.0 http://www.playframework.org/ Not installed https://github.com/mxcl/homebrew/commits/master/Library/Formula/play.rb /usr/local/Library/Formula% git checkout 3caf5c5 /usr/local/Library/Formula/play.rb /usr/local/Library/Formula% git info play ``` 利用出来るバージョンを確認 ```sh: $ brew versions play 2.0 git checkout 351d751 /usr/local/Library/Formula/play.rb 1.2.4 git checkout 3caf5c5 /usr/local/Library/Formula/play.rb 1.2.3 git checkout bee9712 /usr/local/Library/Formula/play.rb 1.2.2 git checkout 3137b6a /usr/local/Library/Formula/play.rb 1.2.1 git checkout bcdf2d5 /usr/local/Library/Formula/play.rb 1.2 git checkout 744b044 /usr/local/Library/Formula/play.rb 1.1.1 git checkout 0476235 /usr/local/Library/Formula/play.rb 1.1 git checkout eea40ff /usr/local/Library/Formula/play.rb 1.0.3.1 git checkout aa76115 /usr/local/Library/Formula/play.rb 1.0.3 git checkout 19edb1f /usr/local/Library/Formula/play.rb ``` 1.2.4をインストールするように設定 ```sh: $ cd /usr/local/Library/Formula $ git checkout 3caf5c5 /usr/local/Library/Formula/play.rb $ brew info play play 1.2.4 http://www.playframework.org/ Not installed https://github.com/mxcl/homebrew/commits/master/Library/Formula/play.rb ``` インストール ```sh: brew install play ``` |
|
| 762位 |
|
|||
|
22:25:58 |
(コロプラ 所属) |
|
Appleのドキュメントもだいぶ読み進めてきました。 今回は「[[PDF] iOS Scroll Viewプログラミングガイド](https://developer.apple.com/jp/devcenter/ios/library/documentation/UIScrollView_pg.pdf)」を読んだメモです。 実はまだ案件で使ったことがない`UIScrollView`。 経験の浅さを痛感します。 ##ScrollViewで大事なプロパティたち | プロパティ | 意味 | |----------|--------| | contentSize | ScrollViewが内包するコンテンツのサイズ。スクロール領域 | | contentInset | いわゆる余白。`contentSize`に`contentInset`を足したものが最終的なScrollViewのサイズになる | | scrollIndicatorInsets | スクロール時に表示されるインジケータのinset。`contentInset`を設定したらこちらも設定しないとスクロールバーが変なところに表示されるので注意 | ##UIScrollViewDelegate スクロールに関するイベントに関してはデリゲートメソッドで受け取ります。 デリゲートメソッドには以下のものが用意されています。 | デリゲートメソッド | 説明 | |-----------------|--------| | - (void)scrollViewDidScroll:(UIScrollView *)scrollView | UIScrollViewが **スクロールしている間** 呼び出される。 | | - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView | アニメーション完了後に呼び出される。(`setContentOffset:animated:`メソッドの`animated`が`NO`の場合は呼び出されません。 | | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView | ドラッグ開始時に呼ばれる | | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate | ドラッグ後、指がデバイスが離れた時に呼ばれる。慣性が効いて動く場合は`decelerate`が`YES`になる | | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView | ドラッグ後、慣性が効いて動いたあとに止まった場合に呼ばれる。(慣性なしでドラッグを終えた場合は呼ばれない) | ---------------------------------------------------- ##プログラムからスクロールさせる プログラムからスクロールさせるには、`setContentOffset:animated:`メソッドを利用します。 ```objc // CGPointの位置が左上角になるようにスクロールする [aScrollView setContentOffset:CGPointMake(50, 50) animated:YES]; ``` ```objc // 以下はどちらも意味は同じ、アニメーションなしでスクロール [aScrollView setContentOffset:CGPoint(50, 50) animated:NO]; // -------------------------- or -------------------------- aScrollView.contentOffset = CGPoint(50, 50); ``` ---------------------------------------------------- ##矩形範囲の表示 ```objc [aView scrollRectToVisible:(CGRect) animated:(BOOL)] ``` 矩形が見える位置までスクロールします。その際、追跡プロパティとドラッグプロパティはどちらも`NO`になります。 ---------------------------------------------------- ##最上部へのスクロール ステータスバーをタップしたときに一番上までスクロールする機能です。 この動作も`UIScrollView`の処理です。 デリゲートメソッドをオーバーライドすることによって、この機能をオフにすることもできます。 例えばこれを利用することで、画面内に複数のScrollViewがある場合に、ステータスバータップ時にどのScrollViewをスクロールさせるかを制御することが可能となります。 ```objc - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView; ``` また、この動作によってアニメーションが完了したときに以下のデリゲートメソッドが呼ばれます。 ```objc - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView; ``` ---------------------------------------------------- ##スクロール中の状態追跡 スクロールにはユーザ操作のものもあれば、プログラムによる操作もあります。 しかしデリゲートメソッドはどれも同様にメッセージを受信するので、状態を把握する必要が出てきます。 そのときに使えるのが以下の状態プロパティです。 | 状態プロパティ | 説明 | |--------------|--------| | tracking | ユーザの指がデバイスに触れている場合に`YES`(つまりリアルタイムな状態) | | dragging | ユーザの指がデバイスに触れていた場合に`YES`(フリップなど、ユーザの指が離れても、ユーザ操作によるものと判断できる状態) | | decelerating | フリックジェスチャ後、またはScrollViewのフレームを超えてドラッグし、バウンスしてScrollViewが減速している場合に`YES` | | zooming | zoomScaleプロパティを変更するためにScrollViewがピンチジェスチャを追跡している場合に`YES` | | contentOffset | ScrollViewの境界の左上角を定義するCGPointの値 | ---------------------------------------------------- ##スクロールの開始と完了の追跡 開始と完了もデリゲートメソッドで追跡することができます。 *※デリゲートメソッド一覧に記載しています。* ---------------------------------------------------- ##デリゲートメッセージシーケンスの全体 デリゲートシーケンスはユーザの画面タッチから開始されます。 シーケンスは以下の状態をたどります。 1. trackingプロパティが`YES`に 2. ユーザの指がデバイスに触れた状態で静止していて、コンテンツビューがタッチイベントに応答するものの場合、シーケンスは完了しています 3. その後、ユーザがデバイスに触れたまま指を動かせばシーケンスが継続します 4. スクロールを開始すると、進行中のタッチ操作をすべて取り消すよう試みます。(筆者注:例えばボタンなどをタッチし反応している状態で、指を動かすとそのアクションが取り消されるのをイメージすると分かりやすい) 5. ScrollViewの`dragging`プロパティが`YES`に設定される 6. `scrollViewWillBeginDragging:`メッセージが送信される 7. (ユーザのドラッグ中)`scrollViewDidScroll:`メッセージがデリゲートに送り続けられる 8. ユーザがフリックジェスチャを行うと、`tracking`プロパティは`NO`に設定される 9. その後、`scrollViewDidEndDragging:willDecelerate:`メッセージがデリゲートに送られる ###減速スピードの制御 フリックジェスチャなどで慣性で動いている状態のScrollViewの減速については、以下のプロパティで制御することができます。 ```objc aScrollView.decelerationRate = UIScrollViewDecelerationRateNormal; // -------------------- or ----------------------- aScrollView.decelerationRate = UIScrollViewDecelerationRateFast; ``` ##ピンチジェスチャを使った基本的なズーム UIScrollViewにはピンチジェスチャを簡単に扱う仕組みが実装されています。 ピンチジェスチャを使うには以下のデリゲートメソッドを実装する必要があります。 また同時に * `minimumZoomScale`プロパティ * `maximumZoomScale`プロパティ のどちらか、あるいは両方を設定する必要があります。(設定しないとデリゲートメソッドが呼ばれません) ```objc - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { // ズーム対象のビュー return self.aView; } ``` ###プログラミングによるズーム 上記はユーザ操作によるズームでした。 ScrollViewは、ダブルタップなどの操作によりプログラムからズームを実行する実装を用意しています。 * `setZoomScale:animated:` * `zoomToRect:animated:` というふたつのメソッドです。 どちらのメソッドもズームを操作するメソッドです。 ズームは、ズーム対象の「中心位置」の周りをズームします。 そのため、意図した箇所をズームする場合は位置と拡大率の調整を行う必要があります。 ###デリゲートへのズーム完了の通知 ズーム操作や処理が完了したとき、`scrollViewDidEndZooming:withView:atScale:`メソッドが呼ばれます。 このメソッドは、ScrollViewインスタンス、スクロールされたScrollViewのサブビュー、ズーム完了時の拡大縮小率が引数として渡されます。 ###ズームでハマった話 Appleのドキュメントからははずれますが、実際にドキュメントを読みつつ作っていてハマった点を書いておきます。 (おそらく、しっかりと理由があると思うんですが原因が分かっていないので書いていることは間違っているかもしれません) ####ズーム後、`contentSize`が変更される 動作の仕組みを理解するために色々なViewを、ScrollViewに適当に`addSubview:`してたんですが、どうもズームをすると表示がおかしくなる場合が。 ズームすると、どうやら`contentSize`が変更されている模様。理由分からず。 ちなみにScrollViewは以下のような感じになってました。 * UIImageViewを3つ並列に追加 * UIViewを最後に追加 * 最初に`addSubview:`したUIImageViewがScrollView内で一番サイズが大きい * 上のimageのサイズを`contentSize`に設定 という状況。 で、`viewForZoomingInScrollView:`メソッドで一番サイズが大きいUIImageViewを返している場合は想定通り動くものの、それ以外を返すと`contentSize`の値が想定通りになっていない。 よくよく見てみると、ズーム対象のコンテンツのサイズが`contentSize`になるように変更されていました。 なのでズーム完了時に、改めてscrollViewの`contentSize`を一番サイズの大きいUIImageViewのimageのサイズにするようにしたところ、想定通りの動きとなりました。 (多分、ズーム対象のViewのサイズにする、みたいな動作がデフォルトなのだと思います) 微妙に想像していた動作と違うので要注意です。 ---------------------------------------------------- ##タップによるズーム ピンチジェスチャに関しては基本的な実装をフレームワーク側で行っていますが、タップによるズーム(ダブルタップでズーム、など)を実装するにはアプリケーション側での実装が必要です。 タップによるズームを適切に実装すれば、ユーザビリティを向上させることもできると思います。(2本指でダブルタップするとズームアウトとか) ###実装方法 アプリケーション側での実装が必要ですが、ScrollViewのサブクラス化は必要なく、`viewForZoomingScrollView:`デリゲートメソッドで返すクラス側でタッチ処理を実装します。 *※コードサンプルも書いていて長くなったので[別記事](http://qiita.com/edo_m18/items/9400bf8fa37ceb7077d7)にしました。* ---------------------------------------------------- ##ページングモードを使用したスクロール UIScrollViewは、iOSでよく見られるページングタイプのスクロールもサポートしています。 (ある決まった幅(1画面分)のコンテンツをスクロールするようなやつ。App Storeのスクリーンショットとかの感じです) ###ページングモードの設定 ページングモードをサポートするには、ScrollViewの`pagingEnabled`プロパティに`YES`を設定する必要があります。 `contentSize`は表示したいコンテンツがちょうど収まるサイズにしておくといいでしょう。 また、通常はインジケータ(スクロールバー)は必要なくなるので非表示にしておいたほうがより自然になります。 ```objc UIScrollView *aScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; aScrollView.pagingEnabled = YES; // スクロールインジケータを非表示に aScrollView.showsHorizontalScrollIndicator = NO; aScrollView.showsVerticalScrollIndicator = NO; ``` ###ページングモードのScroll Viewのサブビューの設定 Appleのプログラミングガイドでは、サブビューの設定についても言及されています。 色々書いてありますが、要は小さいコンテンツはひとつのサブビュー、大きいコンテンツやページ数の多いコンテンツはページ単位でビューを分け、表示すべきビューと隣接するビューのみ生成し、それ以外は位置を動的に判断して生成しよう、というものです。 一言で言えば、メモリ使用量を減らそう、ってことですね。 TableViewのセルも似たような仕組みで動作しています。(Identifierを渡してインスタンス生成を省略する方法ですね) モバイルはメモリが潤沢にあるとは言えないので、できるだけ最適化しよう、というようなことが書いてあります。 |
|
| 763位 |
|
|||
|
14:57:28 |
(シナプス株式会社 所属) |
|
# **前書き**
まだ **[「ファイルアップロードの例外処理はこれぐらいしないと気が済まない」](http://qiita.com/mpyw/items/939964377766a54d4682)** や **[「PHPでデータベースに接続するときのまとめ」](http://qiita.com/mpyw/items/b00b72c5c95aac573b71)** をご覧になっていない方は先にそちらからどうぞ。 …もうこのシリーズ何番煎じだよって感じになってきましたが、気にせず書きますwww # **実装のポイント** ## **ファイルアップロード段階でエラーが発生した場合にも適切に対応する** このシリーズ共通の目標です。 ## **CSVファイルのMIMEタイプは判定できない** 実は **[finfo::file](http://www.php.net/manual/ja/function.finfo-file.php)** メソッドを使って判定しようとすると `text/csv` とはならず、どう頑張っても **`text/plain`** にしかなりません。というわけでこの方法は断念せざるを得ません。これ以降、 **文字コードの厳密な判定** や **CSVとして正しく読み取れたかどうか** などのチェックを交えることによって、ここで実現出来なかった判定に代えようと試みることにします。 ## **アップロードされたファイルの文字コードを確実にUTF-8に変換する** CSVファイルと言えども、いろいろな環境で作成されたものがあるでしょう。そういった **文字コードがよく分からないもの** を全て無難に扱えるUTF-8に変換するためには、 **[mb_convert_encoding](http://www.php.net/manual/ja/function.mb-convert-encoding.php)** を利用する際、php.iniの設定に依存する自動判定の `auto` は使用せずに ```text:日本語ファイルの文字コードを正確に判定できる順番 ASCII,JIS,UTF-8,CP51932,SJIS-win ``` と明示するほうが望ましいです。更に、 **[mb_convert_encoding](http://www.php.net/manual/ja/function.mb-convert-encoding.php)** をすぐには使わず、 _\$strict_ を `true` に指定した **[mb_detect_encoding](http://www.php.net/manual/ja/function.mb-detect-encoding.php)** を併用することで正確性を最大限に引き上げられます。 ## **トランザクション処理を行う** 途中でエラーが発生したとき、後から前に行った処理を取り消しできるように **トランザクション処理** を行うべきでしょう。 ```php:トランザクション処理のひな形 $pdo->beginTransaction(); // トランザクション処理を開始する try { /* ここで目的の処理を行う */ $pdo->commit(); // 行った処理をすべて反映する } catch (Exception $e) { $pdo->rollBack(); // 行った処理をすべて取り消しする throw $e; // 外側に例外を引き継ぐ } ``` ## **[fgetcsv](http://www.php.net/manual/ja/function.fgetcsv.php) 関数を正しく使う** 以下のようにファイル終端に到達して `false` が返されるまでの間ループを続けさせます。 ```php while ($row = fgetcsv($fp)) { /* ここで各行に対する処理を行う */ } ``` ### **ロケールを設定する** PHP5では **[setlocale](http://www.php.net/manual/ja/function.setlocale.php)** 関数を用いて以下を実行しておかないと文字化けが発生するリスクがあります。 ```php setlocale(LC_ALL, 'ja_JP.UTF-8'); ``` ### **途中の空行を検知する** 最終行以外の空行は `array(null)` として取り出されるので、これが見つかった場合はその行はスキップさせるべきでしょう。 ```php if ($row === array(null)) { // 空行はスキップ continue; } ``` 但し、PHP5.3.7より古いバージョンには **バグ** があります。 | | ~ 5.2.9 | 5.2.10 ~ 5.3.6 | 5.3.7 ~ | |:---:|:--------:|:---------------:|:--------:| | `""`<br />(空文字列) | `array("")` | `array(null)` | `array(null)` | | `" "`<br />(半角スペース) | `array("")` | `array(null)` | `array(" ")` | ### **エラーとファイルの終端を区別して検知する** **[fgetcsv](http://www.php.net/manual/ja/function.fgetcsv.php)** 関数が配列以外の値を返す条件は以下のようになります。  これらは **[feof](http://www.php.net/manual/ja/function.feof.php)** 関数によって区別することが出来ます。 ```php if (!feof($fp)) { // ファイルポインタが終端に達していなければエラー throw new RuntimeException('CSV parsing error'); } ``` ### **カラム数の整合性をチェックする** ここまでのチェックを行った後、仕上げに期待するカラム数と配列の要素数が一致するかどうかを調べます。 ```php:期待するカラム数が4の場合 if (count($row) !== 4) { // カラム数が異なる無効なフォーマット throw new RuntimeException('Invalid column detected'); } ``` ## **SQLモードを `TRADITIONAL` に設定する** こうすることで、不適切なデータが強引に適切な形に変換されて格納されようとしたときに、そうする代わりにSQLエラーを発生させてくれます。PDOのエラーモードを **`PDO::ERRMODE_EXCEPTION`** に設定しておくと、発生したSQLエラーが更にPDOExceptionとしてスローされるので、PDOとの相性は抜群とも言えるでしょう。 ```sql SET SESSION sql_mode='TRADITIONAL' ``` # **ソースコード** DSN、テーブル名、カラム数といった情報は適宜ご自分の環境に合わせて修正してください。 ```html+php <?php /* HTML特殊文字をエスケープする関数 */ function h($str) { return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); } // パラメータを正しい構造で受け取った時のみ実行 if (isset($_FILES['upfile']['error']) && is_int($_FILES['upfile']['error'])) { try { /* ファイルアップロードエラーチェック */ switch ($_FILES['upfile']['error']) { case UPLOAD_ERR_OK: // エラー無し break; case UPLOAD_ERR_NO_FILE: // ファイル未選択 throw new RuntimeException('File is not selected'); case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: // 許可サイズを超過 throw new RuntimeException('File is too large'); default: throw new RuntimeException('Unknown error'); } $tmp_name = $_FILES['upfile']['tmp_name']; $detect_order = 'ASCII,JIS,UTF-8,CP51932,SJIS-win'; setlocale(LC_ALL, 'ja_JP.UTF-8'); /* 文字コードを変換してファイルを置換 */ $buffer = file_get_contents($tmp_name); if (!$encoding = mb_detect_encoding($buffer, $detect_order, true)) { // 文字コードの自動判定に失敗 unset($buffer); throw new RuntimeException('Character set detection failed'); } file_put_contents($tmp_name, mb_convert_encoding($buffer, 'UTF-8', $encoding)); unset($buffer); /* データベースに接続 */ $pdo = new PDO( 'mysql:dbname=test_db;host=localhost;charset=utf8', 'root', '', array( // カラム型に合わない値がINSERTされようとしたときSQLエラーとする PDO::MYSQL_ATTR_INIT_COMMAND => "SET SESSION sql_mode='TRADITIONAL'", // SQLエラー発生時にPDOExceptionをスローさせる PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // プリペアドステートメントのエミュレーションを無効化する PDO::ATTR_EMULATE_PREPARES => false, ) ); $stmt = $pdo->prepare('INSERT INTO test_table VALUES (?, ?, ?, ?)'); /* トランザクション処理 */ $pdo->beginTransaction(); try { $fp = fopen($tmp_name, 'rb'); while ($row = fgetcsv($fp)) { if ($row === array(null)) { // 空行はスキップ continue; } if (count($row) !== 4) { // カラム数が異なる無効なフォーマット throw new RuntimeException('Invalid column detected'); } $executed = $stmt->execute($row); } if (!feof($fp)) { // ファイルポインタが終端に達していなければエラー throw new RuntimeException('CSV parsing error'); } fclose($fp); $pdo->commit(); } catch (Exception $e) { fclose($fp); $pdo->rollBack(); throw $e; } /* 結果メッセージをセット */ if (isset($executed)) { // 1回以上実行された $msg = array('green', 'Import successful'); } else { // 1回も実行されなかった $msg = array('black', 'There were nothing to import'); } } catch (Exception $e) { /* エラーメッセージをセット */ $msg = array('red', $e->getMessage()); } } // XHTMLとしてブラウザに認識させる // (IE8以下はサポート対象外w) header('Content-Type: application/xhtml+xml; charset=utf-8'); ?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>CSV to MySQL importation test</title> </head> <body> <?php if (isset($msg)): ?> <fieldset> <legend>Result</legend> <span style="color:<?=h($msg[0])?>;"><?=h($msg[1])?></span> </fieldset> <?php endif; ?> <form enctype="multipart/form-data" method="post" action=""> <fieldset> <legend>Select File</legend> Filename(CSV is only supported): <input type="file" name="upfile" /><br /> <input type="submit" value="Upload" /> </fieldset> </form> </body> </html> |
|
| 764位 |
|
|||
|
15:34:33 |
|
|
Docker上でRedmineをすぐに使い始める
=== はじめに --- 飛ぶ鳥を落とす勢いのDockerですが、まだまだ本番環境には使うのは怖いです。 今のところは開発用のRedmine動かすのに使うくらいがちょうどよさそうです。 あらかじめ配布されているコンテナイメージを使って素早くDocker上のRedmineを使い始めてみます。 構成 --- 今回はVagrantでCoreOSを立ち上げて、CoreOS上にRedmineのコンテナを立ち上げる構成とします。 Vagrantのインストール --- 本記事ではVagrantのインストールの詳細は割愛します。 "vagrant インストール" などで検索してみてください。 CoreOSのイメージダウンロード --- CoreOSはDockerのホストOSとして必要最小限の機能だけを持った軽量なOSです。 githubにCoreOSのVagrantファイルがあるので落としてきます。 ```bash:ホストPC $ git clone https://github.com/coreos/coreos-vagrant/ ``` Vagrantのポートフォワーディング設定 --- CoreOSを立ち上げる前にVagrantのポートフォワーディングの設定をしておきます。 今回はCoreOSの80番ポートをホストPCの3000番ポートにマッピングしてます。 ```rb:Vagrantfile config.vm.network :forwarded_port, guest: 80, host: 3000 ``` CoreOSの立ち上げ --- Vagrantを使ってCoreOSの入っているイメージを立ち上げて、SSHでつなぎます。 ```bash:ホストPC $ vagrant up $ vagrant ssh ``` SSHでCoreOSにつながるはずです。 Redmine入りコンテナイメージの取得 --- [Redmineがすでにセットアップされているコンテナイメージ](https://index.docker.io/u/sameersbn/redmine/)を配布してくださっている方がいますので、ありがたく使います。 docker pullでコンテナイメージを取得します。 ```bash:CoreOS $ docker pull sameersbn/redmine ``` なお、プロキシの設定が必要な場合は下記の感じで環境変数の設定をしてdockerを再起動してからpullしてください。 ```bash:CoreOS $ sudo systemctl set-environment HTTP_PROXY=http://proxy.example.com:8080/ & sudo systemctl stop docker.service & sudo systemctl start docker.service ``` Redmineコンテナの立ち上げ --- 取得したコンテナイメージからコンテナを立ち上げます。 ```bash:CoreOS $ docker run -d -p 80:80 sameersbn/redmine ``` なお、-p 80:80というのはCoreOSとRedmineコンテナとの間のポートフォワーディング設定です。 CoreOSの80番にアクセスすると、Redmineコンテナの80番につながります。 Redmineが正常に起動して、ポートフォワードでつながっているか確認します。 ```bash:CoreOS wget http://localhost/ --no-proxy ``` RedmineのトップページっぽいHTMLが取得できたらOKです。 なお、起動しきる前にアクセスすると500エラーが返ってきます。 少し待ってから確かめてみてください。 ブラウザからアクセスしてみる --- ホストのPCのブラウザから下記を開いてみてください。 ```bash:ホストPC http://localhost:3000/ ``` 無事Redmineのログイン画面が表示されたら終了です。 なお、上で2回ポートフォワーディングの設定をしているので、最終的には下記の感じでつながってます。 ``` ホストPC:3000 → CoreOS:80 → Redmine:80 ``` 終わりに --- ハードウェアをエミュレートするVirtualBoxあたりだと、VMに割り当てたメモリは使ってなくても消費してしまいますが、Dockerなら使っている分のメモリだけ消費します。 なので、より効率的なリソース消費で、何十個も量産的にRedmineを立ち上げられます。 Redmineは開発チームごとにたくさん立てられるのが理想なので、Dockerとの相性がいいと言えるのではないでしょうか。 |
|
| 765位 |
|
|||
|
16:30:17 |
(Increments 所属) |
|
# 前提
会社(Quipper)で今からこういう風にしたい、と宣言した社内ドキュメントを公開する。 枯れてるわけではない。 * coffeescript * Backbone * Backbone.stickit (データバインディング) * Chapling.js(は、オマケなのでどうでもいいがサンプルコードはこう) backbone.stickitは安心と信頼のNYT製。(実質Backbone作ってるDocumentCloudと一緒のところ?) [backbone.stickit](http://nytimes.github.io/backbone.stickit/ "backbone.stickit") ## 目的 データバインディングを全面的に使って再描画を最小限にし、コードの見通しをよくしたい。 モデルの役割を明示的にし、MVVMを導入する。 ## 理想的なAPI 擬似コード ```coffeescript # ビューモデルの定義 class TopicViewModel extends Model defaults: title: '' # たぶんここでパラメータ名(と仮でもいいので初期値)を書いておくと見通しがよい # topic.hbs """ <span class="title"></span> """ # !!! テンプレートはビューモデルにのみ依存してテンプレートを展開する # getTemplateDataは可能な限り使わない # controller ... show: ({topic_id})-> # Collectionから紐付いたものをとってくる。Collectionの中身は事前に初期化されている。 # Railsのコントローラに近いイメージでモデルを取得できるようにしておく # Modelの役割を本来の意味でのモデルに近づける topic = Topics.find {topic_id} #=> Backbone.Collectionはlodashのメソッドをmixinしているのでこういう風に書ける # fetchが必要かどうかはモデル次第。Topicに関しては最初に初期化しておきたい # 各種モデルを用いてビューモデルを組み立てる # ViewModelクラスはBockbone.Modelを継承しているがRESTを叩いたりはせず、ビューの振る舞いだけに興味がある topicViewModel = new TopicViewModel title: topic.title @compose 'topic', new TopicView model: topicViewModel # controllerが各種イベントに対するハンドラをもつ @subscribeEvent 'updateTopicTitle', (title) -> topicViewModel.set 'title', title #=> バインディングがあるのでビューも更新される # 仮にビューバインディングで対応できない系(大域に渡る処理)のものは、ある程度受け入れる @subscribeEvent 'changeContainerView', ($div) -> @$('.content').html $div # 基本的にイベントは受け取るものは、コントローラで処理する # コントローラが膨らむ場合は適宜モデルロジック、ビューロジックに移す # view # getTemplateDataは可能な限り使わず、ビューモデルにだけ依存する class TopicView extends View # model -> TopicViewModel bindings: '.title': 'title' # ビューに特有な値はonSetなどを用いて生成する # ビューイベントをイベント名を翻訳して、コントローラへ向けて発火する render: => super @stickit() # イベントハンドラはrender後に書く @delegate 'click', '.i-want-to-change-topic-title-to-hoge', => #雑… # コントローラが知るべきものはコントローラに向けてイベントを発火する @publishEvent 'updateTopicTitle', 'hoge' ``` ## これによって可能になるもの - サーバーサイドのモデルのコピーと、ビューに紐づくモデルを一貫させ、コードの見通しをよくする。 - パラメータの書き換えを最小限に抑え、再描画を抑えパフォーマンスがよくなる - ビューの変更はビューモデルを通じて行う - テストはビューモデルの変更に対して行う(ビューモデルによって生成されるビューには感知しない) #=> DOMに触れずに済む - テンプレートはビューモデルに一意に依存するので、使えるパラメータが明示的になる ビューモデルはフロントに近い存在なので、多少汚くてもいい。新しく作る概念なので、ある程度闇を引き受けてもらう。 いままでふわっとした扱いだったModelを明示的に取り回したい。 |
|
| 766位 |
|
|||
|
15:04:08 |
(COMPASS, inc 所属) |
|
## delegationとは
delegation(委譲)はオブジェクト指向のテクニックであるが、面倒くさい点がひとつある。 それは、処理を委譲させるだけのメソッドをいちいち定義しないといけない点である。 例えば下記の様なケースを考えてみる。 ```Base.rb class Base def method1 puts "method1" end def method2 puts "method2" end def method3 puts "method3" end end ``` このBaseクラスのmethod1に処理を付け加え、それ以外のメソッドについてはそのままアクセスできるようにしたいとする。 ### 継承の場合 継承を使うと下記の様になる。 ```Extend.rb class Extend < Base def method1 print 'Hello ' super end end ``` 継承を使うと、**処理を付け加えたいメソッドだけ定義する**だけでよく、それ以外のメソッドについては親クラスを参照するようになる。 上記のExtendクラスのインスタンスを作り、method1を実行すると'Hello method1'が出力され、method2を実行すると親クラスの処理の通り'method2'が出力される。 ```extend_sample.rb e = Extend.new e.method1 # => Hello method1 e.method2 # => method2 e.method3 # => method3 ``` 継承は**処理を付け加えたいメソッドだけを定義すればよい**、という点においては非常に便利だが、オブジェクト同士の結びつきを強くしてしまうため、柔軟性にかけてしまう。 ### 委譲の場合 そこで、変更したいメソッドに処理を追加し、それ以外のメソッドはBaseに**処理を委譲**する。 ```Delegation.rb class Delegation def initialize(base) @base = base end def method1 print 'Hello ' @base.method1 end def method2 @base.method2 end def method3 @base.method3 end end ``` 上記の例は、Extendクラスと同じ振る舞いをする。 ```delegation_sample.rb d = Delegation.new(Base.new) d.method1 # => Hello method1 d.method2 # => method2 d.method3 # => method3 ``` ### 委譲のメリット・デメリット 委譲のメリットは継承に比べて柔軟なところである。 例えば、Delegationクラスはmethod3を使用する必要がないのであれば、定義しなければいいだけである。 こういったスコープのコントロールもやりやすくなる。 また、Delegationクラスはmethod2とmethod3の中身を知る必要はない。 呼び出しが行われたら、自分の担当ではないので、Baseクラスに横流しするだけである。 こうすることで、処理の分離をはっきりさせることができる。 逆にデメリットは、冒頭でも記述したが委譲させるだけのメソッドを定義しなければいけない点である。 今回の例ではメソッドが3つしかないため、そこまで苦労はしなかったが、これが100、200となっていった時には、心が折れてしまう。 ## Delegationを簡単に実装する Rubyにはこの委譲テクニックを簡単に実装する2つ方法がある。 ### 1. Forwardableモジュールを使用する RubyにはデフォルトでForwardableモジュールが備わっている。 このモジュールを使うと下記の様にDelegationを実現することができる。 ```forwardable_sample.rb require 'forwardable' class Delegation extend Forwardable def_delegators :@base, :method2, :method3 def initialize(base) @base = base end def method1 print 'Hello ' @base.method1 end end ``` Forwardableモジュールは、def_delegatorsメソッドを持っており、第一引数には委譲先のオブジェクト、それ以降の引数で委譲したいメソッド名を指定する。 def_delegatorsメソッドは指定されたすべてのメソッドをクラスに追加するため、先の例で挙げたものと同じ挙動をするようになる。 ### 2. method_missingメソッドの活用 method_missingメソッドは、存在しないメソッドが呼び出された際に実行されるメソッドで、第一引数には呼びだそうとしたメソッド名のシンボル、それ以降の引数では呼び出された時の引数が渡される。 このmethod_missingとRubyに備えられているsendメソッドを組み合わせると委譲が簡単に実現できる。 #### sendメソッドとは 先のBaseクラスのメソッドをsendメソッドで実行してみる。 ```send_sample.rb b = Base.new b.send :method1 # => method1 ``` オブジェクトにはsendメソッドが備わっており、第一引数に呼び出すメソッド名のシンボル、それ以降の引数にメソッドに渡す引数を指定することができる。 このインターフェースはmethod_missingと同じである。 #### method_missingとsendを組み合わせる 先ほどのDelegationクラスを下記の様に変更してみる。 ```Delegation.rb class Delegation def initialize(base) @base = base end def method1 print 'Hello ' @base.method1 end def method_missing(name, *args) @base.send name, *args end end ``` method1は定義されているが、method2とmethod3は定義されていない。 そのため、method2とmethod3を呼び出しをするとmethod_missingが実行される。 後は、このmethod_missingに渡ってきた内容を、そのままBaseクラスにsendメソッドを使って受け流せば、無事処理は実行される。 ## まとめ method_missingを使うパターンは簡単ではあるが、実行速度が遅くなってしまう、コードがわかりにくくなってしまう、などという代償がある。 そのため、基本的にはForwardableモジュールを使う方法でいいと思う。 こういう方法もあるんだな、ぐらいにとどめておく。 |
|
| 767位 |
|
|||
|
17:08:25 |
(Freelancer 所属) |
|
「カメラ機能をアプリにつけたいけどシャッター音を鳴らしたくない」とか、「カメラ起動時のアニメーションが嫌だ」とか、カメラ機能をもっと自由にカスタマイズしたい場合は、 UIImagePickerController を使うのではなく AVFoundation フレームワークを使う必要があります。
静止画撮影はデフォルトの挙動としてシャッター音がなってしまうようになっていてそこは変えられないので、**動画モードで撮影を開始し、必要なフレームを静止画として取り込む**ことで「シャッター音の鳴らないカメラ」を実現します。 以下、プレビュー表示と「シャッター音の鳴らない」撮影ボタンだけからなる、シンプルな無音カメラアプリの実装手順を説明します。 ##準備 以下のフレームワークをプロジェクトに追加します。 - AVFoundation.framework - AssetsLibrary.framework - CoreMedia.framework - CoreVideo.framework ##ヘッダでの宣言 撮影を制御するのに使用するフラグと、ビットマップ保存領域用のポインタを定義します。 BOOL isRequireTakePhoto; BOOL isProcessingTakePhoto; void *bitmap; また、撮影画像を保持する UIImage 型のプロパティをイメージバッファを定義します。 @property (nonatomic, retain) UIImage *imageBuffer; AVCaptureVideoDataOutputSampleBufferDelegate プロトコルへの準拠を宣言します。 <AVCaptureVideoDataOutputSampleBufferDelegate> ##カメラ初期化処理の実装 まず、撮影画像を保持するためのバッファを確保します。 // バッファ作成 size_t width = 640; size_t height = 480; bitmap = malloc(width * height * 4); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGDataProviderRef dataProviderRef = CGDataProviderCreateWithData(NULL, bitmap, width * height * 4, NULL); CGImageRef cgImage = CGImageCreate(width, height, 8, 32, width * 4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst, dataProviderRef, NULL, 0, kCGRenderingIntentDefault); self.imageBuffer = [UIImage imageWithCGImage:cgImage]; CGColorSpaceRelease(colorSpace); CGDataProviderRelease(dataProviderRef); 次に、カメラデバイスと入力、セッションを初期化します。 // カメラデバイスの初期化 AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; // 入力の初期化 NSError *error = nil; AVCaptureInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error]; // セッション初期化 AVCaptureSession *captureSession = [[AVCaptureSession alloc] init]; [captureSession addInput:captureInput]; [captureSession beginConfiguration]; captureSession.sessionPreset = AVCaptureSessionPreset640x480; [captureSession commitConfiguration]; 出力を初期化します。今回は動画の撮影なので、AVCaptureVideoDataOutput を使います。(静止画の撮影では出力先として AVCaptureStillImageOutput を使います) AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init]; [captureSession addOutput:videoOutput]; デリゲートを下記のように指定します。こうすることで、AVCaptureVideoDataOutputSampleBufferDelegate プロトコルの captureOutput:didOutputSampleBuffer:fromConnection: が毎フレーム呼ばれるようになります。 dispatch_queue_t queue = dispatch_queue_create("com.overout223.myQueue", NULL); [videoOutput setSampleBufferDelegate:self queue:queue]; dispatch_release(queue); ##シャッターボタンのアクションを実装 ここでは「写真撮る」というフラグを立てるだけです。 - (IBAction)pressShutter { if (!isProcessingTakePhoto) { isRequireTakePhoto = YES; } } ##動画のフレーム取得時の処理を実装 カメラが動画のフレームを取得する度に(つまり撮影時の毎フレーム)、AVCaptureVideoDataOutputSampleBufferDelegateプロトコルのcaptureOutput:didOutputSampleBuffer:fromConnection:メソッドが呼ばれるので、そこで必要に応じて画像の保存処理を行います。 ユーザーがシャッターボタンを押すと isRequireTakePhoto フラグが YES になっているので、そのときだけカメラロールへの保存処理を行うようにします。 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { if (isRequireTakePhoto) { isRequireTakePhoto = NO; isProcessingTakePhoto = YES; CVPixelBufferRef pixbuff = CMSampleBufferGetImageBuffer(sampleBuffer); if(CVPixelBufferLockBaseAddress(pixbuff, 0) == kCVReturnSuccess){ memcpy(bitmap, CVPixelBufferGetBaseAddress(pixbuff), 640 * 480 * 4); CMAttachmentMode attachmentMode; // メタデータ取得&orientation情報追記 CFDictionaryRef metadataRef = CMGetAttachment(sampleBuffer, CFSTR("MetadataDictionary"), &attachmentMode); NSMutableDictionary *metadata = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary *)CFBridgingRelease(metadataRef)]; // ここではorientaionは一定(6)とする [metadata setObject:[NSNumber numberWithInt:6] forKey:(NSString *)kCGImagePropertyOrientation]; // フォトアルバムに保存 ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init]; [library writeImageToSavedPhotosAlbum:self.imageBuffer.CGImage metadata:metadata completionBlock:^(NSURL *assetURL, NSError *error) { NSLog(@"URL:%@", assetURL); NSLog(@"error:%@", error); isProcessingTakePhoto = NO; }]; CVPixelBufferUnlockBaseAddress(pixbuff, 0); } } } ##サンプルコード 今回のサンプルコードは、gumroad よりダウンロードしていただけます。 http://gum.co/pzBMA (すいません、無料ではなく、85円です) - iOS 6.0 で動作確認しております。 - コードのみの販売です。サポート等はご容赦ください。 ##参考書籍 上記サンプルは、次の書籍を参考に実装しました。 [iOS4プログラミングブック](http://www.amazon.co.jp/exec/obidos/ASIN/4844329766/22301-22/ref=nosim/) こちらの第3章『マルチメディア』の章とサンプルコードが非常に参考になります。 |
|
| 768位 |
|
|||
|
22:41:25 |
|
|
# 概要
zshでよく使うキーバインドをまとめます。使ってる人からすると当たり前なんですけど、知らない人結構いる気がするんですよね。 # 内容 ## まずはじめに `Meta`について。 例えば`Meta+b`は`Escape`(or `Control+[`)を押してから`b`という意味。 僕は普段MacのiTerm2使ってるんですけど、それの設定で`Option`を押しながら`b`で同じ動作になるように設定してあります。(Preferences -> Profiles -> Keys ->Left(or Right) option key acts as +Esc) 多分Emacsユーザだとここらへん知ってるんですけど、viユーザは知らない人多そう。 ## カーソル移動系 ``` Control+f # 右に移動 Control+b # 左に移動 Meta+f # 右に単語単位移動 Meta+b # 左に単語単位移動 Control+e # 行末へ移動 Control+a # 行頭に移動 Control+n # 下 Control+p # 上 ``` nとpはzshの場合カーソル移動ではない気もする ## 文字操作系 ``` Control+h # 左の文字を削除 Control+d # 右の文字を削除。何も入力がない状態で使うと現在のシェルのログアウト Control+k # カーソルから行末までの文字を切り取り Control+w # 左の単語を切り取り Meta+d # 右の単語を切り取り Control+u # その行全部切り取り Control+y # ペースト Control+/ # undo ``` ## その他 その他でくくるのってどうなん、というのもありつつ ``` Control+m # Enter Control+j # Enter Control+g # 現在の入力を破棄して新しい入力に Control+r # 過去のコマンドを検索。もう一度Control+rで遡る Control+s # 上のコマンドで戻りすぎた時に、最近のコマンドを検索 Control+l # 今のコマンドラインをウィンドウの一番上に持ってくる Control+i # タブ補完 Control+c # プログラムの強制終了 ``` # あとがき こんなもんでしょうか。普段から使ってるけど抜けてるやつあるかもしれません。 あと、色々カスタマイズしてるからどれが自分で設定したやつで、どれがデフォルトか把握出来てない感あるから、間違ってるところあるかも... その他のキーバインドも知りたかったらおもむろに`bindkey`とコマンドを打つと吉。 |
|
| 769位 |
|
|||
|
15:59:42 |
|
|
# はじめに
* 時は2014年。このげいむ業界はUnity帝国の統治により平和な日々(!)を過ごしていた。しかし突然、地底からUE4が・・・(伝記 戦争の歯車の冒頭より) **でも実は、[UEの歴史はすごく古いデス。](http://ja.wikipedia.org/wiki/Unreal_Engine)** <BR> ※僕も実はアンリアル1の頃からMod作るためにちょこっとエディタ触ってました。ValveのSorceエンジンやQuakeエンジンのエディタより断然使いやすかったなぁ( ´∀`) <BR> ## GDC2014で歴史的発表がなされた。 [【GDC 14】Epic Games、「Unreal Engine 4」にサブスクリプションモデルを導入―月額19ドルで全ての機能が利用可能に gamespark](http://www.gamespark.jp/article/2014/03/20/47188.html) **→ 2015年3月3日から無料になりました\(^o^)/** **え?今更オープンソース化?カーマック先生(Quake・Doom開発者・現Oculus CTO)は昔からやってたよ!あれちがう!?安いとは言え金取るのかよ!!今までと変わらんじゃないか!** と発表直後は思いました。 しかしよく調べてみると、 * 最新のエンジン・グラフィックス技術の実装が見放題 * ソースコードは自由に改変可能 * 契約はいつでもやめて良い。その時取得したソースコードはそのまま持っといてもOK。プログラムのアイデアを自分のコードに反映することは全然構わない。(UE以外のプロジェクトへの転用は不可) * 対応プラットフォームは、Windows、Mac、iOS、Android、(PS4・XBOX ONEも対応予定?) * 売上が発生した場合には総収入の5%のロイヤリティ支払えばOK。良心的(・∀・) **あれ。コレって速攻契約しなきゃ損ってやつじゃんよ?** #ってことでVS2013でソースコードビルドするとこまで簡単に説明 * とりあえずビデオ見ながらが早い。[Downloading and Building UE4 Source](https://www.youtube.com/watch?v=usjlNHPn-jo) * このページを見てもなお「ソースコードなんていらねぇやい!」って人は、https://www.unrealengine.com/dashboard の **Latest Download** からDLすれば、ビルド済みのエディタが手にはいります。 ##ビルドに必要なもの * 月々2千円(プライスレス) * GitHubのアカウント * VS2013(無料版でも可らしい) * DirectX SDK * 割りとハイスペックなマシン(エンジンのビルド・実行に相当CPUとメモリを使います) * ソースコードへの愛と情熱<BR> **※あと、[VisualAssistX](https://www.wholetomato.com/)とかあると解析がとても捗ります。購入を強くおすすめします。** <BR> ##ビルドまでの簡単な手順 1. [UE4のページ](https://www.unrealengine.com/blog/welcome-to-unreal-engine-4)にアクセスして、「GET UNREAL」をクリック。アカウント作ります。 1. 淡々と個人情報を入力していきます。クレジットはInternationalなやつじゃないと使えないので注意。住所の入力方法に癖があるのでネットで調べながら入力。 1. アカウントが作成出来たら、[さっきのトップページ](https://www.unrealengine.com/)から、SignIn します。 1. GitHubとの連携的な項目があるので[説明](https://www.unrealengine.com/ue4-on-github)に沿って設定。 1. GitHubは、Git(ギット?)っいう大リーナス神が考案したバージョン管理システムを使ってるので、TortoiseGitをインストールするなり、[SouceTree](https://www.atlassian.com/ja/software/sourcetree/overview)を使うなりしてインストールします。面倒ならZipで落としてきてもOK。 1. あとは、GitHubの [**Getting up and running**](https://github.com/EpicGames/UnrealEngine) に書かれてるとおりに、dependencies(追加の依存ファイル)を落としてきて、ソースフォルダに上書きする。(Optional/Required_1of2.zip/ Required_2of2.zipの3つ) 1. エンジンのルートフォルダの、README.mdの手順に沿って、GenerateProjectFiles.bat を実行。(ここでなんかハマった気がするけど忘れた) 1. VS2013を起動して、フルビルド。しばらくアニメでも見ながらビルドが終わるのを待つ。<BR> 2. ビルドが終わったら実行してみる。 #ソースコード解析のポイントなど * UE4のGUI部分はC#。エンジン部分はC++で書かれてる。 * エディタ起動しながらの、C++コードのホットロードの実力を体感せよ!(エディタ起動しながらビルドして即反映の仕組み) * UnrealEngine\Engine\Source\Runtime\Engine 以下フォルダがエンジンのコアのソースコード * 描画系は、UnrealEngine\Engine\Source\Runtime\Renderer の当たり。 * シェーダーは独自言語。UnrealEngine\Engine\Shaders 以下の.usfファイルがシェーダーファイル。 * 話題沸騰のBluePrint(ノードベーススクリプティング環境。旧Kismet)のコードは、UnrealEngine\Engine\Source\Runtime\Engine\Private\Blueprint.cpp らへん。 * UObject がすべてのクラスの基底クラス。シリアライズ/デシリアライズや、エディタとの連携処理などが実装されてる。 * Actorは、定期更新処理を持つオブジェクトの基底。キャラとかエフェクトとかギミックはコレを継承して作られてる。 * オキュラス関連は、UnrealEngine\Engine\Plugins\OculusRift\Source\OculusRift * APIドキュメントといいつつDoxygenそのまま。あまり整備されてないからちょっと見づらい。 UnrealEngine\Engine\Documentation\CHM\API.chm #まとめ * 月額2000円でソースコード見放題とかそのへんのエッチなサイトより断然安い\(^o^)/(コラ * ソースコードを読むには、ゲームエンジンの構造に関する知識が必要なので、gemsや[ゲームエンジン・アーキテクチャ](http://www.amazon.co.jp/dp/4797360712/ref=cm_sw_r_tw_dp_wGNmtb1N3EB35)など読んで事前に勉強した方が良いかも。ネットにも情報は沢山あります。 * UE4を自作ゲームで使う・使わないに関係なく、プログラムスキルを磨くために見ないと損。 ってことで簡単に導入まで書いてみましたとさ。 # その他ソースコードが読める3D向けゲームエンジン達 ## 無料 * [Blender Game Engine](http://wiki.blender.org/index.php/Doc:JA/2.6/Manual/Game_Engine) * [Helium Engine](https://github.com/HeliumProject/Helium) * [いるりっひたん(irrlicht Engine)](http://irrlicht.sourceforge.net/) * [Project Anarchy](https://projectanarchy.com/jp) * [Torque engine](http://www.garagegames.com/products/torque-3d) * そーすえんじん( ´∀`) (あっ ##有料 * [CryEngine](http://www.cryengine.com/) * [Esenthel Engine](http://www.esenthel.com/) * [Esenthel Engineというゲームエンジンが予想外に面白かったのでメモ](http://qiita.com/yasei_no_otoko/items/a85c677714fb12903f81) by @yasei_no_otoko |
|
| 770位 |
|
|||
|
01:09:10 |
(SAKURA Internet Inc. 所属) |
|
[Dockerfile reference](https://docs.docker.com/engine/reference/builder/)や[Dockerfile Best Practices](http://crosbymichael.com/dockerfile-best-practices.html)にENTRYPOINTとCMDの書き方と使い分け、さらに併用について書かれていました。
# ENTRYPOINTとCMDの引数の書式 [ENTRYPOINTの書式](https://docs.docker.com/engine/reference/builder/#entrypoint)は以下の2種類があります。 * ENTRYPOINT ["executable", "param1", "param2"] \(シェルを介さずに実行。この形式を推奨) * ENTRYPOINT command param1 param2 (シェルを介して実行) シェルを介して実行するほうは/bin/sh -cを使って実行するそうです。 [CMDの書式](https://docs.docker.com/engine/reference/builder/#cmd)は以下の3種類です。 * CMD ["executable","param1","param2"] \(シェルを介さずに実行。この形式を推奨) * CMD ["param1","param2"] \(ENTRYPOINTのデフォルト引数として利用する場合) * CMD command param1 param2 (シェルを介して実行) # ENTRYPOINTとCMDの併用 [Dockerfile Best Practices](http://crosbymichael.com/dockerfile-best-practices.html)の"5. CMD and ENTRYPOINT better together"にENTRYPOINTとCMDを併用する例があります。 ``` ENTRYPOINT ["/usr/bin/rethinkdb"] CMD ["--help"] ``` のように書きます。 すると、docker runのときに ``` docker run crosbymichael/rethinkdb ``` のようにイメージの後に引数無しで実行した場合は、CMDの--helpが使われて、/usr/bin/rethinkdb --helpが実行されることになります。 一方、 ``` docker run crosbymichael/rethinkdb --bind all ``` のようにイメージの後に引数有りで実行した場合は、CMDの値は使われず、/usr/bin/rethinkdb --bind allが実行されることになります。 ## docker run --entrypoint="" で変更も可能 [docker runのリファレンス](https://docs.docker.com/engine/reference/run/#/entrypoint-default-command-to-execute-at-runtime)によると ```--entrypoint``` オプションを使えば ```Dockerfile``` の ```ENTRYPOINT``` の値を上書きすることも可能です。 |
|
| 771位 |
|
|||
|
19:16:56 |
|
|
UnityをC#で超入門してみる #1 Unity入門の章
http://qiita.com/hiroyuki_hon/items/0718a50e6569b6c5037a の続きです。 #目次 超入門#2 ゲームオブジェクトの章 カメラ・移動・変形・削除・色・やりとり 継承関係 Object → GameObject Object → Component → Behaviour ##カメラ カメラを調整しよう カメラでターゲットを追ってみよう ##ゲームオブジェクト ゲームオブジェクトの情報を取得しよう スクリプトの有効、無効を設定してみよう ゲームオブジェクトのタグをチェックしよう Find()でゲームオブジェクトを取得しよう スクリプトでコンポーネントを追加しよう スクリプトからプリミティブオブジェクトを生成しよう 違うプリミティブオブジェクトを表示してみよう スクリプトでオブジェクトのtransformを取得しよう 個別にx,y,zの値を取得しよう 移動させてみよう Cubeを回転させ、Inspectorから数値を変更しよう ゲームオブジェクトを削除しよう 自分自身(スクリプト)を削除しよう 一定時間後に削除されるようにしよう スクリプト色を付けよう 色をRGBAで指定しよう ランダムに色をつけてみよう 他のオブジェクトとのやりとりについて 自身と違うオブジェクトを取得して操作しよう インスタンスを生成しよう nameについて ここまでをまとめ ボタンクリックでインスタンスを生成しよう 画面外に出たインスタンスは削除しよう WebCamTextureを使ってみよう ゲームオブジェクトを無限に生成しよう ゲームオブジェクトを回転させよう ボタン押下でゲームオブジェクトを回転させよう x秒後にゲームオブジェクトを破棄しよう カラーを指定してマテリアルを生成しよう プレハブをスクリプトから生成しよう 触れたものを破壊するオブジェクトを作ろう ##カメラ #カメラを調整しよう Sceneビューでカメラの向きを決めます。 カメラを選択状態にして、 GameObject > Align With View でカメラをSceneビューの視点に設定できます。 #カメラでターゲットを追ってみよう 例えば.LookAtを用いることでカメラなりの向きを追従させることが出来ます。 ```csharp public Transform target; void Update () { transform.LookAt (target); } } ``` ##ゲームオブジェクト Unityは一つ一つの塊をゲームオブジェクトと呼びます。 ゲームオブジェクトにコンポーネントをつけることで 様々な挙動をさせることが出来ます。 ゲームオブジェクトを人、コンポーネントを帽子や アクセサリーに例えると理解が進みます。 #ゲームオブジェクトの情報を取得しよう 主な変数 transform rigidbody camera renderer audio guiText collider gameobject tag ```csharp void Start () { print(gameObject.transform); print(gameObject.audio); print(gameObject.guiText); print(gameObject.gameObject); print(gameObject.tag); } //頭のgameObject.は省略可能 ``` #スクリプトの有効、無効を設定してみよう 変数、enabled を操作することで スクリプトの有効、無効を設定できます。 ```csharp void Start () { print(enabled); enabled = false; print(enabled); /*出力 True エディタのゲームオブジェクトのスクリプトのチェックが外れる */出力 False } ``` #ゲームオブジェクトのタグをチェックしよう CompareTag()を用います。 ```csharp //タグがblockだったらコンソールにblockを出す。 if(CompareTag("block")){ print("block"); } ``` #Find()でゲームオブジェクトを取得しよう Find()を用いることで"ゲームオブジェクト名"から ゲームオブジェクトを取得できます。 適当なPlaneを作ったとします。 ```csharp //適当なGameObject型の変数 gを作成 public GameObject g; void Start () { //g に最初に見つかった名前が Plane のゲームオブジェクトを取得する g = GameObject.Find("Plane"); print ( g ); print ( g.audio); //出力 Plane //出力 null } ``` #スクリプトでコンポーネントを追加しよう 例えばScript2を作成してScript1に追加するとします。 以下のようにすることでStart時にScript1にScript2が追加され、 Script2が実行されます。 ```csharp void Start () { gameObject.AddComponent<Script2>(); } ``` #スクリプトからプリミティブオブジェクトを生成しよう GameObject cube1 = GameObject.CreatePrimitive(PrimitiveType.Cube); と書いてみます。  空のゲームオブジェクトを作って、これにつけて再生すると出ます。  ##違うプリミティブオブジェクトを表示してみよう .Cubeを.Sphere に書き換えてみます。  出ました。  このようにしてプリミティブオブジェクトをスクリプトで生成することが可能です。 #スクリプトでオブジェクトのtransformを取得しよう Unity内の全てのオブジェクトはtransformというプロパティを持っており、 これから位置や角度等を取得することが可能です。 例えばこう書きます。 Debug.Log(transform.position);  このスクリプトをつけて再生すると、transform.position がコンソールに出力されます。 このようになります。  Inspectorにあるtransformの数値の簡略化されたものが表示されています。  ##個別にx,y,zの値を取得しよう 個別にx,y,zの値を取得することも可能です。 Debug.Log(transform.localPosition.x); Debug.Log(transform.localPosition.y); Debug.Log(transform.localPosition.z); と書きます。  すると、このように別々に取得出来ます。  #移動させてみよう こう書きます。  これを貼り付けると移動します。 矢印キーで動かしてみます。 ```csharp public float x = 0.0f; public float z = 0.0f; // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Input.GetKey(KeyCode.RightArrow)) { x = 0.2f; z = 0.0f; } if (Input.GetKey(KeyCode.LeftArrow)) { x = -0.2f; z = 0.0f; } transform.Translate(x, 0, 0, Space.World); if (Input.GetKey(KeyCode.UpArrow)) { z = 0.2f; x = 0.0f; } if (Input.GetKey(KeyCode.DownArrow)) { z = -0.2f; x = 0.0f; } transform.Translate(0, 0, z, Space.World); ``` #Cubeを回転させ、Inspectorから数値を変更しよう Cubeを生成し、 transform.Rotate(0, 1, 0); とスクリプトを書きます。  回ります。 Inspectorから数値を変更出来るようにします。 このように書いてみます。 public float x = 0; public float y = 0; public float z = 0; void Update () { transform.Rotate(x, y, z);  するとInspectorから数値を入力できるようになります。 こうなっています。  #スクリプトでゲームオブジェクトを削除しよう ゲームオブジェクトを削除します。 Destroy(gameObject);  プリミティブのシリンダーを試しに作り、 Destroy(gameObject); のスクリプトを付けます。 これを再生すると、  消えます。  ##自分自身(スクリプト)を削除しよう Destroy(this); これが消えます。   ##一定時間後に削除されるようにしよう 一定時間後にに消えるように指定します。 以下のように書くと5.0秒で消えます。 5.0の部分を書き換えることで時間を指定出来ます。 Destroy(gameObject, 5.0f);  #スクリプトで色を付けよう 試しにCylinderを作成します。 create > Cylinder です。  renderer.material.color = Color.red; と書きます。  こうなります。  ##色をRGBAで指定しよう 色はRGBAを0.0~1.0の値で指定します。 型はfloat型です。 第四引数を指定しない場合は、α値が1.0となります。 例えばこう書きます。 Color col = new Color(0.0f, 0.0f, 0.0f, 1.0f); renderer.material.color = col;  とすると黒になります。  緑にしたい場合は RGBAですから第二引数を1にすれば良いので Color col = new Color(0, 1, 0, 1); renderer.material.color = col; と書いてみます。  するとこうなります。  ##ランダムに色をつけてみよう Color rand = new Color(Random.value, Random.value, Random.value, 1.0f); renderer.material.color = rand; と、書いてみます。 randが変数名です。  ランダムに色が付きます。    ##他のオブジェクトとのやりとりについて GetComponent()や、SendMessage("関数名")を用います。 #自身と違うオブジェクトを取得して操作しよう では名前を利用します。 スクリプトは自分自身をgameObjectから取得できました。 では自身と違うオブジェクトを取得するにはどうすれば良いでしょうか。 まずCubeと空のゲームオブジェクトを作ってみます。  このように書きます。 GameObject kaiten; void Start () {}に kaiten = GameObject.Find("Cube"); void Update () {}に kaiten.transform.Rotate(0, 1, 0); こうなります。  そしてこれを空のゲームオブジェクトに貼付ます。 そして再生をすると  回ります。 回転の制御は kaiten.transform.Rotate(0, 1, 0); です。 このように GameObject.Find("名前"); を用いることで 自分自身以外のオブジェクトから操作を行うことが可能になります。 #インスタンスを生成しよう ゲームオブジェクトに、 このように書いたものをつけて再生してみます。 GameObject obj1 = new GameObject(); obj1.name = "neko"; GameObject obj2 = new GameObject("inu"); GameObject obj3 = new GameObject();  するとこれが、  こうなります。  inu と neko と New Game Object のゲームオブジェクト生成されています。 このことから、インスタンスを生成した時、 後から名前をつける(obj1.name = "neko";)ことも可能であるし、 同時に名前を付けることも出来るし、名無しで生成することも可能であることが分かります。 ##nameについて ところで、ゲームオブジェクトの名前については nameで取得出来ます。 今まではDebug.Logを用いてきましたが、今回はprintを使用してみます。 printも文字列などの表示に使えます。 print(name); と書いたスクリプトをMainCameraとCubeにつけてみます。  名前が取得出来ました。  #ここまでをまとめ さてそれでは、ここまでを一度まとめます。 プロジェクトを新規に開きます。  ここまでのものを詰め込んで色々と作ってみます。  再生するとこうなりました。  Cubeが回転したり、ランダムで色が付いたり、スクリプトでオブジェクトが生成されたり、 時間が経つとオブジェクトが消えたり、コンソールに文字列が表示されたりといったことが 出来るようになりました。 #ボタンクリックでインスタンスを生成しよう GameObjectのターゲットを作成しCubePrefabをエディタから設定します。 Input.GetMouseButtonDown(0) 0=左クリックでインスタンスを生成します。 Rigidbodyを付けておくことで自動落下します。 ```csharp public GameObject CubePrefab; // Update is called once per frame void Update () { if(Input.GetMouseButtonDown(0)){ Instantiate(CubePrefab); } } } ``` #画面外に出たインスタンスは削除しよう OnBecameInvisible()のオーバーライド関数を使うことで カメラの不可視状態になった時の処理を表現出来ます。 Cubeのプレハブにつけることで、クリックで生成したオブジェクトを カメラから外れたら自動で破棄することが出来ます。 ```csharp //カメラから不可視になったら void OnBecameInvisible(){ //デストロイ Destroy(gameObject); } ``` WebCamTextureを使うことで カメラの映像をゲームオブジェクトに貼り付けることが出来ます。 ```csharp void Start () { WebCamTexture webcamTexture = new WebCamTexture(); renderer.material.mainTexture = webcamTexture; webcamTexture.Play(); } ``` #ゲームオブジェクトを無限に生成しよう ```csharp using UnityEngine; using System.Collections; //指定したゲームオブジェクトを無限スポーンさせるスクリプト public class InfinitySpawn : MonoBehaviour { //発生するオブジェクトをInspectorから指定する用 public GameObject spawnObject; //発生間隔用 public float interval = 3.0f; void Start () { //コルーチンの開始 StartCoroutine("Spawn"); } IEnumerator Spawn() { //無限ループの開始 while(true){ //自分をつけたオブジェクトの位置に、発生するオブジェクトをインスタンス化して生成する Instantiate(spawnObject, transform.position, Quaternion.identity); yield return new WaitForSeconds(interval); } } } ``` #ゲームオブジェクトを回転させよう ```csharp //つけたゲームオブジェクトを回転させるスクリプト public class TransformRotate : MonoBehaviour { public float rotateX = 0.0f, rotateY = 1.0f, rotateZ = 0.0f; void Update () { transform.Rotate(rotateX, rotateY, rotateZ); } } ``` #ボタン押下でゲームオブジェクトを回転させよう ```csharp public class GetMouseButtonRotate : MonoBehaviour { public int mouseButtonDownSetting = 1; public float rotateAngleX = 0; public float rotateAngleY = 90; public float rotateAngleZ = 0; void Update () { if ( Input.GetMouseButtonDown(mouseButtonDownSetting) ) { transform.Rotate(rotateAngleX, rotateAngleY, rotateAngleZ); } } } ``` #x秒後にゲームオブジェクトを破棄しよう ```csharp public class GameObjectDestroy : MonoBehaviour { public float desTime = 3.0f; void Start() { //3.0秒後にゲームオブジェクトを破棄する Destroy(gameObject, desTime); } } ``` #カラーを指定してマテリアルを生成しよう ```csharp public class ColorMaterialMake : MonoBehaviour { void Start () { //0.0f~1.0fで指定 Color(R,G,B,α); Color col = new Color(0.0f, 0.0f, 1.0f, 1.0f); renderer.material.color = col; } } ``` #プレハブをスクリプトから生成しよう ```csharp public class PrefabInstantiateCS : MonoBehaviour { public GameObject PrefabX; void Start () { Instantiate(this.PrefabX, new Vector3(0, 0, 0), Quaternion.identity); } } ``` #触れたものを破壊するオブジェクトを作ろう ```csharp public class UnityOnTriggerEnterDestroyCS : MonoBehaviour { void OnTriggerEnter(Collider other) { Destroy(gameObject); } } ``` #つづきます UnityでC#を超入門してみる #3 計算の章 http://qiita.com/hiroyuki_hon/items/3326206dcedb4e5013d4 |
|
| 772位 |
|
|||
|
02:53:13 |
(株式会社キッズスター 所属) |
|
オレオレな Sumlime Text 環境について、まとめています。 主に、Ruby や Rails や Corona SDK などで利用しています。 ## 設定 ```json:Preferences.sublime-settings { "color_scheme": "Packages/RailsCasts Colour Scheme/RailsCastsColorScheme.tmTheme", "corona_sdk_use_daily_docs": true, "draw_white_space": "all", "ensure_newline_at_eof_on_save": true, "font_face": "Anonymous Pro", "font_size": 18, "highlight_line": true, "itg_sidebar_tree_small": true, "itg_xsmall_tabs": true, "margin": 2, "tab_size": 2, "theme": "itg.flat.dark.sublime-theme", "translate_tabs_to_spaces": true, "trim_trailing_white_space_on_save": true, "word_wrap": false, "enable_tab_scrolling": false, "show_encoding": true, "show_line_endings": true } ``` | 設定項目 | 説明 | |:--|:--| | "color_scheme": "Packages/RailsCasts Colour Scheme/RailsCastsColorScheme.tmTheme" | カラースキーマ(#1) | | "corona_sdk_use_daily_docs": true | Corona SDK デイリービルドドキュメント参照(#2) | | "draw_white_space": "all" | スペース常時表示 | | "ensure_newline_at_eof_on_save": true | 最終行に空行自動挿入 | | "font_face": "Anonymous Pro" | フォント | | "font_size": 18 | フォントサイズ | | "highlight_line": true | カレント行のハイライト | | "margin": 2 | ガーターとテキストエリアの間隔 | | "tab_size": 2 | タブサイズ | | "translate_tabs_to_spaces": true | タブを空白に自動変換 | | "trim_trailing_white_space_on_save": true | 保存時に行末の空白を自動削除 | | "word_wrap": false | テキストの折り返し | | "enable_tab_scrolling": false | タブのスクロール表示 | | "show_encoding": true | エンコーディング表示 | | "show_line_endings": true | 行末表示 | (#1) 別途、[RailsCasts Colour Scheme](https://github.com/tdm00/sublime-theme-railscasts) のパッケージインストールが必要です。 (#2) 別途、[Corona Editor](https://github.com/coronalabs/CoronaSDK-SublimeText) のパッケージインストールが必要です。 ## キーバインド ```json:Default(OSX).sublime-keymap [ { "keys": ["super+ctrl+m"], "command": "list_rails_models" }, { "keys": ["super+ctrl+c"], "command": "list_rails_controllers" }, { "keys": ["super+ctrl+v"], "command": "list_rails_views" }, { "keys": ["super+ctrl+h"], "command": "list_rails_helpers" }, { "keys": ["super+ctrl+x"], "command": "list_rails_fixtures" }, { "keys": ["super+ctrl+t"], "command": "list_rails_tests" }, { "keys": ["super+ctrl+i"], "command": "list_rails_javascripts" }, { "keys": ["super+ctrl+y"], "command": "list_rails_stylesheets" }, { "keys": ["ctrl+shift+."], "command": "erb" }, { "keys": ["super+shift+n"], "command": "advanced_new_file"} ] ``` | キー | コマンド | |:--|:--| | ⌘ + ctrl + m | list_rails_models(#1) | | ⌘ + ctrl + c | list_rails_controllers(#1) | | ⌘ + ctrl + v | list_rails_views(#1) | | ⌘ + ctrl + h | list_rails_helpers(#1) | | ⌘ + ctrl + x | list_rails_fixtures(#1) | | ⌘ + ctrl + t | list_rails_tests(#1) | | ⌘ + ctrl + i | list_rails_javascripts(#1) | | ⌘ + ctrl + y | list_rails_stylesheets(#1) | | ⌘ + shift + l | erb(#2) | | ⌘ + shift + n | advanced_new_file(#3) | (#1) 別途、[SublimeRailsNav](https://github.com/noklesta/SublimeRailsNav) のパッケージインストールが必要です。 (#2) 別途、[SublimeERB](https://github.com/eddorre/SublimeERB) のパッケージインストールが必要です。 (#3) 別途、[AdvancedNewFile](https://github.com/skuroda/Sublime-AdvancedNewFile) のパッケージインストールが必要です。 ## パッケージ パッケージコントロールでインストール出来ないパッケージについては、リポジトリを登録してインストールしています。 ```json:PackageControl.sublime-settings { "installed_packages": [ "AdvancedNewFile", "Better CoffeeScript", "Capybara Snippets", "Corona Editor", "Cucumber", "Git", "Git Config", "GitGutter", "Gitignore", "LESS", "rails_tutorial_snippets", "RailsCasts Colour Scheme", "RecentActiveFiles", "RSpec", "Ruby Slim", "SASS Snippets", "SideBarEnhancements", "SublimeERB", "SublimeRailsNav", "SuperNavigator", "Syntax Highlighting for Sass", "Theme - itg.flat" ], "repositories": [ "https://github.com/mhartl/rails_tutorial_snippets", "https://github.com/wallysalami/QuickLook", "https://github.com/noklesta/SublimeRailsNav/tree/ST3" ] } ``` ### AdvancedNewFile [skuroda/Sublime-AdvancedNewFile](https://github.com/skuroda/Sublime-AdvancedNewFile) * 新規ファイルを作成する際に便利です。 * 以下のようなキーバインドで、新規ファイルを作成するように、デフォルトのキーバインドを上書きして利用しています。 ```json:Default(OSX).sublime-keymap { "keys": ["super+shift+n"], "command": "advanced_new_file"} ``` ### Better CoffeeScript [aponxi/sublime-better-coffeescript](https://github.com/aponxi/sublime-better-coffeescript) * Coffee Script のシンタックスハイライトやコマンド、スニペットが利用可能になります。 ### Capybara Snippets [asux/sublime-capybara-snippets](https://github.com/asux/sublime-capybara-snippets) * Capybara のスニペットが利用可能になります。 ### Corona Editor [coronalabs/CoronaSDK-SublimeText](https://github.com/coronalabs/CoronaSDK-SublimeText) * Lua のシンタックスハイライトや Corona SDK のビルドコマンドが利用可能になります。 * Corona SDK のスニペットも多数用意されています。 * Corona API のコードにカーソルを合わせた状態で、`F1ボタン` を押下すると、Corona SDK の公式ドキュメントをブラウザで表示します。 * デバッグ機能も備えていて、ブレイクポイントの設定、ステップ実行、変数の中身を参照することが可能です。 * Corona SDK の提供元である Corona Labs によって提供されています。 * 私の場合、以下のように Daily ビルドのドキュメントを表示するようにしています。 ```json:CoronaEditor.sublime-settings { "corona_sdk_use_docset": "daily" } ``` ### Cucumber [drewda/cucumber-sublime2-bundle](https://github.com/drewda/cucumber-sublime2-bundle) * Cucumber のシンタックスハイライトやスニペットが利用可能になります。 ### Git [kemayo/sublime-text-git](https://github.com/kemayo/sublime-text-git) * Sublime Text 上で git コマンドが利用可能になります。 ### Git Config [robballou/gitconfig-sublimetext](https://github.com/robballou/gitconfig-sublimetext) * `.gitconfig` のシンタックスハイライトが利用可能になります。 ### GitGutter [jisaacks/GitGutter](https://github.com/jisaacks/GitGutter) * ガーターにコードの追加や変更、削除マークを表示します。 * これは必須パッケージと言えそうです。 ### Gitignore [theadamlt/Sublime-Gitignore](https://github.com/theadamlt/Sublime-Gitignore) * GitHub が用意している [.gitignore ファイル](https://github.com/github/gitignore) を利用して、簡単に `.gitignore` を作成可能になります。 * 新規プロジェクトを作成する際に便利です。 ### LESS [danro/LESS-sublime](https://github.com/danro/LESS-sublime) * LESS のシンタックスハイライトが利用可能になります。 ### rails_tutorial_snippets [mhartl/rails_tutorial_snippets](https://github.com/mhartl/rails_tutorial_snippets) * [Ruby on Rails Tutorial](http://ruby.railstutorial.org) で利用されているスニペットが利用可能になります。 * 主に、RSpec のスニペットです。 ### RailsCasts Colour Scheme [tdm00/sublime-theme-railscasts](https://github.com/tdm00/sublime-theme-railscasts) * [RailsCasts](http://railscasts.com) で利用されているカラーテーマが利用可能になります。 ### RecentActiveFiles [jugyo/SublimeRecentActiveFiles](https://github.com/jugyo/SublimeRecentActiveFiles) * 直近で利用したファイル一覧を表示可能になります。 * `⌘ + k` + `⌘ + t` のキーバインドで表示します。 ### RSpec [SublimeText/RSpec](https://github.com/SublimeText/RSpec) * RSpec のシンタックスハイライトが利用可能になります。 ### Ruby Slim [slim-template/ruby-slim.tmbundle](https://github.com/slim-template/ruby-slim.tmbundle) * Slim のシンタックスハイライトが利用可能になります。 ### SASS Snippets [mrmartineau/SASS-Snippets](https://github.com/mrmartineau/SASS-Snippets) * SASS のスニペットが利用可能になります。 ### SideBarEnhancements [titoBouzout/SideBarEnhancements](https://github.com/titoBouzout/SideBarEnhancements) * サイドバーで選択状態のファイルに対する便利な操作を利用可能になります。 ### SublimeERB [eddorre/SublimeERB](https://github.com/eddorre/SublimeERB) * 以下のようなキーバインドで、`<%= %>`, `<% %>`, `<%- -%>`, `<%= -%>`, `<%# %>`, `<% -%>` が簡単に入力可能になります。 ```json:Default(OSX).sublime-keymap { "keys": ["ctrl+shift+."], "command": "erb" } ``` ### SublimeRailsNav [noklesta/SublimeRailsNav](https://github.com/noklesta/SublimeRailsNav) * Rails の モデルやコントローラ、ビューなど、特定のディレクトリにあるファイル一覧を表示可能になります。 * 以下のようなキーバインドで、特定のファイル一覧を表示します。 ```json:Default(OSX).sublime-keymap { "keys": ["super+ctrl+m"], "command": "list_rails_models" } { "keys": ["super+ctrl+c"], "command": "list_rails_controllers" } { "keys": ["super+ctrl+v"], "command": "list_rails_views" } { "keys": ["super+ctrl+h"], "command": "list_rails_helpers" } { "keys": ["super+ctrl+x"], "command": "list_rails_fixtures" } { "keys": ["super+ctrl+t"], "command": "list_rails_tests" } { "keys": ["super+ctrl+i"], "command": "list_rails_javascripts" } { "keys": ["super+ctrl+y"], "command": "list_rails_stylesheets" } ``` ### SuperNavigator [jugyo/SublimeSuperNavigator](https://github.com/jugyo/SublimeSuperNavigator) * カレントファイルのインクリメンタルサーチが利用可能になります。 * `⌘ + k` + `⌘ + a` のキーバインドで表示します。 ### Syntax Highlighting for Sass [P233/Syntax-highlighting-for-Sass](https://github.com/P233/Syntax-highlighting-for-Sass) * SCSS / Sass のシンタックスハイライトを利用可能になります。 |
|
| 773位 |
|
|||
|
16:52:52 |
|
|
いろいろなアイコン画像のサイズ。画像ファイルはInfo.plistで指定。 ## アイコンサイズ |ファイル名|サイズ|説明| |-----|-----|-----| | Icon.png | 57 x 57 | iPhone/iPod touch (3/3G/3GS)| |Icon@2x.png |114 x 114| Retina用 iPhone/iPod touch| |Icon-72.png |72 x 72 | iPad| |Icon-72@2x.png| 144 x 144| Retina用 iPad| |Icon-Small.png| 29 x 29 | iPhone/iPod touch Spotlight and Settings | |Icon-Small@2x.png |58 x 58 |Retina用 iPhone/iPod touch Spotlight and Settings | |Icon-Small-50.png |50 x 50 |iPad Spotlight and Settings | |Icon-Small-50@2x.png| 100 x 100| Retina用 iPad Spotlight and Settings | |iTunesArtwork |1024 x 1024 | Ad Hoc, iTunes用 1024×1024推奨(PNG)| ## スプラッシュ画像サイズ |ファイル名 |サイズ| 説明| |-----|-----|-----| |Default.png |320 x 480 |iPhone/iPod touch (3/3G/3GS)| |Default@2x.png |640 x 960 |Retina用 iPhone/iPod touch| |Default-568h@2x.png |640 x 1136|iPhone 5/iPod touch (4inch)| |Default-Portrait.png |768 x 1004|iPad (portrait)用 ステータスバー(20px)を除いたサイズ| |Default-Portrait@2x.png| 1536 x 2008|Retina用 iPad (portrait)用 ステータスバー(40px)を除いたサイズ| |Default-Landscape.png |1024 x 748 |iPad (landscape)用 ステータスバー(20px)を除いたサイズ| |Default-Landscape@2x.png |2048 x 1496| Retina iPad (landscape)用 ステータスバー(40px)を除いたサイズ| ※iPadのスプラッシュ画像の場合は、ファイル名に以下のオプションも可能 * -PortraitUpsideDown * -LandscapeLeft * -LandscapeRight ## ナビゲーションバー、ツールバー、タブバーアイコン画像サイズ |サイズ| 説明| |-----|-----------| |20 x 20 |NavigationBar,ToolBar用 アイコンサイズ| |40 x 40|Retina用 NavigationBar,ToolBar アイコンサイズ (ファイル名に @2xを付与)| |30 x 30|TabBar用 アイコンサイズ| |60 x 60|Retina用 TabBarアイコンサイズ (ファイル名に @2xを付与)| ## 参考 iOS Human Interface Guidelines: Custom Icon and Image Creation Guidelines http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/MobileHIG/IconsImages/IconsImages.html iOS App Programming Guide: App-Related Resources http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/App-RelatedResources/App-RelatedResources.html#//apple_ref/doc/uid/TP40007072-CH6-SW1 IconKit https://itunes.apple.com/jp/app/iconkit/id507135296?mt=12 Status Barred https://itunes.apple.com/jp/app/status-barred/id413853485?mt=12 |
|
| 774位 |
|
|||
|
14:58:28 |
|
|
Rails Advent Calendar 8日目です。 Arel でサブクエリを組んでみようと思って、どうやるのかググるとこのページがヒット。いろんな例があってわかりやすいです。 [Arelで色んなSQLを組み立ててみる - ryopekoの日記](http://d.hatena.ne.jp/ryopeko/20101215/1292373612) > サブクエリや型変換を伴うようなSQLの組み上げについては調査中です。 えー。 ということで調べたメモ。 ## 基本 Arel::Table というクラスがあって、そこからメソッドをチェーンしていきます。 Arel::Table のオブジェクトは普通にnewして作ることができます。 ```ruby books = Arel::Table.new(:books) ``` または、ActiveRecordのモデルからは arel_table というメソッドで取れます。 ```ruby class Book < ActiveRecord::Base ... end books = Book.arel_table ``` `books[:id]`のように`[]`を使うと、 Arel::Attributes::Attribute というカラムを表すクラスのオブジェクトが取れます。これで条件を組み立てていきます。 ## 例 ryopekoさんの記事からほぼそのまま引用させていただきます。 ```ruby books .project(Arel.star) .where(books[:id].gt(5)) .where(books[:id].lt(10)) .to_sql # => SELECT * FROM "books" WHERE "books"."id" > 5 AND "books"."id" < 10 books .project(books[:id].count.as('id_count'), books[:category_id]) .group(books[:category_id]) .to_sql # => SELECT COUNT("books"."id") AS id_count, "books"."category_id" FROM "books" GROUP BY "books"."category_id" ``` to_sql メソッドでSQL文字列が得られます。 説明するまでもないぐらい直感的ですね。 ## サブクエリ ### JOIN, FROMなどでテーブルとして使うサブクエリ books から project などを呼ぶと Arel::SelectManager のオブジェクトになり、そのままずっとメソッドチェーンしていけるのですが、 SelectManager のままでは join などに渡すことができません(エラーになる)し、`[]`で Attribute を指定することもできません。 最後に as メソッドを使ってテーブル名をつけると、 Arel::TableAlias のオブジェクトになって Arel::Table と同様に扱えます。 <small>これがどこにも書いてなかった…。</small> ```ruby counts = books .project(books[:id].count.as('id_count'), books[:category_id]) .group(books[:category_id]) .as('counts') categories = Arel::Table.new(:categories) categories .project(Arel.star) .join(counts, Arel::Nodes::OuterJoin) .on(categories[:id].eq(counts[:category_id])) .to_sql # => SELECT * FROM "categories" LEFT OUTER JOIN (SELECT COUNT("books"."id") AS id_count, "books"."category_id" FROM "books" GROUP BY "books"."category_id") counts ON "categories"."id" = counts."category_id" ``` FROMに置きたい場合、 Arel::TableAlias からは project などを直接呼べないのですが、適当な Arel::Table から from を使えば呼ぶことができます。このとき Table は何の意味があるのか謎。 ActiveRecordでは、whereなどでチェーンしていくと ActiveRecord::Relation になりますが、そこから arel というメソッドで Arel::SelectManager を得ることができます。 なので、こういうミックスも可能。 <small>内部でArel使ってるからできて当たり前ではありますが…</small> ```ruby counts_ar = Book .select("COUNT(books.id) AS id_count, books.category_id") .group(:category_id) .arel .as('counts') categories .project(Arel.star) .join(counts_ar, Arel::Nodes::OuterJoin) .on(categories[:id].eq(counts_ar[:category_id])) .to_sql # => SELECT * FROM "categories" LEFT OUTER JOIN (SELECT COUNT(books.id) AS id_count, books.category_id FROM "books" GROUP BY category_id) counts ON "categories"."id" = counts."category_id" ``` ### WHEREで使うサブクエリ WHERE条件の中でもサブクエリが使えますが、その場合は ```ruby ids_10 = books .project(books[:category_id]) .group(books[:category_id]) .having(books[:id].count.gteq(10)) categories .project(Arel.star) .where(categories[:id].in(ids_10)) .to_sql # => SELECT * FROM "categories" WHERE "categories"."id" IN (SELECT "books"."category_id" FROM "books" GROUP BY "books"."category_id" HAVING COUNT("books"."id") >= 10) ``` のように in には SelectManager がそのまま渡せます。 INでのサブクエリはたしかActiveRecordでもできましたね。 INを使わずに、`WHERE id = (…)`の形も書けるはず、と思ったのですが、 eq は SelectManager を受け取ってくれません。 ast メソッドを使って SelectStatement に変換すると通るのですが、生成されるSQLに`( )`が入ってない! ```ruby id_10 = books .project(books[:category_id]) .group(books[:category_id]) .having(books[:id].count.eq(10)) .take(1) categories .project(Arel.star) .where(categories[:id].eq(id_10)) # => TypeError: Cannot visit Arel::SelectManager categories .project(Arel.star) .where(categories[:id].eq(id_10.ast)) .to_sql # => SELECT * FROM "categories" WHERE "categories"."id" = SELECT "books"."category_id" FROM "books" GROUP BY "books"."category_id" HAVING COUNT("books"."id") = 10 LIMIT 1 ``` <del>どうやればいいかよくわかりませんでした。 まあ、これはIN使ってもJOIN使っても書けるからいらないですね(笑)</del> <ins>同じく as を使って Arel::TableAlias にすればOKでした。なぜ気づかなかったんだろう…。 ```ruby categories .project(Arel.star) .where(categories[:id].eq(id_10.as('id_10'))) .to_sql # => SELECT * FROM "categories" WHERE "categories"."id" = (SELECT "books"."category_id" FROM "books" GROUP BY "books"."category_id" HAVING COUNT("books"."id") = 10 LIMIT 1) id_10" ``` 結局、全部 as を使えばいいんだということですね。 例は省略しますが、同様にSELECTの中にも書くことができました。 ## 最後に ここまでArelで書く必要があるのだろうか、生SQLでいいんじゃ、という気もするでしょう。 そのクエリで完結するならSQLで書いてもいいのですが、Railsで使うときはscopeにできるというメリットが大きいです。 |
|
| 775位 |
|
|||
|
20:44:16 |
|
|
sublime text3でPHPの開発をしているのですが、
eclipseやnet beansにあったF3やctrl+左クリックで関数の宣言元にジャンプできる機能がないかなと思って 色々と探してみたらできたのでメモしておきます。 sublime text3からの新機能である`goto_definition`というコマンドを設定すればよい模様。 デフォルトではF12に設定されていて、プロジェクト内検索しか対応していないようですが、使えそうです。 メニューの `Preferences > KeyBindings User` を開いて以下を追記。 // F3で関数の宣言元にジャンプ { "keys": ["f3"], "command": "goto_definition" }, // alt+左で戻る { "keys": ["alt+left"], "command": "jump_back" }, // alt+右で戻ったのをまた進める { "keys": ["alt+right"], "command": "jump_forward" }, 再起動せずに即時反映されます。 ##ctrlを押しながら関数名をマウスクリックする時もF3と同じ動きをさせたい マウスのキーバインドはメニューから設定ファイルを開くようになっていないっぽいので悩んだのですが 偉大なるstackoverflowに同じような投稿があったので参考にしました。 > 参考 > [Sublime 3 - Set Key map for function Goto Definition](http://stackoverflow.com/questions/16235706/sublime-3-set-key-map-for-function-goto-definition) `Preferences > Browse Packages`で設定ファイルが入っているディレクトリを開いて 更にUserディレクトリの中に`Default (Windows).sublime-mousemap`というファイルを作成し、 中に以下の設定を書くと反映されました。 [ { "button": "button1", "count": 1, "modifiers": ["ctrl"], "press_command": "drag_select", "command": "goto_definition" }, ] macだと`Default (OSX).sublime-mousemap` linuxだと`Default (Linux).sublime-mousemap`というファイル名になるようです(未確認) また、ctrl+clickにすると、sublime textの魅力の一つであるカーソルを複数置く機能とかぶってしまうので 自分は`"modifiers": ["ctrl"],`の部分を`"modifiers": ["ctrl", "shift"],`として ctrl+shift+clickでジャンプするように設定しました。 ##ついでに 他に設定しているeclipse風ショートカットは以下 `Preferences > KeyBindings User`に追記 // 上の行と入れ替える { "keys": ["alt+up"], "command": "swap_line_up" }, // 下の行と入れ替える { "keys": ["alt+down"], "command": "swap_line_down" }, // 行複製 (windowsだとwindows側のグラフィックオプションのホットキーを無効にしないと画面が回転します) { "keys": ["ctrl+alt+down"], "command": "duplicate_line" }, // ファイル検索&置換 { "keys": ["ctrl+h"], "command": "show_panel", "args": {"panel": "find_in_files"} }, // 行削除 { "keys": ["ctrl+d"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Line.sublime-macro"} }, // ctrl+dを行削除に設定したので、デフォルトにあった文字列選択はctrl+shift+dに設定 { "keys": ["ctrl+shift+d"], "command": "find_under_expand" }, ctrl+マウススクロールでフォントサイズが変更されるのが面倒だったのでその機能を切る `Default (Windows).sublime-mousemap`内に追記 { "button": "scroll_down", "modifiers": ["ctrl"], "command": "null" }, { "button": "scroll_up", "modifiers": ["ctrl"], "command": "null" }, > 参考 > [SublimeText2でマウスでの動作の設定を変更する](http://qiita.com/Fuhduki/items/9a245448996e0dae5876) |
|
| 776位 |
|
|||
|
15:50:31 |
(フリーランス 所属) |
|
最近、スマホやタブレットを使う機会も多いですが、その辺りの端末に動画コンテンツを提供する時に楽なのがmp4形式の動画をvideoタグで貼り付ける事です。
ただ、ちょっとした注意点があって、どういう状況で再生するかによって細かい調整をしておかないと、再生開始までにえらく時間がかかるようになって、非常に不便になります。 ## 既に作成済みの動画を再生する場合 mp4形式の動画を再生するためにはメタデータ情報が必要です。メタデータは通常ファイルの末尾に付与されます。ファイルサイズが小さい時は気付かないのですが、10分、20分ぐらいあるような動画を再生しようとすると末尾まで読んでから再生できるかどうかを判別するので、再生が開始できるようになるまでに異様に時間がかかるようになります。 そこで動画作成時にメタデータをファイルの先頭に移動させておきます。 こうする事で、ファイルサイズが大きい動画でも一瞬で再生開始が可能になります。 PCのブラウザだと、その辺上手いことやってくれるのか末尾にメタデータがあってもすぐに再生できたりするんですが…。 ffmpegで動画を作成している場合には、`-movflags faststart`オプションを利用することでエンコード後にメタデータを先頭に移動させる事ができます。 例としてはこんな感じ。 ``` ffmpeg -i inputfile -f mp4 -vcodec libx264 -b:v 800k -acodec libfaac -b:a 128k -movflags faststart -o output.mp4 ``` 既に存在しているmp4を変換したい場合は[qt-faststart](https://github.com/danielgtaylor/qtfaststart)というツールが利用できます。 ## エンコードしながら追っ掛けて再生する場合 バックグラウンドでエンコードしつつ動画の再生を開始したい場合、PC向けかモバイル向けかで状況が変わります。 ### モバイル向けの場合 モバイル端末で追っ掛けながら再生する場合はHTTP Live Streaming(HLS)を使うのが良い方法です。 HLSは動画ファイルを約10秒程度の短時間に分割しMPEG-TSコンテナに入れたものをひたすら生成しつつm3u8形式のリストファイルにそのファイルを逐次追加していくことで、HTTPのみで擬似的にLive Streamingを実現する方法です。 Appleが提唱したらしく、iOSが最も安定して再生できるのですが一応Androidでも対応します。 (手元のNexus7で試した所、Androidだとシークが出来なくなる) HLSを利用することで、Webカメラ等で録画しつつHTML5のvideoタグで配信する、といった事も可能になりますし、オンデマンドで動画ファイルを変換しながら再生するということもWebブラウザだけで実現可能です。 以前は動画の分割がかなり面倒だったのですが、新しめのffmpegであればsegment分割の機能が組込まれているので、簡単に作成することができるようになりました。 ffmpegでHLSを作成するには以下のように設定します。 ``` ffmpeg -i inputfile -vcodec libx264 -b:v 800k -acodec libfaac -b:a 128k -flags +loop-global_header -map 0 -bsf h264_mp4toannexb -f segment -segment_format mpegts -segment_time 10 -segment_list output.m3u8 stream%04d.ts ``` 自分が試した時には、何故か`-flags +loop-global_header`と`-map 0`と`-bsf h264_mp4toannexb`のオプションが必要になりましたが、詳しい理由は不明です。 重要なのは以下のオプションです。 - `-f` で出力形式を`segment`に指定する - `-segment_format` で分割されたストリームのフォーマットを`mpegts`に指定する - `-segment_time` で分割単位時間を指定する。大体10秒くらいが目安らしい - `-segment_list` で出力するリストファイル名を指定する - 出力名に連番が振られるように`%04d`等を含めておく。この場合4桁の連番になる。 videoタグには出力されたm3u8ファイルを指定しておきましょう。 HLSでwebカメラの映像等を配信している場合、そのままだとガンガンファイルが溜まっていってストレージを圧迫するので、不要になったデータを定期的に削除する仕組みが必要になるかもしれません。 ### PC向けの場合 PC向けのブラウザでは今のところSafariを除いてHLS形式の再生は不可能です。 ちゃんとストリーミングサーバーを用意してrtpなりrtspなりのプロトコルを利用すれば再生できそうですが、中々面倒そうなのでこちらは試していません。一応videoタグでも利用できそうですが未検証です。 インチキな解決策として、fragmented mp4を作成するという方法があります。 通常mp4のメタデータは一箇所にまとめて配置されますが、それをキーフレームごとに分散して配置する方法です。 こうしておくとエンコードが全て終わっていなくても、現在エンコードが進んでいる所までは再生できるようになります。ただ一回読み込んだ時点で動画の長さが認識されるので、途中で止まってしまった場合続きを再生できるようにするには再読み込みが必要です。 一瞬停止しても良いならJSを組み合わせてゴリ押すことも可能かもしれませんが、中々面倒です。 fragmented mp4を作成するのもffmpegでオプションを追加するだけでOKです。 ``` ffmpeg -i inputfile -f mp4 -vcodec libx264 -b:v 800k -acodec libfaac -b:a 128k -movflags frag_keyframe -o output.mp4 ``` というわけで、html5のvideoタグを利用した動画の配信はiOS系が一番進んでいるような気がします。AirPlayも利用できるので、Apple TVがあれば普通にテレビに転送して再生できたりします。 自宅にカメラ置いてモニタするWebシステムを作りたい時とか、自宅のストレージにある動画をどこでも見れるようにしたい時に使えると思います。 まあ、そういう既存のアプリもあるので普通そっち使うのが良いと思いますが、細かい部分を自分に便利な形にしようとすると自分で色々用意するのが結局楽だったりします。 |
|
| 777位 |
|
|||
|
23:38:25 |
|
|
# ちょっと不便なRspecのテスト
Rspecでテストを書いている人は多いと思うんですが,Rspecのテストちょっと不便じゃありませんか? 例えばテスト実行中 ```rspec ......F.....F....F....... ``` + あとどれくらい残ってるの!?いつ終わるの?(どれくらいのテストが残っているかわからない) + テスト落ちたなら,それ先に見せてよ!(どのテストが失敗したかわからない,でも一応最後までテストを実行したい,けど終わるまで結果がわからない) うーん,やっぱりちょっと不便. # そんな時に便利なFuubar https://github.com/jeffkreeftmeijer/fuubar こちらの[ビデオ](http://vimeo.com/16845253)を見てもらうとFuubarがどういうものかわかると思います. Fuubarのいいところは - 落ちたテストからFaiure/Errorメッセージを出してくれる. - 残りのテスト数がわかる - 予想終了時間が表示される 素晴らしい! ## インストール Fuubarを使うには ``` gem install fuubar ``` して,テスト実行時に`--format Fuubar`をつけるだけ ``` rspec --format Fuubar spec ``` もしも気に入ったのなら`.rspec`に ``` --format Fuubar ``` を追加すると,`--format Fuubar`を付けなくても常にFuubarのフォーマットで表示されるようになります. # 参考記事 [Fuubar: the instafailing RSpec progress bar formatter](http://jeffkreeftmeijer.com/2010/fuubar-the-instafailing-rspec-progress-bar-formatter/) [【翻訳】Fuubar: instafailing な RSpec プログレスバーフォーマッタ ](http://keijinsonyaban.blogspot.jp/2010/11/fuubar-instafailing-rspec.html) |
|
| 778位 |
|
|||
|
23:49:57 |
|
|
<a href="http://hibara.org/blog/2011/02/20/c-sharp-net-framework-aes/" target="_blank">自分のブログ</a>でも書いたので、Qiitaでの再掲ともなりますが、そのときよりも少しだけ発展させ、かつ詳説を加えてみたいと思います。
`2015/08/22: コメント欄からのご指摘がいくつかあり、検証や追記、ソースコードの修正を行いました。` `2015/10/08: コメント欄から、さらなるご指摘がいくつかあり、説明やソースコードの記述の修正を行いました。` まず「暗号化」するための常識、というか、ある程度の定石について解説します。 次に実際にソースコードを交えてファイルを暗号化してみます。 .NET Framework 4.0 には、標準で暗号化に必要なクラスがそろっていますので、今回は、それらを駆使して、自分のコードは必要最低限で済ませてみましょう。 ## 暗号化には定石がある 意外とこのポイントを知らずに、ただ暗号化クラスを使っている方も多いと思います。 「定石」とは、暗号アルゴリズムは何を使うか?といったレベルではなく、もっと上のレイヤー、「暗号化とは?」に近い、基礎的な手法を指します。 それを「暗号化モード(Block cipher modes of operation)」と言います。 ## 暗号化モードとは? 基本的に、どの暗号アルゴリズムを使うかに限らず、それが「ブロック暗号方式」であるのなら、まず「暗号化モード」は、何を使うのか?を検討しましょう。 よくわからず、暗号化でやってしまいがちなのは、「ECBモード」でしょうか。  これの何がダメなのでしょうか? このモードでは、同じデータ、同じパスワードだと、毎回同じ暗号化データが生成されてしまうことが問題になります。 「毎回同じデータが生成される」ということは、別のデータと比較することで、悪意ある攻撃者に、少なからず「ヒント」を与えることになります。 たとえば、必ず同じブロックが生成されるなら、それが生成されるまでパスワードの総当り攻撃がしやすくなります。つまり、ファイル全体ではなく、小さなブロックデータなら、解析が容易になるということです。 ただ、毎回同じパスワードと同じデータなど、そんなにあるわけない、と思われるかもしれません。 しかし、日付など同じヘッダ情報が付加されるデータや、同じく終端データなどはどうでしょうか。 ユーザーや開発者が意図しなくとも、知らずに同じデータが入ってしまうこともあるのです。 ## CBCモードを使うのがベター 逆に言えば、同じデータ、同じパスワードでも、毎回暗号化するたびに中身が変われば、攻撃者にヒントを与えにくくするということになります。 そこで出てくるのが、別の暗号化モードです。 実は、暗号化モードには、いろいろな種類があり、.NET Framework にも、ほぼ主要なものはそろっています。 <strong>CipherMode 列挙体</strong> http://msdn.microsoft.com/ja-jp/library/system.security.cryptography.ciphermode(v=vs.110).aspx とはいえ、どのモードにも、メリット、デメリットがあります。 その中で、暗号強度、コストパフォーマンスのバランスから、<strong>CBCモード</strong>を選択するのがベターでしょう。 暗号化の前に、初期化ベクトル(IV)を与えることで、縄のように、データを交互にねじり合せていくイメージです。  暗号化する度にランダムのデータ列が与えられることで、ファイル全体が、毎回ちがう値になることが保証されます。 このモードは、最初に与えるIVの質さえ考慮していれば良く、安全に暗号化を行うことができます。 ちなみに、暗号の大家である<a href="https://www.schneier.com/" target="_blank">ブルース・シュナイアー氏</a>がその著書『暗号技術大全』(日本語訳版は絶版・・・)の中でも、 > ファイルを暗号化するのであれば、CBCモードがベストだろう。このモードを使えば、セキュリティは大きく向上するし、保存したデータに多少エラーが発生しても、同期エラーが発生することはまずない。アプリケーションが(ハードウェアではなく)ソフトウェアベースであれば、CBCがほぼ確実にいちばんいい。 と書いています。 ## ブロック暗号における「パディング」とは? これも、ブロック暗号を行うときに避けては通れないポイントです。 AESでは、128bit(16バイト)と決められたデータサイズで暗号化されます。 ですので、たとえば16バイトで割り切れないデータサイズのファイルを暗号化するときには、「余り」ができてしまうことがあります(もちろんピッタリの場合もありますが)。 暗号化するときには、特に大きな問題は起きませんが、実は、復号するときに問題となります。 なぜなら元のデータと、暗号化データの境界線がわからなくなるからです(元ファイルのサイズがわからなくなる、とも言えます)。 そこで、ブロック暗号には、「暗号化モード」とは別に、「パディングモード」というものも指定する必要があります。 このパディングモードも、.NET Frameworkでは、いろいろ用意されおり、自前で実装する必要がありません。 <strong>PaddingMode 列挙体</strong> http://msdn.microsoft.com/ja-jp/library/vstudio/system.security.cryptography.paddingmode(v=vs.100).aspx 今回は、PaddingMode.PKCS7 を使うことにしましょう。 たとえば、以下の例ですと、データ長が8バイトで、実際のデータ列が9バイトあれば、残りの7バイトは、以下のように埋められます。  つまり「余り」に埋められた合計サイズが、数値として埋められるというわけです。 これにより、復号時に、データ境界線をプログラムで判別できるようになります。 ## AESとは? さて、本題の「AES」についても少し説明をしておきましょう。 <strong>A</strong>dvanced <strong>E</strong>ncryption <strong>S</strong>tandard の略です。 2000年、アメリカ国立標準技術研究所(<a title="NIST" href="http://www.nist.gov/index.html" target="_blank">NIST</a>)による、厳しい選定審査によって選ばれた、オープンな暗号アルゴリズムです。 正式名は、Rijndael(ラインダール)と言いますが、≒ AESです。完全にイコールでない理由は、後述します。 米国産とはいえ、言葉どおり、世界のスタンダードになりつつあります。今のところ、大きな脆弱性もなく、世界各国、いろいろな場所での採用が進んでいます。 共通鍵暗号方式を採用するときは、ほぼ「AES」の一択でしょう。 どうして他のアルゴリズムじゃダメなのか? あるいは、独自暗号じゃダメなのか?という議論もあるでしょう。しかし、原則的には推奨できません。 AESは、アルゴリズムが完全にオープンにされ、多くの数学者や暗号研究家たちの検証に晒された上で耐え、最終選定されています。 それは数学的にも、アルゴリズム的にも、脆弱性が「今のところない」と、公に証明されていることになります。保証されている、とも言い換えられます。 独自アルゴリズムだと、それがないため、未知の脆弱性が多く潜んでいる可能性を否定できません。 また、たとえ万が一、AESに脆弱性が発見されても(そんな事態になったら世界的な危機ですが・・・)、知名度の高さから、ニュースとしてすぐに報じられ、対応策の情報も即座に得やすいでしょう。 と、前置きが長くなりましたが、実際にC#のコードにしてみましょう。 ```csharp AesManaged aes = new AesManaged(); aes.BlockSize = 128; // BlockSize = 16bytes aes.KeySize = 128; // KeySize = 16bytes aes.Mode = CipherMode.CBC; // CBC mode aes.Padding = PaddingMode.PKCS7; // Padding mode is "PKCS7". // パスワード文字列が大きい場合は、切り詰め、16バイトに満たない場合は0で埋めます byte[] bufferKey = new byte[16]; byte[] bufferPassword = Encoding.UTF8.GetBytes(Password); for (i = 0; i < bufferKey.Length; i++) { if (i < bufferPassword.Length) { bufferKey[i] = bufferPassword[i]; } else { bufferKey[i] = 0; } } aes.Key = bufferKey; aes.GenerateIV(); //Encryption interface. ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV); ``` ## 暗号化パスワードについて コメント欄より、[suzukis](http://qiita.com/suzukis)さんからのご指摘があり、以下の書き方だと、「鍵空間が小さくなるのではないか」とのご指摘をいただきました。 ```csharp // パスワード文字列が大きい場合は、切り詰め、16バイトに満たない場合は0で埋めます byte[] bufferKey = new byte[16]; byte[] bufferPassword = Encoding.UTF8.GetBytes(Password); for (i = 0; i < bufferKey.Length; i++) { if (i < bufferPassword.Length) { bufferKey[i] = bufferPassword[i]; } else { bufferKey[i] = 0; } } aes.Key = bufferKey; ``` たしかに、この書き方ですと、たとえば16文字までパスワード文字列を指定できるのに、「a」と一文字だけにしてしまうと、15文字は空いてしまうということになります。  そこで、入力されたパスワードからランダムな文字列を生成して、きちんと16バイト埋めた状態で暗号化した方がより安全ではないか、ということでした。 それは以下のように、` Rfc2898DeriveBytes` クラスを使うことで簡単に実現することができます。 ```csharp //入力されたパスワードをベースに擬似乱数を新たに生成 Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Password, 16); // saltは内部的に生成される // 生成した擬似乱数から16バイト切り出したデータをパスワードにする byte[] bufferKey = deriveBytes.GetBytes(16); ``` ただ、この場合、ランダムな文字列を生成した際の、「salt」をファイルに保持しておかないと(書き込んでおかないと)、復号できなくなるので、その点は注意が必要です。 ## AESの厳密な定義 先ほど、チラリと言及しましたが、NISTでは、Rijndaelアルゴリズムで、ブロックサイズが、<strong>128bit</strong>で、キー長が、<strong>128、192、256bit</strong> を「AES」と定義しています。 p.5 - 1. Introduction http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf こちらも、[suzukis](http://qiita.com/suzukis)さんからのコメントでのご指摘があり、前の記事にあった「ブロックサイズ、キー長ともに128bitである」という記述を修正しました。 ですので、キー長とブロックサイズに、これら以外の値を設定すると、AESではないので、このクラスではエラーを吐きます。 どうしても強度面で、両方とも128bit以上、たとえば256bitで運用したいという場合は、 System.Security.Cryptography.RijndaelManaged クラスを使いましょう。 そのときは、"AesManaged"の部分を置換するだけです。 ```csharp RijndaelManaged aes = new RijndaelManaged(); aes.BlockSize = 256; aes.KeySize = 256; ``` ただ、この場合、厳密には「AESを使っている」とは言えなくなります。「Rijndaelを使っている」となります。 > ※これは定義的な問題で、中身に問題があるわけではありません。たとえば開発依頼された顧客に対して、暗号化内容を説明するときに注意が必要ということです。 ## 暗号化前に圧縮をかけるのも定石 実は、暗号化前に圧縮をかけるというも、定石とされています。 ・・・と、前の記事では書いていたのですが、こちらも[suzukis](http://qiita.com/suzukis)さんからのコメントでのご指摘で、ちゃんと調べてみました。 何度も引用して恐縮ですが、<a href="https://www.schneier.com/" target="_blank">ブルース・シュナイアー氏</a>の著書『暗号技術大全』にも、 > データ圧縮アルゴリズムを暗号アルゴリズムと一緒に使うことは、2つの点で有意義だ: > > - 暗号分析は、平文の冗長さを利用する。暗号化の前に圧縮しておけば、冗長さが減らすことができる。 > - 暗号化は時間のかかる作業だ。暗号化の前に圧縮しておけば、作業時間を短縮できる。 また、僕は前の記事で、「たいていは、暗号アルゴリズムの処理負荷を下げます」と書きましたが、実際に計測してみました。 それぞれ10MB, 100MB, 1000MBデータで、圧縮有無で、10回ずつ試行し、その平均値を求めてみました。 | | 10MBのデータ| 100MBのデータ|1000MBのデータ| |:-----------|------------:|-------------:|-------------:| | 圧縮有り | 592.7 ms | 6100.5ms | 58709.8ms | | 圧縮無し | 253.4 ms | 2592.4ms | 25675.3ms | 結果は圧倒的に「圧縮無し」の勝利。。。 というわけで、[suzukis](http://qiita.com/suzukis)さんのご指摘通りでした。ありがとうございます! とはいえ、処理時間の短縮としては、期待できませんが、平文の冗長さを無くしておくという意義からも、圧縮をやっておきましょう。 ここでは、.NET Framework に標準である System.IO.Compression.DeflateStream クラスを使います。 すべてStream派生クラスですので、親和性が高く、合わせて使えば、ほとんどプログラマーはするべきことがありません。ホント、.NET Framework様々ですね(笑)。 ```csharp using (CryptoStream cse = new CryptoStream(outfs, encryptor, CryptoStreamMode.Write)) { // IV outfs.Write(iv, 0, 16); //ファイル先頭に埋め込む using (DeflateStream ds = new DeflateStream(cse, CompressionMode.Compress)) //圧縮 { using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) { while ((len = fs.Read(buffer, 0, 4096)) > 0) { ds.Write(buffer, 0, len); } } } } ``` ## サンプルコード 一応、そのまま貼り付けて使えるように、以下に、関数化したサンプルソースコードも載せておきましょう。 計測のため、前のソースコードからStopwatch が加えられていますが、先頭から末尾までの時間を計測するだけです。 まずは、暗号化から。↓ ```csharp private bool FileEncrypt(string FilePath, string Password) { //Stopwatchオブジェクトを作成する System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); //ストップウォッチを開始する sw.Start(); int i, len; byte[] buffer = new byte[4096]; //Output file path. string OutFilePath = Path.Combine(Path.GetDirectoryName(FilePath), Path.GetFileNameWithoutExtension(FilePath)) + ".enc"; using (FileStream outfs = new FileStream(OutFilePath, FileMode.Create, FileAccess.Write)) { using (AesManaged aes = new AesManaged()) { aes.BlockSize = 128; // BlockSize = 16bytes aes.KeySize = 128; // KeySize = 16bytes aes.Mode = CipherMode.CBC; // CBC mode aes.Padding = PaddingMode.PKCS7; // Padding mode is "PKCS7". //入力されたパスワードをベースに擬似乱数を新たに生成 Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Password, 16); byte[] salt = new byte[16]; // Rfc2898DeriveBytesが内部生成したなソルトを取得 salt = deriveBytes.Salt; // 生成した擬似乱数から16バイト切り出したデータをパスワードにする byte[] bufferKey = deriveBytes.GetBytes(16); /* // パスワード文字列が大きい場合は、切り詰め、16バイトに満たない場合は0で埋めます byte[] bufferKey = new byte[16]; byte[] bufferPassword = Encoding.UTF8.GetBytes(Password); for (i = 0; i < bufferKey.Length; i++) { if (i < bufferPassword.Length) { bufferKey[i] = bufferPassword[i]; } else { bufferKey[i] = 0; } */ aes.Key = bufferKey; // IV ( Initilization Vector ) は、AesManagedにつくらせる aes.GenerateIV(); //Encryption interface. ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV); using (CryptoStream cse = new CryptoStream(outfs, encryptor, CryptoStreamMode.Write)) { outfs.Write(salt, 0, 16); // salt をファイル先頭に埋め込む outfs.Write(aes.IV, 0, 16); // 次にIVもファイルに埋め込む using (DeflateStream ds = new DeflateStream(cse, CompressionMode.Compress)) //圧縮 { using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) { while ((len = fs.Read(buffer, 0, 4096)) > 0) { ds.Write(buffer, 0, len); } } } } } } //ストップウォッチを止める sw.Stop(); //結果を表示する long resultTime = sw.ElapsedMilliseconds; //Encryption succeed. textBox1.AppendText("暗号化成功: " + Path.GetFileName(OutFilePath) + Environment.NewLine); textBox1.AppendText("実行時間: " + resultTime.ToString() + "ms"); return (true); } ``` 次に復号する場合ですが、暗号化とまったく逆のことをすれば良いだけです。 暗号化では、<strong>先に</strong>圧縮していたので、<strong>復号後</strong>に、解凍処理という順番となっています。 ```csharp private bool FileDecrypt(string FilePath, string Password) { int i, len; byte[] buffer = new byte[4096]; if (String.Compare(Path.GetExtension(FilePath), ".enc", true) != 0) { //The file are not encrypted file! Decryption failed MessageBox.Show("暗号化されたファイルではありません!" + Environment.NewLine + "復号に失敗しました。", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return (false); ; } //Output file path. string OutFilePath = Path.Combine(Path.GetDirectoryName(FilePath), Path.GetFileNameWithoutExtension(FilePath)) + ".txt"; using (FileStream outfs = new FileStream(OutFilePath, FileMode.Create, FileAccess.Write)) { using (FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) { using (AesManaged aes = new AesManaged()) { aes.BlockSize = 128; // BlockSize = 16bytes aes.KeySize = 128; // KeySize = 16bytes aes.Mode = CipherMode.CBC; // CBC mode aes.Padding = PaddingMode.PKCS7; // Padding mode is "PKCS7". // salt byte[] salt = new byte[16]; fs.Read(salt, 0, 16); // Initilization Vector byte[] iv = new byte[16]; fs.Read(iv, 0, 16); aes.IV = iv; /* // パスワード文字列が大きい場合は、切り詰め、16バイトに満たない場合は0で埋めます byte[] bufferKey = new byte[16]; byte[] bufferPassword = Encoding.UTF8.GetBytes(Password); for (i = 0; i < bufferKey.Length; i++) { if (i < bufferPassword.Length) { bufferKey[i] = bufferPassword[i]; } else { bufferKey[i] = 0; } */ // ivをsaltにしてパスワードを擬似乱数に変換 Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Password, salt); byte[] bufferKey = deriveBytes.GetBytes(16); // 16バイトのsaltを切り出してパスワードに変換 aes.Key = bufferKey; //Decryption interface. ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV); using (CryptoStream cse = new CryptoStream(fs, decryptor, CryptoStreamMode.Read)) { using (DeflateStream ds = new DeflateStream(cse, CompressionMode.Decompress)) //解凍 { while ((len = ds.Read(buffer, 0, 4096)) > 0) { outfs.Write(buffer, 0, len); } } } } } } //Decryption succeed. textBox1.AppendText("復号成功: " + Path.GetFileName(OutFilePath) + Environment.NewLine); return (true); } ``` ここで注意していただきたいのは、一度暗号化してしまうと、ファイルを復号しても、ファイル情報が失われてしまうことです。 ファイル情報とは、ファイル名、属性、タイムスタンプなどです。 もしそうした情報も正常に戻せるようにするには、暗号化ファイルにそのデータも含めるなど、少々複雑な処理が必要になってくるでしょう。 また、パスワードが合っているのか、間違っているのかの判定もしていません。 このソースコードでは、誤ったパスワードで復号すると、さらに変なデータ配列に混ぜられてしまいます(おそらく元のデータに戻せなくなります)。 これについても、復号が成功したか、ヘッダに何かしらの印を埋め込んでおいて、それでパスワードの成否を判定する必要があります。 今回は、「ファイルを暗号化する」という部分に焦点を合わせて書きましたので、そういった「完全版」は、また別の機会に取り上げたいと思います。 一応、GitHubにも今回のプロジェクトファイルごと上げました。↓ https://github.com/hibara/FileEncryptSample Visual Studio C# 2010 Express、または、VS Express 2013 for Desktopでの動作、ビルドを確認済みです。 ちなみに、MITライセンスです。ご自由にどうぞ。 |
|
| 779位 |
|
|||
|
13:06:31 |
|
|
導入
==== [CentOS 7]の公式[Yum]リポジトリから提供されている[Apache]で動作を確認しています。それ以外のOSでは、[設定ファイルが置かれている場所] _(英語)_ や[ドキュメントルート]の初期値が異なる場合があるのでご注意ください。 [CentOS 7]: https://ja.wikipedia.org/wiki/CentOS "CentOS(セントオーエス)は、Red Hat Enterprise Linux(RHEL)との完全互換を目指したフリーのLinuxディストリビューションである。" [Yum]: https://ja.wikipedia.org/wiki/Yellowdog_Updater_Modified "Yellowdog Updater Modified (Yum ヤム)はLinuxのRPM Package Managerのパッケージを管理するメタパッケージ管理システムである。" [Apache]: https://ja.wikipedia.org/wiki/Apache_HTTP_Server "Apache HTTP Server(アパッチ エイチティーティーピー サーバ)は、世界中でもっとも多く使われているWebサーバソフトウェアであり、大規模な商用サイトから自宅サーバまで幅広く利用されている。単にApacheとも称されている。開発は、Apacheソフトウェア財団のApache HTTPサーバプロジェクトで行われている。Apache Licenseの下でソースコードが公開および配布されており、代表的なオープンソースソフトウェアの一つである。" [設定ファイルが置かれている場所]: https://httpd.apache.org/docs/current/getting-started.html#configuration "Getting Started — Apache HTTP Server Version 2.4" [ドキュメントルート]: https://httpd.apache.org/docs/current/urlmapping.html#documentroot "URL からファイルシステム上の位置へのマップ — Apache HTTP サーバ バージョン 2.4" Apache設定ファイル (*.conf) のシンタックスハイライト ==================================================== CentOS標準のYumリポジトリで提供されているテキストエディタのうち[Vim]・[Emacs]以外のエディタは、初期状態で`.conf`ファイルのシンタックスハイライトが機能しません。以下のように設定ファイルの編集などを行い、シンタックスハイライトを有効にします。 [Vim]: https://ja.wikipedia.org/wiki/Vim "Vim(ヴィムまたはヴィアイエム)は、vi から派生し、発展した高機能なテキストエディタである。" [Emacs]: https://ja.wikipedia.org/wiki/Emacs "Emacs(イーマックス)とは高機能でカスタマイズ性の高いテキストエディタである。スクリーン・エディタとしての人気が高く、特にUNIXのプログラマを中心としたコンピュータ技術者に愛用者が多い。" [gedit] ------- [Bug 612368 – Language definition for Apache configuration files](https://bugzilla.gnome.org/show_bug.cgi?id=612368#attachment_table)で公開されているファイルを利用します。 ```sh # Apache設定ファイル用の言語設定ファイルをダウンロード wget https://bug612368.bugzilla-attachments.gnome.org/attachment.cgi?id=195525 --output-document=apache.lang # 言語設定ファイル中のclass属性、class-disabled属性を削除。「*.conf」をハイライトするように sed --in-place --regexp-extended --expression 's/\sclass(-disabled)?="[^"]+"//g' --expression 's/httpd\.conf;httpd2\.conf;apache\.conf;apache2\.conf/*.conf/' apache.lang # GtkSourceViewのユーザー定義ディレクトリに移動 sudo mv apache.lang /usr/share/gtksourceview-*/language-specs/ ``` [gedit]: https://ja.wikipedia.org/wiki/Gedit "gedit(ジーエディット)は、GNOMEデスクトップの標準テキストエディタ。通常のテキストエディタとしての機能に加え、複数のプログラミング言語に対応したハイライト表示、コードスニペットの追加といった、プログラム開発者向けの機能も有している。キー操作はWindows系でWindowsに慣れたユーザーにも使いやすい。" [nano] ------ [Arch Linux]の[nano-syntax-highlighting-git]パッケージを利用します。[^nano] [^nanorc] ```sh # INIファイルのシンタックスハイライトを行う設定ファイルをダウンロード wget https://raw.githubusercontent.com/scopatz/nanorc/master/apacheconf.nanorc # .conf で終わるファイルをApache設定ファイルとして扱うようにする sed --in-place 's/httpd\\\.conf/.conf$/' apacheconf.nanorc # シンタックスハイライトを行う設定ファイル保管場所に移動 sudo mv apacheconf.nanorc /usr/share/nano/ # nanoの個人設定ファイルから読み込むようにする echo $'\n\n'## INI files$'\n'include "/usr/share/nano/apacheconf.nanorc" >> ~/.nanorc ``` [^nano]: [シンタックスハイライト | 設定 | nano — ArchWiki](https://wiki.archlinuxjp.org/index.php/nano#.E3.82.B7.E3.83.B3.E3.82.BF.E3.83.83.E3.82.AF.E3.82.B9.E3.83.8F.E3.82.A4.E3.83.A9.E3.82.A4.E3.83.88) [^nanorc]: [37.nanoエディタの設定ファイル、nanorcをいじる — Linuxゲリラ戦記](http://www.garunimo.com/program/linux/linux37.xhtml) [nano]: https://ja.wikipedia.org/wiki/Nano_%28%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF%29 "nanoは、UNIXを中心としたシステムで使われる、cursesを使ったテキストエディタの一種である。スクリーンエディタの一種でありながら、CUIを用いて編集を行なうことが可能である。スクリーンエディタとして有名なものは既に多数存在するが、このエディタの特色は、その操作方法がWYSIWYGに慣れたユーザにとって分かりやすいため、初心者でも比較的容易に扱うことが可能という点にある。" [Arch Linux]: https://ja.wikipedia.org/wiki/Arch_Linux "Arch Linux(アーチ・リナックス、ˈɑrtʃ)は、Linuxディストリビューションの1つであり、「シンプリシティ」、ミニマリズム、エレガンスさ、コードの正しさに焦点を当てて開発されている。「シンプリシティ」(Simplicity、簡潔さ) とは、Arch Linuxの言うところでは「…不必要な追加・修正、あるいは複雑化を伴わない…」ことであり、それは初心者の立場よりも開発者の立場からの見たものだとしている。Arch Linuxは、ローリング・リリースと呼ばれるリリースモデルを採用しており、他のLinuxディストリビューションで見られるような明確な「バージョン番号」や「リリース・スケジュール」を持たない。リポジトリ上のソフトウェアパッケージは日々新しいバージョンへと更新されており、定期的にソフトウェアアップデートを行うことで、システムを常に最新の状態に保つことができる。インストール用のイメージファイルは定期的に更新されているが、これは基本的にシステムパッケージ群の最新スナップショットに過ぎない。" [nano-syntax-highlighting-git]: https://aur.archlinux.org/packages/nano-syntax-highlighting-git/ "nanoのシンタックスハイライト機能を強化する" [Kate] ------ ファイル名が`.conf`で終わるファイルをApache設定ファイルとして開くようにします。 ```xml:/usr/share/kde*/apps/katepart/syntax/.xml <language name="Apache Configuration" section="Configuration" version="1.11" kateversion="2.0" extensions="*.conf;.htaccess*;.htpasswd*" mimetype="" author="Jan Janssen (medhefgo@googlemail.com)" license="LGPL"> <!-- ↑extensions属性値の「httpd.conf;httpd2.conf;apache.conf;apache2.conf」を「*.conf」に置換 --> <!--############## ## 以下省略 ## ##############--> ``` [Kate]: https://ja.wikipedia.org/wiki/Kate "Kate とは KDE のテキストエディタである。頭字語 “Kate” は “KDE advanced text editor” の略である。Kate は KDE リリース 2.2(2002年8月15日)から kdebase パッケージの一部となっている。KDE の一部となっている KParts の機能により、他の KDE アプリケーションに編集コンポーネントとして Kate を組み込むことができる。統合開発環境 KDevelop やウェブ開発環境 Quanta Plus は編集コンポーネントとして Kate を利用している主要な KDE アプリケーションである。" 基本設定 ======== ```apache:/etc/httpd/conf/httpd.conf ############## ## 以上省略 ## ############## <IfModule unixd_module> # # If you wish httpd to run as a different user or group, you must run # httpd as root initially and it will switch. # # User/Group: The name (or #number) of the user/group to run httpd as. # It is usually good practice to create a dedicated user and group for # running httpd, as with most system services. # User apache Group apache ## ↑両方とも daemon から apache に変更する (CentOS7標準のApacheでは設定済み) ## </IfModule> # 'Main' server configuration # # The directives in this section set up the values used by the 'main' # server, which responds to any requests that aren't handled by a # <VirtualHost> definition. These values also provide defaults for # any <VirtualHost> containers you may define later in the file. # # All of these directives may appear inside <VirtualHost> containers, # in which case these default settings will be overridden for the # virtual host being defined. # # # ServerAdmin: Your address, where problems with the server should be # e-mailed. This address appears on some server-generated pages, such # as error documents. e.g. admin@your-domain.com # ServerAdmin webmaster@example.jp ## ↑サーバー管理者への問い合わせ先メールアドレスに変更する ## # # ServerName gives the name and port that the server uses to identify itself. # This can often be determined automatically, but we recommend you specify # it explicitly to prevent problems during startup. # # If your host doesn't have a registered DNS name, enter its IP address here. # ServerName example.jp:80 ## ↑サーバーのホスト名を指定する ## ############## ## 中略 ## ############## # # Relax access to content within /var/www. # #<Directory "/var/www"> # AllowOverride None # # Allow open access: # Require all granted #</Directory> ## ↑<Directory "/var/www"> セクションをコメントアウトする (CentOS7標準のApacheの場合) ## ## ↓一つ上のディレクトリに変更する ## <Directory "/var/www"> # # Possible values for the Options directive are "None", "All", # or any combination of: # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews # # Note that "MultiViews" must be named *explicitly* --- "Options All" # doesn't give it to you. # # The Options directive is both complicated and important. Please see # https://httpd.apache.org/docs/2.4/mod/core.html#options # for more information. # Options FollowSymLinks ## ↑ Indexes を削除する ## # # AllowOverride controls what directives may be placed in .htaccess files. # It can be "All", "None", or any combination of the keywords: # AllowOverride FileInfo AuthConfig Limit # AllowOverride None # # Controls who can get stuff from this server. # #Require all granted ## ↑コメントアウトする ## </Directory> # # DirectoryIndex: sets the file that Apache will serve if a directory # is requested. # <IfModule dir_module> DirectoryIndex index.html </IfModule> ############## ## 中略 ## ############## # # LogLevel: Control the number of messages logged to the error_log. # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. # #LogLevel warn ## ↑コメントアウトする ## ############## ## 中略 ## ############## # # Customizable error responses come in three flavors: # 1) plain text 2) local redirects 3) external redirects # # Some examples: #ErrorDocument 500 "The server made a boo boo." #ErrorDocument 404 /missing.html #ErrorDocument 404 "/cgi-bin/missing_handler.pl" #ErrorDocument 402 http://www.example.com/subscription_info.html # ErrorDocument 403 /not-found ErrorDocument 404 /not-found ErrorDocument 503 /internal-server-error ## ↑それぞれのエラーコードに対応するファイルを指定する ## ############## ## 以下省略 ## ############## ```   <dl><dt>[User ディレクティブ] \([旧バージョンの翻訳][user-old])</dt><dt>[Group ディレクティブ] \([旧バージョンの翻訳][group-old])</dt><dd>`User apache`<br />`Group apache`</dd><dd>root権限で起動したApacheの親プロセスが子プロセスを生成する際に使用するユーザー名とグループ名を指定します。ここでは、`daemon`から、Apacheインストール時に自動作成されたユーザー (グループ) `apache`に変更します。</dd> <dt>[ServerAdmin ディレクティブ]</dt><dd>`ServerAdmin webmaster@example.jp`</dd><dd>サーバー管理者への問い合わせ先メールアドレス (または別サーバーのURL) を指定します。 **`webmaster@example.jp`は例示用のアドレスなので、自分が管理するメールアドレスに置き換えてください。** </dd> <dt>[ServerName ディレクティブ]</dt><dd>`ServerName example.jp:80`</dd><dd>サーバーのホスト名とポート番号を指定します。サーバーのIPアドレスに対して、DNSのPTRレコード (逆引きレコード) で設定したホスト名を指定しておくと良いでしょう。このディレクティブが欠けていると、「正確なホスト名を決定できなかった」という旨のエラーが発生します。 **`example.jp`は例示用のホスト名なので、自分のサーバーのホスト名に置き換えてください。** このディレクティブは、後述する[<VirtualHost> セクション]内でも設定します。</dd> <dt>[Option ディレクティブ]</dt><dd>利用できる機能の制限を強化、または緩和します。最初から記述されている`Options Indexes FollowSymLinks`は、<i>Indexes機能</i>と<i>FollowSymLinks機能</i>が有効です。スラッシュで終わるURLにアクセスがあった場合、 [DirectoryIndex ディレクティブ]で指定したファイルがディレクトリに存在しなければ、ディレクトリ内のファイル一覧を返すのが<i>Indexes機能</i>です。このディレクティブが欠けていると、デフォルト値である`Options All`に設定されるので注意してください。</dd> <dt>[Require ディレクティブ] \(リンク先英語)</dt><dd>サーバーのディレクトリに接続してくるクライアントについて、許可・拒否する条件を指定するディレクティブです。昔は[Allow ディレクティブやDeny ディレクティブ]を利用していました。`Require all granted`は、すべてのクライアントからの接続を許可します。`Require all denied`は、すべてのクライアントからの接続を拒否します。</dd> <dt>[<Directory> セクション]</dt><dd>ディレクティブを適用するディレクトリを指定します。<i>ファイルシステムのパス絶対URL</i> (ドキュメントルートではなくOSのルートディレクトリをルートとするスラッシュで始まるURL) で指定します。`*` `?` `[` `]`をワイルドカードとして使用できます。<i>パス区分</i> (ディレクトリ名など) を`/`以外 (Windowsの`\\`など) で区切るOSであっても、このセクションの引数では`/`を用います。</dd><dd>記述できるディレクティブの制限などはありますが、`<Directory /var/www/html>`内に記述するのと、`/var/www/html/.htaccess`に記述するのは同義です。</dd><dd>最初は`<Directory "/var/www/html">`にドキュメントルートの設定が記述されています。`html`以外のディレクトリへのアクセスがあるなら、`<Directory "/var/www">`に変更し、`Require all granted`をコメントアウトしておきます。</dd> <dt>[Redirect ディレクティブ]</dt><dd>第2引数で指定したURLを、第3引数で指定したURLにリダイレクトします。第1引数にはHTTPステータスコードを指定します。第1引数は省略可能で、省略した場合`302`になります。URLは前方一致で、`Redirect /foo /bar`と記述すると、https://example.com/foo/test が https://example.com/bar/test にリダイレクトされます。この場合、https://example.com/foo2 などには一致しません。</dd><dd>第1引数に[300〜399]以外の数値を指定することもでき、この場合第3引数は指定しません。</dd><dd>第1引数において、`301` `302` `303` `410`は、それぞれ`permanent` `temp` `seeother` `gone`とキーワードで指定することもできます。なお、古いHTTP仕様で`302`はこのキーワード通り`Moved Temporarily`でしたが、仕様が変更され[302 (Found)]となっており、<i>一時的なリダイレクト</i>には現在[307 (Temporary Redirect)]を使うことになっています。</dd> <dt>[RedirectMatch ディレクティブ]</dt><dd>第2引数に正規表現を指定すること以外は、[Redirect ディレクティブ]と同じです。正規表現内の後方参照は、第3引数で`$1`などと数字に`$`を前置して利用できます。たとえば、`RedirectMatch ^/foo(/.*)?$ /bar$1`は`Redirect /foo /bar`と同じ意味になります。</dd> <dt>[ErrorDocument ディレクティブ]</dt><dd>`ErrorDocument 404 /not-found`</dd><dd>エラーが発生したときに返す文書等を指定します。第2引数でファイルを指定する場合は、<i>パス絶対URL</i> (ドキュメントルートをルートとするスラッシュで始まるパス) で指定する必要があります。</dd><dd>ここでは、[ファイルの存在自体を隠蔽するなどの理由で、エラーコードが403の場合でも404の場合と同じ文書を返す]ようにしています。この場合第2引数に、<i>`404`を返すプログラム</i>を指定したり、[RewriteRule ディレクティブ]の[\[R|redirect\]フラグ][R]で`404`を返したりする必要があります。</dd> </dl> [User ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_unixd.html#user "The userid under which the server will answer requests" [user-old]: https://httpd.apache.org/docs/2.2/mod/mpm_common.html#user "リクエストに応答する際に用いるユーザ ID" [Group ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_unixd.html#group "Group under which the server will answer requests" [group-old]: https://httpd.apache.org/docs/2.2/mod/mpm_common.html#group "リクエストに応答する際に所属するグループ" [ServerAdmin ディレクティブ]: https://httpd.apache.org/docs/current/mod/core.html#serveradmin "サーバがクライアントに送るエラーメッセージに含める電子メールの アドレス" [ServerName ディレクティブ]: https://httpd.apache.org/docs/current/mod/core.html#servername "サーバが自分自身を示すときに使うホスト名とポート" [<VirtualHost> セクション]: https://httpd.apache.org/docs/current/mod/core.html#virtualhost "特定のホスト名や IP アドレスのみに適用されるディレクティブを 囲む" [Option ディレクティブ]: https://httpd.apache.org/docs/current/mod/core.html#options "ディレクトリに対して使用可能な機能を設定する" [DirectoryIndex ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_dir.html#directoryindex "クライアントがディレクトリをリクエストしたときに調べる リソースのリスト" [Require ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_authz_core.html#require "Tests whether an authenticated user is authorized by an authorization provider." [Allow ディレクティブやDeny ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_access_compat.html "ホスト (名前もしくは IP アドレス) に基づいたグループ承認" [<Directory> セクション]: https://httpd.apache.org/docs/current/mod/core.html#directory "指定のファイルシステムのディレクトリとサブディレクトリとのみに 適用されるディレクティブを囲む" [Redirect ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_alias.html#redirect "クライアントが違う URL を取得するように外部へのリダイレクトを 送る" [300〜399]: https://triple-underscore.github.io/RFC7231-ja.html#status.3xx "クラス 3xx (Redirection) の状態°コードは、[ 要請が履行されるためには, UA による更なる動作がとられる必要がある ] ことを指示する。" [302 (Found)]: https://triple-underscore.github.io/RFC7231-ja.html#status.302 "状態°コード 302 (Found)は、[ ターゲットリソースが, 一時的に, 異なる URI の下に居る ] ことを指示する。 リダイレクトは, その時々で改められ得るので、クライアントは, 未来の要請に対しては 実効要請 URI を利用し続けるべき.である。" [307 (Temporary Redirect)]: https://triple-underscore.github.io/RFC7231-ja.html#status.307 "状態°コード 307 (Temporary Redirect)は、[ ターゲットリソースが, 一時的に異なる URI の下に居る ] ことに加えて, [ UA は、[ その URI への自動リダイレクトを遂行する ] ときに, 要請メソッドを変更してはならない ] ことを指示する。 リダイレクトは, 時経過に伴って変化し得るので、クライアントは, 未来の要請においても, 元の実効要請 URI を利用し続けるべき.である。" [RedirectMatch ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_alias.html#redirectmatch "mod_alias — Apache HTTP サーバ" [ErrorDocument ディレクティブ]: https://httpd.apache.org/docs/current/mod/core.html#errordocument "core — Apache HTTP サーバ" [ファイルの存在自体を隠蔽するなどの理由で、エラーコードが403の場合でも404の場合と同じ文書を返す]: https://triple-underscore.github.io/RFC7231-ja.html#status.403 "生成元サーバは、禁止されたターゲットリソースの現在の存在を “隠し” たいと望むときは, 代わりに [ 404 (Not Found) ] で応答してよい。" [R]: https://httpd.apache.org/docs/current/rewrite/flags.html#flag_r "Use of the [R] flag causes a HTTP redirect to be issued to the browser. If a fully-qualified URL is specified (that is, including http://servername/) then a redirect will be issued to that location. Otherwise, the current protocol, servername, and port number will be used to generate the URL sent with the redirect." TLS (俗称: SSL) の設定 ====================== サーバー証明書をインストール済みであればこの項は読み飛ばしてください。この記事では、無料でDV証明書を取得できる[Let’s Encrypt]を利用する場合について解説します。[Let’s Encrypt]は証明書を自動発行する[ACMEプロトコル]を採用しており、[Certbot]などのACMEクライアントで簡単に導入できます。 [Let’s Encrypt]: https://letsencrypt.jp/about/ "Let’s Encrypt は、無料で利用できる自動化されていてオープンな認証局(CA)です。公共の利益を図る目的で Internet Security Research Group (ISRG) が運営しています。" [ACMEプロトコル]: http://jxck.hatenablog.com/entry/letsencrypt-acme "Let’s Encrypt を支える ACME プロトコル — Block Rockin’ Codes" [Certbot]: https://letsencrypt.jp/command/ "Certbot クライアントを使用することで、HTTPS / TLS / SSL の証明書を、取得したりインストールしたりすることができます。" Certbotのインストール --------------------- この記事では、サーバー設定も自動で行える[Certbot]をACMEクライアントとして使用します。 [Let’s Encrypt の使い方 — Let’s Encrypt 総合ポータル](https://letsencrypt.jp/usage/ "Let’s Encrypt は、クライアントソフトウェア「Certbot」を使用することで、SSL/TLS サーバ証明書の取得・更新作業を自動化できる仕組みになっています。") 各OSごとの取得方法は上記ページをご覧ください。以下はCentOS7の場合における例です。 ```sh # 【インストールしていない場合】EPELリポジトリとmod_sslのインストール sudo yum install epel-release mod_ssl # Certbotのインストール sudo yum install certbot python-certbot-apache # ↑EPEL7のGPG公開鍵を取り込んでも良いか訊ねられた場合、 # Fingerprintが https://getfedora.org/ja/keys/ の指紋と大文字小文字無視で一致するか確認します。 # 【未設定の場合】443ポートを開ける sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload ``` 証明書の取得・インストール -------------------------- 途中でApacheが再起動される際にエラーが出るため、先にApacheを起動しておきます。 ```sh sudo apachectl start sudo certbot --apache --must-staple --hsts --redirect ``` 途中で証明書を発行するドメイン名の入力 (複数の場合は空白かカンマ区切り)、緊急連絡などに使うメールアドレスの入力、およびLet’s Encrypt利用規約への同意を求められます。また、設定の保存先として `ssl.conf` (CentOSの場合) が提示されるので、そのままそれを <kbd><samp><Select></samp></kbd> します。 [後述の設定で複数のドメインを扱う]場合、`--redirect` の代わりに一旦 `--no-redirect` を指定しておきます。 [後述の設定で複数のドメインを扱う]: #一つのapacheで複数のドメインを扱う仮想ホスト Apacheの設定 ------------ ```apache:/etc/httpd/conf.d/ssl.conf ############## ## 以上省略 ## ############## # Use separate log files for the SSL virtual host; note that LogLevel # is not inherited from httpd.conf. #ErrorLog logs/ssl_error_log #TransferLog logs/ssl_access_log #LogLevel warn ## ↑3行ともコメントアウトする ## # SSL Engine Switch: # Enable/Disable SSL for this virtual host. #SSLEngine on ## ↑コメントアウトする ## ############## ## 中略 ## ############## # Server Certificate: # Point SSLCertificateFile at a PEM encoded certificate. If # the certificate is encrypted, then you will be prompted for a # pass phrase. Note that a kill -HUP will prompt again. A new # certificate can be generated using the genkey(1) command. SSLCertificateFile /etc/letsencrypt/live/example.jp/cert.pem ## ↑CertbotのApacheプラグインによって自動的に修正された行 ## # Server Private Key: # If the key is not combined with the certificate, use this # directive to point at the key file. Keep in mind that if # you've both a RSA and a DSA private key you can configure # both in parallel (to also allow the use of DSA ciphers, etc.) SSLCertificateKeyFile /etc/letsencrypt/live/example.jp/privkey.pem ## ↑CertbotのApacheプラグインによって自動的に修正された行 ## ############## ## 中略 ## ############## # Per-Server Logging: # The home of a custom SSL log file. Use this when you want a # compact non-error SSL logfile on a virtual host basis. CustomLog logs/ssl_request_log \ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" # TLSの基本設定を読み込み Include /etc/letsencrypt/options-ssl-apache.conf ## ↑この行を追加 ## ## ↓これ以降の行が、CertbotのApacheプラグインによって追記された文字列 ## ServerName example.jp ServerAlias sub1.example.jp sub2.example.jp Header always set Strict-Transport-Security "max-age=31536000" SSLUseStapling on SSLCertificateChainFile /etc/letsencrypt/live/example.jp/chain.pem </VirtualHost> <IfModule mod_ssl.c> SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000) </IfModule> ```   <dl><dt>[SSLProtocol ディレクティブ] \(リンク先英語)</dt><dd>`SSLProtocol all -SSLv2 -SSLv3`</dd><dd>使用するSSL/TLSのバージョンを制御します。各引数の先頭に `+` `-` を付け、手前で指定したバージョンに追加するか除外するかを表します。</dd><dd>ここでは、サポートされるすべてのバージョンから、(SSL 2.0)、SSL 3.0 を除外しています。なお、CentOS 7 の `ssl.conf` には最初 `SSLProtocol all -SSLv2` と記述されているものの、Apache 2.4 では SSL 2.0 をすでにサポートしていないので、エラーこそ出ませんがこの指定は無意味です。</dd><dd>もともと SSL 3.0 は標準化されていませんでしたが、[RFC 7568によって使用が禁止されました]。TLS 1.0、およびTLS 1.1は、実装間でより新しいバージョンがある場合は使用すべきでないと[RFC 7525に書かれています]。NetReaderなどのIEコンポーネントブラウザは、Windows VistaだとTLS 1.1に対応していない[^vista]ので、2017年4月11日まではTLS 1.0を有効にしておく必要があるかと思います。[^NetReader]</dd> <dt>[SSLCipherSuite ディレクティブ] \(リンク先英語)</dt><dd>`SSLCipherSuite !DSS:EECDH+HIGH:EDH+HIGH:HIGH:MEDIUM:!aNULL:!MD5:!SEED:!IDEA`</dd><dd>使用する暗号化方式を制御します。各引数の先頭に `+` `-` `!` を付け、手前で指定したバージョンに追加するか除外するかを表します。引数の意味については、[ApacheでOpenSSLのセキュリティを強化する]をご覧ください。</dd> <dt>[SSLHonorCipherOrder ディレクティブ] \(リンク先英語)</dt><dd>`SSLHonorCipherOrder on`</dd><dd>使用する暗号化方式の決定について、クライアントよりサーバーのリストを優先します。既定では無効になっています。</dd> <dt>[SSLCertificateFile ディレクティブ] \(リンク先英語)</dt><dd>`SSLCertificateFile /etc/letsencrypt/live/example.jp/cert.pem`</dd><dd>Apache 2.4.7以下ではサーバー証明書 (Certbotで取得した場合は `cert.pem`) を、Apache 2.4.8以上ではサーバー証明書と中間証明書を結合したファイル (Certbotで取得した場合は `fullchain.pem`) のパスを指定します。 **2.4.7/2.4.8をまたいでバージョンを変更する際は修正する必要があるので注意してください。** </dd><dd>この例はApache 2.4.6のものなので、Certbotによってサーバー証明書のパスが設定されています。</dd> <dt>[SSLCertificateKeyFile ディレクティブ] \(リンク先英語)</dt><dd>`SSLCertificateKeyFile /etc/letsencrypt/live/example.jp/privkey.pem`</dd><dd>秘密鍵を指定します。</dd> <dt>[SSLCertificateChainFile ディレクティブ] \(リンク先英語)</dt><dd>`SSLCertificateChainFile /etc/letsencrypt/live/example.jp/chain.pem`</dd><dd>Apache 2.4.7以下において、中間証明書 (Certbotで取得した場合は `chain.pem`) を指定します。Apache 2.4.8以降、[SSLCertificateFile ディレクティブ]に統合する形で廃止されました。 **2.4.7/2.4.8をまたいでバージョンを変更する際は修正する必要があるので注意してください。** </dd><dd>この例はApache 2.4.6のものなので、Certbotによってこのディレクティブが追加されています。</dd> <dt>[SSLUseStapling ディレクティブ] \(リンク先英語)</dt><dd>`SSLUseStapling on`</dd><dd>[OCSP Stapling]を有効化します。</dd> <dt>[SSLStaplingCache ディレクティブ] \(リンク先英語)</dt><dd>`SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000)`</dd><dd>[OCSP Stapling]におけるキャッシュの保存先と容量を指定します。</dd> </dl> [^vista]: [ウェブブラウザ | 実装 | Transport Layer Security — Wikipedia](https://ja.wikipedia.org/wiki/Transport_Layer_Security#.E3.82.A6.E3.82.A7.E3.83.96.E3.83.96.E3.83.A9.E3.82.A6.E3.82.B6 "TLS 1.1、1.2に未対応:Internet Explorer 9(Windows Vista / Server 2008向け)") [^NetReader]: [Kazzさんのツイート: “NetMarketShareによれば2015年12月時点でのVistaのシェアは1.62% 同じように視覚障碍者のVista利用者が減っていると仮定しても、今IE9を切り捨てると視覚障害者100人のうち2人くらいに対して全くアクセス不能にさせる可能性があるということになる”](https://twitter.com/asamuzakjp/statuses/687878739976294400) [SSLProtocol ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslprotocol "Configure usable SSL/TLS protocol versions" [RFC 7568によって使用が禁止されました]: https://wiki.suikawiki.org/n/Transport%20Layer%20Security#section-歴史 SSL-3.0-の廃止 "2015年6月に出版された RFC 7568 は、 SSL 3.0 を使ってはならないと規定しています。禁止しているのになぜか非推奨と称しており、 廃止ではありません。 SSL 3.0 の RFC である RFC 6101 も廃止はされていませんが、これは元々 Historic で発行されたものなので廃止は必要ないという判断なのでしょうか。" [RFC 7525に書かれています]: http://blog.livedoor.jp/k_urushima/archives/1768181.html "2015年5月7日に「RFC 7525 TLSとDTLSの安全な利用に関する推奨事項(Recommendations for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS))」が公開されました。" [SSLCipherSuite ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslciphersuite "Cipher Suite available for negotiation in SSL handshake" [ApacheでOpenSSLのセキュリティを強化する]: //qiita.com/sion_cojp/items/99fee211ceace3f76cff [SSLHonorCipherOrder ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslhonorcipherorder "Option to prefer the server’s cipher preference order" [SSLCertificateFile ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslcertificatefile "Server PEM-encoded X.509 certificate data file" [SSLCertificateKeyFile ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslcertificatekeyfile "Server PEM‐encoded private key file" [SSLCertificateChainFile ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslcertificatechainfile "File of PEM‐encoded Server CA Certificates" [SSLUseStapling ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslusestapling "Enable stapling of OCSP responses in the TLS handshake" [OCSP Stapling]: //qiita.com/harukasan/items/fe37f3bab8a5ca3f4f92#ocsp-stapling "OCSP Stapling(ステープリング)では、クライアントがOCSP要求を行うのではなく、サーバがOCSP要求を行い、その応答をキャッシュする。サーバはキャッシュしたOCSP応答を、サーバ証明書と一緒にクライアントに送信する。" [SSLStaplingCache ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_ssl.html#sslstaplingcache "Configures the OCSP stapling cache" 証明書の自動更新例 (systemd) ---------------------------- `certbot renew` で期限切れが近い証明書を更新できます。以下は[systemd]を採用しているCentOS 7などにおける例です。 ```ini:/etc/systemd/system/certbot.service [Unit] Description=期限切れが近いサーバー証明書を更新します。 [Service] ExecStart=/usr/bin/certbot renew ``` ```ini:/etc/systemd/system/certbot.timer [Unit] Description=毎週月曜日の0時〜24時のあいだにサーバー証明書を確認し、期限切れが近いものを更新します。 [Timer] OnCalendar=weekly Persistent=true RandomizedDelaySec=86400 [Install] WantedBy=timers.target ``` ```sh # certbot.timerの有効化 (OS起動時に自動的に起動させる) sudo systemctl enable certbot.timer # certbot.timerの起動 sudo systemctl start certbot.timer ``` `systemctl status certbot.timer` を実行して `systemd[1]: [/etc/systemd/system/certbot.timer:6] Unknown lvalue 'RandomizedDelaySec' in section 'Timer'` のようなログが残っていた場合、`RandomizedDelaySec` が機能していません。`RandomizedDelaySec` は、月曜0時に固定された `OnCalendar=weekly` をランダムに遅延させる設定です。2016年11月3日にリリースされた [RHEL] 7.3 で同設定に対応した[^RHEL7.3]ので、CentOSなどの[RHELクローン]であれば2016年11月28日現在のバージョンでは対応していなくても、時期に対応するでしょう。対応したバージョンへのアップデート後は `sudo systemctl daemon-reload` を実行する必要があるかもしれません。 もし使用しているOSのsystemdが `RandomizedDelaySec` に対応する予定がなければ、他のOSと同様に[crontab]を利用するのが良いかと思います。 直近のログは次のようなコマンドで調べます。 - `sudo cat /var/log/letsencrypt/letsencrypt.log` - `systemctl status certbot --full --lines 50` - `systemctl status certbot.timer --full` [^RHEL7.3]: [Red Hat Customer Portal ▸ Support ▸ Product ▸ Documentation ▸ Red Hat Enterprise Linux ▸ 7 ▸ 7.3 Release Notes ▸ Chapter 4. General Updates ▸ New systemd option: RandomizedDelaySec](https://access.redhat.com/documentation/Red_Hat_Enterprise_Linux/7/html/7.3_Release_Notes/new_features_general_updates.html#idp14207184 "This update introduces the RandomizedDelaySec option for systemd timers, which schedules an event to occur later by a random number of seconds.") [systemd]: https://ja.wikipedia.org/wiki/Systemd "systemdはシステム管理デーモン、ライブラリおよびユーティリティの一式であり、管理および設定における中心的プラットフォームとしてLinuxコンピュータオペレーティングシステム用に設計されている。" [RHEL]: https://ja.wikipedia.org/wiki/Red_Hat_Enterprise_Linux "Red Hat Enterprise Linux(レッドハット・エンタープライズ・リナックス)、略してRHEL(レル)とは、レッドハット社によって開発、販売されている業務向けのLinuxディストリビューション。" [RHELクローン]: https://ja.wikipedia.org/wiki/Red_Hat_Enterprise_Linux%E6%B4%BE%E7%94%9F%E3%83%87%E3%82%A3%E3%82%B9%E3%83%88%E3%83%AA%E3%83%93%E3%83%A5%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3 "Red Hat Enterprise Linux派生ディストリビューションとはRed Hat Enterprise Linuxのソースコードを元にしたLinuxディストリビューションである。" [crontab]: https://ja.wikipedia.org/wiki/Crontab "crontab(クロンタブ、あるいはクローンタブ、クーロンタブとも)コマンドはUnix系OSにおいて、コマンドの定時実行のスケジュール管理を行うために用いられるコマンドである。" HTTP応答ヘッダの設定 ==================== ```apache:/etc/httpd/conf/httpd.conf # XHTMLに対応していないブラウザ SetEnvIfExpr "!(%{HTTP_ACCEPT} -strcmatch '*application/xhtml+xml*') || %{HTTP_USER_AGENT} =~ m#UP\\.Browser/6\\.2|Nintendo 3DS| NX/1\\.|Trident/5\\.#" LEGACY_BROWSER ## ↑ファイルの先頭に挿入 ## ############## ## 中略 ## ############## <IfModule mime_module> ############## ## 中略 ## ############## # # Filters allow you to process content before it is sent to the client. # # To parse .shtml files for server-side includes (SSI): # (You will also need to add "Includes" to the "Options" directive.) # #AddType text/html .shtml #AddOutputFilter INCLUDES .shtml ## ↑この2行をコメントアウトする (CentOS7標準のApacheの場合) ## ## ↓<IfModule mime_module> セクションの末尾に追記 ## <If "-n reqenv('LEGACY_BROWSER')"> # XHTMLに対応していなければ AddType text/html .xhtml </If> </IfModule> # # Specify a default charset for all content served; this enables # interpretation of all content as UTF-8 by default. To use the # default browser choice (ISO-8859-1), or to allow the META tags # in HTML content to override this choice, comment out this # directive: # #AddDefaultCharset UTF-8 ## ↑コメントアウトする ## # content-typeヘッダにcharsetパラメータを付加する AddCharset utf-8 .xhtml .html .css .es .js .txt .json .xml .csv ## ↑この行を追加 ## # content-languageヘッダを送出する AddLanguage ja .xhtml .html ## ↑この行を追加 ## ############## ## 中略 ## ############## ## ↓ファイルの末尾に追記 ## ServerTokens ProductOnly Header always set x-content-type-options nosniff <If "%{HTTP_USER_AGENT} =~ m#Trident/5\\.#"> # Internet Explorer 9 の互換表示ボタンを消す Header always set x-ua-compatible IE=edge </If> ``` ```apache:/etc/httpd/conf.d/ssl.conf ############## ## 以上省略 ## ############## ServerName example.jp ServerAlias sub1.example.jp sub2.example.jp Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" ## ↑「includeSubDomains」「preload」パラメータを追加 ## SSLUseStapling on SSLCertificateChainFile /etc/letsencrypt/live/example.jp/chain.pem </VirtualHost> <IfModule mod_ssl.c> SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000) </IfModule> ``` ```apache:/etc/httpd/conf.d/autoindex.conf (CentOS7標準のApacheの場合) # # Directives controlling the display of server-generated directory listings. # # Required modules: mod_authz_core, mod_authz_host, # mod_autoindex, mod_alias # # To see the listing of a directory, the Options directive for the # directory must include "Indexes", and the directory must not contain # a file matching those listed in the DirectoryIndex directive. # # # IndexOptions: Controls the appearance of server-generated directory # listings. # #IndexOptions FancyIndexing HTMLTable VersionSort ## ↑コメントアウトする ## # We include the /icons/ alias for FancyIndexed directory listings. If # you do not use FancyIndexing, you may comment this out. # #Alias /icons/ "/usr/share/httpd/icons/" ## ↑コメントアウトする ## ############## ## 以下省略 ## ############## ``` ```apache:/etc/httpd/conf.d/welcome.conf (CentOS7標準のApacheの場合) # # This configuration file enables the default "Welcome" page if there # is no default index page present for the root URL. To disable the # Welcome page, comment out all the lines below. # # NOTE: if this file is removed, it will be restored on upgrades. # #<LocationMatch "^/+$"> # Options -Indexes # ErrorDocument 403 /.noindex.html #</LocationMatch> #<Directory /usr/share/httpd/noindex> # AllowOverride None # Require all granted #</Directory> #Alias /.noindex.html /usr/share/httpd/noindex/index.html #Alias /css/bootstrap.min.css /usr/share/httpd/noindex/css/bootstrap.min.css #Alias /css/open-sans.css /usr/share/httpd/noindex/css/open-sans.css #Alias /images/apache_pb.gif /usr/share/httpd/noindex/images/apache_pb.gif #Alias /images/poweredby.png /usr/share/httpd/noindex/images/poweredby.png ## ↑すべてコメントアウトする ## ```   <dl><dt>[SetEnvIfExpr ディレクティブ] \([SetEvnIf ディレクティブの日本語訳])</dt><dd>`SetEnvIfExpr "!(%{HTTP_ACCEPT} -strcmatch '*application/xhtml+xml*') || %{HTTP_USER_AGENT} =~ m#UP\\.Browser/6\\.2|Nintendo 3DS| NX/1\\.|Trident/5\\.#" LEGACY_BROWSER`</dd><dd>第1引数の条件式が真なら、第2引数の環境変数を設定します。条件式の構文は[Expressions in Apache HTTP Server]に載っています。この構文は[<If> セクション]や[RewriteCond ディレクティブ]でも利用できます。`LEGACY_BROWSER`のように変数値を省略することは、`LEGACY_BROWSER=1`のように変数値として`1`を指定することと同義です。</dd><dd>[accept要求ヘッダ]に`application/xhtml+xml`が含まれていれば、基本的にそれはXHTML文書に対応しているWebブラウザからのアクセスです。しかしその中には、XHTML文書を正常に扱えず、HTML文書を返した方が良いブラウザもあります。httpd.confの先頭に上記のような[SetEnvIfExpr ディレクティブ]を記述することで、XHTML文書に対応していないブラウザからのアクセスに対し、`LEGACY_BROWSER`という環境変数が設定されるようにしています。ここでXHTML文書に対応していないブラウザとして扱っているのは、[accept要求ヘッダ]に`application/xhtml+xml`が含まれないブラウザ、Openwave Mobile Browser 6.2 (古い[EZブラウザ])、[NetFront Browser NX 1] \([ニンテンドー3DS用インターネットブラウザー])[^3ds]、Internet Explorer 9[^ie9-xhtml] [^ie9] です。</dd> <dt>[AddType ディレクティブ]</dt><dd>`AddType image/vnd.microsoft.icon .ico .cur`</dd><dd>どの拡張子がどのファイル形式に対応するか指定します。`mime.types`ファイルに登録されている情報を元に[content-type応答ヘッダ][content-type]が返されるので、基本的には必要はありません。登録されていなかったり、登録されていても情報が古かったりする場合に、このディレクティブを使用して`mime.types`の設定を上書きします。</dd> <dt>[<If> セクション]</dt><dt>[<ElseIf> セクション] \(リンク先英語)</dt><dt>[<Else> セクション] \(リンク先英語)</dt><dd>`<If "-n reqenv('LEGACY_BROWSER')">`〜`</If>`</dd><dd>ディレクティブを適用する条件式を指定します。ドキュメンテーションにそのような説明がなくエラーも出ませんが、 **これらのセクションを入れ子にすると、内側のセクションが中身ごと無視されます** 。(Apache 2.4.7 現在)</dd><dd>`-n reqenv('LEGACY_BROWSER')`は、<i>`LEGACY_BROWSER`という環境変数が空でなければ</i>真を返す式です。</dd> <dt>[Include ディレクティブ]<br>[IncludeOptional ディレクティブ] \(リンク先英語)</dt><dd>`Include /etc/httpd/conf/extra/httpd-languages.conf`</dd><dd>サーバー設定ファイルから他の設定ファイルを読み込みます。ファイル、またはディレクトリを<i>ファイルシステムのパス絶対URL</i>か、[サーバールート] \(CentOS7標準のApacheの場合、デフォルトでは`ServerRoot "/etc/httpd"`が記述されている) からのパス相対URLで指定します。ワイルドカードも使用可能です。<br>[IncludeOptional ディレクティブ]はほぼ[Include ディレクティブ]と同じですが、ファイル、またはディレクトリが存在しない場合でもエラーが発生しません。</dd> <dt>[ServerTokens ディレクティブ]</dt><dd>[server応答ヘッダ][server]とサーバーが生成するドキュメントのフッターに表示されるサーバー情報を制御します。デフォルト値は`ServerTokens Full`で、`Server: Apache/2.4.7 (Unix)`のように出力されます。`ServerTokens ProductOnly`を指定すると、`Server: Apache`と出力されます。</dd> <dt>[Header ディレクティブ]</dt><dd>`Header always set x-content-type-options nosniff`</dd><dd>HTTP応答ヘッダを制御します。内部ヘッダテーブルにはヘッダの一覧が格納されており、[2xxレスポンス]の場合のみ利用される<i>onsuccessテーブル</i>と、2xxレスポンスを含むすべての場合で利用される<i>alwaysテーブル</i>のどちらを書き換えるか、第1引数で指定します。第1引数は省略可能で、省略した場合`onsuccess`になります。第2引数で内部ヘッダテーブルに対してどのような操作を行うか指定します。</dd><dd>第2引数について、`set`は新しくヘッダを設定しますが、同じ名前のヘッダが既に存在すればそれを置き換えます。`edit`は既存のヘッダの置換を行う設定で、第3引数に正規表現、第4引数に置換後の文字列を指定します。</dd> <dt>[AddLanguage ディレクティブ]</dt><dd>`AddLanguage ja .xhtml .html`</dd><dd>どの拡張子がどの言語に対応するか指定します。上記の指定では、拡張子が`.xhtml` `.html`であるファイルが、HTTP応答ヘッダで`Content-Language: ja`を送出するようになります。</dd> <dt>[AddCharset ディレクティブ]</dt><dd>`AddCharset UTF-8 .xhtml .html .css .es .js .txt .json .csv .xml`</dd><dd>どの拡張子がどの文字コードに対応するか指定します。上記の指定では、拡張子が`.xhtml` `.html` `.css` `.es` `.txt` `.json` `.csv`であるファイルの応答ヘッダに、`Content-Type: application/xhtml+xml; charset=UTF-8`のように`charset`パラメータが付加されます。`.htm`や`.xht`等、これ以外の拡張子を持つテキストファイルが自分のサーバーに存在していれば、それらも追加しておきましょう。文字化けは[XSS]につながる可能性もあるので、設定漏れが無いように注意してください。</dd><dd>なお、`application/json` に `charset` パラメータは存在せず、例の通り記述した場合 `Content-Type: application/json; charset=UTF-8` のような不正なヘッダが出力されます。しかしながら、`Content-Type: application/json` を出力すると、ファイルの中身とブラウザの組み合わせによっては文字化けします。[^json]</dd> </dl> | HTTP応答ヘッダフィールド名 | | |-----------------------------|---| | [content-type] | `application/xhtml+xml`や`text/css`などのファイルの種類と、文字コードを示します。このヘッダが間違っていると、[XSS]の発生につながります。 | | [content-language] | HTML文書、XHTML文書 (以下、HTML) では、このヘッダや[lang属性]によって文章や単語の言語が決定されます。これらの指定が間違っていると、意図したフォントで表示されないといった弊害も起きます。なお、これらの値に指定する[言語タグ]について、`ja-JP` (言語と地域を`-`で繋いでいる) は正しい値ですが、`ja_JP` (言語と地域を`_`で繋いでいる) と書くのは誤りです。 | | [link] | HTMLの[link要素]と同じ意味を持ちます。例えば、`link: </common/development>; rel=stylesheet`は、head要素内に記述した`<link href="/common/development" rel="stylesheet" />`と同じ効果があります。2013年11月現在、Firefoxのみ`rel=stylesheet`に対応しており、HTML以外のファイルを開いた場合にもスタイルシートが適用されます。`rel=icon`には対応していません。 | | [x-content-type-options] | Internet Explorerの[content-type応答ヘッダ][content-type]無視を防ぎます。[XSS]を防止するためにも、このヘッダは必ず送出しなければなりません。 | | [x-ua-compatible] | **本来はmeta要素で使う属性値で、HTTPヘッダとしては非標準です。** Internet Explorer 9[^ie9] の互換表示ボタンを消します。 | | [strict-transport-security] | HTTPS接続を強制する [HTTP Strict Transport Security (HSTS)][strict-transport-security] を有効化します。この指定は、同一ドメインに対し `max-age` パラメータで指定した秒数だけ記憶されます。`includeSubDomains` を指定すると、サブドメインに対してもHTTPS接続を強制できます。次のような条件を満たせば、ブラウザが初めてアクセスするときもHTTPSを矯正する[HSTS Preload]が利用できます。<ul><li>http://〜 から https://〜 にリダイレクトしている</li><li>すべてのサブドメインにHTTPS接続できる</li><li>`max-age` パラメータの値が18週間 (10886400秒) 以上</li><li>`includeSubDomains` パラメータを指定している</li><li>非標準の `preload` パラメータを指定している</li></ul> [HSTS Preload List Submission]から[HSTS Preload]を利用するドメインを登録します。リストはGoogleが管理しています。 | | [content-security-policy]<br>[x-frame-options] | (後述) | [^3ds]: [NetFront Browser NX 1] \([ニンテンドー3DS用インターネットブラウザー]) は、XHTML文書において、属性値が空の属性を削除してしまう不具合がある。 [^ie9-xhtml]: [条件付きコメント]はHTML文書を Internet Explorer 9 で開いた場合のみ機能する。 <ul><li>[asamuzaK.jp : IE9と条件コメント](http://asamuzak.jp/html/ua/315 "IE9と条件コメント 〜IE9の条件コメントの振る舞い〜")</li></ul> [^json]: JSONファイルをブラウザで直接開いたとき、Firefox、Opera、Google Chromeで文字化けするJSONファイルがあることを確認 (2016年10月〜11月現在)。 [^ie9]: Internet Explorer 9 は Windows Vista でしか利用できないが、Windows Vista のセキュリティアップデートは2017年4月11日で終了する。 <ul><li>[Internet Explorerサポートポリシー変更の重要なお知らせ — Microsoft]</li><li>[Windows デスクトップ製品のライフサイクル]</li></ul> [SetEnvIfExpr ディレクティブ]: https://httpd.apache.org/docs/current/en/mod/mod_setenvif.html#setenvifexpr "Sets environment variables based on an ap_expr expression" [SetEvnIf ディレクティブの日本語訳]: https://httpd.apache.org/docs/current/mod/mod_setenvif.html#setenvif "リクエストの属性に基づいて環境変数を設定する" [Expressions in Apache HTTP Server]: https://httpd.apache.org/docs/current/expr.html "Historically, there are several syntax variants for expressions used to express a condition in the different modules of the Apache HTTP Server. There is some ongoing effort to only use a single variant, called ap_expr, for all configuration directives. This document describes the ap_expr expression parser." [accept要求ヘッダ]: https://triple-underscore.github.io/RFC7231-ja.html#header.accept "UA は、[ 応答の媒体型として受容可能なもの ] を指定するために, Accept ヘッダを利用できる。 Accept ヘッダは、[ 要請が, 欲される型からなる小さな集合に特に制限される ] ことを指示するときに利用できる — 例えば, インライン画像に対する要請など。" [EZブラウザ]: https://ja.wikipedia.org/wiki/EZweb "EZweb(イージーウェブ)は、KDDI・沖縄セルラー電話の携帯電話ブランドauが提供している携帯電話IP接続サービスの名称である。" [NetFront Browser NX 1]: https://ja.wikipedia.org/wiki/NetFront_Browser "NetFront Browser(ネットフロントブラウザ)とは、株式会社ACCESSが開発している組込機器用のウェブブラウザである。" [ニンテンドー3DS用インターネットブラウザー]: https://ja.wikipedia.org/wiki/%E3%83%8B%E3%83%B3%E3%83%86%E3%83%B3%E3%83%89%E3%83%BC3DS#.E3.82.BD.E3.83.95.E3.83.88.E3.82.A2.E3.82.A4.E3.82.B3.E3.83.B3.E4.BB.A5.E5.A4.96.E3.81.AE.E5.86.85.E8.94.B5.E6.A9.9F.E8.83.BD "インターネットを接続して、Webサイトなどを見ることができる機能。" [AddType ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_mime.html#addtype "ファイル名の拡張子を指定されたコンテントタイプにマップ" [<If> セクション]: https://httpd.apache.org/docs/current/mod/core.html#if "実行時、リクエストが条件を満たした場合にのみ適用される ディレクティブを包含する" [<ElseIf> セクション]: https://httpd.apache.org/docs/current/en/mod/core.html#elseif "Contains directives that apply only if a condition is satisfied by a request at runtime while the condition of a previous <If> or <ElseIf> section is not satisfied" [<Else> セクション]: https://httpd.apache.org/docs/current/en/mod/core.html#else "Contains directives that apply only if the condition of a previous <If> or <ElseIf> section is not satisfied by a request at runtime" [Include ディレクティブ]: https://httpd.apache.org/docs/current/mod/core.html#include "サーバ設定ファイル中から他の設定ファイルを取り込む" [IncludeOptional ディレクティブ]: https://httpd.apache.org/docs/current/en/mod/core.html#includeoptional "Includes other configuration files from within the server configuration files" [サーバールート]: https://httpd.apache.org/docs/current/mod/core.html#serverroot "インストールされたサーバのベースディレクトリ" [ServerTokens ディレクティブ]: https://httpd.apache.org/docs/current/mod/core.html#servertokens "Server HTTP 応答ヘッダを設定する" [Header ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_headers.html#header "HTTP 応答ヘッダの設定" [2xxレスポンス]: https://triple-underscore.github.io/RFC7231-ja.html#status.2xx "クラス 2xx (Successful) の状態°コードは、クライアントの要請が, 成功裡に [ 受信され, 解され, 受容された ] ことを指示する。" [AddLanguage ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_mime.html#addlanguage "ファイル名を指定された言語にマップ" [AddCharset ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_mime.html#addcharset "ファイル名の拡張子を指定された文字セットにマップする" [XSS]: https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B5%E3%82%A4%E3%83%88%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0 "クロスサイトスクリプティング(英: cross site scripting)とは、ウェブページの部分をユーザからの入力をそのままエコーバック(おうむ返し)することによって生成しているアプリケーションのセキュリティ上の不備を利用して、サイト間を横断して悪意のあるスクリプトを注入する攻撃のことをいう。また経緯上、それを許してしまう脆弱性についても、このように呼ばれている。" [content-type]: https://triple-underscore.github.io/RFC7231-ja.html#header.content-type "Content-Type ヘッダは、結び付けられている表現 — メッセージ意味論に従って決定された, [ メッセージペイロード内に同封された表現, または 選定された表現 ] — の, 媒体型を指示する。 指示された媒体型は、[ Content-Encoding により指示される内容符号法(たち)が復号された後 ] における, [ データ形式, および [ 受信されたメッセージ意味論の視野の中で, 受信者がそのデータをどう処理するものと意図されているか ] ] を定義する。" [server]: https://triple-underscore.github.io/RFC7231-ja.html#header.server "Server ヘッダは、[ 生成元サーバが, 要請を取り扱うために利用しているソフトウェア ] についての情報を包含する" [content-language]: https://triple-underscore.github.io/RFC7231-ja.html#header.content-language "Content-Language ヘッダは、表現に意図される視聴者の自然言語(たち)を述べる。 これは、[ 表現の中で利用される どの言語にも等価にならない ] 場合もあることに注意。" [lang属性]: https://triple-underscore.github.io/HTML-dom-ja.html#the-lang-and-xml:lang-attributes "どの名前空間にも属さない lang 属性 — 以下、 素の lang 属性 と略記する — は、[ 要素の内容, および テキストを包含するような要素の属性 ] に対する第一言語を指定する。 その値は妥当な BCP 47 言語タグか, 空文字列でなければならない。 空文字列は、第一言語が未知であることを指示する。" [言語タグ]: https://ja.wikipedia.org/wiki/IETF%E8%A8%80%E8%AA%9E%E3%82%BF%E3%82%B0 "IETF言語タグ(英語: IETF language tag)は、BCP 47(現在は RFC 5646 と RFC 4647)により定義される技術仕様である。これは HTTP、HTML、XML、PNG、のような多くの技術標準において使われている。" [link]: //qiita.com/mpyw/items/203012d8b9e5386e6f0b "スタイルシートといえば<link>要素を使うのが一般的ですが, 実はRFC的にはHTTPヘッダにLink:を使うことも出来るようになっています. ところがブラウザの対応状況が意外にも芳しく無く…" [link要素]: https://triple-underscore.github.io/HTML-metadata-ja.html#the-link-element "link 要素により、作者は, 自身の文書から他のリソースへのリンクをあてがえるようになる。" [x-content-type-options]: https://triple-underscore.github.io/Fetch-ja.html#x-content-type-options-header "‘X-Content-Type-Options’ 応答ヘッダを利用して、[ 応答の ‘Content-Type’ ヘッダを, 要請の種別に対し検査する ] ことを要求できる。" [x-ua-compatible]: https://triple-underscore.github.io/HTML-metadata-ja.html#attr-meta-http-equiv-x-ua-compatible "この pragma は、実施においては, Internet Explorer に対し より仕様に近くふるまうことを奨励する。" [strict-transport-security]: https://developer.mozilla.org/docs/Web/Security/HTTP_Strict_Transport_Security "HTTP Strict Transport Security(しばしば HSTS と略されます)は、HTTP の代わりに HTTPS を用いて通信を行うよう、Web サイトからブラウザにに伝達するためのセキュリティ機能です。" [HSTS Preload]: //qiita.com/takoratta/items/fb6b3486257eb7b9f12e#preloaded-hsts "HSTSを用いても、最初の接続にHTTPを用いる限り、完全なセキュリティは実現されない。これを解決するために、ChromeではPreloaded HSTS、すなわち最初からブラウザにHSTSを組み込めば良い。これはブラウザ本体にHSTSで接続するドメインのリストを持つことにより実現できる。" [HSTS Preload List Submission]: https://hstspreload.appspot.com/ "This form is used to submit domains for inclusion in Chrome’s HTTP Strict Transport Security (HSTS) preload list. This is a list of sites that are hardcoded into Chrome as being HTTPS only." [content-security-policy]: https://triple-underscore.github.io/CSP3-ja.html "この文書は、ある特定のページが どのリソースを fetch あるいは実行できるか, および セキュリティに関連する数々のポリシー決定について, ウェブ開発者が制御できる仕組みを定義する。" [x-frame-options]: https://developer.mozilla.org/docs/Web/HTTP/X-Frame-Options "X-Frame-Options HTTP レスポンスヘッダは、ブラウザがページを <frame> または <iframe> の内部に表示することを許可するかを示すことができます。" [条件付きコメント]: https://ja.wikipedia.org/wiki/%E6%9D%A1%E4%BB%B6%E4%BB%98%E3%81%8D%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88 "条件付きコメント(じょうけんつきコメント)とは、Microsoft Internet Explorerに対して、コードを渡したり隠したりするのに使用できるHTMLソースコード中にある条件付きのステートメントである。Internet Explorer 5で初めて登場し、バージョン9までサポートされた。" [Internet Explorerサポートポリシー変更の重要なお知らせ — Microsoft]: https://www.microsoft.com/ja-jp/windows/lifecycle/iesupport/ "米国時間の 2014 年 8 月 7 日、マイクロソフトは Internet Explorer のサポートポリシーについて、重要なお知らせ 別ウィンドウが立ち上がりますを致しました。これにより、2016 年 1 月 12 日 (米国時間) を過ぎると、使用しているオペレーティング システムでサポートされる、最新バージョンの Internet Explorer だけが、技術サポートとセキュリティ アップデートを受けられることになります。米国時間の 2014 年 8 月 7 日、マイクロソフトは Internet Explorer のサポートポリシーについて、重要なお知らせ 別ウィンドウが立ち上がりますを致しました。これにより、2016 年 1 月 12 日 (米国時間) を過ぎると、使用しているオペレーティング システムでサポートされる、最新バージョンの Internet Explorer だけが、技術サポートとセキュリティ アップデートを受けられることになります。" [Windows デスクトップ製品のライフサイクル]: https://www.microsoft.com/ja-jp/windows/lifecycle/ "Windows クライアント OS の製品サポートライフサイクル ポリシー更新について (2012 年 2 月)" Content Security Policy ----------------------- サイト製作者の意図しないスクリプトの実行などをブラウザ側でブロックし、[XSS]などを防ぎます。 Internet Explorer は対応していません。 以下に[content-security-policy応答ヘッダ][content-security-policy]の設定例を示します。 ```apache:/etc/httpd/conf/httpd.conf ############## ## 以上省略 ## ############## # サーバー全体で共通のポリシー Header always set content-security-policy "default-src 'none'; disown-opener; frame-ancestors 'none'; style-src 'self'; img-src 'self'; upgrade-insecure-requests; report-uri [レポートの送信先URL]" <If "%{HTTP_USER_AGENT} -strmatch '*Trident/*'"> # Internet Explorer で frame-ancestors 'none' を機能させる Header always set x-frame-options DENY </If> <DirectoryMatch ^/var/www/html/index$> # トップページへのSVG画像の埋め込み Header always edit content-security-policy $ "; object-src 'self'; frame-src 'self'" </DirectoryMatch> <DirectoryMatch ^/var/www/html/chat/index$> # チャットのstyle属性、スクリプト、EventSource、XMLHttpRequest、およびベル音 Header always edit content-security-policy "style-src 'self'" "style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self'; media-src 'self'" </DirectoryMatch> <Directory /var/www/html/link> # リンク集のバナー Header always edit content-security-policy "img-src 'self'" "img-src 'self' example.com example.jp example.co.jp example.ne.jp" </Directory> <Directory /var/www/html/phpMyAdmin> # phpMyAdmin Header always edit content-security-policy "style-src 'self'" "style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self'" </Directory> <DirectoryMatch ^/var/www/html/phpinfo$> # phpinfo()関数の結果を表示するページ Header always edit content-security-policy "style-src 'self'" "style-src 'self' 'unsafe-inline'" Header always edit content-security-policy "img-src 'self'" "img-src 'self' data:" </DirectoryMatch> ``` 以上のコード例のcontent-security-policy応答ヘッダ値は、以下のような意味になります (`disown-opener` `frame-ancestors` `upgrade-insecure-requests` `report-to` 省略)。   <dl><dt>`default-src 'none'; style-src 'self'; img-src 'self'`</dt><dd>サーバー全体に適用するポリシー。同一生成元のスタイルシートと同一生成元の画像のみ許可している。</dd> <dt>`default-src 'none'; style-src 'self'; img-src 'self'; object-src 'self'; frame-src 'self'`</dt><dd>object要素でSVG画像を埋め込む場合。Firefoxは<i>object</i>、OperaとGoogle Chromeは<i>frame</i>と認識するため、両方のディレクティブで許可している。</dd> <dt>`default-src 'none'; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self'; media-src 'self'; img-src 'self'`</dt><dd>同一生成元のスタイルシート、style要素とstyle属性による指定を許可。同一生成元のスクリプトを許可。XMLHttpRequestやEventSourceからの同一生成元に対するアクセスを許可。audio要素とvideo要素で同一生成元のファイルを許可。同一生成元の画像を許可。</dd> <dt>`default-src 'none'; style-src 'self'; img-src 'self' example.com example.jp example.co.jp example.ne.jp`</dt><dd>リンク集に適用するポリシー。同一生成元と直リンクしているバナーの生成元の画像を許可している。</dd> <dt>`default-src 'none'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self'; img-src 'self'`</dt><dd>[phpMyAdmin]に適用するポリシー。phpMyAdminではインラインスクリプトやeval関数が使用されているため、Content Security Policyが有効に活用できない。</dd> <dt>`default-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:`</dt><dd>[PHPのphpinfo関数]の実行結果に適用するポリシー。同一生成元のスタイルシート、style要素とstyle属性による指定を許可。同一生成元と[dataスキーム]の画像を許可。</dd> [phpMyAdmin]: https://ja.wikipedia.org/wiki/PhpMyAdmin "phpMyAdmin(ピーエイチピーマイアドミン)はMySQLサーバーをウェブブラウザで管理するためのデータベース接続クライアントツールで、PHPで実装されている。 phpMyAdminを用いることで、SQL文を記述することなく、MySQLのデータベースに対して様々な操作が行える。 また、ユーザが任意のSQL文を記述して実行することもできる。" [PHPのphpinfo関数]: https://secure.php.net/manual/function.phpinfo.php "PHP の設定情報を出力する" [dataスキーム]: https://ja.wikipedia.org/wiki/Data_URI_scheme "データURIスキーム(英語: data URI scheme)とは外部データ無しにウェブページにデータを埋めこむためのURIスキームである。例えば、通常画像データはHTMLやCSSに外部リンクとして記述され、ブラウザで表示する際には複数のHTTPリクエストが発生する。これは画像データが小さい場合などには非効率的である。データURIスキームを使用すれば内部データとして画像を埋め込むことができるので1つのHTTPリクエストで済み、効率化できる。RFC 2397で定義されている。" ### 主なディレクティブ (指令) | ディレクティブ名 | 構文、作用 | |-------------------------------|------------| | [`default-src`] | 以下のディレクティブが省略されていたときの既定値を指定します。<ul><li>`child-src`</li><li>`connect-src`</li><li>`font-src`</li><li>`img-src`</li><li>`media-src`</li><li>`object-src`</li><li>`script-src`</li><li>`style-src`</ul> これらのディレクティブでは、次のような値を空白区切りで指定します。<ul><li>`'none'` (何も許可しない)</li><li>`'self'` (生成元[^origin] が同じものを許可)</li><li>`https://maxcdn.bootstrapcdn.com/bootstrap/` ([ホストソース式])</li></ul> 以上のような値は[ソースリスト]と呼ばれます。 | | [`style-src`] | スタイルシートの許可。`'unsafe-inline'`を指定すると、あらゆるstyle要素、style属性が許可されますが、[完全に避けるのが最善]とされています。style要素に関しては、[hash-source]を使えば決まった中身のstyle要素のみ許可できますが、2016年11月現在Microsoft Edgeが未対応です。[`'unsafe-hashed-attributes'`]を使えばstyle属性値も[hash-source]により制御できますが、2016年11月現在まだ仕様が不安定です。 | | [`img-src`] | 画像の許可。 | | [`media-src`] | 音声、動画、字幕の許可。 | | [`script-src`] | スクリプトの許可。`'unsafe-inline'` 周りの事情は、上記 `style-src` と大体同じです。[`'strict-dynamic'`] を指定すれば、(ライブラリなどによって) 後から挿入されたscript要素はブロックされなくなりますが、2016年11月現在ブラウザのサポートが進んでいません。 | | [`connect-src`] | 次のようなAPIにおける接続先の許可。<ul><li>[XMLHttpRquest]</li><li>[EventSource]</li><li>[WebSocket]</li><li>[Window#fetch()]</li><li>[a要素のping属性]</li><li>[Navigator#sendBeacon()]</li></ul> | | [`frame-src`] | iframe要素などで埋め込むページの許可。[^frame] | | [`font-src`] | フォントの許可。 | | [`object-src`] | SWFファイルなどのプラグイン内容の許可。 | | [`plugin-types`] | 許可するプラグインのMIME型を指定します。SWFファイルのMIME型は[application/vnd.adobe.flash-movie]ですがAdobe Flash Playerが対応していないので、同ファイルを埋め込む場合はとりあえず `plugin-types application/vnd.adobe.flash-movie application/x-shockwave-flash` のように指定しておきます。**このディレクティブを指定した場合、object要素などのtype属性が必須となります。**[^object] | | [`disown-opener`] | 移動先の[Window#opener]を `null` にします。フィッシング詐欺の手法である[Tabnabbing]対策になりますが、2016年11月現在まだ仕様が不安定です。a要素の場合は `rel=noopener` を付加することで、該当のリンクに対しては同様の効果を持たせられますが、2016年11月現在OperaとGoogle Chromeしか対応していません。そこで、`rel="noopener noreferrer"` と併記する必要があります。[^noopener] | | [`frame-ancestors`] | 現在のページを埋め込める先祖ウィンドウのURLを指定します。[クリックジャッキング]対策などに役立ちます。Internet Explorer は CSP 自体に対応していませんが、[x-frame-options応答ヘッダ][x-frame-options]で似たような対策が可能です。たとえば `frame-ancestors 'none'` に対応する値は `DENY` です。 | | [`report-uri`] | CSP違反が発生したときの報告先。[report-uri.ioというサービスが便利]です。 | | [`upgrade-insecure-requests`] | [混在コンテンツ]になりそうな画像などに対して、自動的にセキュアなURLから取得します。たとえば `<link src="http://example.jp/lib.css" rel="stylesheet" />` のような記述があれば、`http://example.jp/lib.css` ではなく `https://example.jp/lib.css` にアクセスします。2016年11月現在、Firefox、Opera、Google Chromeが対応しており、Safariの開発版には実装されているようです。 | [^origin]: `https`などの「スキーム」、`example.com`などの「ホスト」、「ポート番号」の組み合わせ。 [^frame]: `frame-src` は、[CSP2で`connect-src`に置き換える形で非推奨にされた]が、[CSP3で`connect-src`が`frame-src` `worker-src`に分割される形で非推奨ではなくなった]。 [^object]: `plugin-types` が指定されていない場合でも、Opera、Google Chromeはtype属性がないobject要素に `child-src` を適用しようとするため、どちらにしても必須である。 [^noopener]: [リンクのへの rel=noopener 付与による Tabnabbing 対策 | blog.jxck.io][Tabnabbing] [`default-src`]: https://triple-underscore.github.io/CSP3-ja.html#directive-default-src "default-src 指令は、他の fetch 指令に対するフォールバックとして働く。" [ホストソース式]: https://triple-underscore.github.io/CSP3-ja.html#grammardef-host-source [ソースリスト]: https://triple-underscore.github.io/CSP3-ja.html#framework-directive-source-list "各種 指令のうち, 多くのものは、 ソースリスト ( serialized-source-list )をその値にとる。" [`style-src`]: https://triple-underscore.github.io/CSP3-ja.html#directive-style-src "style-src 指令は、[ どの所在からのスタイルを文書に適用してよいかどうか ] を制約する。" [完全に避けるのが最善]: https://triple-underscore.github.io/CSP3-ja.html#csp-directives "CSP 指令 | Content Security Policy Level 3" [hash-source]: https://triple-underscore.github.io/CSP3-ja.html#grammardef-hash-source [`'unsafe-hashed-attributes'`]: https://triple-underscore.github.io/CSP3-ja.html#unsafe-hashed-attributes-usage "[ 旧来のサイト / 旧来の依存関係を伴うサイト ] では、イベントハンドラをまるごと外部化するのが困難なこともある。 これらのサイトは、その種のハンドラを 'unsafe-inline' で許容して可能化することもできるが、それは, 多数のリスクを抱える諸刃の剣である(また、ナンスやハッシュと共には利用できない)。ソース式 'unsafe-hashed-attributes' は、そのような状況下で, [ ハッシュを介して特定のハンドラを可能化できる ] ようにすることで、開発者が, CSP をより単純かつ安全に配備できるようにすることを目指している。" [`img-src`]: https://triple-underscore.github.io/CSP3-ja.html#directive-img-src "img-src 指令は、[ どの URL から画像リソースを読み込んでよいか ] を制約する。" [`media-src`]: https://triple-underscore.github.io/CSP3-ja.html#directive-media-src "media-src 指令は、[ どの URL から [ 動画, 音声 および 結び付けられているテキストトラック ] リソースを読み込んでよいか ] を制約する。" [`script-src`]: https://triple-underscore.github.io/CSP3-ja.html#directive-script-src "script-src 指令は、[ どの所在からのスクリプトを実行してよいか ] を制約する。 これには、[ script 要素の中に直接的に読み込まれる URL ] のみならず, [ インラインスクリプトブロックや XSLT スタイルシートの様な, スクリプト実行を誘発し得るもの ] も含まれる。" [`'strict-dynamic'`]: https://triple-underscore.github.io/CSP3-ja.html#strict-dynamic-usage "'strict-dynamic' ソース式は、既存のアプリが, 自身が直接的に読み込むスクリプトについては自信があるが, 前面に読み込まれるリソースのリスト を適度に提供する機能については自信がない場合に、 CSP をより単純に配備できるようにすることを目指している。" [`connect-src`]: https://triple-underscore.github.io/CSP3-ja.html#directive-connect-src "connect-src 指令は、[ スクリプトインタフェースを利用して読み込める URL ] を制約する。" [XMLHttpRquest]: https://triple-underscore.github.io/XHR-ja.html "XMLHttpRequest は、クライアントとサーバ間のデータ転送のための, クライアント側のスクリプト機能を提供する API を定義する。" [EventSource]: https://developer.mozilla.org/docs/Web/API/EventSource "EventSource インターフェイスは、Server‐sent events を受け取るために使用します。HTTP でサーバと接続して、接続を切断せずに text/event-stream 形式でイベントを受信します。" [WebSocket]: https://triple-underscore.github.io/WebSocket-ja.html "この仕様は、ウェブアプリがサーバ側プロセスとの双方向通信を可能化するための, WebSocket インタフェースを導入する。" [Window#fetch()]: https://triple-underscore.github.io/Fetch-ja.html#fetch-api "fetch() メソッドは、リソースを fetch するための, 比較的 低レベルの API である。 XMLHttpRequest より少しばかり基盤的部分を対象にする" [a要素のping属性]: https://triple-underscore.github.io/HTML-links-ja.html#ping "ping する URL を与える。" [Navigator#sendBeacon()]: https://triple-underscore.github.io/beacon-ja.html "この仕様は、[ データの送達を, 非同期的かつ他を阻まない( non-blocking )ようにスケジュールする ] ためにサイト開発者が利用できるインタフェースを定義する。 それは、他の時間に厳しい演算との資源競合を最小限に抑えつつ, そのような要請が処理され, 宛先へ送達されることを確保する。" [`frame-src`]: https://triple-underscore.github.io/CSP3-ja.html#directive-frame-src "frame-src 指令は、[ どの URL を入れ子の閲覧文脈内に読み込んでよいか ] を制約する。" [`font-src`]: https://triple-underscore.github.io/CSP3-ja.html#directive-font-src "font-src 指令は、[ どの URL からフォントリソースを読み込んでよいか ] を制約する。" [`object-src`]: https://triple-underscore.github.io/CSP3-ja.html#directive-object-src "object-src 指令は、[ どの URL からプラグイン内容を読み込んでよいか ] を制約する。" [`plugin-types`]: https://triple-underscore.github.io/CSP3-ja.html#directive-plugin-types "plugin-types 指令は、読み込めるリソースの型を制限することにより, 文書の中に埋め込めるプラグインの集合を制約する。" [application/vnd.adobe.flash-movie]: https://www.iana.org/assignments/media-types/application/vnd.adobe.flash-movie [`disown-opener`]: https://triple-underscore.github.io/CSP3-ja.html#directive-disown-opener "disown-opener 指令は、[ ナビゲートされたリソースの onener を null 化する ] ことを確保する。" [Window#opener]: https://triple-underscore.github.io/browsers-ja.html#dom-opener [Tabnabbing]: https://blog.jxck.io/entries/2016-06-12/noopener.html "本サイト以下全ての target=_blank 付きのリンクに rel="noopener noreferrer" の付与を実施した。このプロパティの意味と、これが無い場合のフィッシング詐欺攻撃の可能性について解説する。" [`frame-ancestors`]: https://triple-underscore.github.io/CSP3-ja.html#directive-frame-ancestors "frame-ancestors 指令は、[ frame / iframe / object / embed / applet ] 要素を利用して埋め込めるリソースの URL を制約する。 この指令を利用すれば、敵対的になり得る文脈の中にリソースが埋め込まれるリスクを避けることで、多くの UI Redressing 【 UI の着せ替え】 攻撃避けることができる。" [クリックジャッキング]: https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E3%82%B8%E3%83%A3%E3%83%83%E3%82%AD%E3%83%B3%E3%82%B0 "クリックジャッキング(クリックジャック攻撃、Clickjacking、User Interface redress attack、UI redress attack、UI redressing)は、ウェブページの利用者に対し悪意をもって使用される技術の一種で、リンクやボタンなどの要素を隠蔽・偽装してクリックを誘い、利用者の意図しない動作をさせようとする手法である。" [`report-uri`]: https://triple-underscore.github.io/CSP3-ja.html#directive-report-uri "report-uri 指令は、報告先 — ある特定のふるまいが防止されたとき, 違反報告の送信先になるネットワーク端点 — の集合を定義する。" [report-uri.ioというサービスが便利]: https://blog.jxck.io/entries/2016-03-30/content-security-policy.html#report-uri.io "このレポートの収集と解析を行うサービスとして、 report-uri.io というサービスが最近登場した。" [`upgrade-insecure-requests`]: https://triple-underscore.github.io/webappsec-upgrade-insecure-requests-ja.html#delivery "サーバは、[ upgrade-insecure-requests 指令を包含する Content-Security-Policy ヘッダ ] を送信することにより, [ ある特定の被保護リソースに対する非セキュア要請 ] を昇格するよう, UA に指図してよい。" [混在コンテンツ]: https://developer.mozilla.org/docs/Security/%E6%B7%B7%E5%9C%A8%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84 "もし HTTPS のページの中に通常の平文の HTTP で送られてくるコンテンツが含まれる場合、その接続は部分的に暗号化されたものにすぎなくなります。すなわち、暗号化されていないコンテンツは盗聴可能であり、中間者攻撃の攻撃者によってコンテンツの中身は操作可能となり、もはや接続は保護されません。そのような web ページは「混在コンテンツ」を含むページと呼ばれます。" [CSP2で`connect-src`に置き換える形で非推奨にされた]: https://triple-underscore.github.io/CSP-ja.html#changes-from-level-1 "child-src は、 frame-src を非推奨にして, それを置き換える — それは、[ 被保護リソースが フレームを埋め込む / worker を読み込む ] 機能を制御する。" [CSP3で`connect-src`が`frame-src` `worker-src`に分割される形で非推奨ではなくなった]: https://triple-underscore.github.io/CSP3-ja.html#changes-from-level-2 "CSP Level 2 で非推奨にされた frame-src 指令は, 非推奨でなくなり、 worker-src が追加された。 両者とも無い場合は child-src に先送りする(それも無い場合は default-src に先送りする)。" 一つのApacheで複数のドメインを扱う (仮想ホスト) ==================================================== ```apache:/etc/httpd/conf.modules.d/00-base.conf ############## ## 以上省略 ## ############## #LoadModule request_module modules/mod_request.so #LoadModule sed_module modules/mod_sed.so #LoadModule speling_module modules/mod_speling.so LoadModule macro_module modules/mod_macro.so # ↑ファイルの末尾にこの行を追記 ``` `/etc/httpd/conf/httpd.conf`に`#LoadModule macro_module lib64/httpd/modules/mod_macro.so`のような行があれば、`/etc/httpd/conf.modules.d/00-base.conf`に追記する代わりにそちらのコメントアウトを外してください。   <dl> <dt>[LoadModule ディレクティブ]</dt><dd>`LoadModule macro_module modules/mod_macro.so`</dd><dd>指定した動的共有モジュールをApache起動時に読み込みます。</dd> </dl> ```apache:/etc/httpd/conf.d/ssl.conf ############## ## 以上省略 ## ############## ## ## SSL Virtual Host Context ## #<VirtualHost _default_:443> ## ↑この行をコメントアウト ## # General setup for the virtual host, inherited from global configuration #DocumentRoot "/var/www/html" #ServerName www.example.com:443 ############## ## 中略 ## ############## # Per-Server Logging: # The home of a custom SSL log file. Use this when you want a # compact non-error SSL logfile on a virtual host basis. CustomLog logs/ssl_request_log \ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" # TLSの基本設定を読み込み #Include /etc/letsencrypt/options-ssl-apache.conf ## ↑この行をコメントアウト ## #ServerName example.jp #ServerAlias sub1.example.jp sub2.example.jp ## ↑この2行をコメントアウト ## Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" SSLUseStapling on SSLCertificateChainFile /etc/letsencrypt/live/example.jp/chain.pem #</VirtualHost> ## ↑この行をコメントアウト ## <IfModule mod_ssl.c> SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000) </IfModule> ``` ```apache:/etc/httpd/conf.d/vhosts.conf # すべての仮想ホストから読み込まれるファイル AliasMatch ^/((?:common|polyfill)/.+) /var/www/common/$1 <DirectoryMatch ^/var/www/common/(?:common|polyfill)/.+$> Require all granted </DirectoryMatch> # すべての仮想ホストで共通の設定 <Macro CommonHost $subdomain $directory> ServerName $subdomainexample.jp Include /etc/letsencrypt/options-ssl-apache.conf DocumentRoot /var/www/$directory <Directory /var/www/$directory> Require all granted </Directory> </Macro> <VirtualHost *:80> # 80番ポートへのアクセス ServerName default-virtual-host.invalid # 正規のサブドメインに一致するなら、https://%{HTTP_HOST}/ にリダイレクト RewriteEngine on RewriteCond %{HTTP_HOST} ^(?:sub1|sub2).example.jp$ RewriteRule .* https://%{HTTP_HOST}$0 [redirect=permanent,last] # それ以外の場合は、https://example.jp/ にリダイレクト RewriteRule .* https://example.jp$0 [redirect=permanent,last] </VirtualHost> <VirtualHost *:443> Use CommonHost " " html # アップロードされたデータ Alias /oekaki/data /var/www/share/html/uploader/data <Directory /var/www/share/html/uploader/data> Require all granted </Directory> </VirtualHost> <VirtualHost *:443> Use CommonHost sub1. sub1 AliasMatch ^/((?:index|style)(?:\.[^/]+)?)$ /var/www/share/sub1/core/$1 <Directory /var/www/share/sub1/core> Require all granted </Directory> </VirtualHost> <VirtualHost *:443> Use CommonHost sub2. sub2 </VirtualHost> ``` `/etc/httpd/conf/httpd.conf`に`IncludeOptional conf.d/*.conf`のような行が無ければ、追記しておきます。 ```apache:/etc/httpd/conf/httpd.conf ############## ## 以上省略 ## ############## <IfModule log_config_module> # # The following directives define some format nicknames for use with # a CustomLog directive (see below). # LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined LogFormat "%h %l %u %t \"%r\" %>s %b" common <IfModule logio_module> # You need to enable mod_logio.c to use %I and %O LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio </IfModule> # # The location and format of the access logfile (Common Logfile Format). # If you do not define any access logfiles within a <VirtualHost> # container, they will be logged here. Contrariwise, if you *do* # define per-<VirtualHost> access logfiles, transactions will be # logged therein and *not* in this file. # #CustomLog "logs/access_log" common # # If you prefer a logfile with access, agent, and referer information # (Combined Logfile Format) you can use the following directive. # #CustomLog "logs/access_log" combined ## ↑コメントアウトする ## CustomLog "logs/access_log" "%v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" ## ↑この行を追加 ## </IfModule> ############## ## 以下省略 ## ############## ```   <dl><dt>[Alias ディレクティブ]</dt><dd>第1引数で指定したURLを、第2引数で指定した<i>ファイルシステムのパス絶対URL</i>と結び付けます。URLは[Redirect ディレクティブ]と同じく前方一致で、`Alias /foo /var/www/foo/bar`と記述すると、https://example.com/foo/test が`/var/www/foo/bar/test`と結び付けられます。</dd> <dt>[AliasMatch ディレクティブ]</dt><dd>第1引数に正規表現を指定すること以外は、[Alias ディレクティブ]と同じです。正規表現内の後方参照は、第3引数で`$1`などと数字に`$`を前置して利用できます。`AliasMatch ^/((?:common|polyfill)/.*) /var/www/common/$1`は`Alias /common/ /var/www/common/common/` `Alias /polyfill/ /var/www/common/polyfill/`と同じ意味になります。</dd> <dt>[<DirectoryMatch> セクション]</dt><dd>`<DirectoryMatch ^/var/www/common/(?:common|polyfill)/.+$>`〜`</DirectoryMatch>`</dd><dd>ディレクティブを適用する **ファイルを** 正規表現で指定します。[<Directory> セクション]と同じく<i>ファイルシステムのパス絶対URL</i>で指定します。[<Directory> セクション]よりは[<FilesMatch> セクション]に近く、[AllowOverride ディレクティブ]が使えず、[RewriteBase ディレクティブ]もエラーは出ませんが正しく機能しません。どのOSであっても、<i>パス区分</i>を区切る文字は必ず`/`であるものとして正規表現を記述します。</dd> <dt>[DocumentRoot ディレクティブ]</dt><dd><i>ファイルシステムのパス絶対URL</i>、またはサーバールートからの相対パスでドキュメントルートを設定します。例えば、`ServerName example.jp`と`DocumentRoot /var/www/html`が指定されていた場合、https://example.jp/foo/bar.html にアクセスされたときに`/var/www/html/foo/bar.html`を返します。</dd> <dt>[<VirtualHost> セクション]</dt><dd>`<VirtualHost *:443>`〜`</VirtualHost>`</dd><dd>サーバーのIPアドレスとポート番号の組み合わせごとにディレクティブを適用します。IPアドレスとポート番号の間には`:`を挟み、その組は第2引数以降で複数指定することもできます。ポート番号を省略した場合、どのポート番号にも一致するようになります。ポート番号を省略したセクションより、ポート番号が省略されていないセクションの方が優先されます。</dd><dd>値の同じ[<VirtualHost> セクション]を複数作成し、それぞれに[ServerName ディレクティブ]でドメインを設定すれば、ドキュメントルートなどをドメインごとに設定できます ([名前ベースのバーチャルホスト])。</dd><dd>サーバーにアクセスしたときのドメインが、どの[ServerName ディレクティブ]の設定とも一致しない場合は、IPアドレスとポート番号の条件が一致する[<VirtualHost> セクション]の中で最初に記述したもの (<i>デフォルトの仮想ホスト</i>) が適用されます。<i>デフォルトの仮想ホスト</i>の選択も、ポート番号が省略されていないセクションの方が優先されます。</dd> <dt>[CustomLog ディレクティブ]</dt><dd>`"CustomLog logs/access_log" "%v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""`</dd><dd>第1引数に保存先、第2引数に書式を指定します。</dd><dd>最初に記述されていた `CustomLog "logs/access_log" combined` は、直前の `LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined` から `CustomLog "logs/access_log" "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""` と展開できます。つまり記述例で書き直した書式は、最初に記述されていた書式の先頭に `%v` を追加したものです。`%v` は、サーバーのホスト名に置き換えられます。</dd> <dt>[<Macro> セクション] \(リンク先英語)</dt><dd>`<Macro CommonHost $subdomain $directory>`〜`</Macro>`</dd><dd><i>設定ファイルマクロ</i>を定義します。<i>設定ファイルマクロ</i>は、複数の箇所で同じ設定が必要な場合に使います。プログラミングにおける関数宣言のように、第1引数にマクロ名、第2引数以降に仮引数を記述します。引数の数の違いによる多重定義はできません。</dd> <dt>[Use ディレクティブ] \(リンク先英語)</dt><dd>定義しておいた<i>設定ファイルマクロ</i>を使用します。プログラミングにおける関数呼び出しのように、第1引数にマクロ名、第2引数以降に実引数を記述します。`Use ExampleMacro ""`のように、実引数に空文字列を指定すると、警告が発生します。</dd></dl> 例えば上記コードの最後の[<VirtualHost> セクション]は、<i>設定ファイルマクロ</i>によって以下のように置換されます。 ```apache:置換前 <VirtualHost *:80> Use CommonHost sub2. sub2 </VirtualHost> ``` ```apache:置換後 <VirtualHost *:80> ServerName sub2.example.jp DocumentRoot /var/www/sub2 <Directory /var/www/sub2> Require all granted </Directory> </VirtualHost> ``` [LoadModule ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_so.html#loadmodule "オブジェクトファイルやライブラリをリンクし、使用モジュールの リストに追加する" [Alias ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_alias.html#alias "URL をファイルシステムの位置にマップする" [AliasMatch ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_alias.html#aliasmatch "正規表現を使って URL をファイルシステムの位置にマップする" [<DirectoryMatch> セクション]: https://httpd.apache.org/docs/current/mod/core.html#directorymatch "正規表現にマッチするファイルシステムのディレクトリと サブディレクトリとのみに適用されるディレクティブを囲む" [<FilesMatch> セクション]: https://httpd.apache.org/docs/current/mod/core.html#filesmatch "正規表現にマッチするファイル名に適用される ディレクティブを囲む" [AllowOverride ディレクティブ]: https://httpd.apache.org/docs/current/mod/core.html#allowoverride ".htaccess で許可されるディレクティブの種類" [DocumentRoot ディレクティブ]: https://httpd.apache.org/docs/current/mod/core.html#documentroot "ウェブから見えるメインのドキュメントツリーになる ディレクトリ" [<VirtualHost> セクション]: https://httpd.apache.org/docs/current/mod/core.html#virtualhost "特定のホスト名や IP アドレスのみに適用されるディレクティブを 囲む" [名前ベースのバーチャルホスト]: https://httpd.apache.org/docs/current/vhosts/name-based.html "IP ベースのバーチャルホストでは、応答する バーチャルホストへのコネクションを決定するために IP アドレスを使用します。ですから、それぞれのホストに個々に IP アドレスが必要になります。これに対して名前ベースのバーチャルホストでは、 クライアントが HTTP ヘッダの一部としてホスト名を告げる、 ということに依存します。この技術で同一 IP アドレスを異なる多数のホストで共有しています。" [CustomLog ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_log_config.html#customlog "ログファイルの名前と書式を設定する" [<Macro> セクション]: https://httpd.apache.org/docs/current/mod/mod_macro.html#macro "Define a configuration file macro" [Use ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_macro.html#use "Use a macro" URLの末尾に拡張子を表示しない ============================= ここでは[RewriteMap ディレクティブ]を利用していますが、[mod_negotiationモジュール]や[RewriteCond ディレクティブ]を利用して実現できる場合もあります。 ```apache:/etc/httpd/conf.d/vhosts.conf # Virtual Hosts # # If you want to maintain multiple domains/hostnames on your # machine you can setup VirtualHost containers for them. Most configurations # use only name-based virtual hosts so the server doesn't need to worry about # IP addresses. This is indicated by the asterisks in the directives below. # # Please see the documentation at # <URL:https://httpd.apache.org/docs/2.4/vhosts/> # for further details before you try to setup virtual hosts. # # You may use the command line option '-S' to verify your virtual host # configuration. ## ↓ここから追記 ## RewriteEngine on # rewriteモジュールに関するログをすべて記録する #LogLevel alert rewrite:trace8 # 標準入力の「フルパス,プレフィックスを取り除いたパス」から拡張子付きのファイルを探し、最初に見つかったファイルのパスからプレフィックスを取り除いて返す RewriteMap adjust-extension "prg:/usr/bin/perl -e '$| = 1; while (<STDIN>) { chomp($_); my @params = split(/,/, $_); my $path; if ($#params == 1) { ($path) = glob(@params[0] . \\'.*\\'); if ($path) { $path = substr($path, length(@params[0]) - length(@params[1])); } } print(($path || \\'NULL\\') . \"\\\\x0A\"); }'" <Directory /var/www> # 拡張子無しのURLでアクセスできるようにする RewriteOptions Inherit RewriteRule ^([^.]+)(?<!/)$ ${adjust-extension:%{REQUEST_FILENAME},$1|$1} </Directory> <If "unescape(%{THE_REQUEST}) =~ /\\..*\\s/"> # 要求行の最後の空白より前にピリオドが含まれていれば # Favicon、robots.txt、phpMyAdminディレクトリを除く拡張子付きのURLを拡張子無しのURLにリダイレクト RedirectMatch permanent ^(/(?!(?:favicon\.ico|robots\.txt)$|phpMyAdmin/)[^.]+)\. $1 </If> ## ↑ここまで追記 ## # すべての仮想ホストから読み込まれるファイル AliasMatch ^/((?:common|polyfill)/.+) /var/www/common/$1 <Directory /var/www/common> RewriteBase / </Directory> ## ↑この<Directory>セクションを追加 ## <DirectoryMatch ^/var/www/common/(?:common|polyfill)/.+$> Require all granted </DirectoryMatch> # すべての仮想ホストで共通の設定 <Macro CommonHost $subdomain $directory> ServerName $subdomainexample.jp Include /etc/letsencrypt/options-ssl-apache.conf DocumentRoot /var/www/$directory RewriteOptions Inherit ## ↑この行を追加 ## <Directory /var/www/$directory> Require all granted </Directory> </Macro> <VirtualHost *:80> # 80番ポートへのアクセス ServerName default-virtual-host.invalid # 正規のサブドメインに一致するなら、https://%{HTTP_HOST}/ にリダイレクト RewriteEngine on RewriteCond %{HTTP_HOST} ^(?:sub1|sub2).example.jp$ RewriteRule .* https://%{HTTP_HOST}$0 [redirect=permanent,last] # それ以外の場合は、https://example.jp/ にリダイレクト RewriteRule .* https://example.jp$0 [redirect=permanent,last] </VirtualHost> <VirtualHost *:443> Use " " html # アップロードされたデータ Alias /oekaki/data /var/www/share/html/uploader/data <Directory /var/www/share/html/uploader/data> RewriteBase /uploader/data/ ## ↑この行を追加 ## Require all granted </Directory> </VirtualHost> <VirtualHost *:443> Use sub1. sub1 AliasMatch ^/((?:index|style)(?:\.[^/]+)?)$ /var/www/share/sub1/core/$1 <Directory /var/www/share/sub1/core> RewriteBase / ## ↑この行を追加 ## Require all granted </Directory> </VirtualHost> <VirtualHost *:443> Use sub2. sub2 </VirtualHost> ``` `/etc/httpd/conf/httpd.conf`に`#LoadModule rewrite_module lib64/httpd/modules/mod_rewrite.so`のような行があれば、コメントアウトを外しておきます。   <dl><dt>[RewriteEngine ディレクティブ] \(リンク先英語)</dt><dd>`RewriteEngine on`</dd><dd>rewriteエンジンの有効と無効を切り替えます。初期設定では無効になっています。無効の場合、[RewriteRule ディレクティブ]などが無視されるだけで、エラーは発生しません。</dd> <dt>[RewriteRule ディレクティブ] \(リンク先英語)</dt><dd><code>RewriteRule ^([^.]+)(?<!/)$ ${adjust-extension:%{REQUEST_FILENAME},$1|$1}</code></dd><dd>第1引数の正規表現に一致したパスを第2引数で指定した文字列に置き換えます。一度でも置き換えが行われていると、全てのRewriteRuleが処理された後、内部リダイレクトによってリクエスト処理がやり直されます。`%{REQUEST_URL}`等もURL書き換え後のものになっているので、無限ループが起こらないように注意する必要があります。</dd><dd>rewriteはファイルシステムのパスではなくURLを書き換える処理なので、[Alias ディレクティブ]のようにドキュメントルート外に書き換えることはできません。</dd><dd>引数にワイルドカードを含む[<Directory> セクション]では期待通りに動かないことがあります (<i>per-dir</i>にワイルドカードが含まれたままrewrite処理が行われるため)。</dd></dl> 上記コードのような設定をして https://example.jp/file にアクセスしたときのrewrite処理を、例として示します。この場合、<i>ReqriteRuleを記述したディレクトリのパス絶対URL</i>(<i>per-dir</i>)は`/var/www/`です。 | | 処理前 | 処理後 | 処理内容 | |---|---------------------------------|---------------------------------|----------------------------------------------------------------------------------------------------------------------------| | 1 |/var/www/html/file | html/file | <i>ファイルシステムのパス絶対URL</i>から<i>per-dir</i>を取り除く | | 2 | html/file | html/file.xhtml | <code>^([^.]+)(?<!/)$</code>に一致するので、<code>${adjust-extension:%{REQUEST_FILENAME},$1|$1}</code>に書き換える | | 3 | html/file.xhtml | /var/www/html/file.xhtml | <i>per-dir</i>を前置する | | 4 | /var/www/html/file.xhtml | /file.xhtml | <i>ドキュメントルート</i>を取り除き、内部リダイレクト | | 5 | /var/www/html/file.xhtml | html/file.xhtml | <i>ファイルシステムのパス絶対URL</i>から<i>per-dir</i>を取り除く | | 6 | html/file.xhtml | html/file.xhtml | <code>^([^.]+)(?<!/)$</code>に一致しないので、何もしない | | 7 | html/file.xhtml | /var/www/html/file.xhtml | 書き換えが行われなかったので、<i>ファイルシステムのパス絶対URL</i>を返してrewrite処理終了 |   <dl><dt>[RewriteBase ディレクティブ] \(リンク先英語)</dt><dd>`RewriteBase /`</dd><dd>[Alias ディレクティブ]によってURLがドキュメントルート外のパスと結び付けられていると、そのままでは内部リダイレクト前のドキュメントルートを取り除く処理が行えません。そこで、ドキュメントルートの絶対パスを取り除く代わりに、<i>per-dir</i>を第1引数で指定した<i>基底URL</i>に置き換えます。[^qa@it-alias-rewrite]</dd></dl> 上記コードのような設定をして https://example.jp/polyfill/main にアクセスしたときのrewrite処理を、例として示します。この場合、<i>per-dir</i>は`/var/www/common`、<i>基底URL</i>は`/`です。 | | 処理前 | 処理後 | 処理内容 | |---|----------------------------------|----------------------------------|----------------------------------------------------------------------------------------------------------------------------| | 1 | /polyfill/main | /var/www/common/polyfill/main | [AliasMatch ディレクティブ]によって、<i>絶対URL</i>と<i>ファイルシステムの絶対パス相対URL</i>が結び付けられている | | 2 | /var/www/common/polyfill/main | polyfill/main | <i>ファイルシステムのパス絶対URL</i>から<i>per-dir</i>を取り除く | | 3 | polyfill/main | polyfill/main.js | <code>^([^.]+)(?<!/)$</code>に一致するので、<code>${adjust-extension:%{REQUEST_FILENAME},$1|$1}</code>に書き換える | | 4 | polyfill/main.js | /var/www/common/polyfill/main.js | <i>per-dir</i>を前置する | | 5 | /var/www/common/polyfill/main.js | /polyfill/main.js | <i>per-dir</i>を<i>基底URL</i>に置き換え、内部リダイレクト | | 6 | /polyfill/main.js | /var/www/common/polyfill/main.js | [AliasMatch ディレクティブ]によって、<i>絶対URL</i>と<i>ファイルシステムのパス絶対URL</i>が結び付けられている | | 7 | /var/www/common/polyfill/main.js | polyfill/main.js | <i>ファイルシステムのパス絶対URL</i>から<i>per-dir</i>を取り除く | | 8 | polyfill/main.js | polyfill/main.js | <code>^([^.]+)(?<!/)$</code>に一致しないので、何もしない | | 9 | polyfill/main.js | /var/www/common/polyfill/main.js | 書き換えが行われなかったので、<i>ファイルシステムのパス絶対URL</i>を返してrewrite処理終了 |   <dl><dt>[RewriteCond ディレクティブ] \(リンク先英語)</dt><dd>[RewriteRule ディレクティブ]を適用する条件を設定します。RewriteRule1つに付き、複数のRewriteCondを設定できます。RewriteCondは対象のRewriteRuleよりも前に記述しますが、RewriteCondよりも先にRewriteRuleの条件がチェックされます。</dd> <dt>[RewriteMap ディレクティブ] \(リンク先英語)</dt><dd>`RewriteMap adjust-extension "prg:/usr/bin/perl -e '$| = 1; while (<STDIN>) { chomp($_); my @params = split(/,/, $_); my $path; if ($#params == 1) { ($path) = glob(@params[0] . \\\\'.*\\\\'); if ($path) { $path = substr($path, length(@params[0]) - length(@params[1])); } } print(($path || \\\\'NULL\\\\') . \\"\\\\\\\\x0A\\"); }'"`</dd><dd>第1引数にマップ名 (関数名)、第2引数に<i>マップタイプ</i>と<i>マップソース</i>を`:`区切りで指定し、[RewriteCond ディレクティブ]と[RewriteRule ディレクティブ]で利用できるマッピング関数を定義します。[RewriteMap ディレクティブ]は、<i>サーバ設定ファイルコンテキスト</i> (設定ファイルの中で[<Directory> セクション]や[<VirtualHost> セクション]の外) か<i>仮想ホストコンテキスト</i> ([<VirtualHost> セクション]の中で[<Directory> セクション]などの外) にしか記述できません。[RewriteMap ディレクティブ]を記述した場所でrewriteエンジンが無効になっている場合、関数は定義されません。`${マップ名:引数|デフォルト値}`のように関数を呼び出します。`${関数名:引数}`のようにデフォルト値を書かなかった場合、引数に対応する戻り値が見つからなかった場合 (`map lookup FAILED`) は空文字列が返ります。</dd><dd><i>マップタイプ</i>が`prg`の場合、マップソースは標準入力から引数を受け取り標準出力へ戻り値を書きだすプログラムファイルへのパスです。[User ディレクティブ]で設定したユーザーが、このプログラムファイルの実行権限を持っている必要があります。このプログラムはApache起動時に一度だけ起動されるため、標準入力を受け取るまで処理をブロックし、戻り値を書きだした後はまた標準入力を受け取るまで処理をブロックする無限ループ構造にしておきます。入出力バッファの無効化も必要です。戻り値の末尾に改行 (LF) を1つだけ置きます。文字列`NULL` (+改行) を返せば、`map lookup FAILED`を示すことができます。</dd><dd>プログラムが期待通りの動作をしない場合、SELinuxによってブロックされている場合もあるので、[SELinuxのログの確認]もしてみてください。また、プログラムの標準エラー出力は無視され、Apacheのログには残りません。</dd><dd>第2引数全体を`"`で囲み、プログラムへのパスの後に空白区切りでコマンドライン引数を書いた場合、各コマンドライン引数は文字列としてプログラムに渡されます。シングルクォートで囲むことで、コマンドライン引数に空白を含めることができます。コマンドライン引数の中で`'` `"` `\\`を使う場合は、それぞれ`\\\\'` `\\"` `\\\\\\\\` とエスケープします。</dd> <dt>[RewriteOptions ディレクティブ] \(リンク先英語)</dt><dd>`RewriteOptions Inherit`</dd><dd>rewriteエンジンの追加設定を有効にします。この設定有効化は、サブディレクトリで[RewriteRule ディレクティブ]などを記述した場合にも引き継がれます。`Inherit`は、<i>現在のディレクトリのルールの後に、親ディレクトリのルールをコピーする</i>というオプションです。`Inherit`オプションを<i>仮想ホストコンテキスト</i>で有効にした場合は意味が変わり、<i>メインサーバ設定</i> ([<VirtualHost> セクション]の外) に記述したルールを継承するオプションとなります。[^qa@it-rewriterule]</dd> <dt>[LogLevel ディレクティブ]</dt><dd>`LogLevel alert rewrite:trace8`</dd><dd>エラーログに記録するログの種類を指定します。<i>モジュール</i>と<i>レベル</i>を`:`区切りで指定します。第1引数以降で複数記述することができます。第1引数の<i>モジュール</i>は省略することができ、その場合すべてのモジュールについて同じレベルを指定したことになります。既定は `LogLevel warn` です。</dd> </dl> [^qa@it-alias-rewrite]: [【Apache 2.4】サーバー全体で共通のRewriteRuleとAliasを設定したい - QA@IT](http://qa.atmarkit.co.jp/q/3283#answer_16079) [^qa@it-rewriterule]: [【Apache 2.4】サーバ設定ファイルに記述したRewriteRuleが動かない - QA@IT](http://qa.atmarkit.co.jp/q/3248#answer_16032) [mod_negotiationモジュール]: https://httpd.apache.org/docs/current/content-negotiation.html "Apache は HTTP/1.1 の規格に記述されているコンテントネゴシエーションを サポートしています。 ブラウザにより提供されたメディアタイプ、 言語、文字セット、エンコーディングの優先傾向に基づいて、 最適なリソースの表現を選択できます。 また、不完全なネゴシエーション情報を送ってくるブラウザからのリクエストを もっと賢く取り扱えるよう、いくつか機能も実装してあります。" [RewriteEngine ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriteengine "Enables or disables runtime rewriting engine" [RewriteRule ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriterule "Defines rules for the rewriting engine" [RewriteBase ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase "Sets the base URL for per-directory rewrites" [RewriteCond ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritecond "Defines a condition under which rewriting will take place" [RewriteMap ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritemap "Defines a mapping function for key-lookup" [SELinuxのログの確認]: //qiita.com/100/items/f7f43994568836b4a453#違反ログから新しいルールを定義 "ポリシー違反のログは、`sudo grep avc: /var/log/messages`を実行、又はAuditが起動していた状態なら`sudo ausearch --message AVC`を実行することで確認できます。" [RewriteOptions ディレクティブ]: https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriteoptions "Sets some special options for the rewrite engine" [LogLevel ディレクティブ]: https://httpd.apache.org/docs/current/mod/core.html#loglevel "ErrorLog の冗長性を制御する" 開発環境向けの設定 ================== ```apache:/etc/httpd/conf/httpd.conf ############## ## 以上省略 ## ############## # # EnableMMAP and EnableSendfile: On systems that support it, # memory-mapping or the sendfile syscall may be used to deliver # files. This usually improves server performance, but must # be turned off when serving from networked-mounted # filesystems or if support for these functions is otherwise # broken on your system. # Defaults if commented: EnableMMAP On, EnableSendfile Off # #EnableMMAP off #EnableSendfile on ## ↑仮想マシンで共有ディレクトリを使用している場合、この行をコメントアウトする ## ############## ## 中略 ## ############## # 開発環境 # 403と404を区別する ErrorDocument 403 /forbidden # 開発環境用のスタイルシート Header always set link "</common/development>; rel=stylesheet" # Faviconを変える Alias /favicon.ico /var/www/html/favicon-development.ico # 開発環境へのCSRFを防止する <If "-n %{HTTP_REFERER} && !(%{HTTP_REFERER} -strmatch %{REQUEST_SCHEME} . '://' . %{SERVER_NAME} . '/*')"> # リファラが存在し、かつリファラのホスト名がサーバーのホスト名と一致しなければ Require all denied </If> ``` ブラウザでリファラを変更している場合は、ターゲットのホワイトリストに開発環境のドメインを追加しておきます。   <dl><dt>[EnableSendfile ディレクティブ]</dt><dd>「sendfile」というシステムコールを使って静的ファイルを返す時の性能を向上しようとする機能の有効と無効を切り替えます。<br>Apache 2.4の既定は`EnableSendfile Off`ですが、CentOS7標準のパッケージでは設定ファイルに`EnableSendfile On`が記述されており有効化されています。 **[VirtualBox]などで[仮想マシン]を作成して共有ディレクトリ機能を利用している場合、ファイルの更新が反映されないことがあります。** そのようなときは、`EnableSendfile On`をコメントアウトしてこの機能を無効化します。[^sf]</dd> </dl> ```css:/var/www/common/common/development.css /*==================================== 表示領域の右端に縦書きで文字を表示する */ body::after { font-size: 10vmin; width: 1em; line-height: 2.3; content: "開発環境"; display: block; color: #FFFFFF; font-weight: bold; background: rgba(127, 127, 127, 0.7); border: solid #FFFFFF 0.2em; position: fixed; top: 0; bottom: 0; right: 0; opacity: 0.5; } ``` [link応答ヘッダ][link]によるCSSの設定は、前述のようにFirefoxでしか使えません。開発環境を表すCSSの設定はユーザースタイルシートを使っても良いと思います。 [^sf]: [VagrantやVirtualBoxの共有フォルダ機能で静的ファイルが更新されない — SEEDS Creator’s Blog](http://blog.seeds-std.co.jp/entry/1227.html) [EnableSendfile ディレクティブ]: https://httpd.apache.org/docs/current/mod/core.html#enablesendfile "ファイルのクライアントへの配送時にカーネルの sendfile サポートを 使うかどうか" [VirtualBox]: https://ja.wikipedia.org/wiki/VirtualBox "Oracle VM VirtualBox (オラクル ブイエム バーチャルボックス)とは、x86仮想化ソフトウェア・パッケージの一つ。現在の開発は米国オラクルが行っている。" [仮想マシン]: https://ja.wikipedia.org/wiki/%E4%BB%AE%E6%83%B3%E6%A9%9F%E6%A2%B0 "仮想機械(かそうきかい、仮想マシン、バーチャルマシン、英語: virtual machine、VM)とは、コンピュータの動作をエミュレートするソフトウェアやフレームワークである。また、エミュレートされた仮想のコンピュータそのものも仮想機械という。仮想機械によって、1つのコンピュータ上で複数のコンピュータやOSを動作させたり、別のアーキテクチャ用のソフトウェアを動作させることができる。" 参考ページ・脚注 ================ |
|
| 780位 |
|
|||
|
02:00:35 |
(kayac.com 所属) |
|
音楽の覚えがあるエンジニアとしては、触らなきゃダメだろーと思いつつ、なかなか手を付けられていなかったWebAudioAPI、 [先日のハッカソン](http://qiita.com/fnobi/items/0b94534183b48fb30d55)でようやく着手できました。 その過程でわかったこと・つくったものなどまとめました。 ## 登場人物 HTML5のAudio要素を使う時と違って、 WebAudioAPIの場合は、使っているオブジェクトがけっこう多い。 そのあたりを把握するのが、WebAudioAPIのマスターの第一歩っぽい。 ### context (AudioContext) 音をとりまとめるcontext。 音の再生先(destination)なんかも、このcontextが握っているみたい。 WebAudioAPIに対応しているブラウザは、 window.AudioContext (window.WebkitAudioContext)というのが生えてるので、 これをインスタンス化すると作れる。 ```javascript var context = new window.AudioContext(); ``` ### buffer 再生する音のデータ。 .wavなど外部のファイルを使う場合は、 - XMLHttpRequest(responseType: arraybuffer で!)で読み込む - 読み込んだものを、contextにdecodeしてもらう (```context.decodeAudioData```) という流れでできる。 ```javascript // ここにbufferを格納したい var buffer; // ファイルを取得 (arraybufferとして) var request = new XMLHttpRequest(); request.open('GET', '/sounds/sample.wav', true); request.responseType = 'arraybuffer'; request.send(); request.onload = function () { // 読み込みが終わったら、decodeしてbufferにいれておく var res = request.response; context.decodeAudioData(res, function (buf) { buffer = buf; }); }; ``` ### source こうして作ったbufferを再生するためには、sourceというものを作る。 これもcontextに生えているメソッドを使う。(```context.createBufferSource```) ```javascript // source作成 var source = context.createBufferSource(); // さきほど作ったbufferを、ここにセット source.buffer = buffer; // 再生準備。contextの持っている再生先に接続 source.connect(context.destination); // 再生 (引数はtimeout) source.noteOn(0); ``` ## たとえば、できること ### 音の高さを変える 厳密にはピッチ変更ではないけれど、 単発の音を鳴らすのであれば、sourceのplaybackRate.valueをいじればおk。 ## 気をつけること ### sourceは使い捨て sourceは、 **再生するたびに生成して、使い捨てるというかたち** になっている。 最初は、つくったsourceに対して何度も ```.noteOn``` したりできるのかと思ってた。 ### 一度は、ユーザーアクションをトリガーにして、音再生させないとだめ。 逆に言えば、1度タップのタイミングなどで再生をさせてしまえば、 **その後は好きなタイミングで鳴らし放題** なもよう。 その辺の挙動から、「ユーザーアクションで再生許可」は、おそらくcontextに紐付いてるんじゃないかなぁという印象。 (source単位での許可だとすると、sourceは再生のたびに使い捨ててるので、再生のたびに許可が必要なはず) ちゃんとしらべてないけど。 ちなみに、ユーザアクションから間接的に音再生呼んだ場合、 (clickのlistenerでsetTimeout通して音再生、とか) なんかときどき再生できちゃってたけれど、 確実に鳴らしたいなら、やっぱり1度は直接再生呼び出しした方がいいみたい。 ## つくったもの ### DripMetro - http://dripmetro.fnobi.com/ - http://github.com/fnobi/dripmetro [先日のハッカソン](http://qiita.com/fnobi/items/0b94534183b48fb30d55)で作ったもの。 ぴちょんぴちょん言うメトロノーム。 いちおうBPMを上げるほど、音も高くなるようにしてます。 ### ToneMap - http://github.com/fnobi/ToneMap - http://tonemap.fnobi.com/ ↑のDripMetroを作る過程でできた、WebAudioAPIのwrapper。 (wrapperとはいえ、もうすこし機能追加するつもり) 複数の音源を使う場合でも、やりやすくなる…はず。 デモでは、 画面タッチで音再生→スワイプのスピードに応じて音を高く っていうのやってます。 ## まとめ - たのしい - ↓のデモなど見ても思うけれど、iOSは十分にWebAudioAPIつかえるっぽい - 音をばしばしつかったスマホサイトなんかも、挑戦したいなぁ ## 参考とかっこいいデモ - http://jlongster.com/2012/09/12/web-apps.html - iOSでもぬるぬる動くデモあり すごい! - http://mohayonao.github.io/timbre/ - WebAudioAPIをつかって、sin波を鳴らすライブラリ。べんりそう。 - http://mohayonao.hatenablog.com/entry/2012/09/21/203833 - ↑のtimbre.jsのひとの、iOS WebAudioAPIのはなし。 - http://www.html5rocks.com/en/tutorials/webaudio/intro/ - おなじみhtml5rocks。WebAudioAPIの基礎。 - http://www.html5rocks.com/ja/tutorials/webaudio/games/ - こちらもhtml5rocks。ゲームで音を使う場合のあれこれを解説。 |
|
| 781位 |
|
|||
|
13:17:17 |
|
|
===========================
[Vagrant](http://www.vagrantup.com)は[Chef](http://www.opscode.com/chef)で環境構築を自動化してくれるツールです。仮想マシンには[VirtualBox](https://www.virtualbox.org)などが利用できます。 Windowsは`ssh`や`rsync`などを追加すると[Knife Solo](http://matschaffer.github.io/knife-solo/)が使えます。 [Chef Soloの入門](http://tatsu-zine.com/books/chef-solo)としてRuby(rbenv)の環境を自動構築する方法を紹介します。 (WindowsにUNIX互換の環境を追加するだけなのでChefの使い方はMacやLinuxでも同じです。) # Download & Install こちらをWindowsのインストーラーでインストールします。 ## VirtualBox [VirtualBox for Windows](https://www.virtualbox.org/wiki/Downloads)をインストールします。 VirtualBoxの登場はここだけです。あとはVagrantで仮想マシンをコントロールします。 ## Vagrant [Vagrant for Windows](http://downloads.vagrantup.com)をインストールします。 (この手順のインストールディレクトリは`C:¥opt¥vagrant¥`です。) VagrantにはRubyが必要なので[Ruby for Windows](http://rubyinstaller.org)がインストールされます。 これで[MinGW](http://www.mingw.org/)もインストールされるのでUNIX互換の環境が利用できます。 ([RubyGems](http://docs.vagrantup.com/v2/installation/index.html)は`1.0.x`系なので注意しましょう。) ## Minimalist GNU for Windows VagrantのMinGWにパッケージ管理ツールはないので[MinGW](http://sourceforge.net/projects/mingw/files/)をインストールします。 (この手順のインストールディレクトリは`C:¥opt¥MinGW¥`です。) ## msysGit MinGWのパッケージにGitはないので[Git for Windows](http://msysgit.github.io/)をインストールします。 (この手順のインストールディレクトリは`C:¥opt¥git¥`です。) # Terminal Vagrantのインストールで[Mintty](https://code.google.com/p/mintty/)もインストールされています。 まず`C:\opt\vagrant\embedded\bin\mintty.exe`のショートカットを作ります。 そのショートカットの`リンク先(T)`に`/bin/bash --login`を追加します。 `.minttyrc`ファイルを設定していますが、通常はGUIで設定します。 (Minttyは背景を透過にできます。Macでも作業をするので透過はポイントです。) ```bash $ cat << __EOS__ > ~/.minttyrc Transparency=high FontHeight=12 __EOS__ ``` # Package VagrantのMinGWに`mingw-get`コマンドを追加して`ssh`や`rsync`などをインストールします。 ```bash $ cp -r /c/opt/MinGW/. /c/opt/vagrant/embedded/ $ cd /c/opt/vagrant/embedded/var/lib/mingw-get/data/ $ cat defaults.xml|(rm defaults.xml;sed 's/%R\/msys\/1.0/\/opt\/vagrant\/embedded/g' > defaults.xml) $ cat profile.xml|(rm profile.xml;sed 's/%R\/msys\/1.0/\/opt\/vagrant\/embedded/g' > profile.xml) $ mingw-get update $ mingw-get upgrade $ mingw-get install msys-openssl msys-openssh $ mingw-get install msys-rsync msys-tar $ mingw-get install msys-vim ``` パッケージの一覧はこちらで確認できます。 ```bash $ mingw-get list | grep Package ``` # Editor [Vim](http://www.vim.org/)をインストールしていますが***好きなエディター***を使いましょう。 WindowsのExplorerからの操作でVagrantfileやChefのRecipeを編集できます。 [Sublime Text](http://www.sublimetext.com/)はTerminalとExplorerから操作できるので使用しています。 `.bash_profile`ファイルにエイリアスを設定すると便利です。 ```bash $ cat << __EOS__ >> ~/.bash_profile alias ll='ls -al' alias ee='/c/opt/sublimetext2/sublime_text' __EOS__ $ . ~/.bash_profile ``` # Virtual Machine 仮想マシンは[Getting Started](http://docs.vagrantup.com/v2/getting-started/index.html)と同じ`Ubuntu 12.01`を利用します。 ログインは`vagrant ssh`コマンドではなく`/.ssh/config`ファイルと`ssh`コマンドを使います。 ```bash $ vagrant box add precise32 http://files.vagrantup.com/precise32.box $ mkdir -p ~/vagrant/precise $ cd ~/vagrant/precise $ vagrant init precise32 $ vagrant up $ mkdir ~/.ssh $ vagrant ssh-config --host precise >> ~/.ssh/config $ ssh precise ... Welcome to your Vagrant-built virtual machine. ``` これでゲストOSにログインできました。次は[Vagrantfile](http://docs.vagrantup.com/v2/vagrantfile/index.html)を設定して環境を自動構築しましょう。 # Chef Vagrantは**Chef Solo**が利用されます。リモートマシンにリポジトリ(構築手順)を `rsync`で同期させて`ssh`でコマンドを実行するシンプルな仕組みです。 ``` ホストOS(Knife Solo) <-- ssh --> ゲストOS(Chef Solo) [Chef Repository] <--- rsync ---> [Chef Repository] ``` ## Knife Solo Gemの`knife-solo`をインストールするとChefが使えるようになりますが MSYSに対応していない部分があるのでソースからインストールします。 Cygwinと認識されてディレクトリの先頭に`/cygdrive/`を付けられるので`v0.2.0`をForkして[修正](https://github.com/ogom/knife-solo/commit/ae3cfd3f1e06a604e5efb3c48e4007d0ec0bb5e5)しました。 (CygwinとMSYSを`RbConfig`で区別できないものか… `pull request`をするなら`v0.3.x`系にします) ### Git GitHubからソースを利用するので`git`コマンドを使えるようにします。 また、Chefのリポジトリ管理は[Git](http://git-scm.com/)を使います。 ```bash $ ln -s /c/opt/git/bin/git.exe /c/opt/vagrant/embedded/bin/git.exe $ ln -s /c/opt/git/libexec/git-core /c/opt/vagrant/embedded/libexec/git-core $ ln -s /c/opt/git/share/git-core /c/opt/vagrant/embedded/share/git-core ``` ### Gem ドキュメントが不要なら`.gemrc`ファイルに設定します。 ```bash $ echo 'gem: --no-ri --no-rdoc' > ~/.gemrc $ gem install bundler $ gem install rake $ gem install ffi ``` 修正した`msys`のブランチをインストールします。 ```bash $ git clone git://github.com/ogom/knife-solo.git $ cd ./knife-solo/ $ git checkout msys $ bundle && rake install $ cd .. $ rm -rf ./knife-solo/ ``` ### Cookbook まずは簡単なレシピを作りましょう。`Hello, Chef!`が出力されると成功です。 (個人のクックブックは`site-cookbooks`に作ります。) ```bash $ cd ~/vagrant/precise $ knife configure $ knife solo init chef-repo $ cd ./chef-repo/ $ knife cookbook create hello -o ./site-cookbooks $ echo 'log "Hello, Chef!"' >> ./site-cookbooks/hello/recipes/default.rb $ echo '{"run_list":["hello"]}' > ./nodes/precise.json $ knife solo prepare precise $ knife solo cook precise ... * log[Hello, Chef!] action write ``` ここでGitのリポジトリを作ります。 リポジトリは`knife cookbook`コマンドの前に必要です。 ```bash $ git config --global user.email "user@example.com" $ git config --global user.name "user" $ git init $ git add . $ git commit -m 'first commit' ``` KnifeでゲストOSで'git'コマンドを使えるようにします。 (サードパーティーのクックブックは`cookbooks`を使います。) ```bash $ knife cookbook site install git -o ./cookbooks $ echo '{"run_list":["git"]}' > ./nodes/precise.json $ knife solo cook precise Recipe: git::default * package[git] action install - install version 1:1.7.9.5-1 of package git $ ssh precise vagrant@precise32:~$ git --version git version 1.7.9.5 ``` ### Vagrantfile 先ほどのGitの`recipe`をVagrantに設定します。 ```bash $ cd ~/vagrant/precise/ $ cat << __EOS__ > ./Vagrantfile Vagrant.configure("2") do |config| config.vm.box = "precise32" config.vm.provision :chef_solo do |chef| chef.cookbooks_path = "./chef-repo/cookbooks" chef.roles_path = "./chef-repo/roles" chef.data_bags_path = "./chef-repo/data_bags" chef.add_recipe "git" end end __EOS__ ``` マシンを削除してから、マシンを新しく作ります。 ```bash $ vagrant destroy --force $ vagrant up $ ssh precise vagrant@precise32:~$ git --version git version 1.7.9.5 ``` マシンを再作成しても`git`コマンドが使えます。続いてRubyの環境を自動構築します。 # rbenv [rbenv](https://github.com/sstephenson/rbenv)はRubyのバージョン管理ツールです。これを簡単にインストールできます。 Cookbookを管理するには[Berkshelf](http://berkshelf.com/)が便利です。 ```bash $ gem install berkshelf ``` 先ほどのリポジトリに追加します。(rbenvはGitHubのリポジトリを利用します。) ```bash $ cd ~/vagrant/precise/chef-repo $ cat << __EOS__ > ./Berksfile site :opscode cookbook 'git' cookbook 'ruby_build' cookbook 'rbenv', github: "fnichol/chef-rbenv" __EOS__ $ berks install --path ./cookbooks ``` まずはknifeで試します。 ```bash $ cat << __EOS__ > ./nodes/precise.json { "run_list": [ "git", "ruby_build", "rbenv::user" ], "rbenv": { "user_installs": [ { "user": "vagrant", "rubies": ["1.9.3-p429"], "global": "1.9.3-p429", "gems": { "1.9.3-p429": [ {"name": "bundler"} ] } } ] } } __EOS__ $ knife solo prepare precise $ knife solo cook precise $ ssh precise vagrant@precise32:~$ which rbenv /home/vagrant/.rbenv/bin/rbenv vagrant@precise32:~$ ruby -v ruby 1.9.3p429 ``` `/home/vagrant/`パスに`rbenv`がインストールされています。最後にVagrantで試します。 ```bash $ cd ~/vagrant/precise/ $ cat << __EOS__ > ./Vagrantfile Vagrant.configure("2") do |config| config.vm.box = "precise32" config.vm.provision :chef_solo do |chef| chef.cookbooks_path = "./chef-repo/cookbooks" chef.roles_path = "./chef-repo/roles" chef.data_bags_path = "./chef-repo/data_bags" chef.add_recipe "git" chef.add_recipe "ruby_build" chef.add_recipe "rbenv::user" chef.json = { "rbenv" => { "user_installs" => [{ "user" => "vagrant", "rubies" => ["2.0.0-p195"], "global" => "2.0.0-p195", "gems" => { "2.0.0-p195" => [ {"name" => "bundler"} ] } }] } } end end __EOS__ ``` マシンを再起動します。 ```bash $ vagrant reload $ ssh precise vagrant@precise32:~$ rbenv versions system 1.9.3-p429 * 2.0.0-p195 (set by /home/vagrant/.rbenv/version) vagrant@precise32:~$ ruby -v ruby 2.0.0p195 ``` Rubyのバージョンが変わりました。 ***レシピ通りです。*** (キリッ # Actor アクターが多いので今回の関係を図にしました。  # Tree 今回の主なファイルです。(Chefの[用語](http://wiki.opscode.com/display/chef/Glossary)は慣れましょう。) ``` . └─vagrant [ベイグラントの作業エリア] └─precise [マシンの作業エリア] │ Vagrantfile [ベイグラントの設定ファイル] │ └─chef-repo [シェフのリポジトリ] │ Berksfile [バークシャーの設定ファイル] │ solo.rb │ ├─cookbooks [各種クックブック] │ ├─build-essential │ ├─chef_handler │ ├─git │ ├─rbenv [rbenvのクックブック] │ │ │ Berksfile │ │ │ Gemfile │ │ │ Rakefile │ │ │ Vagrantfile │ │ │ │ │ ├─attributes │ │ ├─libraries │ │ ├─providers │ │ ├─recipes [各種レシピ] │ │ │ default.rb │ │ │ system.rb │ │ │ system_install.rb │ │ │ user.rb │ │ │ user_install.rb [ユーザにrbenvをインストールするレシピ] │ │ │ vagrant.rb │ │ │ │ │ ├─resources │ │ ├─templates │ │ └─test │ └─ruby_build ├─data_bags ├─nodes ├─roles └─site-cookbooks └─hello ├─attributes ├─definitions ├─files ├─libraries ├─providers ├─recipes │ default.rb [Hello, Chef!のレシピ] │ ├─resources └─templates ``` # Tips ## Chef Solo リモートマシンでChef Soloを実施することができます。 ```bash vagrant@precise32:~$ git clone git://github.com/opscode/chef-repo.git vagrant@precise32:~$ cd ./chef-repo/ vagrant@precise32:~$ knife configure vagrant@precise32:~$ knife cookbook create hello -o ./cookbooks vagrant@precise32:~$ echo 'log "Hello, Chef!"' >> ./cookbooks/hello/recipes/default.rb vagrant@precise32:~$ echo '{"run_list":["hello"]}' > ./localhost.json vagrant@precise32:~$ echo 'file_cache_path "/tmp/chef-solo"' > ./solo.rb vagrant@precise32:~$ echo 'cookbook_path ["/home/vagrant/chef-repo/cookbooks"]' >> ./solo.rb vagrant@precise32:~$ sudo chef-solo -c ./solo.rb -j ./localhost.json ... INFO: Hello, Chef! ``` Chef Soloで確認してからKnife Soloを使いましょう。 |
|
| 782位 |
|
|||
|
12:12:49 |
(KAYAC Inc. 所属) |
|
モバイル向けに、Canvasでつくったゲームを
Retinaディスプレイ対応をしたときにハマったこと。 ## CanvasのRetina対応は一筋縄にはいかないッ 基本的にCanvasのRetina対応は以下のように、 実際のサイズの2倍のサイズでつくったCanvas要素を、CSSで実際のサイズに縮める。 ```html:HTML <canvas id="sample_canvas" width="640" height="800" style="width: 320px; height: 400px"></canvas> ``` これで、Retinaディスプレイできれいに表示できる。 しかし、このままでは以下のような問題が起きちゃう。 * 2倍のサイズのCanvasに描画を行うため、処理が重くなる * そのため 一部のAndroid端末にて、ブラウザが落ちる、端末が発熱する * 一部のAndroid端末にて、Canvas要素のstyle属性を動的に変更するとブラウザが落ちる そこで、Retina以外の端末向けに、処理の切り分けが必要になる。 Canvas要素は実際のサイズ。Canvas上の座標系をあわせるために、 Canvasのscaleを0.5にする。 ```html:HTML <canvas id="sample_canvas" width="320" height="400"></canvas> ``` ```javascript:JavaScript var canvas = document.getElementById("sample_canvas"); var context = canvas.getContext("2d"); context.scale(0.5,0.5); ``` これで、Canvas内の座標は同じ数値を指定できるようになる。 window.devicePixelRatioの値に応じてCanvasサイズを決める方法もあるが、 上記のstyle属性をいじると落ちるバグがあるため、 iOSのRetinaディスプレイとそれ以外で切り分けるのがおすすめ。 *** ## その他 注意すること ### widthとheightは変更しない 一部のAndroid端末では、Canvas要素のwidthとheightを変更すると、 Canvasの描画が2重になり、clearRect してもクリアされないというバグがある。 一度設定したwidthとheightは変更しないッ(再代入はOK ### drawImage での縮小だと画像がボケる Canvas APIのdrawImageでも、2倍の大きさの画像を実際のサイズに縮小して表示できる。 ```javascript:JavaScript var image = new Image(); image.src = "sample.png"; // 実際のsample.pngのサイズ 200x200 image.addEventListener("load", function() { context.drawImage(image,0,0,100,100); // 半分のサイズを指定 }, false); ``` しかし、これだとCanvas上のピクセルは2倍にならないため Retinaディスプレイでは画像がボケる。 *** ## まとめ 今回は、Android端末にも広く対応を考えた場合に必要な話なので、 対応端末が限られる場合(iOSだけとか)の場合は気にする必要なしッ とはいえ、結構シェアの高い端末でも発生する問題だったりします...! |
|
| 783位 |
|
|||
|
22:50:08 |
(Increments Inc 所属) |
|
Railsアプリのパフォーマンスチューニング用ツール紹介
[forkwell.com Y U SO SLOW // Speaker Deck](https://speakerdeck.com/a_matsuda/forkwell-dot-com-y-u-so-slow) で[松田さん](http://github.com/amatsuda)が紹介していたツールを使ってRailsアプリのボトルネックを調べたので,そのツールの説明. ## [MiniProfiler/Ruby](https://github.com/SamSaffron/MiniProfiler/tree/master/Ruby) ```ruby:Gemfile # developmentでもproductionでも入るようにしておく gem 'rack-mini-profiler' ``` 設定は以下 ```ruby:application_controller.rb class ApplicationController < ActionController::Base before_filter :profile_for_admins # ... def authorize if Rails.env.development? || current_user.try(:staff?) Rack::MiniProfiler.authorize_request end end end ``` 左上に各リクエスト(XHRなど)でかかった時間が表示される.発行されたSQLごとの処理時間も見られる. 参考リンク: [#368 MiniProfiler - RailsCasts](http://railscasts.com/episodes/368-miniprofiler?view=comments) SQLをざっと見てみて,明らかに無駄なSQLを排除していく. かかった時間ごとにSQLをソートしてくれたらいいのに… ## [flyerhzm/bullet](https://github.com/flyerhzm/bullet) [N+1クエリ問題](http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations)や不要なeager loading, counter cacheを使うべきポイントを検出してくれるツール. [flyerhzm/bullet configuration](https://github.com/flyerhzm/bullet#configuration)にある通りに設定すると以下のようなlogを出してくれる.stacktraceも見られるので問題箇所がわかりやすい. ```txt:log/bullet.log N+1 Query detected Item => [:user] Add to your finder: :include => [:user] N+1 Query method call stack N+1 Query method call stack N+1 Query method call stack N+1 Query method call stack /foo/bar/baz.rb:123:in `some_method' /foo/bar/baz.rb:123:in `other_method' ``` 適切に`Foo.all`->`Foo.includes(:bar).all`などとしてN+1クエリを潰せばよい. ## SQL auto explain(v3.2) http://guides.rubyonrails.org/v3.2.13/active_record_querying.html#automatic-explain のように設定すると,実行に時間がかかったSQLをログ出力してくれる. しかしRails 4.0では削除済.(https://github.com/rails/rails/commit/d3688e02ca52c0b72d3092e8498da51e06b7fc58) |
|
| 784位 |
|
|||
|
17:43:41 |
|
|
試しにgithubでリポジトリを作ったらどう削除していいかわからなくなりました
そこでリポジトリを削除する方法を紹介します 1.リポジトリのページで"Setting"を選びます  2."Delete this repository"をクリック  3.警告を読んで、消したいリポジトリの名前を入力します  4."I understand the consequences, delete this repository"をクリック これでリポジトリを削除することができました。 参考文献 https://help.github.com/articles/deleting-a-repo |
|
| 785位 |
|
|||
|
21:46:28 |
(コロプラ 所属) |
|
Appleの[iOS View Controllerプログラミングガイド](https://developer.apple.com/jp/devcenter/ios/library/documentation/ViewControllerPGforiOS.pdf)を読んでいます。 そこで書いていることを把握した範囲で書いていきたいと思います。 まず、役割。 ##UIViewControllerの役割 いわゆるMVCアーキテクチャのCを担当すると書かれています。 そもそも名前が「Controller」ですもんね。 そしてiOSアプリの中では中心的存在になります。 ###UIViewControllerはリソースを管理する 自身のviewプロパティを親として、(必要であれば)subviewを管理します。 また同時に、その画面で必要なリソース(例えばMVCのModel)も管理します。 つまり、「今表示している画面」についてほぼすべての責任を負っているとも言えます。 そして他のいくつかのViewControllerと連携しながらアプリケーションを構築していきます。 ###他のViewControllerと連携する ViewControllerは他のViewControllerと連携します。 並列な関係もあれば、含有される関係もあります。 親子関係を作る一般的な例は「NavigationController」でしょう。 そして並列な関係は「TabViewController」が分かりやすいです。 ####連携にはデリゲートやプロパティを使う 基本的に、あるViewControllerは他のViewControllerから生成され、追加されます。 その際に生成したViewControllerのプロパティに必要な値を設定したり、あるいは生成されたViewController側で発生したイベントをデリゲートによって、生成した側のViewControllerに処理を委譲したりして処理を進めていくことになります。 ###メモリ管理も担う リソースを管理する、ということはメモリ管理も担っていることになります。 ViewControllerは、メモリ不足が生じるとアプリケーションから`didReceiveMemoryWarning:`が呼ばれます。 ここで、可能であればビューの解放などを行い、メモリ不足が解消できるよう処理をします。 ##UIViewControllerのライフサイクル UIViewControllerはライフサイクルを持っています。 初期化による生成から始まり、必要なくなった際の解放までがひとつのサイクルです。 その中でフレームワークによってメモリを効率的に活用できるよう、適切にライフサイクルに関するメソッドが呼ばれ、処理されていきます。 ###表示に関するライフサイクル 最初勘違いしていたんですが、初期化をしただけではまだビュー郡は生成されていません。 これはメモリ管理の上でも重要です。 ではいつ生成されるのか。それは、自身の`view`プロパティにアクセスがあったとき、とされています。 (ただ、おそらく表示時に`view`プロパティを明示的にsubviewに追加する、みたいなことはせず、ViewControllerの生成・表示フローを行うことで適切に呼ばれると思われます) ####ビュー関連のライフサイクル 1. loadView 2. viewDidLoad ビュー関連は以下のふたつの間で生成されます。 よくよく考えれば、メソッド名そのままですね。`loadView`でviewをロードします。 基本的な処理は空の`UIView`を生成し、自身の`view`プロパティに設定します。 もしカスタムビューを設定したい場合はこれをオーバーライドし、ビュー階層を自前で設定した上で、親となるビューを`view`プロパティに設定します。 ※追記 コメントで指摘もらっていますが、ビュー郡を追加する場合は`viewDidLoad`のタイミングがいいです。 ここでの説明はあくまで`loadView`が本来やってくれる処理を理解するためのものですので注意してください。 #####サンプルコード ```objc - (void)loadView { UIView *rootView = [[UIView alloc] initWithFrame:<anySize>]; UIView *otherView = [[UIView alloc] initWithFrame:<otherSize>]; [rootView addSubview:otherView]; self.view = rootView; } ``` loadViewメソッドが呼ばれ、適切にビュー階層が設定されたのち、`viewDidLoad`メソッドが呼ばれます。 ####表示・非表示 上記が、ビューの生成に関するメソッドです。 表示・非表示に関してはこれとは別にサイクルがあり、最初の生成時以外にも、例えば表示していたモーダルビューが消えるタイミングなどでも表示用のメソッドが呼ばれます。 1. viewWillAppear 2. viewDidAppear 3. viewWillDisappear 4. viewDidDisappear ちなみに、「どのように」表示(あるいは非表示)になるかを知るメソッドも用意されています。 | メソッド名 | 意味 | |----------|------| | isMovingFromParentViewController | viewWillDisappear:、viewDidDisappear:メソッド内から呼び出して、消えようとしている理由が親のViewControllerから削除されたかどうかをBOOLで返します。 | | isMovingToParentViewController | viewWillAppear:、viewDidAppear:メソッド内から呼び出して、表示されようとしている理由が親ViewControllerに子として追加されたかどうかをBOOLで返します。 | | isBeingPresented | viewWillAppear:、viewDidAppear:メソッド内から呼び出して、表示されようとしている理由が別のViewControllerから表示されたかどうかをBOOLで返します。 | | isBeingDismissed | viewWillDisapper:、viewDidDisapper:メソッド内で呼び出して、消えようとしている理由が削除されたためであるかをBOOLで返します。 | ##ビューのレイアウト処理 ViewControllerは、レイアウトが変化した際にアプリケーションから通知されます。 通知された際の処理の流れは以下になります。 1. ViewControllerのビューが新しいサイズに変わる 2. auto layout無効であれば、auto resizing maskに従ってビューの大きさや位置が変更される 3. ViewControllerの`viewWillLayoutSubviews`メソッドが呼ばれる 4. ビューの`layoutSubviews`メソッドが呼ばれます。もしビュー階層を構築するためにauto layoutが使われている場合は、以下の手順でレイアウト制約を更新できる 1. ViewControllerの`updateViewConstraints`メソッドが呼び出される 2. `updateViewConstraints`メソッド内で、ビュー階層のビューの`updateConstraints`メソッドを呼び出す 3. レイアウト制約の更新後、新レイアウトを計算しビューの位置を調整する 5. ViewControllerの`viewDidLayoutSubviews`メソッドが呼ばれる ###理想型 Appleのプログラミングガイドには、以下のように記載されています。 > ビュー自身が位置調整に必要な処理をすべて実行し、ViewControllerはまったく関与しなくてよいのが理想です できるだけこうなるよう設計するのが望ましいということでしょう。 ##ViewControllerの表示とトランジションスタイルの選択 ViewControllerを連鎖して画面を表示していく際、表示時のトランジションスタイルを設定することで、アニメーションに変化をつけることができます。 Appleのプログラミングガイドから引用すると以下の設定があります。 | トランジションスタイル | 使用法 | |--------------------|-------| | UIModalTransitionStyleCoverVertical| このスタイルは、ユーザから情報を収集するために現在のワークフ ローに割り込みを行う場合に使用します。ユーザが変更を加える可 能性のあるコンテンツの表示にも使用できます。<br />このトランジションスタイルでは、Content View Controllerが、その View Controllerを明示的に閉じるためのボタンを提供しなければなりません。通常、これは「完了(Done)」ボタンとオプションの「キャンセル(Cancel)」ボタンになります。<br />トランジションスタイルを明示的に設定しない場合は、デフォルトでこのスタイルが使われます。 | | UIModalTransitionStyleFlipHorizontal | このスタイルは、アプリケーションの作業モードを一時的に変更するために使用します。このスタイルがもっともよく使われるのは、「株価(Stocks)」アプリケーションや「天気( Weather)」アプリケーションなどで、頻繁に変更される可能性がある設定を表示する場合です。これらの設定はアプリケーション全体に影響を与えるものもあれば、現在の画面に特有のものもあります。<br />このトランジションスタイルでは、一般に、アプリケーションの通 常の動作モードに戻るための何らかのボタンを提供します。 | | UIModalTransitionStyleCrossDissolve | このスタイルは、デバイスの向きが変化したときに代替のインターフェイスを表示するために使用します。このような場合に、向きの 変化の通知に応答して代替のインターフェイスを表示したり閉じたりするのはアプリケーションの仕事です。<br />メディアベースのアプリケーションでは、このスタイルを使用して メディアコンテンツを表示する画面をフェードインすることもできます。 | ##カスタムContainer View Controllerを作る NavigationViewControllerみたいに、子ViewControllerを伴ってコンテナとして振る舞うViewControllerを自作することもできます。 これについては面白そうなので、別記事にまとめようと思います。 (Gmailアプリなどでちょっと特殊な遷移を実装しているのは、これの仕組みを使っているのかな?) |
|
| 786位 |
|
|||
|
20:51:18 |
(Quipper, Ltd. 所属) |
|
[hub(1)](https://github.com/defunkt/hub) を使うと簡単にできる。
__追記1__: コメント欄より。 * Issue を Pull Request にすると label が外れる(Pull Request には label がつけられないので) * Asssign 状態は変化しない __追記2__: この機能は hub コマンドの master ブランチでは削除されている(おそらく次期リリースで無くなる) GitHub も将来 API (v4) からこの機能を無くすつもりのようだ。[参考](https://github.com/github/hub/commit/4f70dd126f46dec14fc341c97c18efae417743c7) 例: pullreq ブランチから master ブランチに対して Pull Request を送りたいが、その際に既存の Issue#123 にコードを添付したい ``` $ git checkout -b pullreq $ commit; commit; commit; $ hub pull-request -i 123 https://github.com/kyanny/test/pull/123 ``` ## Organization アカウントの中のリモートブランチ同士で上記の操作を行いたいとき Organization アカウントを使っていると remote URL が自分自身のアカウントと違うものになる。このとき hub(1) は me:branch から organization:master へ Pull Request しようとする (自分のアカウントに fork してきたリモートブランチが存在しないといけない) ``` $ hub pull-request -i 123 Error creating pull request: Unprocessable Entity (HTTP 422) field 'head_sha' is missing field 'base_sha' is missing No commits between our-company-organization:master and kyanny:my-ultra-super-excellent-feature-branch ``` これを回避するには `-h` オプションを利用する (organization:branch という表記に注意) 下の例は our-company-organization:master から our-company-organization:my-ultra-super-excellent-feature-branch への差分を Pull Request として Issue#177 に添付する。 ``` $ hub pull-request -i 177 -h our-company-organization:my-ultra-super-excellent-feature-branch https://github.com/our-company-organization/very-very-cool-project/pull/123 ``` これで fork いらずでさくさくと Issue にコードを添付できる。 参考 defunkt/hub https://github.com/defunkt/hub git - How do you attach a new pull request to an existing issue on github? - Stack Overflow http://stackoverflow.com/questions/4528869/how-do-you-attach-a-new-pull-request-to-an-existing-issue-on-github |
|
| 787位 |
|
|||
|
23:56:04 |
|
|
その1が長くなってしまったので、つづきです。 ## Spork を使う テスト実行時の時間を短縮するため、Spork を導入します。 まず、gem をインストールします。 ``` $ vim Gemfile ``` ```ruby:Gemfile group :development, :test do gem 'rspec-rails', '2.10.1' gem 'spork', '0.9.2' end ``` ``` $ bundle Fetching source index for https://rubygems.org/ Using rake (0.9.2.2) Using i18n (0.6.0) Using multi_json (1.3.6) Using activesupport (3.2.6) Using builder (3.0.0) Using activemodel (3.2.6) Using erubis (2.7.0) Using journey (1.0.4) Using rack (1.4.1) Using rack-cache (1.2) Using rack-test (0.6.1) Using hike (1.2.1) Using tilt (1.3.3) Using sprockets (2.1.3) Using actionpack (3.2.6) Using mime-types (1.18) Using polyglot (0.3.3) Using treetop (1.4.10) Using mail (2.4.4) Using actionmailer (3.2.6) Using arel (3.0.2) Using tzinfo (0.3.33) Using activerecord (3.2.6) Using activeresource (3.2.6) Using bundler (1.0.21) Using coffee-script-source (1.3.3) Using execjs (1.4.0) Using coffee-script (2.2.0) Using rack-ssl (1.3.2) Using json (1.7.3) Using rdoc (3.12) Using thor (0.15.2) Using railties (3.2.6) Using coffee-rails (3.2.2) Using diff-lcs (1.1.3) Using jquery-rails (2.0.2) Using mysql2 (0.3.11) Using rails (3.2.6) Using rspec-core (2.10.1) Using rspec-expectations (2.10.0) Using rspec-mocks (2.10.1) Using rspec (2.10.0) Using rspec-rails (2.10.1) Using sass (3.1.19) Using sass-rails (3.2.5) Installing spork (0.9.2) Using uglifier (1.2.4) Your bundle is complete! It was installed into ./vendor/bundle ``` 次に、Spork の初期設定を行います。 ``` $ bundle exec spork --bootstrap ``` spec/spec_helper.rb に Spork 実行用のコードが追記されますので、RSpec のテストが Spork によって実行されるよう修正します。 下記のように、もともとあった RSpec の設定を Spork.prefork ブロックの中に移動します。 ``` $ vim spec/spec_helper.rb ``` ```ruby:spec/spec_helper.rb require 'rubygems' require 'spork' #uncomment the following line to use spork with the debugger #require 'spork/ext/ruby-debug' Spork.prefork do # Loading more in this block will cause your tests to run faster. However, # if you change any configuration or code from libraries loaded here, you'll # need to restart spork for it take effect. # This file is copied to spec/ when you run 'rails generate rspec:install' ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'rspec/autorun' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} RSpec.configure do |config| # ## Mock Framework # # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: # # config.mock_with :mocha # config.mock_with :flexmock # config.mock_with :rr # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true # If true, the base class of anonymous controllers will be inferred # automatically. This will be the default behavior in future versions of # rspec-rails. config.infer_base_class_for_anonymous_controllers = false end end Spork.each_run do # This code will be run each time you run your specs. end ``` Spork が実行できるよう、.rspec に --drb オプションを追記します。 ``` $ vim .rspec ``` ```ruby:.rspec --colour --drb ``` ## Spork を使ってテストを実行 Spork を別コンソールで起動しておきます。 ``` $ rvm 1.9.2-p290@rails32 $ cd ~/projects/blog $ bundle exec spork Using RSpec Preloading Rails environment Loading Spork.prefork block... Spork is ready and listening on 8989! ``` RSpec のテストを実行します。 ``` $ bundle exec rspec spec .............................. Finished in 0.88647 seconds 30 examples, 0 failures ``` Spork にてあらかじめ Rails のコードがロードされるため、テスト実行までの時間が短くなります。 ## Fixture を使う テストデータを使ったテストを書いてみます。 まず、テストデータを用意する前にテストコードを実装します。 ``` $ vim spec/models/article_spec.rb ``` ```ruby:spec/models/article_spec.rb # coding: utf-8 require 'spec_helper' describe Article do # 追記ここから fixtures(:all) describe '.all' do subject { Article.all } it { should have(1).items } end # 追記ここまで describe '.new' do context 'given valid attributes' do subject { Article.new(:title => 'a', :content => 'a') } it { should be_valid } end context 'given null title' do subject { Article.new(:content => 'a') } it { should have(1).errors_on(:title) } end end end ``` テストを実行し、「失敗」することを確認します。 ``` $ bundle exec rspec spec ................F.............. Failures: 1) Article.all Failure/Error: it { should have(1).items } expected 1 items, got 0 # ./spec/models/article_spec.rb:10:in `block (3 levels) in <top (required)>' Finished in 1.3 seconds 31 examples, 1 failure Failed examples: rspec ./spec/models/article_spec.rb:10 # Article.all ``` テストデータを用意します。 ``` $ mkdir spec/fixtures $ vim spec/fixtures/articles.yml ``` ```spec/fixtures/articles.yml article_001: title: 'MyString' content: 'MyText' ``` テストを実行します。 ``` $ bundle exec rspec spec ............................... Finished in 0.82728 seconds 31 examples, 0 failures ``` テスト実行時に Fixture がテスト DB に読み込まれるようになりましたので、結果は「成功」となります。 ## Fixture の代わりに FactoryGirl を使う Fixture Replacement の代表格、FactoryGirl を使ってみます。 テストデータが DB に残ってしまい、意図しないテスト結果となることを防ぐため、DatabaseCleaner も併せてインストールします。 ``` $ vim Gemfile ``` ```ruby:Gemfile group :development, :test do gem 'rspec-rails', '2.10.1' gem 'spork', '0.9.2' gem 'factory_girl_rails', '3.4.0' gem 'database_cleaner', '0.8.0' end ``` ``` $ bundle Fetching source index for https://rubygems.org/ Using rake (0.9.2.2) Using i18n (0.6.0) Using multi_json (1.3.6) Using activesupport (3.2.6) Using builder (3.0.0) Using activemodel (3.2.6) Using erubis (2.7.0) Using journey (1.0.4) Using rack (1.4.1) Using rack-cache (1.2) Using rack-test (0.6.1) Using hike (1.2.1) Using tilt (1.3.3) Using sprockets (2.1.3) Using actionpack (3.2.6) Using mime-types (1.18) Using polyglot (0.3.3) Using treetop (1.4.10) Using mail (2.4.4) Using actionmailer (3.2.6) Using arel (3.0.2) Using tzinfo (0.3.33) Using activerecord (3.2.6) Using activeresource (3.2.6) Using bundler (1.0.21) Using coffee-script-source (1.3.3) Using execjs (1.4.0) Using coffee-script (2.2.0) Using rack-ssl (1.3.2) Using json (1.7.3) Using rdoc (3.12) Using thor (0.15.2) Using railties (3.2.6) Using coffee-rails (3.2.2) Installing database_cleaner (0.8.0) Using diff-lcs (1.1.3) Installing factory_girl (3.4.0) Installing factory_girl_rails (3.4.0) Using jquery-rails (2.0.2) Using mysql2 (0.3.11) Using rails (3.2.6) Using rspec-core (2.10.1) Using rspec-expectations (2.10.0) Using rspec-mocks (2.10.1) Using rspec (2.10.0) Using rspec-rails (2.10.1) Using sass (3.1.19) Using sass-rails (3.2.5) Using spork (0.9.2) Using uglifier (1.2.4) Your bundle is complete! It was installed into ./vendor/bundle ``` ## FactoryGirl および DatabaseCleaner を設定 一旦 Spork を止め、DatabaseCleaner によりテスト実行毎にテスト DB のデータが削除されるようにします。 また、Factory が追加された際に Spork を再起動しなくても読み込めるよう設定します。 ``` $ vim spec/spec_helper.rb ``` ```spec/spec_helper.rb RSpec.configure do |config| # ## Mock Framework # # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: # # config.mock_with :mocha # config.mock_with :flexmock # config.mock_with :rr # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true # If true, the base class of anonymous controllers will be inferred # automatically. This will be the default behavior in future versions of # rspec-rails. config.infer_base_class_for_anonymous_controllers = false # 追記ここから config.before(:suite) do DatabaseCleaner.strategy = :truncation DatabaseCleaner.clean_with(:truncation) end config.before(:each) do DatabaseCleaner.start end config.after(:each) do DatabaseCleaner.clean end # 追記ここまで end Spork.each_run do # This code will be run each time you run your specs. FactoryGirl.reload # 追記 end ``` 再び Spork を起動します。 ``` $ bundle exec spork ``` ## Factory を使用してテストを実行 テストコードでは Fixture を読み込まないようにし、FactoryGirl を使うようにします。 ``` $ vim spec/models/article_spec.rb ``` ```ruby:spec/models/article_spec.rb # coding: utf-8 require 'spec_helper' describe Article do # fixtures(:all) を削除 describe '.all' do # 追記ここから before do FactoryGirl.create(:article) end # 追記ここまで subject { Article.all } it { should have(1).items } end describe '.new' do context 'given valid attributes' do subject { Article.new(:title => 'a', :content => 'a') } it { should be_valid } end context 'given null title' do subject { Article.new(:content => 'a') } it { should have(1).errors_on(:title) } end end end ``` テストを実行し、「失敗」することを確認します。 ``` $ bundle exec rspec spec ................F.............. Failures: 1) Article.all Failure/Error: FactoryGirl.create(:article) ArgumentError: Factory not registered: article # ./spec/models/article_spec.rb:8:in `block (3 levels) in <top (required)>' Finished in 1.14 seconds 31 examples, 1 failure Failed examples: rspec ./spec/models/article_spec.rb:12 # Article.all ``` Factory を自動生成します。 ``` $ bundle exec rails g factory_girl:model article title:string content:text create spec/factories/articles.rb ``` spec/factories 以下に Factory が生成されます。 ```spec/factories/articles.rb # Read about factories at https://github.com/thoughtbot/factory_girl FactoryGirl.define do factory :article do title "MyString" content "MyText" end end ``` 再度、テストを実行します。 ``` $ bundle exec rspec spec ............................... Finished in 1.23 seconds 31 examples, 0 failures ``` Fixture の代わりに FactoryGirl を使ってテストを実行することができました。 ## RSpec 実行時のオプションを指定(おまけ) RSpec では、テスト実行時の見た目を変更することができます。 ``` $ vim .rspec ``` ```ruby:.rspec --colour --drb --format d ``` テストを実行すると、下記のように表示されます。 ``` $ bundle exec rspec spec ArticlesController GET index assigns all articles as @articles GET show assigns the requested article as @article GET new assigns a new article as @article GET edit assigns the requested article as @article POST create with valid params creates a new Article assigns a newly created article as @article redirects to the created article with invalid params assigns a newly created but unsaved article as @article re-renders the 'new' template PUT update with valid params updates the requested article assigns the requested article as @article redirects to the article with invalid params assigns the article as @article re-renders the 'edit' template DELETE destroy destroys the requested article redirects to the articles list Article .all should have 1 items .new given valid attributes should be valid given null title should have 1 errors on :title Articles GET /articles works! (now write some real specs) ArticlesController routing routes to #index routes to #new routes to #show routes to #edit routes to #create routes to #update routes to #destroy articles/edit renders the edit article form articles/index renders a list of articles articles/new renders new article form articles/show renders attributes in <p> Finished in 1.28 seconds 31 examples, 0 failures ``` 以上でチュートリアルは終わりです。 本格的にアプリケーションを開発するには足りないところが多すぎますが、まずはテストを書き始めるきっかけになればと思います。 |
|
| 788位 |
|
|||
|
20:44:48 |
|
|
# CoreBluetooth
## はじめに * 登場人物 | 人物 | 説明 | | ---- | ---- | | サーバー | データを保持しているデバイス | | クライアント | データの読み書きリクエストをするデバイス | * 用語紹介 | 用語 | 説明 | | ---- | ---- | | ペリフェラル | デバイスを発見してもらうための役割を行う | | セントラル | デバイスを発見するための役割を行う | | アドバタイズ | ペリフェラルがデバイスを発見してもらうために行う、自分を表すパケットデータの発信 | | キャラクタリスティクス | サーバーが持つデータを読み書きする単位 | | サービス | サーバーの機能ごとに1つ以上のキャラクタリスティクスを束ねたもの | | パッシブスキャン | アドバタイジングデータを受信するのみ | | アクティブスキャン | クライアントがサーバーにスキャン要求を行い、アドバタイジングデータに載りきらなかったデータを取得する | | ブロードキャスター | 周囲のクライアントに非接続で情報を広報するサーバー(送れるのは20バイト程度までの少量データ) | | サブスクライブ | セントラルがペリフェラルのキャラクタリスティクスの変更を受取れるようにすること | * できる事 * 動的に変わるデータの通信 * データはバイトデータで渡さるため、どんなデータでも渡せる * セキュアなデータでも送れる * ペアリングが必要 * 特に難しい実装しなくともフラグを設定しておくだけでペアリングフローを実施してくれる * UUID を知らなくてもスキャンすること自体は可能 * 電力消費量的に未指定は非推奨 * サービスの中にサービスを入れることも可能 * included service * バックグラウンドでも動作する * デバイスの検知 * ただしアクティブスキャンは出来ない * アドバタイズ発信 * ただしローカルネームは送信されない * 出来ないこと * お互いの位置を性格に把握すること * サーバーへの書き込みに21バイト以上送信すること * アドバタイズパケットには容量制限がある * 28 バイトまで * UUID は 16 バイトあるので1つしか入れられない * 収まらなかったサービス UUID はアクティブスキャンにて返却可能 ## データ * ペリフェラルがブロードキャストする際にキャラクタリスティクスに設定出来るデータ * UUID * 何のキャラクタリスティクスなのかを定義 * プロパティ (論理和で指定可能) * CBCharacteristicPropertyBroadcast * characteristicの値をブロードキャストする * CBCharacteristicPropertyRead * 読み込み可能 * CBCharacteristicPropertyWriteWithoutResponse * レスポンスのない書き込みが可能 * CBCharacteristicPropertyWrite * 書き込み可能 * CBCharacteristicPropertyNotify * レスポンスがないノーティフィケーションを許可 * CBCharacteristicPropertyIndicate * インディケーションを許可 * インディケーションってなんだろう * CBCharacteristicPropertyAuthenticatedSignedWrites * 認証付き書き込みが可能 * CBCharacteristicPropertyExtendedProperties * 拡張プロパティ * CBCharacteristicPropertyNotifyEncryptionRequired * ペアリングしたクライアントのみノーティフィケーション可能 * CBCharacteristicPropertyIndicateEncryptionRequired * ペアリングしたクライアントのみインディケーション可能 * パーミッション (論理和で指定可能) * CBAttributePermissionsReadable * 読み込みのみ * CBAttributePermissionsWriteable * 書き込みのみ * CBAttributePermissionsReadEncryptionRequired * ペアリングしたクライアントのみ読み込み可能 * CBAttributePermissionsWriteEncryptionRequired * ペアリングしたクライアントのみ書き込み可能 * 値 * バイトデータを指定すればその値はキャッシュされ利用される(デリゲートが呼ばれない) * nil を指定すればその値はオンデマンドで要求される(デリゲートが呼ばれる) * セントラルがスキャンしたて受信できるアドバタイジングデータ * Key - Value ペア * CBAdvertisementDataLocalNameKey * ローカルネーム * CBAdvertisementDataTxPowerLevelKey * ペリフェラルの送信電力 (dBm) * CBAdvertisementDataServiceUUIDsKey * パッシブスキャンで得られたペリフェラルのサービスUUIDリスト * CBAdvertisementDataOverflowServiceUUIDsKey * アクティブスキャンで得られたペリフェラルのサービスUUIDリスト * CBAdvertisementDataServiceDataKey * サービスに関連する任意のバイトデータ * 仕様が特定の製造会社に縛られない一般的なもので、Bluetooth SIGが承認したサービスの定義があるならこっちが使われる * CBAdvertisementDataManufacturerDataKey * ペリフェラルの設計製造会社が設定できる任意のバイトデータ * 仕様が製造業者が独自に定義したものならこっちが使われる * CBAdvertisementDataIsConnectable * 接続可能かのフラグ * CBAdvertisementDataSolicitedServiceUUIDsKey * スキャンする時に指定したサービスUUIDリスト * RSSI * 受信信号強度(Received Signal Strength Indicator) * RSSI と CBAdvertisementDataTxPowerLevelKey を用いるとおおよその距離が図れる * 計算式は知らない ## フロー 1. (ペ)アドバタイズ開始 * (セ)ペリフェラルをスキャン * (セ)発見したペリフェラルのアドバタイジング データを確認 * (セ)ペリフェラルへ接続 * (ペ)セントラルと接続 * (セ)接続したペリフェラルに対してサービスの検索 * (セ)サービスに対してキャラクタリスティクスの検索 * (セ)キャラクタリスティクスへのデータの読み書き要求 * (ペ)読み書き要求に対して処理実行 * (セ)キャラクタリスティクスの変更通知設定 * (ペ)変更のあったキャラクタリスティクスのノーティス * (セ)ペリフェラルと切断 ## 実装 ### ペリフェラル ペリフェラルとして動作するには**CBPeripheralManager**のインスタンスを生成し、**CBPeripheralManagerDelegate**を実装する。 #### 初期化 ``` CBPeripheralManager - (id)initWithDelegate:(id<CBPeripheralManagerDelegate>)delegate queue:(dispatch_queue_t)queue; - (id)initWithDelegate:(id<CBPeripheralManagerDelegate>)delegate queue:(dispatch_queue_t)queue options:(NSDictionary *)options ``` **パラメータ** * delegate * 処理移譲先 * queue * 処理を行うキュー * nil を渡すとメインキューで行われるため、処理が重い場合は別キューを指定する * options * ペリフェラルマネージャーのオプション * CBPeripheralManagerOptionShowPowerAlertKey : NSNumber (Boolean) * BlueTooth がオフの場合にユーザーにアラートを表示するかどうか * CBPeripheralManagerOptionRestoreIdentifierKey : NSString * OSがリストアする時にペリフェラルマネージャーを特定するのに利用される #### ステート変更 以下のデリゲートが呼ばれる ``` CBPeripheralManagerDelegate - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral ``` **パラメータ** * peripheral * 該当のペリフェラルマネージャー * state プロパティを見る **state** * CBPeripheralManagerStateUnknown * 初期値 * CBPeripheralManagerStateResetting * システムとの接続が一時的に切れた * CBPeripheralManagerStateUnsupported * BLE のサーバに機能がない * CBPeripheralManagerStateUnauthorized * BLE のサーバになる権限がない * CBPeripheralManagerStatePoweredOff * BLE が OFF になっている * CBPeripheralManagerStatePoweredOn * BLE が ON になっている state プロパティが CBPeripheralManagerStatePoweredOn の時にアドバイタイズを開始する。 開始する際にサービスとキャラクタリスティクスの構築を合わせて行う。 #### キャラクタリスティクスとサービスの構築 #### ##### キャラクタリスティクスを作る ``` CBMutableCharacteristic - (id)initWithType:(CBUUID *)UUID properties:(CBCharacteristicProperties)properties value:(NSData *)value permissions:(CBAttributePermissions)permissions ``` **パラメータ** * UUID * このキャラクタリスティクスを特定する一意なUID * properties * 上述のプロパティ * value * 値のバイトデータ * permissions * 上述のパーミッション ##### サービスを作る ``` CBMutableService - (id)initWithType:(CBUUID *)UUID primary:(BOOL)isPrimary ``` **パラメータ** * UUID * このサービスを特定する一意なUID * isPrimary * プライマリーか否か。プライマリーに設定したものだけがセントラルから見える。 * 非プライマリーとは、補助的なデータをペリフェラルマネージャーに登録すること。正直イミフ。 ##### サービスにキャラクタリスティクスを設定する ``` CBMutableService @property(retain, readwrite) NSArray *characteristics; ``` ##### ペリフェラルマネージャーにサービスを登録する ``` CBPeripheralManager - (void)addService:(CBMutableService *)service ``` **パラメータ** * service * キャラクタリスティクスを登録したサービス #### サービスの追加が完了 以下のデリゲートが呼ばれる ``` CBPeripheralManagerDelegate - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラルマネージャー * service * 追加されたサービス * error * nil じゃない場合はエラー内容 #### アドバタイズ開始 ``` CBPeripheralManager - (void)startAdvertising:(NSDictionary *)advertisementData ``` **パラメータ** * adverdisementData * アドバタイズパケットに含めるデータ * セントラル側で取得できるデータは上述の通りだが、設定出来るのは以下の2種 * CBAdvertisementDataLocalNameKey * CBAdvertisementDataServiceUUIDsKey #### アドバタイズ開始完了 #### 以下のデリゲートが呼ばれる ``` CBPeripheralManagerDelegate - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラルマネージャー * error * nil じゃない場合はエラー内容 #### セントラルからのリクエストに応答 #### ##### 読み込みリクエスト CBMutableCharacteristic の value に nil を設定したものに対して読み込み要求を出すと 以下のデリゲートが呼ばれる ``` CBPeripheralManagerDelegate - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラルマネージャー * request * リクエストオブジェクト リクエストオブジェクトの中にキャラクタリスティクスを特定する UUID が入っているのでそれで判別し、リクエストオブジェクトの value プロパティに値をセットすることでセントラルにデータが渡せる ##### レスポンスの返却 ``` CBPeripheralManager - (void)respondToRequest:(CBATTRequest *)request withResult:(CBATTError)result ``` **パラメータ** * request * リクエストデリゲートで受け取ったリクエストオブジェクト * result * 結果ステータス * CBATTErrorSuccess * CBATTErrorReadNotPermitted * and more #### 書き込みリクエスト ``` CBPeripheralManagerDelegate - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラルマネージャー * requests * リクエストオブジェクトの配列 リクエストオブジェクトの value プロパティを、対応するキャラクタリスティクスの value に設定する。 合わせて、アプリ内のストレージや表示などを変更する必要がある場合は変更を行う。 #### サブスクリバーへの通知 ペリフェラルがプロパティに通知を設定しているキャラクタリスティクスを公開していて、セントラルがそのキャラクタリスティクスに対してサブスクライブした場合には以下のようになる 1. サブスクライブ登録 2. サブスクライブ登録通知 3. データの更新をサブスクリバーに通知 4. 通知の受信 ##### サブスクライブ登録通知 ``` CBPeripheralManagerDelegate - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラルマネージャー * central * サブスクライブしたセントラル * characteristic * サブスクライブ対象 通知するのにセントラルを知っておく必要があるので保持しておく ##### セントラルへの通知 ``` CBPeripheralManager - (BOOL)updateValue:(NSData *)value forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:(NSArray *)centrals ``` **パラメータ** * value * 値 * characteristic * 変更対象のキャラクタリスティクス * centrals * 通知対象セントラル * nil を指定するとサブスクライブしている全セントラルに通知される 通知に成功した場合は返り値は YES となるが、送信キューがいっぱいだったなどの場合には NO が返る。 この場合、再度利用可能になった場合には peripheralManagerIsReadyToUpdateSubscribers: が呼び出される。 つまり、レスポンスが NO だったら再送用の配列に入れておき、peripheralManagerIsReadyToUpdateSubscribers: の中で再度 updateValue を呼び出すようにするとよい。 ##### 通知送信キュー利用可能 ``` CBPeripheralManagerDelegate - (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラルマネージャー 必要であれば、この中で再度 updateValue を実行する ##### サブスクライブ解除通知 ``` CBPeripheralManagerDelegate - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラルマネージャー * central * サブスクライブしたセントラル * characteristic * サブスクライブ対象 保持していたセントラルを破棄するのに用いる #### アドバタイズ停止 ``` CBPeripheralManager - (void)stopAdvertising ``` ### セントラル #### 初期化 ``` CBCentralManager - (id)initWithDelegate:(id<CBCentralManagerDelegate>)delegate queue:(dispatch_queue_t)queue - (id)initWithDelegate:(id<CBCentralManagerDelegate>)delegate queue:(dispatch_queue_t)queue options:(NSDictionary *)options ``` **パラメータ** * delegate * 処理移譲先 * queue * 処理を行うキュー * nil を渡すとメインキューで行われるため、処理が重い場合は別キューを指定する * options * セントラルマネージャーのオプション * CBCentralManagerOptionShowPowerAlertKey : NSNumber (Boolean) * BlueTooth がオフの場合にユーザーにアラートを表示するかどうか * CBCentralManagerOptionRestoreIdentifierKey : NSString * OSがリストアする時にセントラルマネージャーを特定するのに利用される #### ステート変更 以下のデリゲートが呼ばれる ``` CBCentralManagerDelegate - (void)centralManagerDidUpdateState:(CBCentralManager *)central ``` **パラメータ** * central * デリゲートを発生させたセントラルマネージャー * state プロパティを見る **state** * CBCentralManagerStateUnknown * 初期値 * CBCentralManagerStateResetting * システムとの接続が一時的に切れた * CBCentralManagerStateUnsupported * BLE のクライアント機能がない * CBCentralManagerStateUnauthorized * BLE のクライアント機能の権限がない * CBCentralManagerStatePoweredOff * BLE が OFF になっている * CBCentralManagerStatePoweredOn * BLE が ON になっている #### ペリフェラルを探す ``` CBCentralManager - (void)scanForPeripheralsWithServices:(NSArray *)serviceUUIDs options:(NSDictionary *)options ``` **パラメータ** * serviceUUIDs * 必要とするサービスを持つペリフェラルのみをスキャンする場合に指定する * nil を指定するとフィルタリングせずにすべてのペリフェラルをスキャンする * 電力消費量的に nil は非推奨 * options * スキャンオプション * CBCentralManagerScanOptionAllowDuplicatesKey : NSNumber (Boolean) * YES にすると、ペリフェラルからアドバタイズパケットを受信するたびにペリフェラル発見通知がされる * iBeacon のように非接続で電波強度のみを知りたい * ブロードキャストのデータを取得したい * NO にすると、最初の発見時のみ通知される * CBCentralManagerScanOptionSolicitedServiceUUIDsKey : NSArray <CBUUID> * スキャンするサービスのリストを指定する #### ペリフェラルが見つかった 以下のデリゲートが呼ばれる ``` CBCentralManagerDelegate - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI ``` **パラメータ** * central * デリゲートを発生させたセントラルマネージャー * peripheral * 発見したペリフェラル * advertisementData * ペリフェラルの発信していたアドバタイズデータ * 内容については上述の通り * RSSI * ペリフェラルの RSSI 以後、ペリフェラルとの接続や切断が必要になるため、peripheral は保持しておく #### ペリフェラルに移譲先指定 スキャンして発見したペリフェラルに対して接続する際は、そのペリフェラルに対して移譲先を指定しておくことで、詳細に振るまいを指定することが可能となる ``` CBPeripheral @property(weak, nonatomic) id<CBPeripheralDelegate> delegate ``` #### ペリフェラルとの状態を確認 すでに接続済みであるか等を確認する ``` CBPeripheral @property(readonly) CBPeripheralState state ``` **state** * CBPeripheralStateDisconnected * 未接続 * CBPeripheralStateConnecting * 接続中 * CBPeripheralStateConnected * 接続済み #### ペリフェラルと接続 ``` CBCentralManager - (void)connectPeripheral:(CBPeripheral *)peripheral options:(NSDictionary *)options ``` **パラメータ** * peripheral * 接続先ペリフェラル * options * 接続オプション * CBConnectPeripheralOptionNotifyOnConnectionKey * アプリがサスペンドしている時にペリフェラルとの接続に成功した場合にアラートを表示させる * バックグラウンドモードに bluetooth-central を指定していない時に有効 * CBConnectPeripheralOptionNotifyOnDisconnectionKey * アプリがサスペンドしている時にペリフェラルとの接続が切断された場合にアラートを表示させる * バックグラウンドモードに bluetooth-central を指定していない時に有効 * CBConnectPeripheralOptionNotifyOnNotificationKey * アプリがサスペンドしている時にペリフェラルから通知がきた場合にアラートを表示させる * バックグラウンドモードに bluetooth-central を指定していない時に有効 #### ペリフェラルとの接続成功 ペリフェラルとの接続に成功した場合、以下のデリゲートが呼ばれる ``` CBCentralManagerDelegate - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral ``` **パラメータ** * central * デリゲートを発生させたセントラルマネージャー * peripheral * 接続に成功したペリフェラル #### ペリフェラルとの接続失敗 ペリフェラルとの接続に失敗した場合、以下のデリゲートが呼ばれる ``` CBCentralManagerDelegate - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error ``` **パラメータ** * central * デリゲートを発生させたセントラルマネージャー * peripheral * 接続に失敗したペリフェラル * error * nil じゃない場合はエラー内容 #### サービスの検索 ``` CBPeripheral - (void)discoverServices:(NSArray *)serviceUUIDs ``` **パラメータ** * serviceUUIDs * 必要とするサービスの UUID リスト * nil を指定すると、全てのサービスが検索対象となる * 通信時間と処理時間がかかるので、なるべく nil は指定しない #### サービスが見つかった サービスの検索結果、発見できた場合は以下のデリゲートが呼ばれる ``` CBPeripheralDelegate - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラル * error * nil じゃない場合、エラー内容 CBPeripheral の services プロパティに発見したサービスリストが NSArray<CBService> として格納されている #### キャラクタリスティクスの検索 見つかったサービスに対して、必要なデータのあるキャラクタリスティクスを検索する ``` CBPeripheral - (void)discoverCharacteristics:(NSArray *)characteristicUUIDs forService:(CBService *)service ``` **パラメータ** * characteristicUUIDs * 必要とするキャラクタリスティクスのUUID * nil を指定すると全てのキャラクタリスティクスが検索対象となる * 通信時間と処理時間がかかるので、なるべく nil は指定しない * service * 検索対象のサービス #### キャラクタリスティクスが見つかった キャラクタリスティクスが見つかると、以下のデリゲートが呼ばれる ``` CBPeripheralDelegate - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラル * service * キャラクタリスティクスの見つかったサービス * error * nil じゃない場合、エラー内容 #### ペリフェラルへリクエストの送信 ##### 読み込みリクエスト ``` CBPeripheral - (void)readValueForCharacteristic:(CBCharacteristic *)characteristic ``` **パラメータ** * characteristic * 読み込みたいキャラクタリスティクス ##### 読み込みレスポンス キャラクタリスティクスに読み込みリクエストを送ると、以下のデリゲートが呼ばれる ``` CBPeripheralDelegate - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラル * characteristic * 読み込み対象のキャラクタリスティクス * error * nil じゃない場合、エラー内容 characteristic の value プロパティに NSData 形式のバイトデータが格納されている ただし、キャラクタリスティクスのパーミッションで指定出来るものに上げたとおり、読み込み不可のものもあるため事前に確認しておくとよい ##### 書き込みリクエスト ``` CBPeripheral - (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type ``` **パラメータ** * data * 書き込む内容(20バイトまで) * characteristic * 書き込みたいキャラクタリスティクス * type * 書き込みタイプ * CBCharacteristicWriteWithResponse * レスポンスが欲しい * CBCharacteristicWriteWithoutResponse * レスポンスはいらない ##### 書き込みレスポンス 書き込み要求時に type として CBCharacteristicWriteWithResponse を指定した場合、以下のデリゲートが呼ばれる ``` CBPeripheralDelegate - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラル * characteristic * 書き込んだキャラクタリスティクス * error * nil じゃない場合は、エラー内容 ##### キャラクタリスティクスの更新通知設定 キャラクタリスティクスのパーミッションでノーティフィケーションが可能である場合、以下を設定することでペリフェラル側で更新が発生するたびにそれを知ることができる ``` CBPeripheral - (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic ``` **パラメータ** * enabled * YES : 通知を受取る * NO : 通知を受け取らない * characteristic * 更新通知を設定したいキャラクタリスティクス ##### キャラクタリスティクスの更新通知取得 セントラルで setNotifyValue を行ったキャラクタリスティクスに対して、ペリフェラルで updateValue:forCharacteristic:onSubscribedCentrals: が実行されると、以下のデリゲートが呼ばれる ``` CBPeripheralDelegate - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error ``` **パラメータ** * peripheral * デリゲートを発生させたペリフェラル * characteristic * 更新が発生したキャラクタリスティクス * error * nil じゃない場合は、エラー内容 #### セントラルマネージャーで保持しているペリフェラルリストの取得 ``` CBCentralManager - (NSArray *)retrievePeripheralsWithIdentifiers:(NSArray *)identifiers - (NSArray *)retrieveConnectedPeripheralsWithServices:(NSArray *)serviceUUIDs ``` identifiers で指定したペリフェラルリストを取得 serviceUUIDs を持っている接続済みペリフェラルリスト取得 #### ペリフェラルと切断 ペリフェラルとの通信が終わったら切断をする ``` CBCentralManager - (void)cancelPeripheralConnection:(CBPeripheral *)peripheral ``` **パラメータ** * peripheral * 切断したいペリフェラル #### ペリフェラルと切断完了 ペリフェラルとの切断が完了すると、以下のデリゲートが呼ばれる ``` CBCentralManagerDelegate - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error ``` **パラメータ** * central * デリゲートを発生させたセントラルマネージャー * peripheral * 切断したペリフェラル * error * nil じゃない場合、エラー内容 #### スキャン停止 ペリフェラルのスキャンを停止 ``` CBCentralManager - (void)stopScan ``` ## 最後に せっかくの低消費電力で動作する BLE を利用しているので、アプリで大量に電力消費するようなことのないよう充分に気をつけてコーディングすることが重要である。 iOS 同士の P2P に利用する目的であれば MultipeerConnectivity.framework を利用するのがよい。 |
|
| 789位 |
|
|||
|
14:36:57 |
(株式会社ずんシステム 所属) |
|
# クレデンシャルの送信
クロスオリジンのAJAXリクエストでクレデンシャル(クッキーの送信またはBASIC認証)を必要とする場合は、それを許可するオプションをフロント側Javascriptで付けておく必要があります。デフォルトではCORSリクエストでクッキーは送信されませんし、BASIC認証は送れません。 ## Fetch API の場合 ```js: fetch(url, { mode: 'cors', //クロスオリジンリクエストをするのでCORSモードにする credentials: 'include' //クレデンシャルを含める指定 }) ``` ## jQueryの場合 ```js: $.ajax({ url: "http://example.jp/api/user", xhrFields: { withCredentials: true } }).success(function(res){ console.log(res); }) ``` 毎回付けるのが面倒なら↓こうしておいてもよいです。 ```js: $.ajaxSetup({xhrFields:{withCredentials:true}}); ``` ## AngularJS(1.x系)の場合 AngularJSの場合はこんな感じか。 ```js: yourapp.config(function($httpProvider) { $httpProvider.defaults.withCredentials = true; }); ``` ## XMLHttpRequest を直接使う場合 ```js: var xhr = new XMLHttpRequest(); xhr.withCredentials = true; ``` # Access-Control-Allow-Origin: * は駄目 クレデンシャルを必要とするリクエストの場合、Access-Control-Allow-Originにワイルドカードは使えまえん。キチンと許可オリジンを指定しましょう。 そうは言ってもワイルドカード的に受け付けたい場合はあるので手軽に.htaccessで何とかする設定メモ。これは送られてきたOriginを許可オリジンとしてオウム返しする設定です。 ```text:.htaccess RewriteCond %{HTTP:Origin} (.+) RewriteRule . - [E=CORS_ORIGIN:%1] Header set Access-Control-Allow-Origin %{CORS_ORIGIN}e env=CORS_ORIGIN ``` CORSアクセスを許可するOriginを限定したいなら↓こんな風に書いてやればOK ```text:.htaccess RewriteCond %{HTTP:Origin} (https?://([a-zA-Z0-9\-\.]+\.)?(example\.com|example\.jp)(:[0-9]+)?)$ RewriteRule . - [E=CORS_ORIGIN:%1] Header set Access-Control-Allow-Origin %{CORS_ORIGIN}e env=CORS_ORIGIN ``` # Access-Control-Allow-Credentials: true も付けましょう クレデンシャルを必要とする場合はこのヘッダがないとブラウザはレスポンスを捨ててしまうのでセットで付けておきましょう。 ```text:.htaccess Header set Access-Control-Allow-Credentials true ``` # その他基本のキ よく使うヘッダのコピペ用設定。アプリのフレームワークによっては X-Csrftoken ヘッダも通しておくなど。 ベタ書き版 ```text:.htaccess Header set Access-Control-Allow-Methods "GET, PUT, POST, DELETE, HEAD, OPTIONS" Header set Access-Control-Allow-Headers "X-Requested-With, Origin, X-Csrftoken, Content-Type, Accept" ``` ## クライアントのリクエストに合わせて許可する版 ```httpd.conf # CORS対応 ## Originの許可(オウム返し) RewriteCond %{HTTP:Origin} (https?://([a-z0-9\-\.]+\.)?example\.com)$ RewriteRule . - [E=CORS_ORIGIN:%1] Header set Access-Control-Allow-Origin %{CORS_ORIGIN}e env=CORS_ORIGIN ## リクエストメソッドの利用許可(オウム返し) RewriteCond %{ENV:CORS_ORIGIN} ^http RewriteCond %{HTTP:Access-Control-Request-Method} ^([A-Z]+)$ RewriteRule . - [E=CORS_METHOD:%1,E=CORS_PREFLIGHT:1] Header set Access-Control-Allow-Methods "GET, POST, HEAD, OPTIONS, %{CORS_METHOD}e" env=CORS_METHOD ## リクエストヘッダの利用許可(オウム返し) RewriteCond %{ENV:CORS_ORIGIN} ^http RewriteCond %{HTTP:Access-Control-Request-Headers} "^([a-zA-Z0-9-]+(, ?[a-zA-Z0-9-]+)*)$" RewriteRule . - [E=CORS_HEADERS:%1,E=CORS_PREFLIGHT:1] Header set Access-Control-Allow-Headers "%{CORS_HEADERS}e" env=CORS_HEADERS ## レスポンスヘッダの利用許可 # Header set Access-Control-Expose-Headers "* or X-Hoge, X-Fuga" env=CORS_ORIGIN ## クレデンシャルの送信許可(クッキー,Authorizationヘッダ) # Header set Access-Control-Allow-Credentials true env=CORS_ORIGIN ## CORS許可のキャッシュ(プリフライトリクエストを暫く投げない許可) Header set Access-Control-Max-Age "3600" env=CORS_PREFLIGHT |
|
| 790位 |
|
|||
|
03:26:27 |
(Wantedly, Inc. 所属) |
|
## 事前情報 m3.xlargeインスタンス > 15GB メモリ > 13 ECU = 3.25ECU(2.6GHz) * 仮想4コア DBサイズ > 3.38 GB ## 設定値 ```/etc/postgresql/9.1/main/postgresql.conf max_connections = 100 shared_buffers = 3GB # 実メモリの20% # ここらへんのはとりあえずこのくらいで。小さすぎる分にはlog_temp_filesの設定で書き出されるので後で気づいて調整できる。 temp_buffers = 32MB work_mem = 32MB maintenance_work_mem = 128MB wal_buffers = 16MB # 迷いどころもなく16MB checkpoint_segments = 16 # HDへのフラッシュ回数を減らす effective_cache_size = 7GB # planerのため。実メモリの半分ぐらいがいいらしい ``` ```/etc/sysctl.conf # 7GB max shared mamory kernel.shmall=1835008 kernel.shmmax=7516192768 ``` して`sudo sysctl -p`, `sudo /etc/init.d/postgresql restart` メモ - shared_buffersの経験則は実メモリの20%-30%、もしくは頻繁にアクセスするテーブルのデータが載る程度 - shared_bufferを50%以上にするとカーネルのキャッシュが溢れスワップする - work_memの最大は`(実メモリ-shared_buffers)/max_connections`。そうでないと確実にスワップする - work_memの経験は`実メモリ/max_connections/[4-16]` - log_temp_filesの設定により、一時ファイルを使った場合Logに書き出すことが出来る - wal_buffersは-1でshared_bufferの1/32になる。 - 通常のWALセグメントの大きさである16MBを超えることはないため、大きすぎる場合は設定しなおしたほうが良い。 ## ログ書き出しの設定 http://www.postgresql.jp/document/9.2/html/runtime-config-logging.html#RUNTIME-CONFIG-LOGGING-WHERE ```/etc/postgresql/9.1/main/postgresql.conf #log_destination = 'stderr' logging_collector = on log_rotation_size = 1GB #log_min_messages = warning log_min_error_statement = warning # log_min_messages似合わせる log_line_prefix='[%t][%p][%c-%l][%x][%e]%q (%u, %d, %r, %a) ' #とりあえず全部の情報を出しておけば良いのではという発想 log_min_duration_statement = 500 # 0.5秒以上かかった問い合わせを記録 log_temp_files = 1MB # 1MB以上の一時ファイルを作ったらログに書き出す ``` ## 残存プロセスの除去 AP側が落ちたり、APとのNWが断絶された際、APからの応答をずっと待ってしまうDB側のプロセスが残ってしまう。こうなるとConnection数がMaxに達したり、ロングトランザクション化したりする問題が起こる。タイムアウトを短めにしてこれを防ぐ。 ``` tcp_keepalives_idle = 60 # keep alive パケット送出までの待機時間(デフォルト7200秒) tcp_keepalives_interval = 5 # KeepAliveパケット送出に応答がなかった場合の再送間隔(デフォルト75秒) tcp_keepalives_count = 6 # KeepAliveパケット送出回数の上限(デフォルト9) ``` この設定では1分半で残存プロセスを除去 ## 参考: - http://www.mindcircus.jp/archives/2734 - http://itpro.nikkeibp.co.jp/article/COLUMN/20090703/333173/ - http://www.postgresql.jp/events/event_sozai/Summer_seminar2011_Operation_technique.pdf |
|
| 791位 |
|
|||
|
00:36:05 |
(Picos LLC. 所属) |
|
**追記:**
良い子のみんなはこんなマクロを自分で定義する前に[UIKit Function - String Conversions](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIKitFunctionReference/index.html#//apple_ref/doc/uid/TP40006894-CH3-SW34)とか[CocoaLumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack)とか使うんだよ!!!!! --- ログは出したいけどリリースビルド時には出したくないという時に使う各種ログマクロです。 個人的に使っているもののまとめです。(オープンソース見ているといろいろな種類見かけますね) プロジェクトを作るとテンプレで出来る「アプリ名-Prefix.pch」というヘッダに書くとどのソースでも使えるようになるので便利です。 ```objectivec:xxx-Prefix.pch #ifdef DEBUG #define LOG(...) NSLog(__VA_ARGS__) #define LOG_PRINTF(FORMAT, ...) printf("%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]); #define LOG_METHOD NSLog(@"%s", __func__) #define LOG_METHOD_AND_ABORT LOG_METHOD; abort() #else #define LOG(...) #define LOG_PRINTF(FORMAT, ...) #define LOG_METHOD #define LOG_METHOD_AND_ABORT #endif #ifdef DEBUG #define LOG_POINT(p) NSLog(@"%f, %f", p.x, p.y) #define LOG_SIZE(p) NSLog(@"%f, %f", p.width, p.height) #define LOG_RECT(p) NSLog(@"%f, %f - %f, %f", p.origin.x, p.origin.y, p.size.width, p.size.height) #else #define LOG_POINT(p) #define LOG_SIZE(p) #define LOG_RECT(p) #endif #if DEBUG @interface UIView (DebugPrivate) - (NSString*)recursiveDescription; @end #endif ``` ```objectivec:使用例 /* 普通のNSLogと同じように使えます */ LOG(@"debug log"); // 2013-02-21 00:20:53.025 Test[1235:c07] debug log <UIView: 0x719b9d0; frame = (0 20; 320 548); autoresize = RM+BM; layer = <CALayer: 0x719b140>> /* 先頭のタイムスタンプ等をなしに */ LOG_PRINTF(@"debug log %@", self.view); // debug log <UIView: 0x719b9d0; frame = (0 20; 320 548); autoresize = RM+BM; layer = <CALayer: 0x719b140>> /* クラスとメソッド名を表示 */ LOG_METHOD; // -[ViewController viewDidLoad] // 下記でもセレクタはわかりますが__func__だとクラス名も出るので__func__使ってます NSLog(@"%@", NSStringFromSelector(_cmd)); // viewDidLoad /* LOG_METHOD_AND_ABORTのところでクラッシュします。 デバック中に明示的に止めたい時に使ってます */ switch (x) { case 0: break; case 1: break; default: LOG_METHOD_AND_ABORT; break; } /* CGPointの表示 */ LOG_POINT(self.view.center); // 160.000000, 294.000000 /* CGSizeの表示 */ LOG_SIZE(self.view.frame.size); // 320.000000, 548.000000 /* CGRectの表示 */ LOG_RECT(self.view.frame); // 0.000000, 20.000000 - 320.000000, 548.000000 /* 番外: マクロではないですが、有名なプライベートメソッドでそのviewとsubviews以下を全て表示出来ます プライベートメソッドを呼び出すと審査が通らないので、DEBUG時にのみ有効にして 仮に外し忘れていてもリリースビルドでは通らないようにしています */ NSLog(@"%@", [self.view.window recursiveDescription]); /* <UIWindow: 0x7654d10; frame = (0 0; 320 568); autoresize = W+H; layer = <UIWindowLayer: 0x76558c0>> | <UIView: 0x7673620; frame = (0 20; 320 548); autoresize = RM+BM; layer = <CALayer: 0x7672d90>> | | <UILabel: 0x7673760; frame = (139 156; 42 21); text = 'Label'; clipsToBounds = YES; opaque = NO; autoresize = TM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x7672ca0>> | | <UIRoundedRectButton: 0x7675050; frame = (123.5 252; 73 44); opaque = NO; autoresize = TM+BM; layer = <CALayer: 0x7675190>> | | | <UIGroupTableViewCellBackground: 0x76759d0; frame = (0 0; 73 44); userInteractionEnabled = NO; layer = <CALayer: 0x7675aa0>> | | | <UIImageView: 0x7676f80; frame = (1 1; 71 43); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7677250>> - (null) | | | <UIButtonLabel: 0x76764d0; frame = (12 12; 49 19); text = 'Button'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x76765c0>> */ ``` |
|
| 792位 |
|
|||
|
19:12:59 |
|
|
# 概要
初めてsed触ったから、覚えたことまとめる # 内容 ## 基本 文法 ``` cat before.txt | sed -e 's/before/after/gi' sed -e 's/before/after/gi' begore.txt ``` beforeをafterに変換。gは全部変換。iは大文字小文字無視。-e はいくつでも繋げられる。 ## マッチした文字列の利用 beforeの部分で、`\(`と`\)`でくくると、afterの部分で`\1`で取り出せる。beforeで複数`\(\)`を使うと、afterで`\2\3\4`な感じで取り出せる。 つまり ```bash $ echo 'abcdefghijklmn' | sed -e "s/.*\(d.*g\).*\(l.*$\)/\1\n\2/g" defg lmn ``` こんな感じ。例がおそまつなのは気にしない。 ## はまったところ ### `+`記号 sedでは`+`ではなく`\+`としないと、直前の文字1個以上の意味で使えない。 他にもバックスラッシュが必要なメタ文字が多い。 参考:[正規表現メモ](http://www.kt.rim.or.jp/~kbk/regex/regex.html#SED) [追記] 手元のMacのBSDのsedだと`\+`でメタ文字として動いてくれなかった。みんなGNUのsed使えばいいと思う。 ### 最短一致と最長一致 sedの正規表現は最長一致なので、最短一致させる時は [^] を使ってなんとかする。 参考:[Tech Tips: sedの最短一致](http://techtipshoge.blogspot.jp/2011/10/sed.html) ### シングルクォーテーションとダブルクォーテーション -eの引数をシングルクォーテーションでくくって、その中でシングルクォーテーションを使おうとして、シングルクォーテーションをバックスラッシュでエスケープしても動かなかった。 ``` cat before.txt | sed -e 's/.*\'\([^\']\)\'.*/\1/g' ``` -eの引数をダブルクォーテーションにしたら解決した。 ``` cat before.txt | sed -e "s/.*'\([^']\)'.*/\1/g" ``` あとで調べたら、`\'`はバッファの末尾、という意味のメタ文字だった。 参考:[正規表現メモ](http://www.kt.rim.or.jp/~kbk/regex/regex.html#SED) ## 参考 * [sed - ストリーミングエディタ](http://www.ksknet.net/linuxrz/sed.html) * [シェル・スクリプト・リファレンス - 【 文字列を置換する「sed」 】:ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20060228/231161/) * [正規表現メモ](http://www.kt.rim.or.jp/~kbk/regex/regex.html#SED) * [逆引きUNIXコマンド/sedを使ってスペースの文字列操作を行う - Linuxと過ごす](http://linux.just4fun.biz/%E9%80%86%E5%BC%95%E3%81%8DUNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89/sed%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E3%81%AE%E6%96%87%E5%AD%97%E5%88%97%E6%93%8D%E4%BD%9C%E3%82%92%E8%A1%8C%E3%81%86.html) # 感想 色々調べたり読んだり試したりしたけど、まとめるとたいしたことないなあ。 しかも今まとめたのsedの機能のごく一部っぽいから、その内また調べたい。 |
|
| 793位 |
|
|||
|
15:02:16 |
(Zalando SE 所属) |
|
`Gruntfile.js` は常々長すぎると思っていました。複数ファイルに分割しようが長いものは長いです。 最近、後発の [gulp](http://gulpjs.com/) というものを見つけて使ってみていますが、いい感じです。 **設定ファイルが短く書ける上に、速いです。** 先日 [Grunt 入門の記事「Web デザイナーさん向け Grunt を使った コーディング作業の効率化、はじめの一歩」](http://hyper-text.org/archives/2014/01/grunt_quick_start_for_web_designer.shtml)を読んで、例の Gruntfile.js と同等の内容を gulp で書いたらどうなるかなと思って書いてみました。 - [サンプルファイル含めたプロジェクト全体](https://github.com/shuhei/gulp-quick-start) - [gulpfile.js だけ](https://github.com/shuhei/gulp-quick-start/blob/master/gulpfile.js) 67 行から 29 行に。約半減です。また、短くなっただけではなく、処理の流れがわかりやすくなっていると思います。 ```gulpfile.js var gulp = require('gulp'); var concat = require('gulp-concat'); var prefixer = require('gulp-autoprefixer'); var minify = require('gulp-minify-css'); var imagemin = require('gulp-imagemin'); var plumber = require('gulp-plumber'); gulp.task('css', function () { gulp.src('htdocs/css/**/*.css') .pipe(plumber()) .pipe(prefixer('last 2 version')) .pipe(concat('style.min.css')) .pipe(minify()) .pipe(gulp.dest('htdocs/dist/css')); }); gulp.task('img', function () { gulp.src('htdocs/img/**/*') .pipe(plumber()) .pipe(imagemin()) .pipe(gulp.dest('htdocs/dist/img')); }); gulp.task('watch', function () { gulp.watch('htdocs/css/**/*', ['css']); gulp.watch('htdocs/img/**/*', ['img']); }); gulp.task('default', ['css', 'img']); ``` ## そもそも gulp って? gulp は [Node.js の stream](http://nodejs.org/api/stream.html) を使って Grunt の行っているようなことをもっとうまくやろうというものです。以下のような特徴があります。 - **設定ファイルの可読性**: 例えば CSS に対して何の処理をするのか一目瞭然です。 - **スピード**: タスクはできる限り並列に実行されるので速いです。中間ファイルも生成されません。 - **拡張性**: プラグインが対応していないことをやりたければ、自分で少しプログラムを書くだけすみます。 ## そもそも stream って? ざっくり言うと、データの流れを抽象化するためのインターフェイスだそうです。 ここでは詳しく説明しませんが gulp の開発者達は [substack's stream handbook](https://github.com/substack/stream-handbook) を読めと言っています。 ## gulp の stream は? 通常 gulp のタスクは、ざっくり言うと以下のような流れです。 1. `gulp.src()` でファイルの stream を作ります。 2. 各プラグインが stream の中を流れるファイルを処理して、また stream に流します。 3. `gulp.dest()` で stream の中を流れてきたファイルを保存します。 ここで注意すべきは、gulp の扱う stream の中を流れるのは、ファイル一個に対応するオブジェクトだということです。具体的には [vinyl という npm パッケージ](https://github.com/wearefractal/vinyl) で決まっている `File` オブジェクトです。ざっくり言うと、ファイルのパスとファイルの中身の情報を持っています。 例えば `htdocs/css/**/*.css` にマッチする CSS ファイルが 3 つあったとすると、`gulp.src('htdocs/css/**/*.css')` は `File` オブジェクトを 3 つ流す stream を返します。 こうやってファイルに対応する object を stream に流すことで、複数のファイルをどんどん処理できるわけですね。 ちなみに 2. のところを厳密に言うと、各プラグインは、入ってくる File オブジェクトを変換して出す stream を作ります。いわゆる [Transform Stream](http://nodejs.org/api/stream.html#stream_class_stream_transform) ですね。 ## プラグインは充実してるの? あまりいろいろ試していないのですが、思いつくようなものはだいたいあるんじゃないでしょうか。 - [オフィシャルサイトのプラグインページ](http://gratimax.github.io/search-gulp-plugins/) - あとは普通に [npm](https://npmjs.org/) で "gulp hoge" と検索 基本的には、もともとある npm パッケージをラップするだけなので、なければ誰かがすぐ作ってしまいます。 いざとなれば [Grunt タスクを gulp から使うためのプラグイン](https://github.com/gratimax/gulp-grunt) もあるようです。 ## 使ってみよう 例を見ればすぐわかるシンプルさなので、ぜひためしに使ってみてください。 1. まだなら Node.js をインストール。 2. `npm install -g gulp` 3. プロジェクトに `package.json` がなければ `npm init` 4. `npm install -save-dev gulp gulp-hoge` で gulp と必要なプラグインを入れる。 5. `gulpfile.js` を書く。 6. `gulp` ほしい機能のプラグインも、"gulp hoge" とかで検索すれば大抵すぐ出てきます。 ## 参考資料 - [公式サイト](http://gulpjs.com/) - [GitHub](https://github.com/gulpjs/gulp) - [作者による紹介スライド](http://slid.es/contra/gulp) - [Stream Handbook](https://github.com/substack/stream-handbook) |
|
| 794位 |
|
|||
|
14:05:29 |
|
|
curlでHTTPステータスコードだけを取得する方法をいつも忘れるのでメモ。
```shell-session $ curl -LI mazgi.com -o /dev/null -w '%{http_code}\n' -s 200 ``` こんな感じで `-w` (write out)で `http_code` を指定しつつ、他は `-o` (output)で `/dev/null` にでも捨てる。 そして `-s` (silent)を指定して「進捗どうですか?」を表示しないようにする。 |
|
| 795位 |
|
|||
|
21:28:00 |
|
|
JAXB(Java Architecture for XML Binding)の使い方メモ。
JAXB とは、 XML と Java オブジェクトを相互変換するための API 仕様のこと。 Java SE6 からは標準ライブラリに組み込まれているので、特に jar を追加することなく使える。 #オブジェクト → XML ```java:変換元Javaクラス public class Hoge { private int id; private String value; // Getter, Setter は省略 } ``` ```java:変換処理 import javax.xml.bind.JAXB; public class JAXBSample { public static void main(String[] args) { Hoge hoge = new Hoge(); hoge.setId(10); hoge.setValue("hoge"); JAXB.marshal(hoge, System.out); } } ``` ```xml:変換結果 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <hoge> <id>10</id> <value>hoge</value> </hoge> ``` #XML → オブジェクト ```java:変換処理 public static void main(String[] args) { String xml = "<?xml version=\"1.0\"?>" + "<hoge>" + " <id>20</id>" + " <value>hogehoge</value>" + "</hoge>"; Hoge hoge = JAXB.unmarshal(new StringReader(xml), Hoge.class); System.out.println("id=" + hoge.getId() + ", value=" + hoge.getValue()); } ``` ```text:出力結果 id=20, value=hogehoge ``` #アノテーションを使って調整する ##フィールドをタグの属性と紐付ける ```java:XmlAttributeアノテーションで属性と紐付ける import javax.xml.bind.annotation.XmlAttribute; public class Hoge { private int id; private String value; @XmlAttribute public int getId() { return id; } // 以下 Getter, Setter 省略 } ``` ```xml:XML変換結果 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <hoge id="10"> <value>hoge</value> </hoge> ``` `XmlAttribute` アノテーションを、属性と紐付けたいフィールドの __getterメソッド__ に付与する。 ##タグと属性の名前を任意の値に変更する ```java:各アノテーションのnameで名前を指定する import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; public class Hoge { private int id; private String value; @XmlAttribute(name="hoge-id") public int getId() { return id; } @XmlElement(name="hoge-value") public String getValue() { return value; } // Setter メソッドは省略 } ``` ```xml:XML変換結果 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <hoge hoge-id="10"> <hoge-value>hoge</hoge-value> </hoge> ``` タグ名の場合は `XmlElement` アノテーションの `name` に、 属性名の場合は `XmlAttribute` アノテーションの `name` にそれぞれ設定したい名前を指定する。 ## 要素の順序の決定 順序を決定するためには、 `XmlType` アノテーションの `propOrder` を付与する。 ```java:順序の決定 @XmlType(propOrder={"one", "two", "three"}) public class Hoge { private String one = "1"; private String three = "3"; private String two = "2"; // Getter, Setter は省略 } ``` ```xml:XML変換結果(デフォルト) <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <hoge> <one>1</one> <two>2</two> <three>3</three> </hoge> ``` ※ [toenobu](http://qiita.com/toenobu) さんに追記していただきました。ありがとうございます。 ##ルートタグの名前を変更する ```java:XmlRootElementアノテーションでルートタグの名前を変更する import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name="hoge-tag") public class Hoge { private int id; private String value; // Getter, Setter 省略 } ``` ```xml:XML変換結果 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <hoge-tag> <id>10</id> <value>hoge</value> </hoge-tag> ``` XML のルートタグとなるクラスに `XmlRootElement` アノテーションを付与し、 `name` に設定したいタグの名前を指定する。 ##Listの変換をいい感じにする `List<String>` のような `List` のフィールドを XML に変換すると、デフォルトだと以下のようになる。 ```java:Listフィールドを持つクラス public class Hoge { private List<String> listValue; // Getter, Setter は省略 } ``` ```xml:XML変換結果(デフォルト) <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <hoge> <listValue>aaa</listValue> <listValue>bbb</listValue> <listValue>ccc</listValue> </hoge> ``` `List` の中身が同じ高さで出力されて格好悪い。 できれば以下のようにしたい。 ```xml:XML変換結果(希望形) <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <hoge> <list> <value>aaa</value> <value>bbb</value> <value>ccc</value> </list> </hoge> ``` 上記のように変換するためには、次のように Java クラスでアノテーションを設定する。 ```java:希望形で変換させるためのアノテーション設定 import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; public class Hoge { private List<String> listValue; @XmlElementWrapper(name="list") @XmlElement(name="value") public List<String> getListValue() { return listValue; } // Setter 省略 } ``` `XmlElementWrapper` アノテーションの `name` に `List` の各要素タグをラップするタグの名前を、 `XmlElement` アノテーションの `name` に要素タグの名前を設定する。 ##特定のフィールドを変換対象外にする ```java:XmlTransientアノテーションで対象外を指定する import javax.xml.bind.annotation.XmlTransient; public class Hoge { private int id; private String value; @XmlTransient public String getValue() { return value; } // Getter, Setter 省略 } ``` ```xml:XML変換結果 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <hoge> <id>10</id> </hoge> ``` `XmlTransient` アノテーションを付与したフィールドは XML 変換対象外になる。 ##enum をマッピングする ```java:MyClass package jaxb; public class MyClass { private MyEnum hoge = MyEnum.HOGE; private MyEnum fuga = MyEnum.FUGA; private MyEnum piyo = MyEnum.PIYO; // getter, setter 省略 } ``` ```java:MyEnum package jaxb; import javax.xml.bind.annotation.XmlEnumValue; public enum MyEnum { HOGE, @XmlEnumValue("fuga") FUGA, @XmlEnumValue("Piyo") PIYO, } ``` ```xml:XML変換結果 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <myClass> <fuga>fuga</fuga> <hoge>HOGE</hoge> <piyo>Piyo</piyo> </myClass> ``` enum は、デフォルトでは列挙子の名前を文字列にした値とマッピングされる(HOGE)。 任意の値とマッピングさせたい場合は、 enum の列挙子に対して `@XmlEnumValue` を付与し、 value にマッピングしたい文字列を指定する(fuga, Piyo)。 ##1つのインターフェースフィールドに対して、複数の実装クラスをマッピングする ```java:MyInterface.java package jaxb; public interface MyInterface { void hello(); } ``` ```java:Hoge.java package jaxb; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlElements; public class Hoge { private List<MyInterface> list; private MyInterface any; @XmlElementWrapper(name="list") @XmlElements({ @XmlElement(name="fuga", type=Fuga.class), @XmlElement(name="piyo", type=Piyo.class) }) public List<MyInterface> getList() { return this.list; } @XmlElements({ @XmlElement(name="fuga", type=Fuga.class), @XmlElement(name="piyo", type=Piyo.class) }) public MyInterface getAny() { return this.any; } // setter 省略 } ``` ```java:Fuga.java package jaxb; public class Fuga implements MyInterface { private int num; @Override public void hello() { System.out.println("fuga : num = " + this.num); } // setter, getter 省略 } ``` ```java:Piyo.java package jaxb; import javax.xml.bind.annotation.XmlAttribute; public class Piyo implements MyInterface { private String value; @Override public void hello() { System.out.println("hoge : value = " + this.value); } @XmlAttribute public String getValue() { return this.value; } // setter 省略 } ``` `MyInterface` インターフェースを実装した `Fuga` クラスと `Piyo` クラスがある。 `Hoge` クラスでは `MyInterface` 型でフィールドを定義して、 `@XmlElements({@XmlElement(...), ...})` という形でタグ名と実装クラスのマッピングを定義している。 上記クラスは、↓のような XML に変換できる。 ```xml:test.xml <?xml version="1.0" encoding="UTF-8"?> <hoge> <fuga> <num>123</num> </fuga> <list> <fuga> <num>543</num> </fuga> <piyo value="piyopiyo" /> </list> </hoge> ``` ↑の XML を読み込んで、 `MyInterface#hello()` を実行すると、↓のようになる。 ```java:JaxbMain.java package jaxb; import java.io.File; import javax.xml.bind.JAXB; public class JaxbMain { public static void main(String[] args) { Hoge hoge = JAXB.unmarshal(new File("test.xml"), Hoge.class); hoge.getAny().hello(); System.out.println(); for (MyInterface any : hoge.getList()) { any.hello(); } } } ``` ```text:実行結果 fuga : num = 123 fuga : num = 543 hoge : value = piyopiyo ``` ##名前空間が宣言されている XML をマッピングする 次のように名前空間が宣言されている XML をマッピングする。 ```xml:XML <?xml version="1.0" encoding="UTF-8"?> <entity xmlns="hoge-namespace" xmlns:fuga="fuga-namespace"> <id>20</id> <fuga:value>VALUE</fuga:value> </entity> ``` ###フィールドごとに名前空間を設定する ```java:Entity.java package sample.jaxb; import javax.xml.bind.annotation.XmlElement; public class Entity { private int id; private String value; @XmlElement(namespace="hoge-namespace") public int getId() { return this.id; } @XmlElement(namespace="fuga-namespace") public String getValue() { return this.value; } // setter 省略 } ``` `@XmlElement` アノテーションの `namespace` 属性で名前空間を指定する。 ※`namespace` 属性は、 `@XmlAttribute` アノテーションにもある。 ###`package-info.java` で共通の名前空間を設定する `package-info.java` に `@XmlSchema` アノテーションを付けることで、パッケージ内に存在する全てのクラスに対して名前空間を一括で設定できる。 ```java:package-info.java @XmlSchema( namespace="hoge-namespace", elementFormDefault=XmlNsForm.QUALIFIED ) package sample.jaxb; import javax.xml.bind.annotation.XmlSchema; import javax.xml.bind.annotation.XmlNsForm; ``` ```java:Entity.java package sample.jaxb; import javax.xml.bind.annotation.XmlElement; public class Entity { private int id; private String value; @XmlElement(namespace="fuga-namespace") public String getValue() { return this.value; } // getter, setter 省略 } ``` `hoge-namespace` は `package-info.java` の `@XmlSchema` アノテーションで指定しているので、 `Entity.java` では名前空間の指定を省略できる。 ある名前空間に属するタグが複数存在する場合は、この方法を使うと設定が1カ所にまとめられて楽になる。 ただし、 `package-info.java` で指定した設定が適用されるのは、そのパッケージ直下のクラスだけなので注意。 ###Java オブジェクトから XML に変換するときの QName のプレフィックスを指定する 名前空間が設定された状態で Java オブジェクトを XML に変換すると、以下のように QName (qualified name=修飾された名前)のプレフィックスに適当な値が使用される。 ```java:Entity.java package sample.jaxb; import javax.xml.bind.annotation.XmlElement; public class Entity { private int id; private String value; @XmlElement(namespace="hoge-namespace") public int getId() { return this.id; } @XmlElement(namespace="fuga-namespace") public String getValue() { return this.value; } // setter 省略 } ``` ```java:JaxbMain.java package sample.jaxb; import javax.xml.bind.JAXB; public class JaxbMain { public static void main(String[] args) { Entity entity = new Entity(); entity.setId(12); entity.setValue("ENTITY"); JAXB.marshal(entity, System.out); } } ``` ```xml:実行結果 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <entity xmlns:ns2="fuga-namespace" xmlns:ns3="hoge-namespace"> <ns3:id>12</ns3:id> <ns2:value>ENTITY</ns2:value> </entity> ``` `ns2`、 `ns3` という適当なプレフィックスが設定されている。 プレフィックスを任意の値にしたい場合は、 `package-info.java` の `@XmlSchema` アノテーションで以下のように設定する。 ```java:package-info.java @XmlSchema( xmlns={ @XmlNs(prefix="hoge", namespaceURI="hoge-namespace"), @XmlNs(prefix="fuga", namespaceURI="fuga-namespace") } ) package sample.jaxb; import javax.xml.bind.annotation.XmlSchema; import javax.xml.bind.annotation.XmlNs; ``` ```xml:変換結果 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <entity xmlns:fuga="fuga-namespace" xmlns:hoge="hoge-namespace"> <hoge:id>12</hoge:id> <fuga:value>ENTITY</fuga:value> </entity> ``` プレフィックスが `hoge` と `fuga` に変わっている。 #参考 - [JAXBの簡単な解説(1) | Akira Koyasus WebLog](http://www.akirakoyasu.net/2011/02/27/251/) - [JAXBの簡単な解説(2) | Akira Koyasus WebLog](http://www.akirakoyasu.net/2011/11/20/simple-tutorial-of-jaxb-2/) - [Java技術最前線(第73回 JAXB その1~第80回 JAXB その8) - Java技術最前線:ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20060915/248243/?ST=develop) - [XML名前空間の簡単な説明](http://www.kanzaki.com/docs/sw/names.html) - [JAXBと名前空間と - Starlight](http://d.hatena.ne.jp/Kazuhira/20120716/1342441007) |
|
| 796位 |
|
|||
|
13:00:48 |
(フリーランス 所属) |
|
自分は根っからのPHPerですが最近はPHPerといえども色々な言語のツールなども駆使しないと現代社会では生けていけない状態になって参りました。 そこで最近ホットなものも開発できる環境を一から作りなおした時のメモです。 ## あるあるこんなこと * 開発を複数受け持っている場合に言語のバージョンが違う * webサーバーもnginxとapacheの組み合わせが多くなってきた * DBもRDBMSとnoSQLと選択多くなってきた * MYSQLの複数インスタンス欲しい * kvsとかもたまに使うよね?(memcacheもね) * 継続的インテグレーションをやるためにはjenkinsも入れなきゃいけない 上記を踏まえた上での開発環境構築手順を紹介させて頂きます。 ## 環境 * macosx10.8(ホストOS) * centos6.3(ゲストOS) 尚、構築手順はMACOSXでの構築手順になります。 ※windowsも適時コマンドを読み替えれば特に問題ないと思います。 ## 入れるもの ### [VirtualBox](https://www.virtualbox.org/) CentOSを動かすための仮想開発環境 ### 開発言語 #### PHP PHPerなんで * [phpenv](https://github.com/CHH/phpenv) PHPのバージョン変更がコマンド一発でできるもの * [php-fpm](http://php-fpm.org/) nginxでPHPを使うために必要なプロセスマネージャー * memcache * mongo etc… (適当に必要そうなもの全部) #### ruby ビルドツールとかshell以外で構築するにあたって便利なものが色々あります。(Capistranoとか)言語切替とかのスクリプトは大体rubyで書かれていることが多いです。 * [rbenv](https://github.com/sstephenson/rbenv/) rubyのバージョン変更がコマンド一発でできるもの * [chef](http://higelog.brassworks.jp/?p=654) 環境構築を自動化できるツール。当初はこれでやろうと思ったが理解力が乏しく断念しました。 * [fluentd](http://fluentd.org/) ログを何でもかんでもjsonにできるツール。jsonって便利ですよね #### node.js scoket.ioとか聞いてるとワクワクしますよね!! * [nodebrew](https://github.com/hokaccha/nodebrew) node.jsのバージョン変更がコマンド一発でできるもの node.jsは頻繁にアップデートするのでバージョン切り替えがないと死ねるかもしれません。 #### JAVA 自分はJAVA知らないがjenkinsは使いたかったので * [jenkins](http://jenkins-ci.org/) 自動ビルドとかに使うツール ### WEBサーバー #### apache いつもどおり #### nginx 最近はこれ多い ### DB #### MYSQL いつもどおり * phpMyAdmin 最近は使いやすくなりました #### mongodb 最近話題のnoSQL * rockmongo mongodb版phpmyadminのようなもの ## 目次 * [virtualbox導入](http://qiita.com/items/31029ea9ac3fcb34abdc) (OSインストールからSSHでホストOSの接続確認まで) * [yum外部レポジトリの追加](http://qiita.com/items/662e41376efa97251575) * [rubyインストール](http://qiita.com/items/1cb882b48b1a827a420d) * [virtualbox(centos)でのhttpdインストールメモ](http://qiita.com/items/5ef125b0384a9c23ba6a) * [fluentdインストール](http://qiita.com/items/ae25133156aeddd7c24b) * [nginxインストール](http://qiita.com/items/40b527c390f068985f75) * [MYSQLインストール](http://qiita.com/items/73df4ab45c96092b63b8) * [PHPインストール](http://qiita.com/items/7d031440bb174a55a6a1) * [php-fpm設定](http://qiita.com/items/b51b7ec9d7c378f60237) * [phpMyAdminインストール](http://qiita.com/items/dae3649e412c323015c0) * [mongodbインストール](http://qiita.com/items/3ed551c440cf7b48bbfe) * [PECL::mongo追加](http://qiita.com/items/ed7c86d3a7c183c76081) * [PECL::memcached追加](http://qiita.com/items/b2ce06e8f2ee0237d723) * [PHP composerインストールメモ](http://qiita.com/items/43beef25d26966fff8cc) * [node.jsインストール](http://qiita.com/items/c85fcca393492a12ec38) * [jenkinsインストール](http://qiita.com/items/e41a821a8287091d7510) 本当はデプロイツールの[capistrano](https://github.com/capistrano/capistrano)とかも書きたかったのですが断念しました。また時間があれば書きます。 |
|
| 797位 |
|
|||
|
19:47:30 |
|
|
Ruby2.0, Rails4で確認 いつか試そういつか試そうと思っていたOmniauthを使ってのTwitterログインを試してみました。 [詳細記事はこちらをご参照ください](http://blog.hello-world.jp.net/?p=847) ### 初期設定 * Twitterアプリの作成 callback URLに「http://127.0.0.1:3000/auth/twitter」を指定しておきます。 後々投稿もさせたいのでAccessを「Read and Write」にしてあります。 「Allow this application to use 'Sign in with Twitter」にチェックを入れておくと、二度目のログイン以降は認証画面を省略することができます。 * Gemfile追加 ``` gem 'omniauth' gem 'omniauth-twitter' gem 'settingslogic' ``` * bundle install ``` ./bin bundle install ``` * settingslogic initializer設定 ```config/initializers/0_settings.rb class Settings < Settingslogic source "#{Rails.root}/config/settings.yml" namespace Rails.env end ``` * settings.ymlにtwitter設定を記述 ```config/settings.yml defaults: &defaults development: <<: *defaults twitter: consumer_key: YOUR CONSUMER KEY consumer_secret: YOUR CONSUMER SECRET test: <<: *defaults production: <<: *defaults ``` * omniauth initializer設定 ```config/initializers/omniauth.rb Rails.application.config.middleware.use OmniAuth::Builder do provider :twitter, Settings.twitter.consumer_key, Settings.twitter.consumer_secret end ``` ## model作成 * userモデル作成 ``` ./bin/rails g model user provider:string uid:string screen_name:string name:string ``` * migrate ``` ./bin/rake db:migrate ``` * app/models/user.rb ```rb:app/models/user.rb class User < ActiveRecord::Base def self.create_with_omniauth(auth) create! do |user| user.provider = auth['provider'] user.uid = auth['uid'] user.screen_name = auth['info']['nickname'] user.name = auth['info']['name'] end end end ``` ## controller作成 * controller作成 ``` ./bin/rails g controller base ./bin/rails g controller home index ./bin/rails g controller sessions ``` * routing追加 ```config/routes.rb root 'home#index' get "home/index" get '/auth/:provider/callback', :to => 'sessions#callback' post '/auth/:provider/callback', :to => 'sessions#callback' get '/logout' => 'sessions#destroy', :as => :logout ``` * BaseController ```app/controllers/base_controller.rb class BaseController < ActionController::Base protect_from_forgery def login_required if session[:user_id] @current_user = User.find(session[:user_id]) else redirect_to root_path end end helper_method :current_user private def current_user @current_user ||= User.find(session[:user_id]) if session[:user_id] end end ``` * HomeController ```app/controllers/home_controller.rb class HomeController < BaseController def index end end ``` * SessionController ```app/controllers/sessions_controller.rb class SessionsController < ApplicationController def callback auth = request.env['omniauth.auth'] user = User.find_by_provider_and_uid(auth['provider'], auth['uid']) || User.create_with_omniauth(auth) session[:user_id] = user.id redirect_to root_path end def destroy session[:user_id] = nil redirect_to root_path end end ``` ## View変更 * layout追加 ```app/views/layouts/application.html.erb <% if current_user %> <%= current_user.name %> <%= link_to 'ログアウト', logout_path %> <% else %> <%= link_to 'ログイン', '/auth/twitter' %> <% end %> ``` ブラウザから[http://127.0.0.1:3000](http://127.0.0.1:3000) にアクセスするとログインリンクが表示され、Twitter認証ができるようになりました。 ## 次回は ログインしたアカウントでTweet投稿ができるようにしてみます。 ### 参考 * [RailsでOmniauthを使ってTwitterログインする](http://blog.hello-world.jp.net/?p=847) * [intridea / omniauth](https://github.com/intridea/omniauth) * [(メモ) Rails+OmniAuthによるTwitterログイン](http://d.hatena.ne.jp/m-kawato/20130105/1357399125) * [OmniAuth 1.1.4とRails 4.0.1でTwitterアカウントによる認証](http://blog.iotaworks.jp/2013/11/omniauth-twitter-with-rails.html) * [Railsでomniauthを使ってtwitter,facebookの簡単ログイン認証](http://www.geekzshu.com/rails/1562) * [Twitter OAuth の挙動が変わりました](http://koiroha.blogspot.jp/2013/03/changes-to-the-sign-in-with-twiter-flow.html) * [241: 簡単なOmniAuth](http://ja.asciicasts.com/episodes/241-simple-omniauth) |
|
| 798位 |
|
|||
|
22:48:37 |
|
|
[現場で使えるソフトウェアテスト Java編](http://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%86%E3%82%B9%E3%83%88-Java%E7%B7%A8-%E9%A3%AF%E5%B1%B1-%E6%95%99%E5%8F%B2/dp/4798114634)を読んだので要点をまとめ。
#Step1 テストとは ソフトウェア開発では、様々な問題が発生するが、そのなかでよくあるのが`動かない`、`誤動作`、`パフォーマンス問題`人が作る上でミスは起こるのでテストが必要 ``` 網羅的検証(ソフトウェアテスト) = 利用時に問題が起こらない品質を確保するためにバグを見つける活動 ``` ##テストの流れ 1. 品質目標を立てる テスト密度(目標、上限、下限値)、バク密度(目標、上限、下限値) 1. テスト計画 ソフトウェアテストの全体計画作成 実施スケジュール、予算、体制、環境構築手順、必要ツール利用手順、成果物の様式、バージョン管理、設計書の準備 1. テスト作成 期待動作、パターンの洗い出し、テスト環境構築、テストデータの作成、テストケース作成、レビュー 1. テスト実施 作成したテストケースの実行 1. テスト検証 結果の確認、テスト関係者以外の利害関係者との調整(設計書管理、仕様管理、修正管理)、テスト実施者の作業管理、テスト報告のとりまとめ、テスト全体報告、再テスト有無判断 1. 再テスト 検証で再テストを判断したケースの再テスト 1. 品質評価、判断 ・品質管理者(テストが要求品質を達成できているか判断) ・テスト管理者(主に開発リーダーなどのテストなどにおける経験によりテストの実施、人員のとりまめ) ・テスト実施者 → 小規模プロジェクトでは各役割を兼任することもある。また開発者がテストの関係者に移行する場合もある。 #Step2 テスト前のバグ予防策 単体テスト工程でバグ発見は可能だが、そもそもバグ自体を予防することで開発をよりスムーズに進めることが出来る ## 静的解析ツールとソースレビューによるバグ予防手順 1. コーディング規約作成 言語特有のコーディング作法、プロジェクトごとのルール(変数、メソッドの命名規則)、要件定義で指定された業務ロジックとの整合性 1. 品質レベルの定義 保守性、作法のレベル定義の明確化 読みやすさの定義 ルール数は適度に → 多くしすぎるとプログラマーが把握しきれない ※Javaコーディング規約2004などをたたき台にするとよい 1. 静的解析ツール選択、設定 __静的解析を行うことで、レビュー時の確認内容が削減できる__(コーディング規約にそうように静的解析ツールの設定を行う) 予算と機能のバランスを考え選択 低品質なソースに関してソースレビューは非効率なため、予め静的ツールの指摘率(指摘数÷ソースコード行数)を決めてく 静的ツールにあえてエラーになるような記述をする場合には、それをリスト化するとレビューがスムーズになる 1. レビュー観点の作成 *目的* ・ソースコードの品質均一化 ・ソースコードの保守性向上 ・開発チームへのノウハウ横展開 ・修正コストの削減 レビュー時にレビューアーの認識に差異ができないように事前に作成する コーディング規約から抽出 → 静的解析ツールでチェックできないものなど 仕様から抽出 → 外部インターフェイスの呼び出し方のマナー、業務ロジックなど目視でなければ確認できないようなプログラミング方法、アルゴリズムや設計に関連する問題など 1. プログラミング 事前にプログラマーにコーディング規約とレビュー観点を徹底して説明 → 認識に違いがあるとレビュー、修正などの余計な時間が発生する フォーマッタは必須 1. レビュー実施 事前に静的解析を完了したソースに対し、レビュー観点に沿ったレビューを行う QCDのバランスを考慮したレビューが重要。具体的には下記のような感じ ・プログラマーごとに最低1つのソースコード ・アプリケーションが提供する機能別に、そのて機能の中心となるソースコード ・プロジェクトとして初めて提供する機能のソースコード ・プロジェクトメンバーが難易度が高いと指定したソースコード 全てのプログラマレベルは均一ではないため、必ず全員をレビューする。また難易度を面からレビュー対象を選定すると効率的 レビュー観点としては、クラスの仕様、設計、アルゴリズム、コメント記述など 1. ソースコードレビューの評価:レビューの指摘で上がった問題を分析する 評価する観点 ・バグの種類(メモリーリーク、デッドロック等) ・バグの傾向(クラス、ファイル、メソッド、プログラマ等) ・バグの原因(設計ミス、プログラムミス等) 上記を確認し、今後同じような指摘が発生しないように対策を検討 → メンバーにフィードバックを行う *静的解析ツール、コードレビュー重要!!* ###ソースコードレビューの必要性 > 平均的バグ検出率は30%、その後のテスト35%。複数人によりソースコードレビュー実施時のバグ検出率は60%。(Code Complete 第2版) #Step3 テストコードの作成 ここに記載する内容を理解し、手順に従いテストコードの作成を行う ## 品質の高いソフトウェア 作るには、`要求をプログラムレベルまで把握`、`要求がプログラミングできているか厳密にテスト`の2点が必要 * テストコードのプログラミング テスティングフレームワークを使用することで、開発効率、品質、開発容易性、統一性、メンテナンス性などの向上が望める → 要はメジャーなフレームワークは品質も情報量も開発メンバーの経験値も高いのでメリットが多い * 単体テスト 事前に「目的」「作業範囲」を決めておくことが重要 限られた期間、予算内で単体テストを行うにはメソッドの処理パターンを分析し、網羅性を確保し、重複を省いたコード作成が必要 「テスト実行前状態」「テスト実行後状態」を考慮した単体テストを作る(例えば、入力値による分岐だけでなく、処理中に行うDBの値によって変化するようなものは、DBの状態も複数作成する) * ブラックボックステスト ・同値分割法 分岐処理などで、各分岐内に定量的なデータを使用する ・境界値分析 同値分割後、分岐内の境界を分析し使用データを * ホワイトボックステスト コードガバレッジを把握する ・命令網羅 コード内の全ての命令を1回以上実行すると100% ・分岐網羅 分岐に対して、真偽を1回実行すると100% ・条件網羅 条件に対して、全ての値を1回実行すると100% ・複合条件網羅 テスト内の分岐全ての組み合わせを1回以上実行すると100% 命令網羅や分岐網羅、複合条件網羅が100%でも品質が確保されているとは限らない → 例外処理や仕様理解不足など考慮しきれな場合はあるのであくまでもカバレッジは指標として使用する * Webアプリケーションのテスト Webアプリケーションなど、サーバ、HTTP通信などの複雑なクラスが存在するため、それらをサポートするツール、ライブラリなどを利用し、クラス間の関係を極力省いたテストを行う →[Javaのユニットテスト・ライブラリ・ツールまとめ](http://qiita.com/disc99/items/808a57e8de41fe75a5dd) ##テストケース作成手順 1. 仕様理解 メソッド仕様を理解し入出力のパターン、関係を分析 1. テストケース抽出(仕様観点) 仕様からテスト処理の粒度、テストケースの量を把握 1. プログラム構造理解 メソッドの分岐、繰り返し、例外などの把握 1. テストケース抽出(プログラム構造観点) プログラム構造も考慮したテストケースの把握 1. 省略できるテストケースの分析 ここまでの作業で抽出した情報をもとに省略できるケースの判断 1. 実行するテストケース数の決定 今までの情報をもとに、プロジェクトのQCDを考慮したテストケースの決定 1. テストシナリオ作成 どういった仕様をテストするためのテストケースか?、実行するためにどんな入力値、実行前状態が必要か?、実行後出力、テスト後状態がどうなるか?なども考慮する 1. テストデータ決定 効率の高いテストデータを利用する ## テストコード規約を作成する テストコード規約を疎かにすると、保守性が低下し、仕様変更時などにテストコードを修正する手間がかかるので必ず作成する ###よく使われるテストコードの構成 ``` パッケージ構成 テストクラスと同一パッケージ ※フォルダを別にし、フォルダをソース側は「main」、テストコード側は「test」 クラス名 「テスト対象クラス + Test」 メソッド名 「test + テスト対象メソッド名 + 連番」 テストメソッド宣言で必ず「throws Exception」を使用する 1つのテストメソッドでえは1つのテスケースだけをプログラミングする ``` #### テストコードを作成する テストの前後処理はsetUp、tearDown(JUnit4以降なら、@Before、@Afterアノテーション)メソッドを使用する(ネットワーク接続/切断、DB初期化、外部ファイル初期化) テストクラス内で一回だけ実行するなら、@BeforeClass、@AfterClassを使用する ## テストコードレビューをする *レビューの注意点* * プログラマごとに最低1つのテストコードを必ずレビューする * 機能別に、中心となるソースコードのテストコードは必ずレビューする * プロジェクトとして初めて提供する機能を実現するソースのテストコードは必ずれびゅーする * 難易度の高いソースのテストコードレビューは必ずする レビュー観点 * 規約が守られていること * 事前状態、引数、検証内容がテストケースの内容と一致していること * 処理ロジックに誤りがないこと * 前処理、後処理が適切に記述されていること * 実行順序によって結果が変わるような記述になっていないこと * テスト内容を表すコメントが適度に記述されていること * スタブ、ドライバが正しく利用されていること #Step4 効率的な単体テスト 作成したテスト内容やテストコードを実行するだけではなく、ここで示すことに注意してテストを行う ##単体テストの実行 テスティングフレームワークを使用しテスト実施 * テスト環境の統一 OS、ミドルウェア、外部リソース(ファイルなど)、クラス変数、DB(テーブル内の値) → VMなどを利用するプロジェクトも増えている 機能テストと非機能テストは分けて行う → 環境やツールを変更しなければならないことも多いので、作業効率が悪い 非機能テストの記録も行う * テスト結果確認 実行結果と期待値の比較、その他レスポンス、リソースの増加などの観点にも注意 ##テスト結果記録 所定のフォーマットで記録する ###記録を行う項目 * 作成日付 * 記録した人 * テスト環境 * テスト対象のソースコードを一意に識別できる情報 * 予想どおりの結果を返さなかったテストコードのクラス・メソッド名 * 再現方法 * 結果 * 重要度 * 現在の対応状況 → *バグ管理ツールを使うのが最適* ###バグの解析、修正(デバッグ) 起こる故障原因リスト * 設計書記述ミス * ソースコードミス * テストケースミス * テスト環境ミス * テスト実施ミス * その他、ツールやライブラリ自体にバグがあるなどのミス → バグの原因により対処方法を検討する ##テストコードの修正 修正内容の量を把握し再テストまでの予定を建てて修正を行う → 完了後、再テスト:テスティングフレームワークを使用することで回帰テストの効率化が図れる #Step5 ソフトウェアの品質評価 単体テスト実施結果をもとに、作成したソースコードが期待どおりの品質か確認する ###なぜ テストの妥当性、バグの出しきり確認 → 取りまとめて品質評価が必要 ###誰 メインは開発リーダー、品質管理者。ただし、プログラマも自分の担当分に関しては責任を持つ ###いつ 全てのクラステスト完了後では手遅れになる可能性があるので以下のタイミングで行う * プログラマ 1クラスのプログラミング、単体テスト完了後 * 開発リーダー、品質管理者:定期的(できれば毎日) → バグが発生しても早期発見、対策が可能 ##品質評価の作業手順 1. 品質評価データ取得 1. 品質評価データ分析、品質評価の実施 1. 問題箇所の見直し/修正 1. 品質報告書の作成 ##QC7つ道具 * 特性要因図 特性(結果)と要因との関係を表す、通称「魚の骨」。開発において発生した問題の原因を洗い出すときに使用 * パレート図 現象、原因など項目件数を大きい順に並べた棒グラフとその追跡わの折れ線グラフを重ねて表す。 バグの原因で使用すると、上位のバグ原因がどのくらいの割合かわかる * ヒストグラム データ範囲を区間分けし、各区間の出現頻度を棒グラフ化。データの分布、形、中心位置、バラつきを把握できる * 散布図 2項目を縦横軸にとり、分布状況をプロット。バラつきから2項目間の相関関係有無を確認 * チェックシート 項目表でチェック漏れを防止。ソースコードレビューなどで使用 * 層別 データの特徴ごとにグループ分け。ソースの特性ごとに層別を行い、品質評価基準をグループ化する * 管理図 データ変動の折れ線グラグと、その上下限の管理限界線を明示。管理限界線を超えると異常な状態を示す ##品質評価の技法 ###定量評価(メトリクス) テストにおいて十分のバグを出しきったことを評価する * テスト密度 テスト対象のソースコード用数に対して順分にテストケースを実施しているか ``` テスト密度 = テストケース数 ÷ ソースコード行数 ``` * コーガバレッジ テスト対象の構造に対して、十分なテストが実施されてるか ``` 命令網羅 = テストで実行した命令文数 ÷ 全命令文数 分岐網羅 = テストで実行した分岐数 ÷ 全分岐数 ``` * 未修正バグ 修正していないバグが残っていないか `検出されたバグのうち修正されていいないものの数` * バグ密度 テスト対象のソースコード行数にたいして十分なバグを検出しているか ``` バグ密度 = 検出バグ数 ÷ ソースコード数 ``` ###その他メトリクス * サイクロマチック数 if、switchなどの制御パス数をカウントし処理ロジックの複雑性を判断。「バグの作りこまれやすさ」、「テストのしづらさ」などが分かる * ネストの深さ ネストの深さを計測し、ソースの可読性、バグの作りこまれやすさを判断 * 凝集度 クラス、メソッド間の結びつくを判断。凝集度が低い場合、別クラス化を検討 * 結合度 依存関係のあるクラス数をカウント。結合度が低いほど、クラスの独立性が高く、修正コストが低いことを表す ##メトリクスを利用した評価の方法 どのくらいの値ならOK、NGを事前に決めておく(目標値と、その上限値、下限値) → 過去のプロジェクトなどを参考にする また、メトリクスの組み合わせも可能で、例えばテスト密度が高く、バグ密度が低いと、テストを実施し過ぎたのに網羅性が低いなど、それぞれの状況に合わせ判断ができる ##時系列データにより品質評価(バグ収束曲線) テスト実施日の時間軸と、累計バグ数の折れ線グラフでバグの収束予測、また予測に対する差異による問題点を判断する ##定性評価 メトリクスを利用した、品質評価は、`数字では測れない部分`、`測定対象データの誤り`などが含まれる可能性があるため、全ての品質評価、ソースコード、テストケース、バグ情報をもとに、定性的な品質評価を行う 方法としては、テストケースのバグの内容をグラフや表化して判断 例えば、仕様、プログラミング、テストミスとその内容(期待値の相違、例外発生、性能劣化、トランザクションエラー)ごとに表化し、ミス内容ごとにグラフ化する #おわりに ここでまとめたことは全てではないので、プロジェクトや現場に合わせて必要な部分を変更しながらベストなテストを! |
|
| 799位 |
|
|||
|
21:42:05 |
|
|
# 自分のライブラリをPyPIに登録したい
...が、PythonにはRubyの[bundler](http://bundler.io/)のようなものが なく、(合ったとしても統一されてないと思う) プロダクト別に自由なディレクトリ構成になっている状態... せめて自分のプロダクトについては統一したいなあと思ったので Pythonのライブラリを作る場合のディレクトリ構成や 環境構築などについてメモを書く。忘れちゃうので... 利用するPythonのバージョンは3.3以降を意識してる。 それより前(3.2, 3.1...2.x)は考慮しない。問題無いと思うけど... # テスト目的で作成したライブラリ  **[fizzbuzz](https://github.com/futoase/fizzbuzz)**というアレなライブラリを作った。 ライブラリというかコマンドラインツールだけれど。 このライブラリの構成を元に、メモを書いていく。 # ライブラリのディレクトリ・ファイル構成 以下のディレクトリ・ファイル構成とする。 ``` fizzbuzz └lib # ライブラリ本体 └bin # 実行スクリプト本体 └fizzbuzz # ライブラリ名のディレクトリ └ __init__.py # ライブラリimport時の初期化設定はここで └test # テストファイル requirements.txt # travis-ci及びsetup.pyで利用する。pip --freezeで作れる。 info.py # パッケージの作者情報などを収めるファイル version.py # バージョン情報を収めるファイル setup.py #キモとなるファイル ``` こういうのでいいんじゃないかな。 # pyvenvの環境をライブラリディレクトリ以下に構築する Python 3.3より、```virtualenv``` が包含された。 ライブラリの動作テストを行うために、以下のコマンドで ローカルなPython実行環境を作成する。 ``` > pyvenv .venv ``` virtualenvの頃から使っているため、.venv以下に pyvenvの環境を作るようにしているが、任意で良いと思う(.pvenvとか)。 ローカルなPython実行環境を構築したので、 ```activate``` を shellの```source``` コマンドにて取り込む。 こうすることで、```.venv``` 以下のPython環境を使う形になる。(pythonコマンドとか) ``` > source .venv/bin/activate ``` ## setuptoolsのインストール virtualenvのとあるバージョンまでは システムのライブラリをvirtualenvで取り込んでくれたりしたけど、 virtualenvの新しいバージョン及びpyvenvだと取り込んでもらえない。 pyvenvを実行するとピュアな環境が構築される。 そのため、```easy_install``` が無い状態で環境が作られる。 以下のコマンドを実行して```setuptools``` をセットアップしよう。 setuptoolsのPyPIページより引用。 ``` > wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | python ``` これでeasy_installコマンドを実行し、PyPIにある パッケージをインストールできるようになる。 が、easy_installでPyPIのパッケージをインストールするより、 ```pip``` を使うほうが使いやすい。pipを入れよう... ## pip のインストール easy_installを入れている状態であれば、pipは気軽にインストールすることができる。 ``` > easy_install pip ``` 以上で準備完了。 # 必要なライブラリのリストを作成する 自作ライブラリが他のライブラリを使っている場合、 setuptools.setup 関数に渡すhashに install_requiresを指定しないといけない。 pipを利用すると気軽に必須ライブラリのリストを作ることができる。 ``` > pip freeze > requirements.txt ``` TravisCIもrequirements.txtを利用するので setup.pyにて、```requirement.txt``` を利用してinstall_requiresを指定するようにする。 ```py setup({ install_requires: open('requirements.txt').read().splitlines(), }) ``` これでライブラリが必要としている外部ライブラリがインストールされる。 # パッケージの情報をどこに持たせるか ## パッケージ作成者の情報 PyPIで表示されるライブラリ作成者の情報について、どのファイルに持たせるか なんか迷ったけど、package root直下に```info.py``` として置く形にした。 ```py:info.py # package information. INFO = dict( name = "PyFizzBuzz", description = "FizzBuzz cli tool", author = "Keiji Matsuzaki", author_email = "futoase@gmail.com", license = "MIT License", url = "https://github.com/futoase/fizzbuzz", classifiers = [ "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "License :: OSI Approved :: MIT License" ] ) ``` classifiersは、[ここのリスト](https://pypi.python.org/pypi?%3Aaction=list_classifiers)より自分のライブラリが マッチしてそうなジャンルを選択し、設定する。 ## パッケージバージョン ```version.py``` の中にVERSIONを指定する。 ```py:version.py VERSION = "0.0.3" ``` ## MANIFEST.in ファイルの作成 **20131017追記** ```package_dir``` では```info.py```, ```version.py```, ```requirements.txt``` ファイルを指定していないため、 MANIFEST.inファイルを書かないといけない。 この記事を書いたときにチェックを怠って居たため、includeされていない問題を発見できませんでした。 ので追記。 ```py:MANIFEST.in include info.py include version.py include requirements.txt ``` # パッケージのテスト pipコマンドを利用し、パッケージのテストを行う ``` > pip install . ``` 自分自身のディレクトリ内で自分自身を指定してpip installを行うことで、 ```pyvenv``` の環境内で自己のライブラリを利用することができる。 ``` > pip list konira (0.3.2) pip (1.4.1) PyFizzBuzz (0.0.3) setuptools (1.1.5) ``` もし、pip install後、自分のライブラリにバグが合った場合は pyvenvの環境以下で以下のコマンドを実行すれば良い。 ``` > pip uninstall PyFizzBuzz ``` Gemfileに```gemspec``` を指定するような...気持ちでコマンドを実行すればいいと思う... ## 動作チェック 今回、fizzbuzzはコマンドとして実行されるようにしたので、 ```.venv/bin/fizzbuzz``` にコマンドが置かれている。 ``` (.venv) > fizzbuzz 10 | head -3 1 2 Fizz ``` 動作チェックは行えた。 # PyPIへの登録 PyPIへの登録を行っていない場合は、以下のコマンドを実行し、登録する。 ```~/.piprc``` ファイルを生成していない場合はユーザー名、パスワードの登録を促される(はず)... ``` > python setup.py register ``` PyPIへパッケージのアップロードを行う。 ``` > python setup.py sdist upload ``` [PyPIにライブラリのページが作成されればOK](https://pypi.python.org/pypi/PyFizzBuzz) C拡張のライブラリの場合は異なるかもしれない。 (eggファイルのアップロードについては考慮していない...) # PyPIへのアップロード後 一日のダウンロード数や、過去にアップロードしたバージョンのファイルの一覧などを PyPIページから見ることができる。(要ログイン済) 結構楽しい。 # 余談 setuptoolsがdistributeと統合されたので、setuptoolsのみimportしている形になったけど、 統合される前は以下の形でsetup.pyにてimportするパッケージの切り分けをしていた。 ```py:setup.py try: import setuptools except ImportError: from distribute_setup import use_setuptools use_setuptools() from setuptools import setup, find_packages ``` distribute_setup.pyを[ここから落として](http://guide.python-distribute.org/installation.html)(なんか書いてる今アクセスできないや)、パッケージに含める形で対応していた。 setuptoolsに統一されてよかった。(この記事では下位互換性のことは考慮してないからこうできるのかもしれない...) |
|
| 800位 |
|
|||
|
23:16:26 |
(Unicast Inc. 所属) |
|
「コマンドの結果をファイルに出力したいけど、標準出力でも出力を見たい!」、という欲張りな要望に答えてくれる意外と頼もしいコマンド。
## 簡単な使い方 ### ファイルを上書きする ```bash vagrant ssh-config --host 192.168.33.20 | tee result.txt ``` ### ファイルに追記する `-a` オプションをつける。 ```bash vagrant ssh-config --host 192.168.33.20 | tee -a result.txt ``` ### sudo でファイルに書き込みできるようにする リダイレクト先に書き込むときに特権が必要な場合、そのままではうまく書き込みできません。 以下は swappiness を変更する場合の例です。 ``` $ cat /proc/sys/vm/swappiness 60 $ sudo echo 0 >/proc/sys/vm/swappiness -bash: /proc/sys/vm/swappiness: 許可がありません ``` そこで tee コマンドを使います。 ``` $ echo 0 | sudo tee /proc/sys/vm/swappiness 0 $ cat /proc/sys/vm/swappiness 0 ``` いい感じにオンザフライで変更できました。 #### swappiness の蛇足 ちなみに蛇足ですが swappiness の設定で 0 を設定するのは Kernel 2.x 系では物理メモリを使い果たしてからスワップアウトするという意味になりますが、 Kernel 3.5 以上になると 0 の取扱いが変わって、物理メモリがなくなるとスワップアウトせずに OOMKiller が発動します。 なのでKernel 3.5 以上では 1 が適当です。 - [Swappiness - Wikipedia](https://en.wikipedia.org/wiki/Swappiness) - [スワップされて困っちゃうのでswappinessを設定する - Qiita](http://qiita.com/ikuwow/items/f0b4d1f509a0b83b5d7e) - [vm.swappiness=0は重要 | Ore no homepage](https://hiroakis.com/blog/2012/06/13/vm-swappiness0%E3%81%AF%E9%87%8D%E8%A6%81/) ## 標準エラー出力もファイルに保存する リダイレクトを使う。これをやらないと標準エラー出力だけファイルにロギングされないという事態になってしまう。 ```bash ping 9999 2>&1 | tee result.txt ``` ## 参考サイト - [UNIXの部屋 コマンド検索:tee (*BSD/Linux)](http://x68000.q-e-d.net/~68user/unix/pickup?tee) - [Linuxコマンド集 - 【 tee 】 標準入力を標準出力とファイルに出力する:ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20060227/230898/) - [UNIXの部屋 コマンド検索:リダイレクト (*BSD/Linux)](http://x68000.q-e-d.net/~68user/unix/pickup?%A5%EA%A5%C0%A5%A4%A5%EC%A5%AF%A5%C8) |
|
| 801位 |
|
|||
|
10:59:46 |
|
|
CSSをリファクタリングしようと思うと、どっから手をつけたら良いかわからなくないですかね?僕はわかりません。
特にSassのような`@import`でいろいろファイル分割していると、あれこれなんのファイルだったお(^ω^;)ってことがよくありませんかね?僕はないです! ということで、あなたがCompassプロジェクトを使用しているのならば、`config.rb`のある場所で下記のコマンドを打ってみましょう。 ```shell-session $ compass stats ``` そうすると下記のようなCSSに関する情報が表示されます。  どこで何回ミックスインが定義されて使用されているか、ファイルサイズがどれくらいかプロパティがどれだけ宣言されているかなど、一目瞭然ですね。これで、どこからリファクタリング・手をつけていったらいいのかなど参考にできるかと思います。 ちなみに、`stats`コマンドのヘルプは下記 ```shell-session $ compass stats --help Usage: compass stats [path/to/project] [options] Description: Compile project at the path specified (or the current directory if not specified) and then compute statistics for the sass and css files in the project. ``` `css_parser`のgemが入ってないとCSSがパース出来ないので、`CSS Selectors`と`CSS Properties`が`DISABLED`になっているはずなのでインストールしておく。 ```shell-session $ gem install css_parser ``` ときどき、`stats`コマンドを打ってCSSのヘルスチェックしてみてください。カジュアルにミックスイン使ってると、ほんと膨れ上がるんで!! |
|
| 802位 |
|
|||
|
10:42:45 |
|
|
## Selenium IDE
> セレン (英: selenium) は原子番号34の元素。元素記号は Se。カルコゲン元素の一つ。セレニウムとも呼ばれる。 (wikipediaより) 今回、記事にしたのは元素のセレニウムではなく、WebUIの自動テストツールです。 (実はプログラマに化学系出身の方が多いというのは、私の周りだけでしょうか) Selenium IDEは、Webブラウザのテストを自動化するためのツールでFireFoxのアドオンで提供されています。 Selenium IDE以外にSelenium WebDriverというプログラムからテストできるライブラリがあります。 Selenium WebDriverについては 今回は省かせて頂きます。 Seleniumにはいくつか種類がありますが、下記のサイトで解説されていますので参考になると思います。 http://blog.trident-qa.com/2013/05/so-many-seleniums/ ## Selenium IDEの導入 Selenium IDEはFirefox上でブラウザの操作を記録してテストケースを生成することができます。 Selenium IDE公式ドキュメント(英語)を参考にしました。 http://docs.seleniumhq.org/docs/02_selenium_ide.jsp 1. [公式サイト](http://docs.seleniumhq.org/download/) からSelenium DIE 2.3.0 のxpiファイルをダウンロード ※2014/7/17現在 Selenium IDE 2.5.0 2. Firefoxのアドオンマネージャ(Firefoxの[アドオン]メニューで表示可能)にドラッグ&ドロップしてインストール (FireFoxのアドオン検索ではIDEのプラグインしか出てこない) ## Selenium IDEの使い方 今回は簡単な例で google 検索を行いたいと思います。 1. まず赤丸を押すとブラウザの操作を記録してくれます。  2. google.co.jp で qiita と検索 すると以下のFireFoxの操作がコマンドとして記録されます。 もう一度、赤丸を押して記録を止めて下さい。  3. 左上の ! を押して、記録したテストを実行 4. ( __以下はエラーが出たら参考に__ ) *「[error] Element id=lst-ib 」* のエラーが出るかもしれません。 これはページを読み込みより早くテストの実行が行われて、 *lst-ib* というテキストボックスが見つかりません!!! というエラーです。 5. テキストフィールドが見つかるまで待つコマンドを追加します。  右クリックで [コマンドの挿入]を選択 以下のようにコマンドを入力 コマンド : waitForElementPresent 対象 : id=lst-ib 値 : (何も入力しない) これは *lst-ib* というフィールドが見つかるまで待ちます ≒ (読み込むまで待ちます) というコマンドです。 ここでテストコマンド入力枠について少し説明します。 テストで実行するコマンドですが以下の3つのパラメータがあります。 コマンド : 動作する (ex. 開く、クリックする、閉じる) 対象 : どこを動かすか (ex. google.com、検索、タブを) 値 : 何を渡すか (ex. テキストボックスに検索ワードを) という感じになっています(かなり適当な解説ですみません)。 6 コマンドを追加したらもう一度左上の  を押下します。 google 検索で qiita と検索できるはずです。 ## 最後に 私が使用したいるのは Sencha Ext JS なのですが、 JSが動的にページを生成するので、HTMLを読み込む前にコマンドが実行されてしまって... 動的生成でHTMLのIDもランダムに生成されたり... すこし詰まりましたが、色々工夫する事で非常に効率良くテストが行えています。 例では FireFoxでしたが、私はchromeでも使用しています。 Selenium IDE を chromeで使う方法は気が向いたら追記したいと思いますが、 とりあえず参考サイトのURLを下に貼っています。 とりあえず私の環境では問題無く Selenium IDE を導入できましたが、 環境によっては上手く動かなかったりするかもしれません。 ## 参考サイト この記事のソースは全て下記のサイトから引っ張ってきてます。 Selenium 公式サイト http://docs.seleniumhq.org/ Selenium IDEの導入から簡単な使用例まで http://news.mynavi.jp/column/ide/157/index.html Selenium IDEのもう少し詳しい情報掲載(よく使うコマンドの解説) http://iflaglabs.jp/original7.html Selenium IDEでループをさせる方法(拡張スクリプトの解説) http://iflaglabs.jp/original10.html google chromeでIDEを使う http://blog.trident-qa.com/2013/05/selenium-ide-on-chrome-ie-etc/ Selenium IDEのプラグインについての解説 http://blog.trident-qa.com/2013/06/selenium-ide-plugins/ 日本Seleniumユーザーコミュニティ https://groups.google.com/forum/?hl=en#!forum/seleniumjp |
|
| 803位 |
|
|||
|
16:46:27 |
|
|
#Go言語でGUIしたいかー?! おー!!
とまあ、そんなノリで始まります。 ##Go言語でGUIするには? Go言語でGUIするライブラリは意外と色々あります。 * __[go-ui](https://github.com/visualfc/go-ui)__ 「Go言語 GUI」でググった時に、おそらく一番情報の多いライブラリ。ただ情報はあるけど大して気合の入ったライブラリではない模様。 __go-ui__なんて大層な名前をしてるけど、その実態はQtバインディングだったりする。 開発はもう終わったみたいで、 __[GoQt](https://github.com/visualfc/goqt)__ というのが後継らしいけど、 __GoQt__ の方はまだリポジトリを作っただけのような状態だった。 _Windowsで導入するのがほぼ無理ゲー_。(最重要事案) * __[go-gtk](https://github.com/mattn/go-gtk/)__ mattn氏が開発の中心となっているGo言語のGtk+バインディング。多分Go言語のGUIライブラリで一番開発が活発。 _[Windowsでも動くらしい](http://mattn.kaoriya.net/software/lang/go/20110201010655.htm)_けれど、Win64な環境ではしったこっちゃないし、そもそもGtkベースだからLGPLなわけで、嫌LGPLな現代っ子の僕には向いてない。 * __[wxGo](https://github.com/JeroenD/wxGo)__ 名前の通りwxWidgetsのGoバインディング。SWIGを使っている。 既に二年近くメンテされてないし、どうしてもwxWidgets使いたい人向け。 他にもいくつかある。[ここ](http://go-lang.cat-v.org/library-bindings)ら辺を参照。 ここまで見た感じだと、まともに開発されているライブラリはgo-gtkしかない気がします。でもなぁ、Gtkなんだよなぁ…。 ##そんなあなたに、WALKはいかが? __[WALK](https://github.com/lxn/walk)__という、Windows向けのGUIライブラリがあります。これは、何かのGo言語バインディングというわけではなくて、Go言語から直接Win32APIを叩くことでGUIを作るライブラリです。 開発は盛んな方だと思います。600コミット以上されていて、最近もコミットされ続けていますし。 __go-gtk__ ほどじゃないのが残念ですが。 どれだけの人に伝わるかわかりませんが、Rubyで言えば[VisualRuby](http://www.osk.3web.ne.jp/~nyasu/software/vrproject.html)みたいなものです、多分。 ぶっちゃけたことを言ってしまえばただWin32APIのラッパなのですが、うまい具合に抽象化されていて、結構使いやすいです。 あと、完全にWindows向けかつGo言語単体で完結しているので、Windowsならインストールが楽ちんです。 ##なにはともあれインストール 僕の環境はWindows 8上のgo version 1.1.1 windows/amd64ですが、Windowsならおそらくはうまくいくと思います。 インストール方法は、コンソール上で、 ``` go get github.com/lxn/walk ``` を叩くだけ。簡単ですよね? 何も出力されなければ成功です。Go言語は怖いぐらいに _「沈黙は成功なり」_ の法則を順守している気がします。せめて依存関係ぐらい出力してくれてもいいのに…。 ちなみに、何か出力されていたら十中八九エラーだと思います。恐らく一番多いエラーは、バージョン1.1.x未満のGo言語を使っていることによるので。とりあえず[最新版のGo言語](http://code.google.com/p/go/downloads/list)をインストールしてきてください。 他にも、環境変数`GOPATH`が設定されていなかったり、Gitがインストールされていなかったりするとエラーが出ます。注意してください。 ##それでは使ってみよう テキストエリアの文字列を検索する、というプログラムを作ることにします。名前は`searchbox`です。 まずは、`mkdir searchbox && cd searchbox`で、新しいディレクトリを作って、移動してください。ここに必要なファイルを置いていきます。 (なぜわざわざディレクトリを掘る必要があるの? って感じですが、あとあと`go build`して実行可能ファイルを作ることになるので、ディレクトリを分けておいたほうが都合がいいのです) 次に、プログラムの主役ソースコードを書きます。 とりあえず僕が書いたコードを載せておきます。(このコードの[Gist](https://gist.github.com/alucky0707/6199515)) ```go:searchbox.go package main //WALK関連のライブラリ import ( "github.com/lxn/walk" . "github.com/lxn/walk/declarative" ) //その他標準ライブラリ import ( "fmt" "log" "strings" ) func main() { mw := &MyMainWindow {} if _, err := (MainWindow { AssignTo: &mw.MainWindow, Title: "SearchBox", MinSize: Size {300, 400}, Layout: VBox {}, Children: []Widget { GroupBox { Layout: HBox {}, Children: []Widget { LineEdit { AssignTo: &mw.searchBox, }, PushButton { Text: "検索", OnClicked: mw.clicked, }, }, }, TextEdit { AssignTo: &mw.textArea, }, ListBox { AssignTo: &mw.results, Row: 5, }, }, }.Run()); err != nil { log.Fatal(err) } } //structにまとめることで、グローバル変数を作らない type MyMainWindow struct { *walk.MainWindow searchBox *walk.LineEdit textArea *walk.TextEdit results *walk.ListBox } func (mw *MyMainWindow) clicked() { word := mw.searchBox.Text() text := mw.textArea.Text() model := []string {} for _, i := range search(text, word) { model = append(model, fmt.Sprintf("%d文字目に発見", i)) } log.Print(model) mw.results.SetModel(model) } //textからwordを検索して、位置をUnicode単位で返す func search(text ,word string) (result []int) { result = []int {} i := 0 for j, _ := range text { if strings.HasPrefix(text[j:], word) { log.Print(i) result = append(result, i) } i += 1 } return } ``` 簡単にこのコードを説明すると、最初の`import`でWALK関連のパッケージを読み込み、 ```go: //WALK関連のライブラリ import ( "github.com/lxn/walk" . "github.com/lxn/walk/declarative" ) ``` main関数の中でWindowの構造を作り(この部分がかなり宣言的でイケてると思います)、Runメソッドを呼びGUIを初めています。 ```go: if _, err := (MainWindow { AssignTo: &mw.MainWindow, Title: "SearchBox", MinSize: Size {300, 400}, Layout: VBox {}, Children: []Widget { GroupBox { Layout: HBox {}, Children: []Widget { LineEdit { AssignTo: &mw.searchBox, }, PushButton { Text: "検索", OnClicked: mw.clicked, }, }, }, TextEdit { AssignTo: &mw.textArea, }, ListBox { AssignTo: &mw.results, Row: 5, }, }, }.Run()); err != nil { ``` AssignToで実施のWidgetとの結びつけをするのですね。 で、その後のclickedメソッドが「検索」ボタンを押した時に呼ばれるものです。 ```go: func (mw *MyMainWindow) clicked() { word := mw.searchBox.Text() text := mw.textArea.Text() model := []string {} for _, i := range search(text, word) { model = append(model, fmt.Sprintf("%d文字目に発見", i)) } log.Print(model) mw.results.SetModel(model) } ``` search関数は、本当は`index/suffixarray`パッケージを使いたかったのですが、suffixarrayがbyteにしか対応していなかったため、byte位置を教えられてもなあ、という気がしたので定義したものです。 あとはまあ、雰囲気で察してください。 ソースコードができたら、マニフェストファイルを書きます。書かないと起動しないので注意してください。 マニフェストファイルは`実行可能ファイル名.manifest`という名前で保存します。 この場合は`searchbox.exe.manifest`で問題ないでしょう。 ```xml:searchbox.exe.manifest <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="SomeFunkyNameHere" type="win32"/> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/> </dependentAssembly> </dependency> </assembly> ``` ここまできたら、あとはbuildしてコンパイルするだけです。 ``` go build ``` (起動時にコマンドプロンプトが表示されないようにするには、 ``` go build -ldflags="-H windowsgui" ``` として下さい) でオーケーです。 早速、起動してみましょう。 ``` searchbox.exe ``` こんな画面が立ち上がれば、無事成功です。  青空文庫から適当に文章を引っ張ってきて「検索」をしてみると、こんな風になります。  良い感じじゃないでしょうか? ##最後に 説明をかなり簡略化したため、わかりにくいところ(コードの意味とか)は多いと思います。そこはWALKのexamplesやgodocを見て補完してください。(他力本願ですみません) Go言語でGUIをする、という記事はあまり無くて、しかも大抵は環境がOS XかLinuxという状況だったので、この記事がWindowsでGo言語やろうとしてる人の支えに、またGo言語を始める切っ掛けになったらいいな、なんて思ってみたりもしています。 それではっ。 #イヤッッホォォォオオォオウ!Go言語最高ー!! この前AIRをやったばかりなんです。叫びたい年頃なんです。 |
|
| 804位 |
|
|||
|
21:16:40 |
|
|
AngularJS で構築するアプリケーションで $locationProvider.html5Mode をどう設定するか小一時間悩んだ結果をまとめてみました。
導入 ------------------------------------------ AngularJS では $routeProvider を使用してクライアントサイドでのルーティングが可能です。 ```javascript angular.module('app').config(['$routeProvider', function($routeProvider) { $routeProvider .when('/aaa/', {controller: 'AaaController', templateUrl: 'aaa.html'}) .when('/bbb/', {controller: 'BbbController', templateUrl: 'bbb.html'}); }]); ``` こんなカンジで指定することでURLに応じて Controller と View が変更されるようになります。便利だね! ところで、このURLですが標準ではこんなカンジになります。 ``` http://www.example.com/#/aaa/ ``` これは **Hashbang Mode** と呼ばれ、ハッシュ(#)以降の部分をアプリケーションのパスとして利用するモードです。 ちなみに Hashbang とは "#!" のことで、一昔前に Ajax を多用したサイトでURLによく使われていました(なぜ多用され、現在廃れつつあるのかは "Hashbang URL" とかでググりましょう)。 AngularJS の標準では "!" つまりbang部分がないのでなんだかちょっと違和感があります。 ちなみに hashPrefix という設定をすることで正当な(?) Hashbang にすることもできます。 ```javascript angular.module('app').config(['$locationProvider', function($locationProvider) { $locationProvider.hashPrefix('!'); }]); ``` 本題 ------------------------------------------ 前述の Hashbang を利用したURLは現在では「あまり使うべきではない」と言われています。 また HTML5 で pushState という機能が採用され Hashbang を使わずともブラウザヒストリを記録しつつクライアントサイドで画面を書き換えることができるようになりました。 AngularJS では $locationProvider.html5Mode を変更することで Hashbang Mode から HTML5 Mode に変更することができます。 ```javascript angular.module('app').config(['$locationProvider', function($locationProvider) { $locationProvider.html5Mode(true); }]); ``` Hashbang Mode と HTML5 Mode それぞれでURLは次のようになります。 ``` # Hashbang Mode http://www.example.com/#/aaa/ # HTML5 Mode http://www.example.com/aaa/ ``` ただし pushState に対応していない IE9 などのブラウザもまだまだ現役です。 AngularJS ではそういった pushState 未対応ブラウザの場合は自動で Hashbang Mode にフォールバックしてくれます。超便利だね! Hashbang Mode と HTML5 Mode の違い ------------------------------------------ 先述の通り Hashbang を使用したURLは廃れつつあります。……が、HTML5 Mode も万能というわけではありません。 先ほどのように HTML5 Mode を使うと http://www.example.com/aaa/ の様にURLにハッシュが含まれなくなります。 その結果、Hashbang Mode と HTML5 Mode で次のような違いが現れてしまいます。 ``` # Hashbang Mode http://www.example.com/#/aaa/ -> WEBサーバー www.example.com の / にリクエストが飛ぶ # HTML5 Mode http://www.example.com/aaa/ -> WEBサーバー www.example.com の /aaa/ にリクエストが飛ぶ ``` つまり次の通り。 - Hashbang Mode ではクライアントサイドでページを生成すればOK。 - HTML5 Mode ではサーバー側で /aaa/ に対応するページを生成する必要がある。所謂 pjax にサーバー側で対応しなくてはならない。 場合によっては後者はとても面倒です。 Hashbang URL の問題点とは? ------------------------------------------ ところで Hashbang を使うべきではない理由とはなんでしょうか。[参考サイト](http://d.hatena.ne.jp/karasuyamatengu/20110212/1297465199)によればざっくり次のようなカンジです。 - GoogleBot 以外の Hashbang に対応していないクローラー、ボットなどがコンテンツにアクセスできなくなる - ブラウザ外のキャッシュができなくなる - Microformats が使い難くなる - Facebook の Like widgets などが難しくなる - Referer にページレベルの情報が無くなる - JavaScript にエラーがあるとなにも表示されない - arrayの終りに,が残っているだけでIE利用者には真っ白な画面しかみえなくなる - 開発者が console.log を間違えて残してしまうとほとんどのユーザー(IE利用者)が真っ白な画面を見ることになる - サイト管理者のコントロール下にない広告のJavaScriptにエラーがあるとコンテンツが見れなくなる 結論:どちらのモードを使うべきか ------------------------------------------ 先ほどの参考サイトにもほぼ同じ事が書いてありますが、次のように考えることができると思います。 - 商品ページなどSEO等が重要になる所謂「コンテンツ」の場合は積極的に HTML5 Mode を採用するべき。 - 管理画面や、フォームなどコンテンツというよりは「アプリケーション」と呼ぶべきページの場合は場合によって Hashbang Mode の採用を検討する。 - ただし、一般に公開されるお問い合わせフォームなどで、外部の広告用スクリプトなどを利用する場合は細心の注意を払う(できるなら HTML5 Mode)を使う。 以上。 参考 ------------------------------------------ - [AngularJS: Using $location](http://docs.angularjs.org/guide/dev_guide.services.$location) - [さらなる「#!」URL批判 - karasuyamatenguの日記](http://d.hatena.ne.jp/karasuyamatengu/20110212/1297465199) |
|
| 805位 |
|
|||
|
01:46:56 |
(フリーランス 所属)
|
|
by @mixiappwchr http://youtu.be/l0EUo7nGNDg
 最近iBeacomなどBluetooth LE周りの技術に感心が集まっており、iOSでの開発も活発になってきていると思います。 iOSではCoreBluetoothをはじめとしたBluetoothを扱うためのフレームワークが複数存在し、用途別に選べるくらい充実しています。 [iOSでBluetoohLE アプリを作るための基礎知識] (http://qiita.com/appwatcher/items/5c4585f61fc0a0d6269b) その中でも一番低レベルの部分まで扱うCoreBluetoothフレームワークは本格的にBluetooth LEを扱うにはさけては通れません。 しかし、このフレームワークはdelegateの嵐で扱うのに一苦労です。 そこで、もっと簡単に扱えるようにBlocksで処理がかけるようし、さらにもう少し簡単に扱えるようにしたライブラリを書きました。 Beaconとして動かすクラスやCLLocationManagerによるBeaconモニタリングクラスのラッパーも用意しています。 https://github.com/nyankichi820/MYBluetoothBlocks サンプルプロジェクト動画  http://youtu.be/l0EUo7nGNDg 結構癖のあるフレームワークなのでこれでBluetooth開発に弾みを付けてもらえればと思います。 ##### appwchr post ---- [iOSの開発をする上で絶対に使うべき!外せない!webサービス、開発ツール集](http://qiita.com/appwatcher/items/07a3babcb9b6cefb307e) [iOSでこんなアプリ,こんな機能を作りたかったらこれを見ろ!作りたいアプリに対応するクラス、フレームワーク、ライブラリのまとめ!] (http://qiita.com/appwatcher/items/b02255026a3ee6d95142) [注目のiBeacomなどの波に乗り遅れないために!iOSのBluetooth開発を容易にするライブラリを書きました。] (http://qiita.com/appwatcher/items/7491beffd7260b713542) [まだまだあった!iOSの開発を劇的に改善する最新のwebサービス、開発ツール集1] (http://qiita.com/appwatcher/items/f0024fe2ac34da345f04) [さらに快適なアプリ開発を!iOSの開発をもっと劇的に改善する最新のwebサービス、開発ツール集2] (http://qiita.com/appwatcher/items/c15d7311e71b4c2b77f1) [スパゲッティから脱出!iOS開発における遷移の問題をすっきり解決する便利ルーティングライブラリをご紹介] (http://qiita.com/appwatcher/items/259e8d13fff0547e90af) |
|
| 806位 |
|
|||
|
19:01:06 |
|
|
[**Flycheck**](http://www.flycheck.org/) は、コーディング中にリアルタイムでコードのシンタックスチェックやスタイルチェックを行なってくれる拡張機能です。以下のように、エラーが検出された行には **!** が付いて、そこにカーソルをあわせるとエラー内容が表示されます。
似たような拡張として **Flymake** がありますが、 Flycheck はデフォルトで[多くの言語](http://www.flycheck.org/manual/latest/Supported-languages.html#Supported-languages)のシンタックスチェックに対応していることが特徴です。  Flymake も Flycheck も使ったことがない、という方は Flycheck を使ってみることをおすすめします。 # インストール ELPAパッケージを [MELPA](http://melpa.milkbox.net/#/flycheck) から手動でインストールするか、 [Cask](http://cask.readthedocs.org/) や [El-Get](http://tapoueh.org/emacs/el-get.html) を使うと良いみたいです。 ```:ELPA M-x package-install RET flycheck ``` ```el:Cask (source gnu) (source melpa) (depends-on "flycheck") ``` Cask は 結構便利で、パッケージ管理にはおすすめです。Flycheck に限った話じゃないですが。 # 設定 Flymake と比べると Flycheck が使えるようになるまでの設定はかなりシンプルです。 `init.el` に以下の一文を加えるだけで動きます。 ```el:init.el (add-hook 'after-init-hook #'global-flycheck-mode) ``` あとは、対応している言語のメジャーモードが起動すると、自動的にシンタックスチェックが走るようになります。 # 使い方 ## エラーが出ているかどうか モードラインの **FlyC:xx/yy** を見るとエラーが出ているかわかります。 **xx** はエラーの数で、 **yy** はワーニングの数です。 ## エラー箇所にジャンプする `flycheck-next-error` で次のエラー箇所に、 `flycheck-previous-error` で前のエラー箇所にジャンプすることができます。よく使うので、キーバインドを設定しておいた方が良いでしょう。 ## 使用するシンタックスチェッカーを選ぶ 基本的には、 [Supported languages](http://www.flycheck.org/manual/latest/Supported-languages.html#Supported-languages) に載っているシンタックスチェッカーはデフォルトでほとんど有効になっています。 ただ、ものによっては自分で設定を加えたりしないと有効にならないものもあります。例えば、 `javascript-gjslint` はデフォルトでは有効になっていません。 `M-x flycheck-select-checker RET javascript-gjslint` を実行することで有効化することもできますが、この方法だと逆に `javascript-jshint` などが無効になってしまいます。 チェッカーを追加する形で利用するには、 `init.el` に以下のような設定を加えます。 ```el:init.el (flycheck-add-next-checker 'javascript-jshint 'javascript-gjslint) ``` ## ファイル毎に別々の設定を適用する このファイルだけはあのチェックを無効化したい、有効化したい。などの設定は以下のようにコメントで `Local Variables:` `End:` で囲むことで記述できます。例えば、以下はこのファイルのシンタックスチェッカーを `python-pylint` に設定しています。 ```py # Local Variables: # flycheck-checker: python-pylint # End: ``` ちなみにこれは Flycheck 独自の機能ではなくて、 Emacs の機能のようです。 [Specifying File Variables](http://www.gnu.org/software/emacs/manual/html_node/emacs/Specifying-File-Variables.html) # Flycheck extension ## flycheck-pos-tip 私が Flycheck を使い始めてとりあえず気になったのが、エラー内容がエコーエリアに表示されることでした。エコーエリアに表示されると視点の移動が結構大きいので、その場にツールチップで表示してくれないかなーと思ったので、自分で [flycheck-pos-tip](https://github.com/flycheck/flycheck-pos-tip) というものを作ってみました。  こんな感じで表示されます。 ### インストールと設定 MELPA に登録してあるので、本体と同じようにインストールできます。逆に Marmalade には登録してないです。 ```:ELPA M-x package-install RET flycheck-pos-tip ``` ```el:Cask (source gnu) (source melpa) (depends-on "flycheck") (depends-on "flycheck-pos-tip") ``` `init.el` には以下のような設定を加えます。 Flycheck はエラーを表示するための関数をカスタマイズできるように設計されていたので楽ちんです。 ```el:init.el (eval-after-load 'flycheck '(custom-set-variables '(flycheck-display-errors-function #'flycheck-pos-tip-error-messages))) ``` ぜひ使ってみてください。 |
|
| 807位 |
|
|||
|
12:54:35 |
|
|
# 超エコで超高速なやつらの性能比較: Go vs Node.js (vs Vert.x)
今時のWebで大量接続で高負荷になりそうなサイトを作りたい人は必見。 スタートアップ起業が選ぶなら、こういう言語やフレームワークを選ぶべき。 人気が出て急激な負荷に耐えられる様に、最初から正しいアーキテクチャを選んでね。 妙なやつを選んだらサーバはたくさん必要だし、クラウドでも費用が高くなるし大変だよ。 Node.js vs Go の記事を読んだのでこの記事を書いています。 http://yosuke-furukawa.hatenablog.com/entry/2014/02/10/134014 https://speakerdeck.com/yosuke_furukawa/benchmarking-node-dot-js-vs-golang http://sssslide.com/speakerdeck.com/yosuke_furukawa/benchmarking-node-dot-js-vs-golang  # Go Go 速いっスねぇ。 やっぱりコンパイラだね。 Goroutine とかが効いているんだろうな。 結構エコだと思う。 プログラマが少ないのがちょっと気になる。 C/C++ でやるよりは Go をお勧めします。 メモリーリークさせるより GC で解決する方が今時だよね。 # Node.js でもしばらく Node.js で行きたいかな。 JavaScript プログラマはいっぱいいるし。 簡単でエコだと思う。 ES6 がデフォルトで動く node v4/v5 を使うといいかな。 ES6 generators/yield を使った aa や co とか koajs が革命を起こすと信じている。 (ES7 async/await を ES6 generators/yield と aa/co で実現できる) co-monk (MongoDB) もちゃんとやってみたいね。 (coroutine の co ね, async-await の aa ね) コールバック地獄に陥っている人、是非注目してね。 他の言語の yield と同じと思っている人は大間違いだよ。 スレッドをほとんど使わないのが超エコなんだよね。 # Vert.x Java VM が好きな人は Vert.x でやってね。 Java, JavaScript, Ruby, Python プログラマはたくさんいるし 既存のライブラリも呼べていいと思う。 Groovy もあるし Scala, Clojure, とかもそのうち。 で、しっかりとエコだと思う。 Java 8 以降がいいかな。 Lambda 式とか使えるし。 # Go も Vert.x も速いね ## Go 速い http://www.techempower.com/benchmarks/#section=data-r8&hw=i7&test=json ## Vert.x も速い http://www.techempower.com/benchmarks/#section=data-r8&hw=i7&test=plaintext # 参考: Go 関係 http://www.slideshare.net/derekcollison/go-conference-japan http://www.slideshare.net/mgiglesias # 参考: Node.js 事例 * LinkedInの事例: Ruby Rails から Node.js http://www.infoq.com/jp/news/2012/10/Ruby-on-Rails-Node-js-LinkedIn * PayPalの事例: Java から Node.js http://www.infoq.com/jp/news/2014/01/paypal-java-javascript * アメーバピグ, ピグライフ http://www.slideshare.net/akuwano/ss-8876695 http://ameblo.jp/ca-1pixel/entry-11476850674.html http://next.rikunabi.com/tech/docs/ct_s03600.jsp?p=001987 * [Node.js について知っておくべきこと](http://readwrite.jp/archives/2125) この記事を読んで、同感だなぁ... と思った。 そうなんだよ。他の言語じゃダメなんだよ。 というか他の仕組みじゃダメなんだよ。 という事でこの記事を書いています。 * Life is beautiful - node.js と thread hog の話 以前、これ読んでうれしくなった。 ・Life is beautiful - [node.js と thread hog の話(1)](http://satoshi.blogs.com/life/2012/10/thread_hog.html) ・Life is beautiful - [node.js と thread hog の話(2)](http://satoshi.blogs.com/life/2012/10/thread.html) ・Life is beautiful - [node.js と thread hog の話(3)](http://satoshi.blogs.com/life/2012/10/closure.html) ・[Life is beautiful](http://satoshi.blogs.com/life/) * [Node.jsはC10K問題を解決する銀の弾丸か](http://tech-public.blogspot.jp/2012/12/nodejsc10k.html) 他の言語で C10K サーバを実装してみるとどれだけ苦労するのかがわかり Node.js がどれだけ簡単に記述できるのかわかると思うよ。 * 自分でも Node.js で C10K サーバ書いてみた 大体 16000 までくらいなら接続できる様だ。C16K だね。 また別記事書く予定。 * [サーバーマシン1台で同時接続者数1万名を実現するにはどうすればいいのかというノウハウと考え方](http://gigazine.net/news/20120831-10000-jointer-1-server-cedec2012/) * [サーバーマシン1台でMMO同時接続者数10,000名を実現する方法](http://cedec.cesa.or.jp/2012/program/AB/C12_I0284.html) |
|
| 808位 |
|
|||
|
00:00:08 |
|
|
# 1.はじめに
Unityアドベントカレンダー12日目の記事となります。 以前より、UnityでAndroid開発をどうやって始めたら良いの?や、 何が出来るのというお話をよく聞きます。 そこで、ハッピーバースデートゥミーな私UnityBearことリラゼクスがAndroidのネイティブ開発良いよ! …といった内容を書いていきたいと思います。 初心者向けとは思っていますが、難しかったらごめんなさい(´・ω・`) # 2.自己紹介 2010年iPhoneを買うか、Xperia X10を買うか悩んだ挙句、Xperia X10を買ったことにより Android開発に興味を持ち、ネイティブ開発によるゲーム製作などをやってまいりました。 基本Android寄りな開発者ですが、必要に応じてiOS開発も時々やってます。 # 3.Unity使用歴について えーと…、Unityワカラナイヨー。初心者ダヨー。[要出典] # 4.ネイティブプラグインで出来ること UnityとAndroidのネイティブでできることの違いは?という話をよく聞きます。 Unityでは3Dモデルや2D画像の描画や物理エンジンの計算などの機能に特化しています。 (ネイティブでも上記は実現可能ですが、Unityはより簡易的に機能を使えるよう提供していると考えてください) 当然Unityで提供されてる機能をわざわざネイティブで書くということはないので、 以下の様な用途でネイティブプラグインは使われています。 ・OS組み込みダイアログを表示 ・Webページをゲーム内に表示 ・Push通知を利用 ・課金機能を利用 ・ゲームスコア、実績機能の利用 これらはAssetStoreでも提供されています。 その他ネイティブコードを使うメリットとして、通常のAndroidネイティブ開発向けに作られたライブラリを 利用することも可能な点が挙げられます。 # 5.ネイティブプラグイン開発入門編 ## 5.1 前置き Androidのプラグインは以下の作り方が存在します。 ・Java言語で記述し、Unityのコードから呼び出し ・Unityのコード上のみからネイティブコードを呼び出し ・C言語で記述し、Unityのコードから呼び出し ・Java言語で記述し、C++言語を橋渡しにUnityのコードから呼び出し この中では、一番上の「Java言語で記述し、Unityのコードから呼び出し」が最も利用されます。 コードの書き方としてよく参考になると挙げているのはWebViewの実装です。 (1)Activityを上書きする方法 https://github.com/keijiro/unity-webview-integration (2)Activityを上書きしない方法 https://github.com/gree/unity-webview 上記2つは双方同じWebViewの実装ですが、実装方法が異なっています。 (※Unity4.3では仕様変更により上記のプラグインは動きません) WebViewについては涙無くしては語れないことが多々あるんですが、 長いので割愛させていただきます。 ActivityとはAndroidで画面を構成する枠組みです。 その枠組みの中にボタンなどのパーツを配置してAndroidの1画面が完成します。 (※本当はウィンドウと言ってしまいたいんですが語弊がありそうなので枠組みと表現しています) Activityを上書きする場合にはパッケージ名がプラグイン、Unityプロジェクト、 AndroidManifest(Androidアプリの設定ファイル)で一意でなければなりません。 (確かこれを守らないとアプリが実機で実行された瞬間クラッシュした気がします・・・) 一方でActivityを上書きしない場合は、大抵の用途には使用できますが、 Activity継承メンバを上書きしたい場合や、 メインスレッドで何か処理を実行させたいなどといった用途では使用できません。 それぞれの用途によりけりで、一概にどっちが良いとは言えませんが、 基本は後者の「Activityを上書きしない方法」で作るのがオススメです。 ## 5.2 いざ実践(`・ω・´) ただでさえ、内容が長くなるため、あらかじめAndroidSDKの導入とパスは通しており、 UnityからのAndroid出力が可能な前提で話を進めさせていただきます。 ネイティブコードのプラグインを作成するためにはEclipseが利用できます。 ここでは、AndroidSDK,ADTは導入済として説明を行います。 ### 5.2.1 プラグイン用プロジェクトの作り方 (a) File -> New -> Other -> Android Application Projectを選択します。 (b) Application名、プロジェクト名、パッケージ名を入力します。 下部でSDKのバージョンを指定します。 (SDKのバージョンは使用者の用途で変わりますが、とりあえずAPI10なら色々都合がいいです)  (c) Create custom launcher icon、Create activityのチェックを外します。  (d) 作成したパッケージからsrcを右クリックし、New -> Packageを選択し、 先ほど入力したパッケージ名を入力します。 (パッケージ名を忘れてしまった場合はAndroidManifest.xmlを開いてみると書いてあります) (e) 作成したパッケージを右クリックし、New -> Classを選択します。 クラス名を入力し、クラスを作成します。 (f) 作成したクラスにコードを記述していきます。 ### 5.2.2 プラグインjarの作り方 (a) コードを記述し終えたらプロジェクトを右クリックし、Exportを選択します。  (b) Javaカテゴリ以下のJAR fileを選択します。  (c) 左の項目のプロジェクト名の横の三角を押し、プルダウンメニューを開き、 src以外のチェックを外します。 (d) 右の項目のチェックを全て外します。 (e) 保存先を選択し、Finishを押します。  ### 5.2.3 Unityプラグインの追加 (a) プロジェクトを右クリックし、プロパティを開きます。 (b) 左項目からJava Build Pathを選択し、 右項目でLibrariesタブが開かれてるのを確認し、Add External Jars...ボタンを押します。  (c) Unityで用意されてるJarファイルを選択します。 例えば、デフォルトのインストール先になっていれば以下の場所を選択します。 C:\Program Files (x86)\Unity\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar ### 5.2.4 Unity上での作業 (a) Assets以下にPluginsフォルダを作成します。 (b) Plugins以下にAndroidフォルダを作成します。 (c) UnityのC#スクリプトをPlugins以下に、jarライブラリ、ManifestをAndroidフォルダに それぞれ配置します。 ### 5.2.5 コードについて 今回は簡単な文字の受け渡しを行うプラグインを例に取り、説明を行っていきます。 まずはJavaコードからです。 ```java:Hoge.java package com.relboxes.unity.hoge; import com.unity3d.player.UnityPlayer; import android.util.Log; public class Hoge{ // (1) Unity側から呼び出されて何か処理を行う関数 public void FuncA(final String str) { Log.d("Unity Native", str); } // (2) Unity側に値を返す関数 // C#とJavaでバイト数が同じ型同士ならやりとり可能。 // ただし、C#のstring型とJavaのString型など同じ型でも名前が違うものもあるので注意。 // C#のint, long, bool(Javaだとboolean型)が使用可能。 public String FuncB(final String str) { return "Back " + str; } // (3) Unity側のスクリプトのonCallBack関数を呼び出す関数 // スクリプトがAttachされたGameObjectの名前を渡し、スクリプト内指定の // 関数を呼び出すことが可能。呼び出し先の関数がvoid型の場合は // UnityPlayer.UnitySendMessage(gameObjName, "onCallBack", ""); のように記述。 // 3番目の引数は必ず記述しないとコンパイルエラーになった気がする…。 public void FuncC(final String gameObjName, final String str) { UnityPlayer.UnitySendMessage(gameObjName, "onCallBack", str); } } ``` 次にUnity側のC#コードです。 以下のコードをPluginsフォルダに入れておきます。 ```csharp:HogeScript.cs using UnityEngine; using System.Collections.Generic; public class HogeScript: MonoBehaviour { static AndroidJavaObject m_plugin = null; static GameObject m_instance; public void Awake () { // gameObject変数はstaticでないのでstatic関数から呼ぶことが出来ない. // そのためstaticの変数にあらかじめコピーしておく. m_instance = gameObject; #if UNITY_ANDROID && !UNITY_EDITOR // プラグイン名をパッケージ名+クラス名で指定する。 m_plugin = new AndroidJavaObject( "com.relboxes.unity.hoge.Hoge" ); #endif } // NativeコードのFuncA 関数を呼び出す. // Native側のコードが引数を持たない関数なら、 // m_plugin.Call("FuncA"); のように引数を省略すればOK。 public static void CallFuncA(string str) { #if UNITY_ANDROID && !UNITY_EDITOR if (m_plugin != null){ m_plugin.Call("FuncA", str); } #endif } // NativeコードのFuncB 関数を呼び出す. public static string CallFuncB(string str) { string modoriValue = null; #if UNITY_ANDROID && !UNITY_EDITOR if (m_plugin != null){ modoriValue = m_plugin.Call<string>("FuncB", str); } #endif return modoriValue; } // NativeコードのFuncC 関数を呼び出す. public static void CallFuncC(string str) { #if UNITY_ANDROID && !UNITY_EDITOR if (m_plugin != null){ m_plugin.Call("FuncC", str); } #endif } // ネイティブコードから呼ばれる関数 // publicでかつ、非static関数でないと呼ばれない. public void onCallBack(string str) { Debug.Log("Call From Native. (" + str + ")"); } } ``` # 6.ネイティブのデバッグについて ネイティブでの実行内容をコンソール出力したいという場面は多々発生します。 Androidでは公式に提供されているLogCatというアプリを利用することが可能です。 LogCatを利用するときは、その他の機能も組み込まれたddmsを使うと便利です。 ddmsは以下のディレクトリに存在します。 (AndroidSDKインストールパス)\android-sdk\tools\ddms.bat ・使い方 端末をUSBデバッグ接続後、(A)で端末を選択すると(B)にログが表示されます。 (C)のテキストボックスでログから特定項目を抽出できるのでUnityと入力するのがオススメです。 (D)から更に細かい絞り込みが出来ます。  (分かる人は分かるかもしれない余談) この方法ではUnityアプリ関連しか取れないため、何か関連のサービスなどを取りたい場合は cygwin導入が前提ですが、コマンドから"adb shell ps | grep (パッケージ名)" と入力し、プロセスIDで抽出すると便利かもしれないですが、実行の度に変わってしまうので、 本当に確認が必要な場面にしか使えないのが玉にキズですね…。 # 7.ネイティブプラグイン開発応用編 …はい。書いてる当人も疲れてきたので急ぎ足で進行していきます。 さて、ここからは実際にネイティブで何が出来るのかというを実例を交えつつ 説明していきます。 ## 7.1 何作ろう? 今回はTTS(Text to Speech)を例に取りたいと思います。 TTSとは、音声読み上げのことであり、Androidでは早い時期から導入されている機能でした。 (iOSでは7から利用できるそうなのでちょっと検証してみたいかなと思ったりしてます…) このTTSですが、標準のものでは日本語読み上げは出来ず、英語読み上げとなっています。 そこでKDDIが無料で提供しているN2を使いたいと思います。 N2はGooglePlayの以下のURLからダウンロード可能です。 https://play.google.com/store/apps/details?id=jp.kddilabs.n2tts インストールの注意点ですが、GooglePlayから落とすだけではダメで、 一回起動してダイアログの許可ボタンを押すのと、 端末設定から言語と入力→テキスト読み上げ→規定エンジンをN2に変更しておく必要があります。 (この設定は端末により、場所が異なる可能性があります) ## 7.2 早速使ってみる まずは、ネイティブ側のコードからです。 ```java:TTSManager.java package com.relboxes.unity.tts; import java.util.Locale; import com.unity3d.player.UnityPlayer; import android.app.Activity; import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech.OnInitListener; public class TTSManager implements OnInitListener{ private TextToSpeech tts; public void Init() { final Activity a = UnityPlayer.currentActivity; tts = new TextToSpeech(a.getApplicationContext(), this); tts.setLanguage(Locale.JAPANESE); } public void onDestroy() { if (tts != null){ tts.shutdown(); } } @Override public void onInit(int status) { if (status == TextToSpeech.SUCCESS){ // TTS Setup Done. } else{ // TTS Not Setup. } } public void Speak(String message) { if (tts.isSpeaking() == false){ tts.speak(message, TextToSpeech.QUEUE_FLUSH, null); } } public void ForceStop() { tts.stop(); } public void SetPitch(float pitch) { tts.setPitch(pitch); } public void SetPitchRate(float pitch) { tts.setSpeechRate(pitch); } public boolean GetIsSpeeking() { if (tts != null){ return tts.isSpeaking(); } return false; } } ``` 続いてUnity側のPlugins以下に配置するC#コードです。 ```csharp:TTSManager.cs using UnityEngine; using System.Collections.Generic; public class TTSManager : MonoBehaviour { static AndroidJavaObject m_tts = null; static Queue<string> m_mesQueue = null; void Awake() { #if UNITY_ANDROID && !UNITY_EDITOR m_tts = new AndroidJavaObject( "com.relboxes.unity.tts.TTSManager" ); m_tts.Call("Init"); m_mesQueue = new Queue<string>(); #endif } void Start() { var pitch = PlayerPrefs.GetFloat("VoicePitch"); Debug.Log("Pitch:" + pitch); if (pitch != 0){ TTSManager.ChangePitch( pitch * 3.0f ); } var pitchRate = PlayerPrefs.GetFloat("VoicePitchRate"); Debug.Log("PitchRate:" + pitchRate); if (pitchRate != 0){ TTSManager.ChangePitchRate( pitchRate * 3.0f ); } } void Update() { #if UNITY_ANDROID && !UNITY_EDITOR if ((m_mesQueue != null) && (m_mesQueue.Count > 0) && (isSpeech == false)){ if (m_tts != null){ var message = m_mesQueue.Dequeue(); m_tts.Call("Speak", message); } } #endif } public static bool isSpeech{ get{ #if UNITY_ANDROID && !UNITY_EDITOR if (m_tts != null){ return m_tts.Call<bool>("GetIsSpeeking"); } #endif return false; } } public static void ChangePitch(float pitch) { #if UNITY_ANDROID && !UNITY_EDITOR if (m_tts != null){ m_tts.Call("SetPitch", pitch); } #endif } public static void ChangePitchRate(float pitch) { #if UNITY_ANDROID && !UNITY_EDITOR if (m_tts != null){ m_tts.Call("SetPitchRate", pitch); } #endif } public static void StopSpeech() { #if UNITY_ANDROID && !UNITY_EDITOR if (m_mesQueue != null){ m_mesQueue.Clear(); } if (m_tts != null){ m_tts.Call("ForceStop"); } #endif } public static void Speech(string message) { Debug.Log("Call Speech"); #if UNITY_ANDROID && !UNITY_EDITOR if (m_mesQueue != null){ m_mesQueue.Enqueue(message); } #endif } void OnApplicationPause(bool pauseStatus) { if (pauseStatus){ if (m_mesQueue != null){ m_mesQueue.Clear(); } StopSpeech(); } } } ``` 更に、テスト用のC#コードです。 ```csharp:TTSTestScript.cs using UnityEngine; using System.Collections; public class TTSTestScript : MonoBehaviour { void Start () { } void Update () { } void OnGUI() { if (GUI.Button(new Rect(0, 0, 100, 100), "Push Me")){ TTSManager.Speech("進捗どうですか"); } } } ``` 使い方としては、適当な空のGameObjectに上記のTTSManagerとTTSTestScriptの 2つをコンポーネントとして追加し、Android 端末で実行できたらボタンを押すだけです。 ほら、かんたんでしょ(棒 実はこのクラスはマスコットアプリ用に作ったのですが、 最近UnityとAndroidで音声認識の話も出来ると聞いたので、 これで会話アプリが実現できますね! 夢が広がります!!!!11 …ちなみにUnity4.3でも動作は確認しております。 # 8.まとめ ここまでかなり駆け足ですが、Unityネイティブ開発について書いてきました。 もしかすると間違っていたり、情報が古い可能性がありますので、 その際はご指摘頂けますと助かります。 正直、開発時の注意点などまだまだ書きたいことは山ほどあるのですが、 現時点での長文からお察しください…。 (どこかで書けたり話せる機会があれば良いなとポツリ。) この記事が今後ネイティブコードを書く人の助けになれば幸いです。 |
|
| 809位 |
|
|||
|
14:37:58 |
|
|
同じ内容はhttp://sinapps.hatenablog.jp/
にも載せています。 独自の認証機能を作る場合は、非常に役に立つメソッド。Deviseなどを使うのもいいが、拡張したいときに面倒くさいので、そういったことが想定される場合は自分で作ったほうが早い。 ##機能 Bcryptで暗号化したものをセットしたり、それで認証する機能を提供するメソッド。 パスワードの存在性をテストしてくれる。(passwordとpassword_confirmationアトリビュートは勝手に追加される。) ##使用するために必要なこと - password_digestカラムを追加する - bcrypt-ruby Gemを追加(or コメントアウト) ##コードリーディング 全体のソースコードは[こちらに載っています](https://github.com/rails/rails/blob/b965ce361b89ad33a4a4b422f8e564233926c723/activemodel/lib/active_model/secure_password.rb#L42) まず最初に ```ruby: def has_secure_password(options = {}) ... end ``` とあり、引数としてハッシュを受け取るようになっている。 次にpasswordをアトリビュートとして設定。 ```ruby: attr_reader :password ``` これによって、passwordが仮想アトリビュートとして使える。仮想という意味は、メモリ内に存在しデータベースには保存されないという意味。 そのあと引数のoptionsは以下のように処理されている。 ```ruby: if options.fetch(:validations, true) validates_confirmation_of :password, if: lambda { |m| m.password.present? } validates_presence_of :password, :on => :create validates_presence_of :password_confirmation, if: lambda { |m| m.password.present? } before_create { raise "Password digest missing on new record" if password_digest.blank? } end ``` 条件式の中では、optionsの中からvalidationsキーが見つかればその値を返し、そうでない場合はデフォルトのtrueが返されるようにfetchが使われている。 if文の中の1~3行目はこのメソッドを呼んだクラスにバリデーションの追加をしている。上から順に、パスワードが存在するときのpasswordとpassword_confirmationの同値性、作成時点でのpasswordの存在性、password存在時のpassword_confirmationの存在性をチェックしている。`validates_confirmation_of :password`は仮想アトリビュートであるpassword_confirmationを加える。最後の一行は保存される前にpassword_digestが空である場合は例外を出す。 ```ruby: if respond_to?(:attributes_protected_by_default) def self.attributes_protected_by_default #:nodoc: super + ['password_digest'] end end ``` このメソッドを使っているクラスでattributes_protected_by_defaultメソッドがオーバーライドされている場合は、そこにpassword_digestも加えている。 これで下地はできているので、そこに機能を追加するモジュールをインクルードしている。 ```ruby: include InstanceMethodsOnActivation ``` このモジュールはauthenticate, password=, password_confirmation=の3つのメソッドを提供している。まず最初に、authenticateは以下のようになっている。 ```ruby: def authenticate(unencrypted_password) BCrypt::Password.new(password_digest) == unencrypted_password && self end ``` ここで注意したいのが、==はBcryptのメソッドであるということ。暗号化されていない値とされている値を比べて同じである場合はtrueを返し、そうでない場合はfalseを返す。 &&でつないでいるのでtrueが返った場合はselfをメソッドの返り値として返す。(Rubyの場合&&で返るのはtrue,falseや1,0ではなくて最後に評価された値。) 次に、password=を見ていく。 ```ruby: def password=(unencrypted_password) unless unencrypted_password.blank? @password = unencrypted_password cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine::DEFAULT_COST self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost) end end ``` 引数が空でない場合(blank?でfalse)に中身が実行される。最初にpasswordインスタンス変数に引数突っ込んで、コストを決めてパスワードのダイジェスト生成。暗号化するには多少の時間を食う(コスト)ため、それを最小限に抑えるかどうかを決めている。 最後のpassword_confirmationのところはコード通りただの格納。 ##参考 - Rails API doc - http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html - bcrypt-ruby - https://github.com/codahale/bcrypt-ruby |
|
| 810位 |
|
|||
|
01:19:26 |
(RareJob.com 所属) |
|
フレームワークのロガーを追っていたら
基本的なPHPのログ出力方法についてわからなくなったので 基本に戻ろうと自分なりにまとめてみました。 # 環境 CentOS release 6.3 (Final) Apache/2.2 PHP 5.3 # php.iniの設定 webにHTMLとして表示する場合は ログ出力レベルを指定し、ログ表示設定をONにします。 ```php.ini error_reporting = E_ALL | E_STRICT display_errors on ``` ※商用などでは必ずOFF ファイルに残すために、log_errorsを設定します。 ``` log_errors = On ``` # httpd.confの確認 php側で設定しない場合、phpのエラーログはサーバの設定に依存します。 httpd.confのErrorLogにて設定されています。 ```httpd.conf ErrorLog logs/error_log ``` ServerRootの設定 + ErrorLogの設定 = ログ置き場ってことですね。 ## 吐き出すファイルの設定 php.iniのerror_logにログの出力先を設定します。 ファイル名を記述する場合は、絶対パスで記載し、 webサーバのユーザが書き込める必要があります。 ※相対パスでも書き込めますが、ドキュメントルートからになります。 ``` error_log = /var/log/php/error.log ``` syslogを通して吐き出すこともできます ``` error_log = syslog ``` syslogを使用しているなら/etc/syslog.confの設定値にもよりますが、 デフォルトでは/var/log/messages に吐かれます。 ※syslogを使用して、phpのエラーログを別ファイルで保存するためには ファシリティuser、レベルinfoを指定します。 例 ```/etc/rsyslog.conf user.info /var/log/php/error.log ``` 設定適用のためリスタート ``` /etc/rc.d/init.d/rsyslog restart ``` syslogでやると結構楽ですね。 ただphp以外にuser.infoで吐かれるsyslogがあると混乱しそう。 |
|
| 811位 |
|
|||
|
00:34:42 |
|
|
私が今年イベントで喋ったプログラマはたぶん50人近くいて、その度にJSのテストについて話を伺っておるのですが、ちゃんとJSのテスト書いてる人は1人しかいませんでした。html5カンファレンスの会場でオーディエンスに聞いた時もjsのテストを書いている人は2%くらいしかいませんでした。その多くが、書いた方がいいってのはわかってるんだけどねぇ〜みたいな感じでした。
#なぜ書かないか? テストコードを書くのがめんどうだから テストコードを修正するのがめんどうだから 非同期なテストを書きにくいから #じゃあ、その問題解決するからテスト自動化しようぜ テストを書かずにテストの自動化?そんなことできるのかよ #Porkyならできるよ! どうやって使うの? #jsファイルを4つ読み込むだけ [https://github.com/puriketu99/porky](https://github.com/puriketu99/porky)からcloneして、必要なファイルを読み込む ```sh:clone git clone https://github.com/puriketu99/porky.git ``` ```html:必要なjavascriptファイルを読み込む <script src="/porky/jquery.js"></script> <script src="/porky/indexeddb.shim.js"></script> <script src="/porky/jquery.indexeddb.js"></script> <script src="/porky/porky.js"></script> ``` #テストの登録は? 下記の関数をテストケースに登録する場合を考えます。 ```coffeescript:テスト対象の関数 append = function(){$("body").append("test case1");}; ``` javascriptコンソールから、下記を実行します。 ```javascript:console >porky.register({name:"test1",func:"append"}) ``` nameはテストの名称、funcは関数の名前です。 ajaxの場合は、is_ajax:trueを引数のオブジェクトに追加してください。 これでテストの登録は完了です。 #テストの実行は? Chromeのjavascriptコンソールから下記を実行します。 ```javascript:console >porky.run() ``` pokry.runを実行すると、下記のようにテスト結果がconsole上に出力されます。 ```yaml:結果 Porky Runner test1 UI test success JSON test success test1: 13ms Porky: 25ms ``` フィードバックがあればよろしくお願いします。 https://github.com/puriketu99/porky |
|
| 812位 |
|
|||
|
13:15:17 |
(Bracket, Inc 所属) |
|
Tig の表示方法あれこれ
このエントリーはGitアドベントカレンダーの十一日目です。十日目は [kyanny](http://qiita.com/users/kyanny) さんの[「Git における SSH オプション指定方法あれこれ」](http://qiita.com/items/c397370862095c305cbe)でした。タイトルは、パクr...リスペクトしました! ## Tigとは? Tig は ncurses ベースの Git のためのテキストユーザインタフェースです。 Gitリポジトリ内の変更内容を、Vimライクな操作で高速に閲覧することができます。 ## インストール Mac なら Homebrew か MacPorts でインストールできます。 あとは[こちら](http://jonas.nitro.dk/tig/)で。 ## 基本的な使い方 Git レポジトリ内で tig コマンドを打つと、カレントブランチの変更履歴が表示されます。 h でヘルプが見られるので、ビューの切り替え方法などの操作方法を調べることができます。 ## 本題 tig コマンドに引数を渡す事で、開き方を変えることができます。 特定の1つまたは複数のブランチを表示: $ tig master new_feature 全てのブランチを表示: $ tig --all リモートブランチの最新コミットを表示: $ tig origin/HEAD 2つのブランチ間の差を表示: $ tig test..master 単一ファイルの変更点を表示: $ tig -- README.rdoc 特定のリビジョンにおける README.rdoc ファイルの内容を表示: $ tig show v3.2.5:README.rdoc README.rdoc ファイルの、ある期間のリビジョンを表示: $ tig --after="2012-01-01" --before="2012-06-11" -- README.rdoc README.rdoc ファイルの、直近 1 週間のリビジョンを表示: $ tig --since=1.week -- README.rdoc コードのそれぞれの行を、誰が、いつコミットしたのかを表示: $ tig blame README.rdoc tig 便利ですね! ## 参考 * man tig - ネタ元。すべてここに書いてあります。 * [The tig Manual](http://jonas.nitro.dk/tig/manual.html) - man よりももう少し突っ込んだ使い方が書いてあります。 ----- 明日は [T_Hash](http://qiita.com/users/t_hash) さんです。 |
|
| 813位 |
|
|||
|
01:33:39 |
(株式会社メルカリ 所属) |
|
curl_multiはselectシステムコールを使って同時に複数のHTTPリクエストを行うことができます。マルチスレッドやらマルチプロセスを使っているわけではなく、原理はnode.jsなんかと近いI/O多重化で、一度に一つのことしかしていません。
PHPのcurlはlibcurlのAPIをほぼ踏襲しており、ちょっと取っつきにくいです。クラスでラッピングして、curl_close()などはデストラクタで呼ばれるように自動化すると、もう少しすっきりすると思います。 ```php:curl_multi.php <?php /** * curl_multiでHTTP複数リクエストを並列実行するテンプレ * */ //タイムアウト時間を決めておく $TIMEOUT = 10; //10秒 /* * 1) 準備 * - curl_multiハンドラを用意 * - 各リクエストに対応するcurlハンドラを用意 * リクエスト分だけ必要 * * レスポンスが必要な場合はRETURNTRANSFERオプションをtrueにしておくこと。 * - 全てcurl_multiハンドラに追加 */ $mh = curl_multi_init(); $urls = array( 'http://localhost/sleep.php?wait=3', 'http://localhost/sleep.php?wait=2', 'http://localhost/sleep.php?wait=1', ); foreach ($urls as $u) { $ch = curl_init(); curl_setopt_array($ch, array( CURLOPT_URL => $u, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $TIMEOUT, CURLOPT_CONNECTTIMEOUT => $TIMEOUT, )); curl_multi_add_handle($mh, $ch); } /* * 2) リクエストを開始する * - curl_multiでは即座に制御が戻る(レスポンスが返ってくるのを待たない) * - いきなり失敗するケースを考えてエラー処理を書いておく * - do~whileはlibcurl<7.20で必要 */ do { $stat = curl_multi_exec($mh, $running); //multiリクエストスタート } while ($stat === CURLM_CALL_MULTI_PERFORM); if ( ! $running || $stat !== CURLM_OK) { throw new RuntimeException('リクエストが開始出来なかった。マルチリクエスト内のどれか、URLの設定がおかしいのでは?'); } /* * 3) レスポンスをcurl_multi_selectで待つ * - 何かイベントがあったらループが進む * selectはイベントが起きるまでCPUをほとんど消費せずsleep状態になる * - どれか一つレスポンスが返ってきたらselectがsleepを中断して何か数字を返す。 * */ do switch (curl_multi_select($mh, $TIMEOUT)) { //イベントが発生するまでブロック // 最悪$TIMEOUT秒待ち続ける。 // あえて早めにtimeoutさせると、レスポンスを待った状態のまま別の処理を挟めるようになります。 // もう一度curl_multi_selectを繰り返すと、またイベントがあるまでブロックして待ちます。 case -1: //selectに失敗。ありうるらしい。 https://bugs.php.net/bug.php?id=61141 usleep(10); //ちょっと待ってからretry。ここも別の処理を挟んでもよい。 do { $stat = curl_multi_exec($mh, $running); } while ($stat === CURLM_CALL_MULTI_PERFORM); continue 2; case 0: //タイムアウト -> 必要に応じてエラー処理に入るべきかも。 continue 2; //ここではcontinueでリトライします。 default: //どれかが成功 or 失敗した do { $stat = curl_multi_exec($mh, $running); //ステータスを更新 } while ($stat === CURLM_CALL_MULTI_PERFORM); do if ($raised = curl_multi_info_read($mh, $remains)) { //変化のあったcurlハンドラを取得する $info = curl_getinfo($raised['handle']); echo "$info[url]: $info[http_code]\n"; $response = curl_multi_getcontent($raised['handle']); if ($response === false) { //エラー。404などが返ってきている echo 'ERROR!!!', PHP_EOL; } else { //正常にレスポンス取得 echo $response, PHP_EOL; } curl_multi_remove_handle($mh, $raised['handle']); curl_close($raised['handle']); } while ($remains); //select前に全ての処理が終わっていたりすると //複数の結果が入っていることがあるのでループが必要 } while ($running); echo 'finished', PHP_EOL; curl_multi_close($mh); ``` レスポンスに1秒、2秒、3秒かかる3つのAPIにリクエストしていますが、合計3秒程度で処理が完了します。 http://techblog.yahoo.co.jp/architecture/api1_curl_multi/ この記事とか、selectを使わず無限ループでレスポンスを待っている例が散見されるけど、待っている間CPUを食い潰してとても非効率なので、必ずselectを使いましょう。 サンプルでリクエストしているAPIは↓こんなの。waitに指定した秒数だけ待ってレスポンスを返します。 ```php:sampleAPI.php <?php /* * セキュリティ的に問題のあるスクリプトなので実験以外では使わないでね。 */ sleep((int)$_GET['wait']); header('Content-Type: text/plain'); echo $_GET['wait']; ``` |
|
| 814位 |
|
|||
|
00:56:42 |
|
|
##JUnitによるユニットテスト ###4フェーズテスト ```java:Example public class FourPhaseTest { @Test public void testCase() throws Exception{ // 1.初期化 Calc sut = new Calc(); int expected = 7; // 2.テスト実行 int actual = sut.add(3.4); //3.アサーション assertThat(actual, is(expected)); //4.終了処理(必要な場合) sut.shutdown(); } } ``` ###例外送出のテスト ```java:Sample @Test(expected=Exception.class) public void testCase() { } ``` ###タイムアウト ```java:Sample @Test(timeout=1000L) public void testCase() { } ``` ###アノテーション |アノテーション|説明| |:--|:--| |@Test|テストケース| |@Ignore|テストの実行を除外| |@Before|初期化| |@After|後処理| |@BeforeClass|初期化(クラスごと)| |@AfterClass|後処理(クラスごと)| |@RunWith|テストランナー| ###アサーション |アサーション| |:--| |assertThat(actual, is(expected));| |fail("未実装");| ###ルール |ルール|説明| |:--|:--| |TemporaryFolder|一時ファイル| |ExternalResource|外部リソース| |Verfier|事後検証| |ErrorCollector|エラーの収集| |ExpectedException|例外の検証| |Timeout|タイムアウト| |TestWatcher|テストの監視| |TestName|テストケース名| ```java:Sample public class RuleTest { @Rule public TestRule rule = new SomeRule(); @ClassRule public static TestRule RULE = new SomeRule(); } ``` ###構造化テスト ```java:Sample @RunWith(Enclosed.class) public class EnclosedTest { public static class XXの場合 { @Before public void setup() { } @Test public void testCase() { } } public static class YYの場合 { @Before public void setup() { } @Test public void testCase() { } } } ``` ###パラメータ化テスト ```java:Sample @RunWith(Theories.class) public class ParameteriedTest { @DataPoints public static int[] PARAMS = { 1, 2, 3, 4 }; @Theory public void test(int x) { } } ``` ###カテゴリ化テスト ```java:Sample @Category(DbTests.class) public class CategoriedTest { @Test @Category(SlowTests.class) public void testCase() { } } ``` ##Matcher API ###基本機能 |クラス|メソッド|概要| |:--|:--|:--| |CoreMatchers/Matchers|is|対象オブジェクトが期待値と一致するかチェック| |CoreMatchers/Matchers|equalTo|対象オブジェクトと期待値オブジェクトの一致チェック。オブジェクトのequalToメソッドで比較する。| |Matchers|hasToString|対象オブジェクトをtoStringした結果が期待文字列と一致するかチェック| |CoreMatchers/Matchers|not|macherの非定型を作成する。| ###複数の期待条件を組み合わせる機能 |クラス|メソッド|概要| |:--|:--|:--| |CoreMatchers/Matchers|allOf|対象オブジェクトを複数のMacherでチェックして、全てがtrueであるかどうかをチェック| |CoreMatchers/Matchers|anyOf|対象オブジェクトを複数のMacherでチェックして、どれか一つでもtrueであるかどうかをチェック| |CoreMatchers/Matchers|both|対象オブジェクトを二つのMacherでチェックして、両方共にtrueかどうかチェック| |CoreMatchers/Matchers|either|対象オブジェクトを二つのMacherでチェックして、どちらか一方でもtrueかどうかチェック| |Matchers|isIn|対象オブジェクトが期待するCollectionオブジェクトの中に含まれているかどうかをチェック。期待値にCollectionをセットする。| |Matchers|isOneOf|対象オブジェクトが期待するCollectionオブジェクトの中に含まれているかどうかをチェック。期待値に可変長引数をセットする。| ###文字列の検証に使用する機能 |クラス|メソッド|概要| |:--|:--|:--| |Matchers|isEmptyString|対象文字列が空白かどうかチェック。nullはNG| |Matchers|isEmptyOrNullString|対象文字列が空白、もしくはnullかどうかチェック| |CoreMatchers/Matchers|startsWith|対象文字列の先頭が期待値で始まっているかをチェック| |CoreMatchers/Matchers|endsWith|対象文字列の末尾が期待値文字列であるかどうかをチェック| |CoreMatchers/Matchers|containsString|対象文字列に期待文字列が含まれているかをチェック| |Matchers|equalToIgnoringCase|対象文字列と期待値文字列の一致チェック。大文字と小文字を区別しない。| |Matchers|equalToIgnoringWhiteSpace|対象文字列と期待値文字列の一致チェック。空白スペースがある場合に空白スペースの数を意識しない。| |Matchers|samePropertyValuesAs|対象文字列に対し、期待値の文字が順番で含まれているかをチェック| |Matchers|stringContainsInOrder|対象文字列に対し、期待値の文字が順番で含まれているかをチェック| ###数値の検証に利用する機能 |クラス|メソッド|概要| |:--|:--|:--| |Matchers|comparesEqualTo|対象数値が期待数値と一致しているかチェック。isよりもメッセージが詳細| |Matchers|greaterThan|対象数値は期待数値より大きい(actual > expected)かどうかをチェック| |Matchers|greaterThanOrEqualTo|対象数値は期待数値より以上(actual >= expected)かどうかをチェック| |Matchers|lessThan|対象数値は期待数値未満(actual < expected)かどうかをチェック| |Matchers|lessThanOrEqualTo|対象数値は期待数値以下(actual <= expected)かどうかをチェック| |Matchers|closeTo|対象数値が期待される基準値から±指定値の範囲内に収まっているかをチェック| ###nullチェック系(文字列のチェックについては上記「文字列の検証に使用する機能」参照) |クラス|メソッド|概要| |:--|:--|:--| |CoreMatchers/Matchers|notNullValue|対象オブジェクトがnullでないことをチェック| |CoreMatchers/Matchers|nullValue|対象オブジェクトがnullであることをチェック| ###Listや配列など、チェック対象値が複数の場合に使用する機能 |クラス|メソッド|概要| |:--|:--|:--| |Matchers|array|対象配列オブジェクトの要素それぞれに対して、期待値の条件を満たすかどうかをチェック| |Matchers|arrayContaining|対象オブジェクトのそれぞれに対して期待値と一致するかチェック。順番の整合性もチェック| |Matchers|arrayContainingInAnyOrder|対象配列オブジェクトのそれぞれに対して期待値と一致するかチェック。順番の整合性はチェックしない。| |Matchers|arrayWithSize|対象配列オブジェクトのサイズが期待値と一致するかチェック| |Matchers|contains|対象オブジェクトのそれぞれに対して期待値と一致するかチェック。順番の整合性もチェック| |Matchers|containsInAnyOrder|対象オブジェクトのそれぞれに対して期待値と一致するかチェック。順番の整合性はチェックしない| |Matchers|empty|対象Colelctionオブジェクトが空かどうかをチェック| |Matchers|emptyArray|対象配列が空オブジェクトかどうかをチェック| |Matchers|emptyCollectionOf|対象配列オブジェクトが空かどうかをチェック。型のチェックも行う。| |Matchers|emptyIterable|対象Iterableオブジェクトが空かどうかをチェック| |Matchers|emptyIterableOf|対象Iterableオブジェクトが空かどうかをチェック。型のチェックも行う。| |CoreMatchers/Matchers|everyItem|対象オブジェクトの全ての要素に対して期待値を満たすかどうかをチェック| |CoreMatchers/Matchers|hasItem|対象Iterableオブジェクトの中に期待値を満たすオブジェクトが含まれているかどうかをチェック| |Matchers|hasItemInArray|対象配列オブジェクトの中に期待値を満たすオブジェクトが含まれているかどうかをチェック| |Matchers|hasItems|対象Iterableオブジェクトの中に期待値を満たすオブジェクトが含まれているかどうかをチェック。期待値には複数条件を設定出来る。| |Matchers|hasSize|対象Collectionオブジェクトのサイズチェック| |Matchers|iterableWithSize|対象Iterablesの型と要素数が期待値と一致するかをチェック| ###Mapのチェックに使用する機能 |クラス|メソッド|概要| |:--|:--|:--| |Matchers|hasEntry|対象Mapに期待のkey,valueが入っているかチェック| |Matchers|hasKey|対象Mapに期待されるkeyが入っているかをチェック| |Matchers|hasValue|対象Mapに期待されるvalueが入っているかチェック| ###インスタンスや型をチェックする系 |クラス|メソッド|概要| |:--|:--|:--| |CoreMatchers/Matchers|any|対象オブジェクトが期待するクラスのインスタンスであるかをチェック。instanceOfと同じ| |CoreMatchers/Matchers|instanceOf|対象オブジェクトが期待するクラスのインスタンスであるかをチェック| |CoreMatchers/Matchers|isA|対象オブジェクトのクラスが期待値のものであるかをチェック| |CoreMatchers/Matchers|sameInstance|対象オブジェクトが期待するオブジェクトと同じインスタンスであることをチェック| |CoreMatchers/Matchers|theInstance|対象オブジェクトが期待するクラスのインスタンスであるかをチェック| |Matchers|typeCompatibleWith|対象オブジェクトが期待するクラスを継承しているかチェック| ###その他 |クラス|メソッド|概要| |:--|:--|:--| |CoreMatchers/Matchers|anything|常にtrueになる。このオブジェクトはチェックしなくて良いということを明示する為の機能| |CoreMatchers/Matchers|describedAs|エラー時に表示される文言を上書きする| |Matchers|eventFrom|対象イベントオブジェクトの発生元が期待値であるかをチェック| |Matchers|hasProperty|対象JavaBeansオブジェクトに期待する名称のプロパティが存在するかチェック| |Matchers|hasXPath|対象XMLドキュメントに期待するパスが存在するかをチェック ###カスタムMatcher ```java:Sample //カスタムMatcher定義 public class EvenNum extends TypeSafeMatcher<Integer> { private Integer expected; public EvenNum(Integer expected){ this.expected = expected; } @Override protected boolean matchesSafely(Integer item) { return item % expected == 0; } @Override public void describeTo(Description description) { description.appendText("<"+expected+"で割り切れる>"); } } //カスタムMatcherによるassert @Test public void test() { assertThat(10, is(new EvenNum(2))); } ``` ##jMockitによるモック化 ###メソッドのモック化 ```java:Sample public class SimpleTest { @Mocked private Simple mockSimple; @Test public void testGetSample() throws Exception { new NonStrictExpectations() {{ mockSimple.getSample(); result = "Mock Text"; }}; } } ``` ###staticメソッドのモック化 ```java:Sample public class SimpleTest { @SuppressWarnings("unused") @Mocked private Simple mockSimple; @Test public void testGetSample() throws Exception { new NonStrictExpectations() {{ Simple.getSample(); result = "Mock Text"; }}; } } ``` ###内部newクラスのモック化 ```java:Sample public class SimpleTest { @Mocked private SampleInner mockSampleInner; @Test public void testGetSample() { new NonStrictExpectations() {{ mockSampleInner.isSample(anyString); result = true; }}; } } ``` ###privateメソッドのモック化 ```java:Sample public class SimpleTest { @Mocked("isSample") private Simple sample; @Test public void testGetSample() { new NonStrictExpectations() {{ invoke(sample,"isSample",anyString); result = true; }}; } } ``` ###例外のモック化 ```java:Sample public class SimpleTest { @Mocked private Simple mockSimple; @Test public void testGetSample() throws Exception { new NonStrictExpectations() {{ mockSimple.getSample(); result = new Exception(); }}; } } ``` ###メソッドのみモック化 ```java:Sample public class SimpleTest { @Test public void testPutSample() { new MockUp<Simple>() { @Mock void putSample() { } }; } } ``` ###コンストラクタのモック化 ```java:Sample public class ConstructorSampleTest { @Test public void test() { new MockUp<ConstructorSample>() { @Mock void $init() { } }; new ConstructorSample(); } } ``` ###メソッドチェインをモック化 ```java:Sample class Hoge { public Fuga getFuga() { ... } } class Fuga { public String getText() {...} } public class SimpleTest { @Cascading Hoge hoge; @Test public void test() { new NonStrictExpectations() {{ hoge.getFuga().getText(); result="sample"; }}; } } ``` ###privateな要素にアクセスする ```java:Sample public class SimpleTest { @Test public void test() { Fuga sample = Deencapsulation.newInstance(Sample.class); Deencapsulation.invoke(sample, "method"); Deencapsulation.setField(sample, "instanceField", "fugaaa"); } } ``` ## 参考 [JUnit実践入門](http://www.amazon.co.jp/JUnit%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-~%E4%BD%93%E7%B3%BB%E7%9A%84%E3%81%AB%E5%AD%A6%E3%81%B6%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E6%8A%80%E6%B3%95-WEB-PRESS-plus/dp/477415377X/ref=sr_1_1?ie=UTF8&qid=1392442555&sr=8-1) [技術開発事業部blog 最強モックツール JMockit・Matcherの使い方シリーズ](http://genesis-tdsg.blogspot.jp/2013/08/jmockit.html) [jMockit そんな無理矢理privateなトコロに…](http://qiita.com/NewGyu/items/9fbc6fae4104ce64aa8f) [JMockit使い方メモ](http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6) []() []() []() []() |
|
| 815位 |
|
|||
|
00:54:17 |
|
|
「[《REST思想》と《リソース指向》と《Webページ》を一緒にしてはいけない](http://qiita.com/irxground/items/cd83786b10d81eecce77)」についてです。 用語の問題などについてはきっちりJxckさんが指摘されているので、私は自分で復習しつつ思ったことを書いてみます。 なんかRESTって難しい…とはあんまり思ってほしくないんですが、少なくともRails使うとちゃんとしたことが楽にできますよ。 > ### REST思想 > 本に対してCRUD操作を行うインタフェースとして、以下のようなものを提供します。 > > |役割|method + URL| > |----|----| > |本一覧を取得|GET /books| > |一つの本を取得|GET /books/1| > |新しい本を追加|POST /books| > |既存の本を修正|PUT /books/1| > |既存の本を削除|DELETE /books/1| これはいわゆるWeb APIについて、ということかなと推測しました。RESTというのはAPIのプロトコルのことだと思われている傾向がありますが、そういうわけではありません。Web全体についてのもので、APIについてもWebアプリについても適用されるものです。 実はRESTでは「統一インターフェイス」の制約からメソッドについて規定されていますが、URLの形については特に規定されません(もちろんAddressabilityの面で重要であることは言うまでもありません)。なので実は`/books`,`/books/1`でなくてもいいのですが、これを規約(CoC)でズバッときれいに決めてしまったのがRailsのすごいところの1つです。 > 本の追加や削除を行う場合は、本情報をJSON形式でPOSTリクエストのボディとして送ります。`application/x-www-form-urlencoded`形式で送ることは避けるべきです。 > (中略) > しかし、正常系・異常系ともにアプリケーション内で統一された形式を利用してください。 このへんは、表現のフォーマット(メディアタイプ)の話なので直接RESTと関係するわけではありません。対応できるなら複数のフォーマットに対応すると便利だけど、実装の手間を考えて1つのフォーマットに絞る、というのは十分ありえます。 Railsの場合、デフォルトで`application/x-www-form-urlencoded`もJSONも扱えるので、実はそんなに手間でもありません。 > ### リソース指向 > 本の情報は`/books`にあるとして、拡張子を変えることで、様々なフォーマットでデータを利用できるようにします。 > 例えば本の一覧をJSON形式で欲しければ`/books.json`にリクエストし、csv形式で欲しければ、`/books.csv`にリクエストします。 `/books.xls`のようにバイナリを提供してもかまいません。 これこそリソースと表現(Representation)の違いですね。 「リソース」はURLで表されますが、抽象的な概念であって、データではないので、送ることができません。送られるのはすべて表現です。(すべてのデータは表現、と言い換えてもよい) 表現にどのフォーマットを使用しているかを表すのがHTTPの`Content-Type`ヘッダで、どのフォーマットなら受け入れられると伝えるのが`Accept`ヘッダです。 ``` GET /books HTTP/1.1 Host: example.jp Accept: application/json ``` ``` HTTP/1.1 200 OK Content-Type: application/json [ {"name": "foobar"}, ... ``` --- ``` GET /books HTTP/1.1 Host: example.jp Accept: text/html ``` ``` HTTP/1.1 200 OK Content-Type: text/html <!DOCTYPE html> <html> <head> ... ``` HTTPにはフォーマットを使い分ける機能がすでにあったんですね。 ブラウザで試すときなど、URLで表したほうが便利なこともあるので、`/books.json`などの拡張子的なURLもよく使われます。(Railsではデフォルトでどちらも対応) ただし、たとえば`.json`であっても`application/json`とは限らず、`application/ld+json`とか`application/hal+json`とかほかのタイプもありうるので、拡張子の指定よりは`Accept`,`Content-Type`の指定のほうが正確といえるでしょう。 ということで、 > `/books.csv`に対する更新を行う場合は、はPOSTリクエストのボディとして何を送ればよいのでしょうか。CSV形式で良いのでしょうか。JSONでしょうか。 >また、更新に失敗したときのエラーはどう返すべきでしょうか。もし`/books.csv`にリクエストししたにも関わらず、JSON形式でエラーを返ってくることになると、クライアントは苦労するでしょう。 このへんの心配は、ちゃんと実装すれば杞憂ですし、Railsなら実装も比較的簡単です。 > ### Webページ > (中略) > > |役割|URL| > |----|----| > |一覧画面を表示|GET /books| > |詳細画面を表示|GET /books/1| > |追加formを表示|GET /books/new| > |追加アクション†|POST /books/new| > |編集formを表示|GET /books/1/edit| > |編集アクション†|POST /books/1/edit| > |削除確認画面を表示(任意)†|GET /books/1/delete| > |削除アクション†|POST /books/1/delete| (引用注:†はRailsのデフォルトにはありません) Railsを使いはじめるとこの`new`とか`edit`ってのは何なんだ、と思ってしまうのですが、これも別に必須というわけではありません。例えば、一覧画面の中に追加フォームを入れてしまうというのは、よく見かけますね。 どちらがいいかは場合によるのですが、Railsでは別ページにするのがより一般的という考えから、このデフォルトになっているというわけです。 ある意味`new`や`edit`は、表現の種類の1つと考えられるかもしれません。 > - 更新FormページのURLとPOST先のURLを同じにする > > (中略) > URLを同じにすることで、CSSなどのリソースのパスや、リクエストパスに依存したコードを書きやすくなります。 相対パスを書くと`/books`と`/books/new`の違いでハマることがある、ということですね。これは少し経験があるけど、全体としては些細なことです。というかRailsなら相対パスを使わずに済みます。 ## 問題だと思うこと Railsのコントローラの、ほぼscaffoldのコードはこんな感じです。 ```ruby def create @book = Book.new(book_params) respond_to do |format| if @book.save format.html { # 302 Found (or 303 See Other) redirect_to @book, notice: 'Book was successfully created.' } format.json { # 201 Created render action: 'show', status: :created, location: @book } else ... end end end ``` `respond_to`を使うと、ロジック部分をそのままにフォーマットの違いに対応できる!すごい! というのは確かなんですが、なんでフォーマットによってステータスコードが違うの? 新規作成したら、ほんとは201 Createdを返したいんですが、ブラウザは201を受け取ると自動的にはリダイレクトしない。そこさえ同じなら、`respond_to`すら書かなくてよくなるのに。 Webブラウザで必要とされる画面遷移が特殊なのかもしれないですが、画面遷移(状態遷移)こそ[HATEOAS](https://www.google.co.jp/search?q=HATEOAS)の重要な要素であってRESTでも大切なのに、歴史的な事情か、仕様や実装に足りない部分がある気がするんですよね。 そういう意味では、XHR(Ajax)のブレイクスルーはかなり大きかったですね。PUT/DELETEも可能になったり、ブラウザの機能すら補ってくれた。昔「[Web ApplicationをRESTfulに近づけていくことで、Web Serviceとの垣根をなくす方向を目指したい](http://www.4bit.net/archives/2005/06/restful_web_app.html)」と言っていたのを思い出して懐かしい気分になりました。 ## 読書会とミートアップのお知らせ 『[RESTful Web APIs](http://restfulwebapis.com/)』という書籍の読書会を毎月第2・4木曜日に行っています。これはRailsや特定の言語にまったく依存しない内容です。RESTやWeb APIに興味のある方の参加をお待ちしています。 http://www.circleaf.com/groups/19 その著者のMike Amundsen氏の来日にあわせて、ミートアップを4/12 19:00から渋谷で開催します。こちらもよろしくお願いします。 http://sendagayarb.doorkeeper.jp/events/10103 |
|
| 816位 |
|
|||
|
11:54:51 |
|
|
## はじめに iPhone5sはA7チップが採用され、世界初の64ビットスマートフォンとして話題になりました。その話を聞いて興奮された方、そうでない方、多々いらっしゃると思いますが、私は後者です(^_^; 64ビット端末が意味する事とは!?どんなお付き合いをすればいいのか!?などなどをアプリ開発者目線でざっくばらん(独断と偏見に満ちています)にまとめてみました。 ## なんでこのタイミングで64ビット化したの? 64ビット化と聞いてアプリ開発者が一番想像し易いメリットはおそらく「使用できるメモリ量の上限が4GB(2GB)を超えられる」という点だと思いますが、現状まだそこまでメモリを搭載していませんし、今後発売される端末(iPhoneに限らず)にきっと大量のメモリを搭載してくるから、先攻して開発できるようにしたのかな?などと勝手に想像しています。 64ビット化の理由について他にも考察されている方がいらっしゃいますのでリンクを貼っておきます。 参考:[なぜiPhone 5sは64ビット化を果たしたのか。その効果と未来とは?(AppBankさん)](http://www.appbank.net/2013/10/27/iphone-news/690428.php) 参考:[iOS 7 が 64-bit 対応になった意味(藤 シローさん)](http://maclalala2.wordpress.com/2013/09/15/ios-7-が-64-bit-対応になった意味/) ## 64ビットに対応する必要はあるの? 今のところAppleが64ビット対応を強制していないので、64ビット対応は各自の判断に任されていますが、以下のような消極的な理由で64ビットに対応しておくべきかなと考えてはいます。 - Apple公式ドキュメント等で、パフォーマンスの向上が期待できるとしているため(ただしパフォーマンスの計測は行ってね、とのこと)。 - 64ビット端末で32ビットバイナリを実行すると、一部の標準機能が正しく動作しないため。 - 顔認証が動作しないそうです(未検証) 参考:[32bit/64bit同梱ファットバイナリだとAppStoreからのリリースバイナリのみiOS6で起動できなくなる問題について(いたのくまんぼうさん)](http://ninebonz.net/fat-binary) - Core Imageのフィルター処理でGPUではなくCPUを指定して処理させようとした時に動作しない (iOS7.0.4 + iPad Airで検証済み)。 - GameKitのBluetooth機能を使うとログに"Bluetooth problem in 32-bit processes on 64-bit system: skipping check"と出力される(実際にどんな不具合が生じるかは不明)。 - (新規アプリの場合)将来64ビットに対応させる手間が発生するならば、後でワーキャー言うより最初から対応しておいた方が楽そうだから。 ## 64ビットでビルドしたら32ビット端末で動かないんじゃないの??? ここが最初謎だったのですが、Xcodeで64ビット対応アプリをアーカイブすると32ビット/64ビットそれぞれのバイナリがビルドされるようです(中間言語などではなく、ネイティブコンパイルされる)。 こうして複数のCPU用のバイナリを格納したものを、一般的には「ファットバイナリ」だとか「同梱バイナリ」だとか言うようです(公式ドキュメントでは「混成バイナリ」とありますが、直訳の予感)。 Xcodeのプロジェクト設定内の「Build Settings」タブを見ますと、「Architectures(アーキテクチャーズ)」という項目があり、ここで対応させるアーキテクチャ(CPU)を指定します。  armv7はiPhone4Sなど、armv7sはiPhone5やiPhone5c、arm64はiPhone5sに積まれているCPUのアーキテクチャです。 このように複数のアーキテクチャを指定している場合、アーカイブ時にはプロジェクトがアーキテクチャごとにビルドされて、それが1つの実行バイナリファイル(ipaファイル)になるというイメージでしょうか(armv7, armv7s, arm64対応だと3回はビルドが走るって事ですね)。 ちなみにarm64アーキテクチャに対応した混成バイナリの動作条件はiOS5.1.1以降だそうです(Xcode上のドロップダウンリストには出てきませんが直接入力すると反映されるようです)。 アーキテクチャなど、あまり馴染みのない単語が出てきますが、くだけた説明が出来そうにないのでwikipediaへのリンクを貼っておきますね。 参考:[ARMアーキテクチャ (wikipedia)](http://ja.wikipedia.org/wiki/ARMアーキテクチャ) 参考:[コンピュータ・アーキテクチャ (wikipedia)](http://ja.wikipedia.org/wiki/コンピュータ・アーキテクチャ) ## 64ビットへ対応するための手順 公式日本語ドキュメントに全部載っていますのでこれに目を通して頂ければ問題ないかと思います。 参考:[Cocoa Touch 64ビット移行ガイド](https://developer.apple.com/jp/documentation/CocoaTouch64BitGuide.pdf) 32ビットから64ビットへ移行する時にハマりやすいポイントは網羅されていると思いますが、個人的に特に気にした点&ハマった点を書いておきます。 ### データ型のバイト長やアライメントが変わっている点に注意 公式ドキュメントにも書いてありますが現時点では32ビットも64ビットも両方対応させる必要がありますので、例えば32ビットー64ビット端末間で通信を行ったり、iTunes経由でバイナリファイルをやりとりする際に異なるバイト長の型を使ってしまうと酷い事になるのは明らかです。 「だったら同じバイト長の型を最初から使っていればいいじゃない!」...と考えてしまうわけなのですが、`CGFloat`の型が32ビットでは`float`型だったのが64ビットで`double`型になってしまいCoreGraphics系のすべて(?)の型のバイト長が揃いません。`CGRect`や`CGAffineTransform`ももちろんアウトです。 回避策として、あまり精度を気にしないのであればシリアライズの際に各値を`float`型へ変換して格納するといった手が使えるかと思います。 ### オープンソースは修正しないと正しく動かないことも `typedef long INT32;` こんな感じで明らかに32ビットの整数型を宣言しているにも関わらず`long`型を使っているものは(動作はすると思いますが)バイナリファイルに値を書き出している場合は64ビット端末では誤動作する可能性があります。 必要に応じて`int`や`int32_t`に置き換えないといけません。 ### objc_msgSendでクラッシュする こちらも公式ドキュメントに症状も回避方法も書いてある内容です(参考項目:Objective-Cのメッセージはメソッド関数のプロトタイプを使ってディスパッチする)。 `[NSObject performSelector:withObject:]`を使うと警告が出るのが嫌だったり、引数が足りなかったりするため、`objc_msgSend`を使っている場合がありますが、そのままだと実機でクラッシュします(シミュレータがではクラッシュしません)。 キャストすれば回避できます。 ## 終わりに あまりコンピュータやコンパイラの仕組みに精通していないため、書いていて自信のない所が多かったです。 突っ込みはお手柔らかにお願いいたしますm(_ _)m |
|
| 817位 |
|
|||
|
21:59:38 |
|
|
macのOSをMavericksにしたらxcodeのcommand line toolsのインストール方法がわからなくてちょっと調べたのでメモ
## 手順 * メニューからXcode->Open Developer Tool->More Developer Tools  * Downloads for Apple Developerページが開くのでそこのリストから”Command Line Tools(OS X Mavericks) for Xcode”をダウンロードしてインストール  |
|
| 818位 |
|
|||
|
18:13:00 |
(慶應数学科休学 -> freee -> webpay -> aisaac.inc founder 所属) |
|
どうも。今、やっとこの「モヤッ」が解消されたgogotanakaです。 この記事はrubyやrailsはそれなりに書けるけど 実はclassとかmoduleの継承とかインスタンスとかそいう言葉を出されると「モヤッ」となる、 方々のためにこの辺をまとめてみましたよ。 #という体裁の自分用の備忘録である。 ##表記法 よく本とかに載っている表記法だけど一応確認。 ###ThisIsClass#this_is_method これは`ThisIsClass`クラスのインスタンスのメソッド`this_is_method`を指してる ###ThisIsClass.this_is_method ###ThisIsClass::this_is_method これは`ThisIsClass`クラスのクラスメソッド`this_is_method`を指している ## 一回 class Hoge ~ end を忘れよう rubyをいじった事がある方にはおなじみ、 ```ruby:ex1.rb class Foo < Bar def foo p "foo↑↑" end end ``` この構文ある種の糖衣構文(シンタックスシュガー)です. 一回忘れると良いです. ちなみにこのある種の糖衣構文を使わなければ、 ```ruby:ex1.rb Bar = Class.new Foo = Class.new(Bar) do |c| def foo p "foo↑↑" end end ``` まあ、`def` もある種の糖衣構文とも言えるのだけどそれは後ほど. ##What is "self" ? `self` とはカレントオブジェクトと呼ばれたりもするが、 その時点でのメソッドの呼び出し口、と言っていいだろう. ```ruby:self.rb class ThisIsClass ##self は ThisIsClassクラス自体 def this_is_method ##self は ThisIsClassクラスのインスタンス puth 'うんこしたいよー' end end ``` この辺でモヤッとなってる方は一度この記事に目を通してからもう一度読み直すと良いだろう。 ##クラスメソッド 以下はThisIsClassクラスにクラスメソッドthis_is_methodを定義するやり方を4つ紹介している。 ```ruby:class-method.rb class ThisIsClass def ThisIsClass.this_is_method puth 'うんこしたいよー' end end class ThisIsClass def self.this_is_method puth 'うんこしたいよー' end end class ThisIsClass class << ThisIsClass def this_is_method puth 'うんこしたいよー' end end end class ThisIsClass class << self def this_is_method puth 'うんこしたいよー' end end end ``` ##インスタンスメソッド ```ruby:instance-method.rb class ThisIsClass def ThisIsClass.this_is_method puth 'うんこしたいよー' end end ``` >this_is_instance = ThisIsClass.new ThisIsClassクラスに対してクラスメソッドnewを呼び出すとThisIsClassのインスタンスが返ってくる。 インスタンスメソッドとは、そいつらに対してのメソッドである。 ##クラス定数 いつか書くお ##クラス変数 いつか書くお ##インスタンス変数 いつか書くお ##クラスの拡張 既に定義されたクラスにメソッドを新たに追加する事。(上書きではなく新規作成 ```ruby:クラスの拡張.rb class ThisIsClass def this_is_method puts 'うんこしたいよー' end end class ThisIsClass def this_is_additional_method puts 'やっぱしたくないよー' end end ``` ##継承 クラスを定義する時に #class 新しいクラス << 親クラス という形式で定義すると、親クラス内で定義したメソッドを新しいクラス内でも使う事が出来る。 さらに親クラスのメソッドの編集も行える。 差分を新しいクラスで実装する事が出来る。 ```ruby:class-method.rb class ParentClass def this_is_method(n) puts "#{n.to_s}回うんこしたいよー" end end class ChildClass << ParentClass def this_is_method(n) m = n + 1 super(m) end end ``` super は同名のメソッドを親クラスから呼び出す事を意味する。 これでChildClassクラスのメソッドthis_is_methodはParentClassクラスのメソッドthis_is_methodよりも一回多くうんこをしたい事になる。 #モジュール #以下編集中だけどだしちゃう *インスタンスを持たない *継承できない クラスはモノと処理だがモジュールは処理だけ 1.名前空間の提供 2.モジュール関す 3.mix-in ```ruby:class-method.rb module Hoge CONSTANT = 123 def this_is_method(n) puts "#{n.to_s}回うんこしたいよー" end module_function :hello end ``` >p Hoge::CONSTANT #=> 123 >p Hoge.this_is_method(2) >include Hoge >p CONSTANT #=> 123 >this_is_method(n) カプセルか ポリモルフィズムを積極的に活用したのがダックタイピング 「アヒルのように歩きアヒルの用に泣くものはアヒルに違いない」 つまり「オブジェクトを特徴付けるのは実際のクラスや継承関係ではなく、そのオブジェクトがどのように振る舞うか」 「種類ではなくどのように振る舞うか」 ```ruby:class-method.rb module Hoge def hey(ary) ary[1] end end ``` hey({ 1 => "uuu"})でも動く ArrayかHashは関係ない |
|
| 819位 |
|
|||
|
18:59:51 |
|
|
ios6 → ios7対応をする際にios6のデザインで対応する際の覚え書き # レイアウト崩れ ### 原因は、ほぼこれ >In iOS 7, view controllers use full-screen layout. At the same time, iOS 7 gives you more granular control over the way a view controller lays out its views. In particular, the concept of full-screen layout has been refined to let a view controller specify the layout of each edge of its view. https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TransitionGuide/AppearanceCustomization.html#//apple_ref/doc/uid/TP40013174-CH15-SW1 ## 全体的に上部に寄ってしまっているのを対処  xcode4で作成したアプリをxcode5で見ると全体的に上部に寄ってしまう。 ### Storyboardでの修正 1. Storyboard上で対象のViewControllerを選択する 2. Cmd+Alt+4でAttributes Inspectorを開く 3. Under Top Barsをオフにする  ### コードでの修正 ``` - (void)viewDidLoad { [super viewDidLoad]; if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) { self.edgesForExtendedLayout = UIRectEdgeNone; } } ``` ### 結果  ## StoryBoardで作成したモーダル画面のnavigationbarがstatusbarと重なるのを対処  これは正しくnavigation barを使っていないために起こる (モーダルは関係なくて、Storyboardを分割して運用する場合に起きやすい間違い) Storyboard上でnavigation barを置く場合は、以下のようにする 1. ViewControllerを配置 2. メニューのEditor → Embed in → Navigation Controllerを選択 左図がEmbed inで作成していない場合、右図がEmbed inで作成している場合  ### 結果  ## navigationbarでbackground画像を利用している場合にデザインが崩れるときの対処 基本的に以下のページのTable 5-1に合わせて画像を作成すれば問題ない https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TransitionGuide/Bars.html#//apple_ref/doc/uid/TP40013174-CH8-SW1 高さ45ポイント以上の画像をバックグラウンドに置くと以下のように超えた分が引き延ばされる(以下の画像は高さ45pxで1px緑の線を引いた画像)  ### 44ポイントの画像を用意して対処  ### 64ポイントの画像を用意して対処  この場合は、ios6でも64ポイントのnavigationbarになってしまうので、44と64の両方の画像を用意して制御する ``` if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed: @"navi_background"] forBarMetrics:UIBarMetricsDefault]; }else{ [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed: @"navi_background_ios7"] forBarMetrics:UIBarMetricsDefault]; } ``` # ボタンをios7のデフォルトにしたくない これはボタンを画像にすることで解決できる ## 戻るボタンをios6のように表示したい 戻るボタン用にこんな画像を用意  ただし、以下のように書いても、ios7.0で何故か最初の表示時に画像が表示されない ``` - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ・・・・ UIImage* backImageOff = [UIImage imageNamed:@"back_btn_off.png"]; UIImage* backImageOn = [UIImage imageNamed:@"back_btn_on.png"]; backImageOff = [backImageOff stretchableImageWithLeftCapWidth: 15.0 topCapHeight: 30.0]; backImageOn = [backImageOn stretchableImageWithLeftCapWidth: 15.0 topCapHeight: 30.0]; [[UIBarButtonItem appearance] setBackButtonBackgroundImage:backImageOff forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [[UIBarButtonItem appearance] setBackButtonBackgroundImage:backImageOn forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]; [[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(6.2, 0) forBarMetrics:UIBarMetricsDefault]; return YES; } ``` ### 解決 それぞれのストーリーで画像を設定する ``` - (void)viewDidLoad { UIImage* backImageOff = [UIImage imageNamed:@"back_btn_off.png"]; UIImage* backImageOn = [UIImage imageNamed:@"back_btn_on.png"]; backImageOff = [backImageOff stretchableImageWithLeftCapWidth: 15.0 topCapHeight: 30.0]; backImageOn = [backImageOn stretchableImageWithLeftCapWidth: 15.0 topCapHeight: 30.0]; [self.navigationItem.backBarButtonItem setBackButtonBackgroundImage:backImageOff forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [self.navigationItem.backBarButtonItem setBackButtonBackgroundImage:backImageOn forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]; } ``` さらに ストーリーボードの対象のNavitation ItemのAttribute InspectorのBack Buttonに文字を入れる(これをコードで記述しても動作しなかった)  あとはTint Colorがios7では青がデフォルトに変わったので、自分で設定する ``` [[UINavigationBar appearance] setTintColor:[UIColor whiteColor]]; ``` |
|
| 820位 |
|
|||
|
16:12:46 |
(Increments inc. 所属) |
|
Rubyコードを静的にチェックしてくれる[Rubocop](https://github.com/bbatsov/rubocop)をVimから実行するプラグインには専用の[vim-rubocop](https://github.com/ngmy/vim-rubocop)もあるんですが、このためだけのためにインストールするのが嫌だったので、[syntastic](https://github.com/scrooloose/syntastic)から実行するようにしました。
追記: 2017年現在、 syntastic から [ale](https://github.com/w0rp/ale) に乗り換えました。ale は syntastic と異なり非同期で実行されるので Vim が固まりません。 # インストール 何はともあれNeoBundleでsyntasticをインストール(NeoBundle自体の使い方については [Vim - NeoBundleの導入 - Qiita](http://qiita.com/puriketu99/items/1c32d3f24cc2919203eb)などを参照してください) ```vim:.vimrc NeoBundle 'scrooloose/syntastic' ``` それとRubocopもインストールします。 ```zsh gem i rubocop ``` # Rubocopを使うように設定する ```vim:.vimrc NeoBundle 'scrooloose/syntastic' let g:syntastic_mode_map = { 'mode': 'passive', \ 'active_filetypes': ['ruby'] } let g:syntastic_ruby_checkers = ['rubocop'] ``` `syntastic_mode_map` は `'active'` もしくは `'passive'` を指定します。 `'active'` だとバッファを保存するたびにsyntasticが走り、 `'passive'` の場合は `:SyntasticCheck` 実行時に走ります。 `'active_filetypes'` は保存の度にsyntasticを走らせるファイルタイプを指定します。 2つをあわせると、基本的にsyntasticは走らせないけど、rubyのときだけは自動的に走らせる、という設定になります。 `syntastic_ruby_checkers` はrubyでsyntasticが走らせるツールを指定するものです。 # 使ってみる あとはrubyファイルを開いて保存するだけです。 syntasticはVimをブロックするので大きなファイルだとストレスたまるかも知れません。その場合は `'active_filetypes'` ではなく`'passive_filetypes'` に指定して、必要なタイミングで `:SyntasticCheck` を実行するようにするといいでしょう。 もしくは `:SyntasticToggleMode` を実行すると active と passive を切り換えることもできます。 # 合わせて読みたい - [Ruby - Rubocopを使ってコーディングルールへの準拠チェックを自動化 - Qiita](http://qiita.com/yaotti/items/4f69a145a22f9c8f8333) |
|
| 821位 |
|
|||
|
11:54:30 |
(株式会社ハートレイルズ 所属) |
|
ていうか try! って何よ。
```ruby a.try(:hoge) #=> nil a.try!(:hoge) #=> NoMethodError ``` はーぁん、にゃーるほーどねー。 これ見て初めて気付いたのですが、 **「try メソッドには 2 つの効用がある」** のですね。 1. a が nil の時、何もしない 1. a に hoge が実装されてない時、何もしない 両方の効用を持つのが ```try``` 1 つ目の効用しか持たないのが ```try!``` というわけです。 2 つ目の効用も便利っちゃあ便利ですが、メソッド名を typo するとわかりづらいバグになることもありそうです。 実際 try を使うときは、レシーバ (上の例で a ) が nil かどうかで使う 1 つ目の効用を利用したい場合が殆どだと思われるので、積極的に ```try!``` を使っていくといいかと思います。 http://apidock.com/rails/Object/try # ruby2.3記念追記 rails にまず導入されたのが try で、その後、「NoMethodErrorを捕捉できないのは危険」ということで try! が rails4 から導入されたようです。そして、 ruby2.3 からは「ぼっち演算子」を try! の代わりに使うことができます。 ```ruby a.try!(:hoge) #=> a&.hoge ``` NoMethodError を起こさない try と、ブロック渡す try は引き続き現役です。 |
|
| 822位 |
|
|||
|
06:58:55 |
|
|
<h1>
<a href="http://colourco.de/"> colourcode </a> </h1> <a href="http://colourco.de/">  </a> <h1> <a href="http://colorschemedesigner.com/"> #Color Scheme Designer </a> </h1> <a href="http://colorschemedesigner.com/">  </a> <h1> <a href="http://www.147colors.com/"> 147 Colors </a> </h1> <a href="http://www.147colors.com/">  </a> <h1> <a href="http://www.colordic.org/"> WEB色見本 原色大辞典 </a> </h1> <a href="http://www.colordic.org/">  </a> |
|
| 823位 |
|
|||
|
20:13:03 |
(Sprocket, Inc. 所属) |
|
[ブログ投稿](http://yumewaza.yumemi.co.jp/2012/12/python-deeplearning.html)の続きとして、スライドに書けなかったTheanoの細かい部分についてもう少しまとめておこうと思います。 まず、[Theano 解説](http://d.hatena.ne.jp/saket/20121207/1354867911) はTheano特徴を簡潔に表現されているので、一読をオススメします。 ここでも書かれていますが、Theanoの特徴として、 * 実行時にCコードを生成してコンパイル * GPUでの実行のサポート(要CUDA) * 自動微分 などがあげられると思います。 Theanoの超簡略チュートリアル ========== http://deeplearning.net/software/theano/tutorial/index.html#tutorial の乱暴な要約です。 まず常にImportしておく3つ ------------------------ import numpy import theano import theano.tensor as T この3つはお約束です。 これだけ知っておけば概ね大丈夫 ------------------ 以下の事柄がだいたい理解できれば、Deep Learningの実装を読んで理解したり、変更を加えたりすることができると思います。 * T.scalar, T.vector, T.matrix: 変数(シンボル)の話 * シンボルの演算結果はシンボルである * function: 関数周りの話 * T.grad: 微分周りの話 * shared: 共有変数 * T.gradと共有変数とupdates: 典型的な勾配法の実装パターン ### 変数(シンボル)の話 Theanoで扱う変数は「テンソル(tensor)」という概念で扱われています。 テンソル周りの型や演算はtheano.tensor (as T) 以下にだいたい定義されています。 私はいまいちテンソルとか理解してないのですが、とりあえず 「**T.* 以下には、変数の型や主要な汎用数学関数(expやlogやsinとか)がある**」 ということです。 ここで「**変数の型**」というのは * 「スカラー、ベクトル、行列」のような次元的(テンソル用語では「階数」と呼ぶらしい)な意味 * 要素の型(整数、実数など)の意味 があります。これらを組み合わせて、変数の型(テンソルの型)を表しています。 名前としては、 * **scalar**: スカラー(階数=0) * **vector**: ベクトル (階数=1) * **matrix**: 行列(階数=2) であり、 * **d**: double * **l**: long などです(他にもあります)。 これらを組み合わせて、 * 実数(float64)の行列→T.dmatrix() * 整数(int64)のベクトル→T.lvector() などと表します。 例えば以下のようになります。 x = T.lscalar("x") m = T.dmatrix() これら、x,m は「記号(シンボル)」であって実際の値を持っていません。 この辺が普通のPythonの変数とは少し異なるところです。 変数の生成についてより詳しくは、下記をご覧ください。 http://deeplearning.net/software/theano/library/tensor/basic.html#libdoc-basic-tensor ### シンボルの演算結果はシンボルである 例えば、 ``` x = T.lscalar("x") y = x*2 z = T.exp(x) ``` と実行したとします。 x は 値を持たないシンボルですので、yにも値が入ることはなく、yも「x*2」を意味するシンボルになります。 zも同様に exp(x) を表すシンボルです。(実際にはPythonのObject) ニューラルネットワークを構成する計算も、実際に値が与えられるまでは、このシンボル同士の演算の塊(要するに式)として扱われます。 式のまま扱うので、人間にとっても理解しやすいですし、後述する自動微分などが可能になったり、実行時の最適化が可能になるんだと思います。 ### function: 関数周り 実際に計算をさせるには、「関数」を定義する必要があります。 例えば、 「**f(x) = x*2** 」を作りたければ、 f = theano.function([x], x*2) とか y = x*2 f = theano.function([x], y) などとします。 f は一つの関数になり、呼び出すと >>> f(3) array(6) となります。 functionは * 第一引数に「引数のシンボルのリスト」 * キーワード **inputs** として指定も可能 * 第二引数に「計算する式(シンボル同士の演算)」 * キーワード **outputs** として指定も可能 を指定します。 この時点で関数がコンパイルされるようで、複雑な関数でも高速に実行されたりします。 functionには、**givens** というキーワードがあります。 givensにはその名の通り「式の中のシンボルを別のシンボルや値で置き換える」ような働きがあります。 例えば、 ``` >>> x = T.dscalar() >>> y = T.dscalar() >>> c = T.dscalar() >>> ff = theano.function([c], x*2+y, givens=[(x, c*10), (y,5)]) >>> ff(2) array(45) ``` ということができます。 本来計算する値は 「x*2+y」なのですが、関数自体の引数は「c」というシンボルを取ることになっています。 実際には、xやyが与えられないと計算できないわけですが、このgivensの部分でxやyの値を与えるという形でも計算できます。 これは、今後のTutorialの中で機械学習でデータを部分的に利用する際に使われたりしています。 ### T.grad: 微分周り Theanoの目玉機能の一つがこの微分機能です。自動微分と呼ばれる「式を分析して微分した式を求める」ことができます。 例えば、 x, y = T.dscalars("x", "y") # ※まとめて宣言する書き方 z = (x+2*y)**2 という式を x について微分したら、 dz/dx = 2(x+2*y) になりますが、それを gx = T.grad(z, x) で式変換できます。 yについての微分ならば、 dz/dy = 4(x+2*y) ですが、それを gy = T.grad(z, y) で式変換できます。 実際に値を求めるときは、やはり関数化が必要で、 fgy = theano.function([x,y], gy) >>> fgy(1,2) array(20.0) などとします。 ### shared: 共有変数 変数=theano.shared(object) という形で上記**function**の中で参照可能な共有型のデータを宣言できます。 例えば、 ``` >>> x = T.dscalar("x") >>> b = theano.shared(numpy.array([1,2,3,4,5])) >>> f = theano.function([x], b * x) >>> f(2) array([ 2., 4., 6., 8., 10.]) ``` などと使うことができます。 shared変数の値を参照したりセットしたりするには、 ``` >>> b.get_value() array([1,2,3,4,5]) >>> b.set_value([10,11,12]) ``` などとします。 先ほど定義した関数にも即座に反映されもう一度 **f(2)** を実行すると結果が変わっていることがわかります。 ``` >>> f(2) array([ 20., 22., 24.]) ``` ### T.gradと共有変数とupdates: 典型的な勾配法の実装パターン function には**updates**というキーワード引数があり、これによって共有変数を更新することができます。 例えば、cを共有変数として、関数fが実行される度に 1 増やすようにするには以下のように記述します。 ``` c = theano.shared(0) f = theano.function([], c, updates= {c: c+1}) ``` **updated={c: c+1}** という部分が、 **c = c + 1** というプログラミング言語ではお馴染みの値の更新を表しています。 これを実行すると以下のようになります。 ``` >>> f() array(0) >>> f() array(1) >>> f() array(2) ``` これらを使うと勾配法を実装することができます。 例えば、データ**x=[1,2,3,4,5]** に対して **y=sum((x-c)^2)** が最小となるような**c**を求めたいとします。 コードとしては、例えば下記のようになります。 ``` x = T.dvector("x") # input c = theano.shared(0.) # これを更新していく。初期値はとりあえず0. y = T.sum((x-c)**2) # y=最小化したい値 gc = T.grad(y, c) # y を c について偏微分 d2 = theano.function([x], y, updates={c: c - 0.05*gc}) # 実行する度に c を更新して、現時点のyを返す ``` として、**d2()**に**[1,2,3,4,5]**を何度か与えると、 ``` >>> d2([1,2,3,4,5]) array(55.0) >>> c.get_value() 1.5 >>> d2([1,2,3,4,5]) array(21.25) >>> c.get_value() 2.25 >>> d2([1,2,3,4,5]) array(12.8125) >>> c.get_value() 2.625 ``` となります。yが徐々に小さくなり、cが徐々に「3」に近づいていくのがわかると思います。 ロジスティック回帰の実装 =================== この辺りまで理解できれば、以下のチュートリアルのロジスティック回帰がどうのように動くかわかってくるのではないかと思います。 http://deeplearning.net/software/theano/tutorial/examples.html#a-real-example-logistic-regression (え、最初からわかるって?^^;) |
|
| 824位 |
|
|||
|
12:58:01 |
|
|
# ホゲェ〜
なんか色々とまとめといた方が良さそうだ。 自分にとって数が多くて意味がわからんし。 まだ社内データは収集する環境を整えている状態だ。 整えているといってもできてるんだけど、 なんか色々と新しいツールが出てくるしそれに追っついて 書き換えちゃったりを繰り返している。 意味がわからなくなってきたのでまとめてみよう。 社内で共有するにはQiitaに上げたほうが良さそう。 あげちゃまずいものは書いてないつもりだ。 まずかったら消す。 データ解析チームが何やってるのかをまとめてみた。 各担当者の名前を出して問題なさそうなら出そうかなあ # Aimingデータ解析チームについて データ解析チームだとつまらんし愛情がわかないのでチーム名をつけている。 **Monolith**だ。モノリス。あのモノリス。 @shibacowさんが考えだした。トテモ良いチーム名だと思う。 ## チームメンバー は以下の3名 - @shibacow (解析スペシャリスト) - @ckazu (Railsスペシャリスト) - @futoase (腕力解決型エンジニア) # 使っているサービス ## Treasure Data http://www.treasuredata.com/  これは大きい。データ解析はデータがないと意味が無い。 入れ物がないと意味がとってもない。 自分たちでデータを格納するための、インハウスな環境を整えるのはだるすぎる。 落ちない環境を整えるためのチームを育てるにはどれくらいの時間とお金が... ということでTreasure Dataを使っている。 ボコボコテストデータやらなんとかデータやら入れていってる。 Hive QLを書けば、MySQLでSQL Queryに親しんだ 化石と化した僕のようなおじさんでも気軽に溜め込んだデータから 面白い動向を見るための要求を出せる。 でまあ落ちないしデータはTreasure Dataにある!といえる 状態になってるのですごく安心して使ってる。 何か疑問が合ってもサポートの方にチャットで聞けば問題のない 状態になってるし便利。インターネットバンザイ。 ## pivotal tracker https://www.pivotaltracker.com/  ストーリーを起こして、ポイント付けて、ベロシティあれこれ... という極めてアジャイルな使い方をしてる。 週1、金曜日に棚卸しをしたり月曜日には企画の人と一緒に ストーリーを起こしたりしている。 ## Trello https://trello.com/  pivotal trackerはストーリーを立てて、という流れなのだけれど 細かいタスク、「xxの環境を構築するけど新しいことに挑戦するための調査」 みたいなものや、その場で発生した問題に対処するために 時間が取られちゃってるとかはpivotalで管理しづらい。 ベロシティが常に変化しちゃってよくわからなくなるし。 口頭で報告ということもやっているのだけれど、 Trelloを使ってみようと提案して使ってみている。 ## idobata https://idobata.io  データ解析チームで試しに使ってる。 使い始めてから不満はない。 特定のエンドポイントに対しcurlでデータを送ってやると チャットルーム内にメッセージを送れる。 つまるところpush専用のbotを作れる。 iOSアプリもリリースされているので出先から確認可能なのが助かる。 # 使っているソフトウェア ## fluentd http://fluentd.org/  fluentdを利用している。これがないとデータが集めらんない。 弊社の場合、利用しているサーバ(クラウド含め)は1社1様というわけには行かない。 fluentdというかtd-agentのセットアップについてデータ解析チームがサポートしている。 まだサポート対象の数が多くないからできることなのかなと思う。 出来そうなチームはチームに任せちゃって良いと思ってたりする。 今のところchef、capistranoを利用してデプロイしている。 ## HRForecast  https://github.com/kazeburo/HRForecast Treasure Dataには **Sending Query Results to a Web Server** という ウェッブの口に対してHive QLの結果を投げつける機能がある。 http://docs.treasuredata.com/articles/result-into-web その機能を活かして、HRForecastに結果を投げつけるようにしている。 ただ、Treasure Dataが吐き出しているjsonの形式はHRForecastは食えないし、 そもそも食える状態じゃない。そのため、 Treasure Data が吐き出してるjsonをHRFが食えるPOST形式のリクエストに変更する ブリッジするものを用意した。sinatraで書いてpumaで動かしてる。 先に入っていた先輩エンズィニアのヘルプ(ペアプロ)のもと書いた。 元々はHRForecastが描画していたグラフをそのまま利用していたけど HighChartsを導入して以降、HRForecastはAPIのみ利用している ## GrowthForecast  https://github.com/kazeburo/GrowthForecast これはリアルタイムなデータを流し込むのに利用してる。 さっき(1分前ぐらい)ログインしてきたユーザーとか。そんなの。 使い方はHRForecastみたく時系列にデータを評価したい、というのと対照的に 速報としてデータを貰いたいときに利用する こちらもHRForecastと同じく、グラフ描画にはHighChartを利用する形になった。 APIサーバとして利用している。 ## HighChart http://www.highcharts.com/  きれいなグラフを出してくれるjsのライブラリ。 予算出してもらって買ってもらった。 HRF, GRFのAPI経由でもらったcsvをHighChartに食わせて描画している。 ## chef http://www.getchef.com/  ミドルウェアのデプロイに利用している。 鉄板な感じがするのだけど、cookbookが増えてくるにしたがって 実行時間の長さが辛くなってきた。 またchefのバージョンアップに気づきづらい。 chef謹製のcookbookの構造が変わったりするので困っている部分も。 yum cookbookとか... ## capistrano http://capistranorb.com/  Railsアプリのデプロイに利用している。 v2, v3のものが混じってて整頓中。 v3からはgitのbranchを判別するようになってるので楽だ。 ただ、taskの書き方が若干変わってたりするが... ## ansible http://www.ansible.com/  chefから乗り換えよう他のものを探してて、まあこれかなって思ったので採用した。 ansibleについて今まで避けてきていたのだけど 使ってみたらかなりよかった。 デプロイ元でansibleをインストールしておけば良いだけで助かる。 ansibleそのものも巨大じゃないのと、 playbookに書き込むのは基本的にコマンドなので 他の開発者がレビューするときにも楽だ。 上から下に評価されて実行されるし。 ## docker https://www.docker.io/  chefで毎度のごとくruby, perlのビルドをするのが辛くなってきた。 GRF, HRFのビルドもだるくなってきた。 楽しいんだけどだるい。どうにかしたい。 ということでdockerを使い始めてみることにした。 RHEL系のディストリに0.7から対応し始めたし。 ansibleとdockerを組み合わせることで デプロイが楽になった。簡略化が進んだ! docker indexというサービスがあるのだけれど、 そこに個人的に作成したGRF, HRF各imageを登録した。 - https://index.docker.io/u/futoase/docker-growthforecast/ - https://index.docker.io/u/futoase/docker-hrforecast/ どうにかこうにかstagingで立ち上げているが問題なく動いているようだ。 docker imageのビルドをdocker indexというサービスにに任せる(!)ことができ、 外部リソースを使ってビルドすることによりリソース省力化ができる。 ...といってもdocker buildした後にprivate docker-registryに対して pushすればよいのだけれど。 # これから使おうとしてるもの ## deploy https://github.com/visionmedia/deploy  capistranoの次として採用検討中(個人的に) https://github.com/visionmedia/deploy 簡素な方が良いという考えから。 どんな塩梅で使えるのか確認するため個人的に確認してみた。 http://qiita.com/futoase/items/c2ac39cfe28813b79bc4 ## dokku https://github.com/progrium/dokku  localで確認できるよう導入してみたが、 ubuntuしか対応してないとのことでcentosもサポートしてもらえると助かる:) 個人的に以下のエントリがとても参考になった。 http://blog.coiney.com/2013/08/10/create-my-own-heroku/ # これからの展望 1. デプロイツールはdeployを使っていく(問題がでなければ...) 2. ミドルウェアインストール・マシン環境構築にはansibleを使っていく 3. アプリケーションイメージを常にdockerで持つようにする 4. docker exportでコンテナをバックアップ 5. 母艦が変わってもdockerが入れられるディストリであればdocker importで移行終了にする とにかくインフラ、環境構築については簡素にする。 大切なのは何をやるか、だからだ。 まだ色々とある。試すことも沢山あるので逐次書いていく。 |
|
| 825位 |
|
|||
|
12:08:32 |
|
|
## 現在時刻が 2014-01-08 11:53:06 +0900 のとき # 良く使う | format | example | memo | |:--------:|---------|------| | %Y | 2014 | 西暦を表す数 | | %m | 01 | 月を表す数字(01-12) | | %d | 08 | 日(01-31) | | %H | 11 | 24時間制の時(00-23) | | %M | 53 | 分(00-59) | | %S | 06 | 秒(00-60) (60はうるう秒) | | %w | 3 | 曜日を表す数(0-6) 日曜日が0 | # 一括 | format | example | memo | |:--------:|---------|------| | %F | 2014-01-08 | %Y-%m-%d と同等 (ISO 8601の日付フォーマット) | | %D | 01/08/14 | 日付 (%m/%d/%y) | | %T | 11:53:06 | 24時間制の時刻。%H:%M:%S と同等。 | | %X | 11:53:06 | 時刻 ( ロケールに適切な時刻?とのこと ) | | %R | 11:53 | 24時間制の時刻。%H:%M と同等。 | # 0埋め無し | format | example | memo | |:--------:|---------|------| | %e | ` 8` | 日。一桁の場合、半角空白で埋める ( 1..31) | | %k | 11 | 24時間制の時。一桁の場合、半角空白で埋める ( 0..23) | | %I | 11 | 12時間制の時(01-12) | | %-m | 1 | 月の0埋め無し。おぼれられるかこんなの | | %-d | 8 | 日の0埋め無し。 | # あまり使わない | format | example | memo | |:--------:|---------|------| | %c | Wed Jan 8 11:53:06 2014 | 日付と時刻 | | %x | 01/08/14 | 日付 | | %C | 20 | 世紀 (2009年であれば 20) | | %y | 14 | 西暦の下2桁(00-99) | | %u | 3 | 月曜日を1とした、曜日の数値表現 (1..7) | | %A | Wednesday | 曜日の名称(Sunday, Monday ... ) | | %a | Wed | 曜日の省略名(Sun, Mon ... ) | | %B | January | 月の名称(January, February ... ) | | %b | Jan | 月の省略名(Jan, Feb ... ) | | %h | Jan | %b と同等 | | %j | 008 | 年中の通算日(001-366) | | %l | 11 | 12時間制の時。一桁の場合、半角空白で埋める ( 0..12) | | %N | 546615000 | 秒の小数点以下。桁の指定がない場合は9桁 (ナノ秒)、%6N: マイクロ秒 (6桁)、%3N: ミリ秒 (3桁) | | %L | 546 | ミリ秒 (000.999) | | %s | 1389149586 | 1970-01-01 00:00:00 UTC からの経過秒 | | %P | am | 午前または午後(am,pm) | | %p | AM | 午前または午後(AM,PM) | | %r | 11:53:06 AM | 12時間制の時刻。%I:%M:%S %p と同等。 | | %v | 8-JAN-2014 | VMS形式の日付 (%e-%b-%Y) | | %V | 02 | ISO 8601形式の暦週 (01..53) | | %U | 01 | 週を表す数。最初の日曜日が第1週の始まり(00-53) | | %W | 01 | 週を表す数。最初の月曜日が第1週の始まり(00-53) | | %Z | JST | タイムゾーン | | %z | +0900 | タイムゾーン。UTCからのオフセット (例 +0900) | | %n | | 改行 (\n) | | %t | | タブ文字 (\t) | |
|
| 826位 |
|
|||
|
15:24:22 |
(Cyber Agent, Inc. 所属) |
|
node.jsのインストールやバージョンアップはnvm(Node Version Manager)をつかう
https://github.com/creationix/nvm って書いたけど、そのあとnodebrewというものがあることを知った。 こっちのほうがオヌヌメ。 <a href="http://qiita.com/strsk/items/db64f4dd27484a875d20">nodebrewで手軽にnode.jsバージョンアップ - Qiita [キータ]</a> ### nvmのインストール ``` $ git clone git://github.com/creationix/nvm.git ~/.nvm $ source ~/.nvm/nvm.sh ``` ### インストールされているnode.jsのバージョンを確認 ``` $ nvm ls ``` ### インストール可能なnode.jsのバージョンを表示 ``` $ nvm ls-remote ``` インストールされているバージョンは青くなる ### 指定したバージョンをインストール ビルド番号を指定しないと指定したマイナーバージョンのlatestなものがインストールされる ``` $ nvm install 0.11 ``` ### 使用するバージョンを切り替える ``` $ nvm use 0.11 ``` ### 確認してみる ``` $ node -v v0.11.9 ``` |
|
| 827位 |
|
|||
|
13:16:34 |
|
|
# 経緯 - pyenv - virtualenv - Python 3.3.1 で、Scikit-learnを使った機械学習をやろうと思い立った。ついでに図の可視化もやろうと考えてmatplotlibも使うことにした。matplotlibを簡単に使うためにpylabをimportしたところ、RuntimeError: Python is not installed as a frameworkというエラーが発生したので、matplotlibの設定を変えて、問題を解決した。 # インストール手順 ```terminal $ pip install scikit-learn $ brew install freetype $ sudo pip install python-dateutil $ sudo pip install pyparsing $ sudo pip install matplotlib ``` matplotlibのインストールには - freetype - python-deteutil - pyparsing が必要。ほかにも色々必要なものがある。詳しくは[公式ページ](http://matplotlib.org/users/installing.html)にて なお、freetypeをbrew installしたら依存しているlibpngも入る。 参考 http://djakarta-trap.net/blog/2013/06/07/install_matplotlib/ # 問題発生 対話環境で ```python:ipython3 import pylab ``` とすると ``` RuntimeError: Python is not installed as a framework. The Mac OS X backend will not be able to function correctly if Python is not installed as a framework. See the Python documentation for more information on installing Python as a framework on Mac OS X. Please either reinstall Python as a framework, or try one of the other backends. ``` なるエラーが発生した。 ## 調査 まず同様のエラーが起こって困っている人、そして解決した人がいないか調べた。 ytというmatplotlibを利用したプロジェクトでの議論 http://hg.yt-project.org/yt/issue/642/cannot-import-pylab-on-os-x-using 同じ問題にぶつかった日本語ブログ記事 http://asahima.hatenablog.com/entry/2013/10/11/201119 matplotlib本家githubでのissue https://github.com/matplotlib/matplotlib/issues/2361 どうも2013年10月段階でもまだ解決していないみたい。 ## 原因 matplotlibの画像描画バックエンド(参考 http://matplotlib.org/faq/usage_faq.html#what-is-a-backend )はデフォルトの設定ではCocoaのAPIを使ってレンダリングを行う"macosx"である。デフォルトではないバックエンドとしてGTKAggやQt4Aggがある。LinuxやWindowsから使うときは当然macosx以外のバックエンドを設定する。 ### 僕の推論 ※(この項の内容は想像で考えたことなので、間違っている可能性があります) 問題は、僕の環境ではpyenvを使ってOSXシステムにもとから入っているのとは違う場所にPython 3.3をインストールしていたことだ。この場合、CocoaのAPIで描画できない可能性があるので、pylabをimportしたときに、念のためにエラーを発生させてしまう。 ※(推論終わり) バックエンドをmacosx以外のものに指定すれば、問題は解決する。 ## 解決方法 matplotlibをpipでインストールしたので、 ~/.matplotlib というディレクトリができているはずだ。そこにmatplotlibrcというファイルを作る。 ``` ~/.matplotlib/matplotlibrc backend : TkAgg ``` と、TkAggをバックエンドに使うよう指定する。 matplotlibrcの書き方参考 http://matplotlib.org/users/customizing.html ## 実験 こちらのブログ記事 http://sucrose.hatenablog.com/entry/2013/05/25/133021 にて紹介されている、sklearnの結果をpylabで可視化するコードを対話環境で実行した。 ``` from sklearn.datasets import load_digits import pylab as pl digits = load_digits() pl.gray() pl.matshow(digits.images[0]) pl.show() ```  このように、TkAggでもきれいに図を描画できた。 |
|
| 828位 |
|
|||
|
11:41:16 |
|
|
GithubからJenkinsへのServer Hookを調べた結果をまとめておく。
以下の3つの方法を調べた。 * WebHook URLsを用いる方法 * Jenkins (GitHub plugin) を用いる方法 * Ver1.8で確認 * Jenkins (Git plugin) を用いる方法 * Ver2.0で確認 この方法を用いるとGithubのコードに修正を加えると対応するJenkinsジョブを走らせることができる ----- ## WebHook URLsを用いる方法 * Githubからコミットの情報を受け取って自力で処理する方法 * 好きなようにhook処理できるが実装が必要 * [Githubの特定ブランチにpushした時だけhookでjenkinsビルドする - Qiita [キータ]](http://qiita.com/horimislime/items/cc4897f8fbf8b8358fe5)が詳しい ### Jenkinsの設定 * [Jenkins Parameterized Trigger plugin](https://wiki.jenkins-ci.org/display/JENKINS/Parameterized+Trigger+Plugin)をインストールしておく ### HookするJenkinsのジョブの設定 * 「ビルドのパラメータ化」で「文字列」のpayloadを指定しておく * Hook時にcommitやpushに関するJSON形式のパラメータ([Post-Receive Hooks · GitHub Help](https://help.github.com/articles/post-receive-hooks))が渡される * セキュリティ設定時は「リモートからビルド」をチェックして認証トークンを設定しておく * payloadで渡されたパラメータを解析して対象のリポジトリやブランチのジョブを呼び出すなりする ### Githubの設定 * 「Settings」=> 「Server Hooks」=>「WebHook URLs」 で「URL」に[Jenkins URL]/job/[Job Name]/buildWithParametersを指定して「Update Settings」する * セキュリティ設定時は「URL」に?token=[Token Name]を追加  ----- ## Jenkins (GitHub plugin) を用いる方法 * GithubからHookを受け取ると対応したジョブが実行される ### Jenkinsの設定 * [GitHub plugin](https://wiki.jenkins-ci.org/display/JENKINS/GitHub+plugin)をインストールしておく * セキュリティ設定は特に必要なさそう(認証ありにしてもhookできた) ### HookするJenkinsのジョブ設定 * 「ソースコード管理」の「Git」でリポジトリ(ブランチ)を指定しておく * 「ビルド・トリガ」の「Build when a change is pushed to GitHub」を設定しておく ### Githubの設定 * 「Settings」=> 「Server Hooks」=>「 Jenkins (Github plugin)」で「Jenkins Url」に[JenkinsのURL]/github-webhookを設定する  ----- ## Jenkins (Git plugin) を用いる方法 * GithubからHookを受け取ると対応したジョブが実行される ### Jenkinsの設定 * [Git plugin](https://wiki.jenkins-ci.org/display/JENKINS/Git+plugin)をインストールしておく * セキュリティ設定は特に必要なさそう(認証ありにしてもhookできた) ### HookするJenkinsジョブ設定 * 「ソースコード管理」の「Git」でリポジトリ(ブランチ)を指定しておく * 「ビルド・トリガ」の「SCMのポーリング」を設定しておく ### GIthubの設定 * 「Settings」=> 「Server Hooks」=>「 Jenkins (Git plugin)」で「Jenkins Url」にJenkinsのURLを設定する  ---- ## その他 ### githubからのHookアクセスIPについて <del>GithubのWebHookの説明によると204.232.175.64/27, 192.30.252.0/22らしい。</del> [https://api.github.com/meta](https://api.github.com/meta)のhooksで確認可能(コメントで教えて頂きました) * API仕様:[Meta | GitHub API](http://developer.github.com/v3/meta/) ### httpsでのHookについて * 2013/11/07時点のhttpsでのHookはWebHook URLs/Jenkins (GitHub plugin) /Jenkins (Git plugin) でほぼ不可能だった * JenkinsでHookされない、Apacheレベルでログが残っていない * [GitHub pluginのコメント](https://wiki.jenkins-ci.org/display/JENKINS/GitHub+Plugin?focusedCommentId=63144887#comment-63144887)でGithubの問題でhttpsが使えないらしいというものがあった * Hook可否について(×:Hook不可、○:Hook可) | - | WebHook URLs | Jenkins (GitHub plugin) | Jenkins (Git plugin) | |:-----------|:-----------|:-----------|:-----------| |https(自己署名証明書) | × | × | × | |https([ジオトラスト](https://www.geotrust.co.jp/)の証明書) | × | ○ | × | |
|
| 829位 |
|
|||
|
23:12:28 |
|
|
golang最近やっているので、いろいろぐぐったり[JSON and Go](http://blog.golang.org/json-and-go)などを読みながらやってみました。
```json { "period": "yy", "exec_period": { "start": { "month": 1, "week": 2, "day": 3, "hour": 4, "minute": 5 }, "end": { "month": 6, "week": 7, "day": 8, "hour": 9, "minute": 10 } }, "backup": [ { "local_dir": "directoryLo1", "server_dir": "directoryLo2", "server_host": "domaineName" }, { "local_dir": "directoryLo1", "server_dir": "directorySe2", "server_host": "domaineName" } ], "incremental_save": "1Y2M" } ``` こんな感じのjsonがあるとします。 これを読み込むには標準ライブラリの[encoding/json](http://golang.org/pkg/encoding/json/)を使ってこんな感じに書けます。 ```go package main import ( "encoding/json" "fmt" "os" ) type time struct { Month int Week int Day int Hour int Minute int } type execPeriod struct { Start time End time } type directories struct { LocalDir string `json:"local_dir"` ServerDir string `json:"server_dir"` ServerHost string `json:"server_host"` } type data struct { Period string ExecPeriod execPeriod `json:"exec_period"` Backup []directories IncrementalSave string `json:"incremental_save"` } func main() { dec := json.NewDecoder(os.Stdin) var d data dec.Decode(&d) fmt.Printf("%+v\n", d) } ``` ```bash:result $ ./json < test.json {Period:yy ExecPeriod:{Start:{Month:1 Week:2 Day:3 Hour:4 Minute:5} End:{Month:6 Week:7 Day:8 Hour:9 Minute:10}} Backup:[{LocalDir:directoryLo1 ServerDir:directoryLo2 ServerHost:domaineName} {LocalDir:directoryLo1 ServerDir:directorySe2 ServerHost:domaineName}] IncrementalSave:1Y2M} ``` json側と同じようにstructを定義して、json.Decoderに渡してあげればおkっぽいです。ただしkeyの名前がjson側と違う場合は、`json:"key_name"`みたいにして違いを吸収してあげる必要があります。 [JSON and Go](http://blog.golang.org/json-and-go)の後ろの方に、jsonの構造がわからない場合のやり方も載っていて、interfaceを使いなよ!的なことが書いてあります。interfaceよくわかってないので調べつつやったのが以下です。 ```go package main import ( "encoding/json" "fmt" "os" ) func assert(data interface{}) { switch data.(type) { case string: fmt.Print(data.(string)) case float64: fmt.Print(data.(float64)) case bool: fmt.Print(data.(bool)) case nil: fmt.Print("null") case []interface{}: fmt.Print("[") for _, v := range data.([]interface{}) { assert(v) fmt.Print(" ") } fmt.Print("]") case map[string]interface{}: fmt.Print("{") for k, v := range data.(map[string]interface{}) { fmt.Print(k + ":") assert(v) fmt.Print(" ") } fmt.Print("}") default: } } func main() { var data interface{} dec := json.NewDecoder(os.Stdin) dec.Decode(&data) assert(data) fmt.Println() } ``` ```bash:result $ ./json < test.json {period:yy exec_period:{start:{month:1 week:2 day:3 hour:4 minute:5 } end:{month:6 week:7 day:8 hour:9 minute:10 } } backup:[{local_dir:directoryLo1 server_dir:directoryLo2 server_host:domaineName } {local_dir:directoryLo1 server_dir:directorySe2 server_host:domaineName } ] incremental_save:1Y2M nulltest:null } ``` interfaceすげー [Effective Go](http://golang.org/doc/effective_go.html)のType switchの例まんまなのですが、型アサーションの結果によって処理を振り分けることができます。[ここの説明](http://golang.org/pkg/encoding/json/#Unmarshal)によるとDecode結果は、bool、float64、string、[]interface{}、map[string]interface{}、nilのどれかになるはずなので、一旦interface{}で受けてから、それぞれに対応した処理に振り分けます。 実際使うときはstruct定義した方があとあと使いやすいのでいい気がしますが、golangのinterfaceはこんなの以外にもいろいろ出来て面白そうですね。 |
|
| 830位 |
|
|||
|
02:06:10 |
|
|
# Ubuntu での Clojure と Leiningen 超入門
JDK は既に用意出来ているものとしますね.`sudo apt-get install openjdk-7-jdk` とでもしておけば大丈夫なんじゃないでしょうか? (ダメだったら教えて) まず `lein` を取ってきてどこかパスの通ったところに設置します.そして実行権限を付与して実行します. ```bash # パスとかなんのこっちゃ勢向けコピペ用コマンド # これ実行するだけでとりあえず動かせまっせ export PATH=$HOME/local/bin:$PATH if [ ! -d $HOME/local/bin ]; then mkdir -p $HOME/local/bin; fi # ダウンロード & インストール cd $HOME/local/bin wget https://raw.github.com/technomancy/leiningen/stable/bin/lein chmod +x lein ./lein ``` はい,これで Leiningen がインストールされました.`lein --version` と打てばバージョンが確認出来,`lein --help` と打てばちょっと手間取りつつも利用可能なタスク一覧が出てきたと思います.ちなみにうちの場合バージョンは `2.3.1` でした. この辺のことは [Leiningen](http://leiningen.org/) の Install を参考にしました. `command not found: lein` 等と出たら現状間違いなく PATH の設定ミスです. で,このタスクの中の `repl` というのを使っていきます.REPL (Read Eval Print Loop) とは対話型の実行環境で,Ruby や Python を触ったことのある方はお馴染みではないでしょうか?馴染みのない方もこれからすぐに馴染みます. `lein repl` と打ってみて下さい.ズラズラっと REPL や Clojure のバージョン情報とかなんやかんやが表示された後,`user=>` という状態で止まっていると思います.ここに Clojure のコードを書いて Enter をッターンすれば,なんとそれが即座に実行されます.言語仕様を覚えたいとか,即座に何かを試したいだけとか言う時にはこれを使わない手は無いですね. 早速ここで最速入門をしてしまいましょう. ```clojure (print "Hell World") ; Hell Worldnil ; ↑これは出力されるもの.ここまでコピペしたらあかんで (してもいいけど). ; ちなみに Clojure ではコメントは ; で始まる行になる.この行もコメントです. ``` Oops! 地獄になってしまったしなんかよく分からないものがくっついてますね.`nil` です.これは,print 関数の戻り値というか評価結果が `nil` になったことを意味しています.最後に改行が無い様がしっかり現れていて,つまりそれを直せばいいのねと推察出来ます. ```clojure (println "Hello World") ; Hello World ; nil ``` やったぜ.`println` は行を表示する関数ですね.勝手に改行が最後に入ります.もちろん `(print "Hello World\n")` としても良かったということです. ちょいと計算もしてみましょう. ```clojure (+ 1 2) ; 3 (* (+ 1 2) 3) ; 9 (* (+ 1 2) (- 3 4)) ; -3 ``` ナニコレ… となった人,「前置記法」「ポーランド記法」などと Google 先生にお伺いを立ててみて下さい.話はそれからです. 先程は評価結果が `nil` になっていましたが,今回は数値が出てきていますね.四則演算関数を使ったので, もうちょっと Lisp っぽいことをしてみましょう. ```clojure (cons 1 '(2 3)) ; (1 2 3) (first '(1 2 3)) ; 1 (rest '(1 2 3)) ; (2 3) ``` Lisp の伝統的な `car` と `cdr` は `first` と `rest` になりました.ぶっちゃけこの方が本来の機能を正しく表現する名前ですね. `'(1 2 3)` にくっついてる `'` は,評価を遅らせるものです.ここでもし評価されてしまうと,1 という関数に 2 3 を引数として与えるという意味になってしまいます. ```clojure (1 2 3) ; ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/eval4108 (NO_SOURCE_FILE:1) ``` `1` は関数になれない,って感じです. ```clojure (doc cons) (source cons) ``` `doc` 関数と `source` 関数はそれぞれドキュメントとその関数のソースを表示してくれます.活用しましょう. じゃあ自分で関数を作ってみましょうか. ```clojure (fn [x y] (+ x y)) ; #<user$eval4111$fn__4112 user$eval4111$fn__4112@15f3386a> ``` はい,引数として与えられた x y を足す関数が出来ました. 関数を作るには `fn` という関数を使いま… いえ実は `fn` は関数では無いです.マクロとか言うブツです.あんまり詳しいことは私も知りません.でも `fn` 関数に引数リスト渡してそれらの扱い方を指定してるみたいな感じですね.そんな風に今の私には見えます.もうちょっと達者になってくれば違う見方が出来てくるのかもしれません. ちなみに引数リストは `[]` で定義します.覚えましょう.そういう仕様です. で,せっかく作ったこの関数,残念ながらこのままでは使えません.この関数には名前が無いからです. ```clojure (defn add [x y] (+ x y)) ; #'user/add ``` こうすれば大丈夫です.`(add 1 2)` は `3` になります.`add` という名前で関数が作られたので,無事 `add` として我々は使えるようになったのです. 待てよ…?何も関数を使ってみたいだけなら `add` なんて名前は不要じゃないか…? ```clojure ((fn [x y] (+ x y)) 1 2) ; 3 ``` Excellent! 出ました.無名関数です.`(add 1 2)` の `add` の部分がそっくりそのまま名無しで作っただけの前述の関数に置き換わっただけです. 無名関数が出来るのか… 匿名関数は出来るのか? ```clojure ((fn add-anonymous [x y] (+ x y)) 1 2) ; 3 (add-anonymous 1 2) ;CompilerException java.lang.RuntimeException: Unable to resolve symbol: add-anonymous in this context, compiling:(NO_SOURCE_PATH:1:1) ``` 出来ました.`fn` は名前を付けることも出来なくはなかったということです. ちなみにこれが出来るということは再帰関数も普通に書けるということです. ```clojure ((fn f [x] (if (= x 0) 1 (* x (f (- x 1))))) 10) ; 3628800 ``` オーケーオーケー.`)))))` とかふざけんなって言いたいんだろう? こう書き直してみましょう. ```clojure ( (fn f [x] (if (= x 0) 1 (* x (f (- x 1))))) 10) ; 3628800 ``` repl では改行含めてコピペしても大丈夫です. そんなことより,やっぱり `)))))` あるじゃねーか!いえ違います.もはやそんなもの気にしなくていいのです. 2 つ目,つまり 2 行目にある `(` に注目しましょう.これは,関数の定義が始まる位置ですね (`fn` の直前).つまり,それと同じ深さになっている 10 もあわせて考えると,`( (なにか関数の定義) 10 )` という形になっているわけで,何かこれから関数を作って `10` を渡すのですね. ではその「これから作る関数」をちょっと見て行きましょう. プログラムとしてこれが出来なきゃ話にならんだろう代表選手,`if` の登場です.条件分岐が出来ないプログラムはただのカカシですな. `(if cond then else)` という形になっています.`cond` を評価しそれが `true` なら `then` を,`false` なら `else` を評価して評価結果として使います.使われない方は評価されません.どういう意味か?C 言語とかの if 文と一緒ってことですよ. そろそろ分かって来ましたね.`x` が `0` なら `1` を,そうでなければ `x` と `(- x 1)` を再び自分自身 `f` に与えたものとの積を評価結果とする.これはまさしく階乗計算です. インデントさえしっかり… いえ,「普通に」書けていれば,実は閉じ括弧とはそれほど重要なものでは無くなるのです. 疲れました.そろそろ for ループの話でもして終わりましょうか? 馬鹿め.for ループなぞ要らぬわ. `map`, `reduce`, `filter`, `nth`, `take`, `repeat`, `iterate` この辺りの簡単な例をいくつか見てみましょう. ```clojure (map (fn [x] (+ x 1)) '(1 2 3)) ``` ```clojure (reduce + 0 '(1 2 3 4)) ``` ```clojure (filter (fn [x] (< x 3)) '(1 2 3 4)) ``` ```clojure (nth '(1 3 5 7) 2) ``` ```clojure (take 3 '(10 20 30 40 50)) ``` ```clojure (take 5 (repeat 0)) ``` ```clojure (nth (iterate (fn [x] (+ x 2)) 0) 5) ``` 結果は君たちの目で確かめよう! そしてそれぞれの関数の説明は `doc` あるいは `source` 関数で確かめよう! これらの関数を知っておくだけでも,「ああ,for とかいらんやん」と思えるはずですよ!きっと!でもまぁぶっちゃけ割とすぐに `loop` と `recur` という「これ実質 for 文じゃないですかやだー!」なシロモノに遭遇すると思いますけども. 私もまだ `lazy-seq` とかよく分かってないし Leiningen の扱い方分からないことだらけですし,無限リストのちからってすげーくらいのノリなのでこれから勉強していきます. フレームワークが多すぎて Web アプリ一つ作ってみようとしても結局いろんなライブラリの README 読んでばっかりです.決められません.英語つらい. 誰か 1 人でも,この記事で Clojure 初めてくれれば嬉しい限り. |
|
| 831位 |
|
|||
|
16:51:47 |
(codeTakt (ex- mixi) 所属) |
|
重複した記事があるとの指摘がありました。
下記のほうが図とかステップが充実しています! http://qiita.com/items/8bc1a11f1382409f1d2a 【もはやエミュレータとは言わせない】 ## そもそも超速エミュレータが必要な理由 AndroidアプリやWebアプリを作る際、OSバージョンが多すぎて実機とか全部揃えていられないので、エミュレータを使ってデバッグすることになると思います。しかしこのエミュレータが非常に重たくて、MacBook Airなんかで使えたものではありません。これはスマホのARMアーキテクチャのCPUを、PCのx86アーキテクチャのCPU上でエミュレートしているためです。 この問題の解決策として、Intelがx86 CPUで直接動作可能なAndroidを配布しています。Androidの各種アプリは基本的にJava製なので、「Java(Dalvik)仮想マシン」の層でCPUの違いは吸収され、x86版でも全く差異なく動作します。 ## 超速エミュレータの導入手順 超速エミュレータを使用するには、通常のAndroid SDKに加えて2つのコンポーネントが必要になります。 * CPUの仮想化支援機能を利用するカーネルモジュール * x86版AndroidのOSイメージ ### カーネルモジュールの導入方法 [Intel® Hardware Accelerated Execution Manager](http://software.intel.com/en-us/articles/intel-hardware-accelerated-execution-manager)からOSにあったものをダウンロードし、インストールします。途中で仮想マシンに割り当てる(最大?)メモリ容量について聞かれますが、デフォルトで良さそうです。なおMacの場合、再度インストーラを実行することで容量を変更できるようです。 (注)2013/02/04現在:少なくともMountain Lionについては、release_1.0.4-hotfixの方をインストールしてください。通常版をインストールすると、Mac OSがクラッシュします。 Macの場合は、インストール後に下記のコマンドを実行する必要が有るようです。(再起動ごとに実行が必要なのかはまだ試していません・・。) sudo kextload -b com.intel.kext.intelhaxm ### OSイメージの入手方法 こちらは簡単で、SDKのディレクトリ/tools/androidで起動できるAndroid SDK Managerから、数クリックでインストール出来ます。Androidの各バージョンに「Intel x86 Atom System Image」という項目がありますので、これにチェックを入れてインストールします。  ## エミュレータの設定、起動方法 Android Virtual Device Manager上で、仮想デバイスを追加or編集する際に、TargetとCPUを、インストールしたx86版のものに設定するだけで完了です。CPU部分にx86と書かれていればOKです。後は、通常通りeclipseまたはManagerから起動すると、超速で立ち上がってきます。  ※「HAX is working and emulator runs in fast virt mode」のように表示されない場合は、CPUの仮想化機能が有効(搭載されていて、BIOSでEnabledにされている)かどうか、またカーネルモジュールがインストールされてロード済みかどうかもう一度確認してください。 ### 追記:Android 2.3.3のx86エミュレータを使う場合 イメージのディレクトリ構成にバグがあるので、VM作成時に「userdata.imgがない」みたいなエラーが出てしまいます。 予め、解凍したディレクトリ/sdk/system-images/android-10/x86/images/x86の中身を、解凍したディレクトリ/sdk/system-images/android-10/x86にコピーしておけば解決します。 Macはこちらでどうぞ↓ cd sdk/system-images/android-10/x86/images/x86/ ln -s images/x86/* ./ Happy 超速 Android Hacking! |
|
| 832位 |
|
|||
|
00:31:24 |
(Increments Inc 所属) |
|
レポジトリの特定ブランチを使いたいとか,ローカルでちょっと変更して試したいときのGemfileの変更方法.
変更してbundleを実行すればよい. ref: http://gembundler.com/man/gemfile.5.html ```ruby # あるレポジトリの特定ブランチ gem 'albino', :git => 'git://github.com/yaotti/albino.git', :branch => "enable-g-flag" # 追記: GitHubのmasterならこういう指定もできる(bundler v 1.1以降) gem 'rails', :github => 'rails/rails' # ローカルにあるgem gem 'albino', :path => '/Users/you/path/to/albino' ``` |
|
| 833位 |
|
|||
|
04:25:39 |
|
|
#はじめに
* 今回は、メモリアロケーションに関して、Java・C#などプログラミング言語やUnity・UDKなどのゲームエンジンにおいてはメモリの事なんて全然気にしなくていい時代になっている(!?)中、ゲーム開発におけるメモリアロケーションについて、[えーでるわいす](http://edelweiss.skr.jp/)で現在使われているゲームエンジンでの実装も踏まえて一回まとめてみようかと。。。 <BR> <BR> 発端は、[@aizen76](https://twitter.com/aizen76)さんの[「カスタムメモリマネージャと高速なメモリアロケータについて」](http://www.slideshare.net/alwei/ss-11521742?ref=http://www.slideshare.net/alwei?utm_campaign=profiletracking)というスライドを偶然見つけたのがきっかけで、自分は今まで深く理解せず何となくでやってきてしまっていた気がしたのでちょっとまとめて見ようと思い立った感じです。 # 対象 * **C++初級者~中級者ぐらい** <BR> <BR> ※ **[ガベコレ](http://ja.wikipedia.org/wiki/%E3%82%AC%E3%83%99%E3%83%BC%E3%82%B8%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)だのスマートポインタだの[mallocの実装によるマルチスレッド環境下での速度](http://www.soum.co.jp/misc/tatuhiko/multi-thread/)がどうとかその辺の話は止まらなくなるので我慢します( ´∀`)** # メモリアロケーションって何なの?(念のため軽く基礎から。。。) ## メモリアロケーションとは? プログラムが起動する際に、そのプログラムが動作するために必要となるメモリー領域を、OSがCPUのメモリー上に確保することである。[(IT用語辞典)](http://www.sophia-it.com/content/%E3%82%A2%E3%83%AD%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3) ## メモリアロケーションのパターンはおおまかに2つ [(ウィキペディア)](http://ja.wikipedia.org/wiki/%E5%8B%95%E7%9A%84%E3%83%A1%E3%83%A2%E3%83%AA%E7%A2%BA%E4%BF%9D) ### 静的アロケーション → コンパイル時にメモリ領域を確定 * **メリット** * アロケーションに処理を必要としないため速度面で有利。メモリリークも存在しない!みんな幸せ\(^o^)/ * **デメリット** * どの場面でもメモリサイズが固定になってしまうため、必要の無い場面でもメモリを多く消費してしまう。<BR>(例:1面では敵が2体しか出ないけど、2面は20体出るので、予め20体分配列を用意しないといけない)<BR> ### 動的アロケーション → プログラム実行時に、並行してメモリ領域の確保と解放 * **メリット** * 場面に応じて柔軟に必要なメモリ使用量を変化させることができる。 * **デメリット** * それなりに処理を食われる。 → 空いてるメモリを探したり登録・解除に処理を食う。 * メモリ解放忘れ(メモリリーク) **通称お漏らし** を起こす可能性がある。 * リークや断片化でメモリがなくなると最悪クラッシュ/(^o^)\<BR> **※STLとか使ってると、断片化や処理負荷の問題が顕著にあらわれてくる。[知らず知らずのうちにリーク](http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1477911114)することも(・∀・)** <BR> **あれ?ゲームなら敵の数とか出るエフェクト数大体決まってるし静的確保でいいんじゃね?w** <BR> **しかしながら、えーでるわいすのエンジン・エフェクトシステム・キャラクタ処理などでは、何故か動的アロケーションが多用されています/(^o^)\** ## なぜ動的アロケーションを使うのか? * 出るものが多すぎて正確な最大メモリ使用量が読めない。メモリの最大使用量とかいちいち気にするのめんどい。 * STLとか便利だしいっぱい使いたい! **ただそれだけ!(ひどい)** <BR> **だがしかし、動的アロケーションは処理を食う** **大した事してないのにゲームが重いとか目も当てられない!!** ## でなんでいちいちアロケータをカスタムするの? * Windows標準のメモリアロケーションの仕組みは細かいメモリアロケーションに向いてなくて速度的にいまいち(らしい) → (2014/04/27追記)デバッガを繋げてると、性能が極端に落ちるらしい → [詳細](http://blog.techlab-xe.net/archives/1698) * Windowsのメモリリーク・メモリ破壊チェックはいまいち使いづくて激重なので自分好みにしたい。 **ただそれだけ!(更にひどい!)** # えーでるわいすにおける動的メモリアロケーションの実装につい ## 概要 * new/deleteを乗っ取って[dlmalloc(メモリ管理ライブラリ)](http://g.oswego.edu/dl/html/malloc.html)に渡す処理を実装。<BR> → dlmallocがいかに素晴らしいか?についてはぐぐってくださいw * メモリの管理領域は、STL用/エンジン用/ゲーム用 に分割。<BR> →空き領域検索処理を少しでも軽くするため。 * メモリリーク/破壊チェックのために、メモリ確保情報をリストで管理 * メモリリークはゲーム終了時や、ステージ切替時にチェックが走ってログに出力。 VSで決められてる書式でログを出してるので、ログ出力ウィンドウのソースファイル名のところをクリック するとソースが開く!便利\(^o^)/ ``` [警告 #023](04:18:31) メモリリークしてまっせ。[sys] ..\src\Core\TGLMemory.cpp(675) ..\src\sAppMain.cpp(78) : {80} [area:5 adress:0x06230fb0 size: 0.00Kbyte] dump< [ヘヘヘヘ] cd cd cd cd ``` * メモリ破壊は、確保領域の後ろのアドレスにチェック用の値を忍ばせて、確保・開放時にチェック。→ メモリを0xCDCDCDCDで塗りつぶして、値が書き換わってたら壊れたとみなす。 **そんな感じ。特に何も難しいことはしてない(・∀・)**<BR> ※配布時はデバッグ処理は重いので省かれてます。 ## 実装 ### new/delete乗っ取り * new/deleteを直接使わずに独自のdefineで置き換え。 * デバッグ用にソース名とライン数を引数で渡してます。(ゲームを配布する際にデバッグ処理は省かれます) * ゲームなので、メモリが確保出来なかったりした時の例外処理は無効にしてある。(例外処理は重いし面倒なので) * 詳細は、operator new オーバーライド でぐぐると出てきます。 * 念のため、Windows標準のアロケータに簡単に戻せるようにしてあります。 * メモリマネージャーの初期化よりも先にアロケーションが来ることがあるのでゴニョゴニョする必要がある。(後述) ```TGLTypes.h #if defined(_TGL_DEBUG) || defined(_TGL_RELEASE) #define SYS_NEW new(TGL::Memory::AREA_TYPE_SYS,__FILE__, __LINE__) #define APP_NEW new(TGL::Memory::AREA_TYPE_APP,__FILE__, __LINE__) #else #define SYS_NEW new(TGL::Memory::AREA_TYPE_SYS) #define APP_NEW new(TGL::Memory::AREA_TYPE_APP) #endif ``` ```TGLMemory.h inline void* operator new( std::size_t size, TGL::Memory::AREA_TYPE newFlag,const char* pszFile,s32 nLine ) throw() { void* ptr = TGL::Memory::Allocater::Malloc( size ,newFlag,pszFile,nLine); #if 0 #ifdef _USE_EXCEPTION // 例外処理を許可しているときのみコンパイル if ( NULL == ptr ) { throw std::bad_alloc(); } #endif #endif return ptr; } inline void* operator new[]( std::size_t size, TGL::Memory::AREA_TYPE newFlag,const char* pszFile,s32 nLine ) throw() { return ::operator new( size ,newFlag,pszFile,nLine); } inline void* operator new( std::size_t size, TGL::Memory::AREA_TYPE newFlag) throw() { void* ptr = TGL::Memory::Allocater::Malloc( size ,newFlag); #if 0 #ifdef _USE_EXCEPTION // 例外処理を許可しているときのみコンパイル if ( NULL == ptr ) { throw std::bad_alloc(); } #endif #endif return ptr; } inline void* operator new[]( std::size_t size, TGL::Memory::AREA_TYPE newFlag ) throw() { return ::operator new( size ,newFlag); } inline void operator delete( void* ptr, TGL::Memory::AREA_TYPE newFlag) throw() { TGL::Memory::Allocater::Free( ptr ,newFlag); } inline void operator delete[]( void* ptr, TGL::Memory::AREA_TYPE newFlag) throw() { ::operator delete( ptr ,newFlag); } ``` ### STLのアロケータを置き換え * STLのアロケータは、デバッグ用とゲーム用で分けた。 * " **STL カスタムアロケータ** "で検索すると詳細が出てきます。 ```TGLSTL.h //! STLのカスタムアロケータ template <class T, s32 N = 1,TGL::Memory::AREA_TYPE AREA = TGL::Memory::AREA_TYPE_STL> class Allocator { public: // 型定義 typedef T value_type; typedef T *pointer; typedef const T *const_pointer; typedef T &reference; typedef const T &const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; // アロケータをU型にバインドする template <class U> struct rebind { typedef Allocator<U, N> other; }; // コンストラクタ Allocator() throw(){} Allocator(const Allocator&) throw(){} template <class U> Allocator(const Allocator<U>&) throw(){} // デストラクタ ~Allocator() throw(){} // メモリを割り当てる pointer allocate(size_type num, void *hint = 0) { hint; //return (pointer)( ::operator new( num * sizeof(T) ) ); return (pointer)(TGL::Memory::Allocater::Malloc(num * sizeof(T),AREA)); } // 割当て済みの領域を初期化する void construct(pointer p, const T& value) { // コンストラクタを無理やりコール new( (void*)p ) T(value); } // メモリを解放する void deallocate(pointer p, size_type num) { num; //::operator delete( (void*)p ); TGL::Memory::Allocater::Free((void*)p,AREA); } // 初期化済みの領域を削除する void destroy(pointer p) { p; p->~T(); } // アドレスを返す pointer address(reference value) const { return &value; } const_pointer address(const_reference value) const { return &value; } // 割当てることができる最大の要素数を返す size_type max_size() const throw() { return std::numeric_limits<size_t>::max() / sizeof(T); } }; // システム向け namespace sys { template <class T, s32 N = 1> class SystemAllocator : public stl::Allocator<T,N,TGL::Memory::AREA_TYPE_STL> { public: // アロケータをU型にバインドする template <class U> struct rebind { typedef SystemAllocator<U, N> other; }; // コンストラクタ SystemAllocator() throw(){} SystemAllocator(const SystemAllocator&) throw(){} template <class U> SystemAllocator(const SystemAllocator<U>&) throw(){} // デストラクタ ~SystemAllocator() throw(){} }; // カスタムアロケータを指定したSTL typedef std::basic_string<char, std::char_traits<char>, SystemAllocator<char> > string; typedef std::basic_string<wchar_t , std::char_traits<wchar_t>, SystemAllocator<wchar_t> > wstring; template<typename T> class vector : public std::vector<T, SystemAllocator<T> > {}; // その他必要に応じて各コンテナに対して定義を書いていく。// ``` ### メモリマネージャー(主要なコードを抜粋) * [dlmallocについて ぽこつん研究所](http://pokotsun.mydns.jp/?p=1205) ```TGLMemory.cpp #include <malloc.h> // dlmalloc //========================== //! メモリエリア管理ラッパー //========================== class MemoryArea { public: MemoryArea(){} ~MemoryArea(){} //! メモリスペースの作成 bool CreateMspace(const char* pszAreaName,size_t size=DEFAULT_ALLOC_SIZE) { m_size = size; strcpy(m_AreaName,pszAreaName); msp = create_mspace(size , 0 ); ASSERTMSG(msp,"メモリスペースの作成に失敗しました。[%s]",pszAreaName); return (msp != null); } //! メモリスペース削除 void DeleteMspace() { destroy_mspace( msp ); } //! Malloc void* Malloc(size_t size) { return mspace_memalign( msp, DEFAULT_ALIGNMENT_SIZE , size ); } //! ReAlloc void* ReAlloc(void *p,size_t size) { return mspace_realloc( msp, p , size ); } //! 開放 void Free(void *p) { mspace_free( msp, p ); } //! 情報取得 stl::dbg::string Info() { stl::dbg::string str; mallinfo info = mspace_mallinfo(msp); str = stl::FormatString<stl::dbg::string>("%12s size %sbyte / free %sbyte / use %sbyte", m_AreaName, stl::GetFormatUnitString<stl::sys::string>(m_size).c_str(), stl::GetFormatUnitString<stl::sys::string>(info.fordblks).c_str(), stl::GetFormatUnitString<stl::sys::string>(info.uordblks).c_str()); return str; } private: char m_AreaName[64]; mspace msp; size_t m_size; }; MemoryArea s_MemorySpaceArray[TGL::Memory::AREA_TYPE_MAX]; //---------------------------- // 初期化 //---------------------------- void Allocater::Initialize() { #ifndef ENABLE_CRT_DEBUG s_MemorySpaceArray[AREA_TYPE_DEBUG].CreateMspace("AREA_TYPE_DBG",DBG_ALLOC_SIZE); s_MemorySpaceArray[AREA_TYPE_STL].CreateMspace("AREA_TYPE_STL",STL_ALLOC_SIZE); s_MemorySpaceArray[AREA_TYPE_SYS].CreateMspace("AREA_TYPE_SYS",SYS_ALLOC_SIZE); s_MemorySpaceArray[AREA_TYPE_APP].CreateMspace("AREA_TYPE_APP",APP_ALLOC_SIZE); #else // Windows標準のアロケータの場合 // Get current flag int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); // Turn on leak-checking bit tmpFlag |= _CRTDBG_LEAK_CHECK_DF; tmpFlag |= _CRTDBG_ALLOC_MEM_DF; //tmpFlag |= _CRTDBG_CHECK_EVERY_128_DF; // _CRTDBG_CHECK_EVERY_16_DF とすれば16回に一回 デフォは1024回に一回 // Turn off CRT block checking bit //tmpFlag &= ~_CRTDBG_CHECK_CRT_DF; // Set flag to the new value _CrtSetDbgFlag( tmpFlag ); #endif } //------------------------ // 終了処理 //------------------------ void Allocater::Terminate() { #ifndef ENABLE_CRT_DEBUG for(s32 i = 0;i<AREA_TYPE_MAX;++i) { s_MemorySpaceArray[i].DeleteMspace(); } #endif s_bInit = false; OutputDebugString("Memory Allocater Terminate\n"); } //------------------------ // デバッグ機能付きmalloc //------------------------ void* Allocater::Malloc(size_t size,TGL::Memory::AREA_TYPE areaType,const char* pszFile,s32 nLine) { ASSERT(s_bInit&&"メモリマネージャーが初期化されてない"); #ifdef ENABLE_CRT_DEBUG void* adress = Malloc(size); #else size_t alloc_size = size; #ifdef CHECK_MEMORY_BREAK // メモリ破壊検地用領域 alloc_size += MEMORY_TRAP_SIZE; #endif void* adress = Malloc(alloc_size,areaType); #ifdef CHECK_MEMORY_LEAK // デバッグ情報 if( adress ) { TGL::Thread::ScopedLock lock(s_pLock_dbg); // ブレーク if( s_BreakAllocCount == s_AllocCount ){BP();} DebugMemoryInfo info; info.pAdress = adress; info.size = size; info.filename = stl::FormatString<stl::dbg::string>("%s(%u)",pszFile,nLine); info.count = s_AllocCount; info.areaType = areaType; if( !m_checkPointName.empty() ) { info.checkName = (*m_checkPointName.begin()); } info.Fill(); m_MemoryInfo[info.Adress()] = info; s_AllocCount++; } #endif #endif // ENABLE_CRT_DEBUG return adress; } //!Malloc void* Allocater::Malloc(size_t size,TGL::Memory::AREA_TYPE areaType) { if( !s_bInit){ T_WARNING("メモリマネージャーが初期化されてない"); //T_CONSOLE("メモリマネージャーが初期化されてない\n"); #ifndef _TGL_FINAL BP(); #endif void *p = (int *)malloc( size ); return p; } #ifdef ENABLE_CRT_DEBUG void *p = (int *)malloc( size ); #else TGL::Thread::ScopedLock lock(s_pLock); //void *p = mspace_memalign( s_mspArray[areaType], DEFAULT_ALIGNMENT_SIZE , size ); void* p = s_MemorySpaceArray[areaType].Malloc(size); #endif ASSERTMSG(p,"メモリ領域の確保に失敗[%s]",stl::GetFormatUnitString<stl::sys::string>(size).c_str()); return p; } //------------------------ // 開放 //------------------------ void Allocater::Free(void *p,TGL::Memory::AREA_TYPE areaType) { if( !s_bInit){ T_WARNING("メモリマネージャーが初期化されてない"); //T_CONSOLE("メモリマネージャーが初期化されてない\n"); #ifndef _TGL_FINAL BP(); #endif free(p); return; } #ifdef ENABLE_CRT_DEBUG free(p); #else #ifdef CHECK_MEMORY_LEAK // デバッグ情報削除 if( p && !m_MemoryInfo.empty()) { TGL::Thread::ScopedLock lock(s_pLock_dbg); u32 add = reinterpret_cast<u32>(p); MEMORY_INFO::iterator it = m_MemoryInfo.find(add); if( it != m_MemoryInfo.end() ) { m_MemoryInfo.erase(it); } } #endif { TGL::Thread::ScopedLock lock(s_pLock); //mspace_free( s_mspArray[areaType], p ); s_MemorySpaceArray[areaType].Free(p); } #endif } //! リークのダンプ void Allocater::DumpMemoryLeak(const char* pszName) { #ifndef ENABLE_CRT_DEBUG #ifdef CHECK_MEMORY_LEAK if( m_MemoryInfo.empty() ) return; typedef stl::sys::vector<DebugMemoryInfo> LEAK_ARRAY; LEAK_ARRAY leakArray; if( !pszName ) pszName =""; foreach(MEMORY_INFO,m_MemoryInfo,it) { DebugMemoryInfo info = (*it).second; if( info.checkName == pszName ) { leakArray.push_back(info); } } if( !leakArray.empty() ) { PRINT("--------------------------\n"); T_WARNING("メモリリークしてまっせ。[%s]",pszName); s32 num = 0; foreach(LEAK_ARRAY,leakArray,it) { DebugMemoryInfo info = (*it); stl::sys::string str = stl::FormatString<stl::sys::string>("%s : {%d} [area:%d adress:0x%08x size:%sbyte]\n", info.filename.c_str(), info.count, info.areaType, info.Adress(), stl::GetFormatUnitString<stl::sys::string>(info.size).c_str()); PRINT(str.c_str()); info.Dump(); if( ++num>10 ){ PRINT("リークし過ぎなのでとりあえず終わる"); break; } } PRINT("--------------------------\n"); } stl::clear(leakArray); if( m_checkPointName.size() > 0 ) m_checkPointName.pop_front(); #endif #endif // ENABLE_CRT_DEBUG ``` ###自前のメモリマネージャーの初期化が一番最初に来るようにゴニョゴニョする ```sTGLMain.cpp //------------------------ //! 初期化オブジェクト //------------------------ class Initialize { public: Initialize() { //! メモリ管理初期化 Memory::Allocater::Initialize(); } ~Initialize() { //! メモリ管理終了 Memory::Allocater::Terminate(); } }; //! 初期化順制御 #pragma warning(disable: 4074) #if defined(__cplusplus) && defined(__GNUC__) Initialize init __attribute__((init_priority( 111 ))); #elif defined (_MSC_VER) #pragma init_seg(compiler) Initialize init; #else #error not supported. #endif //------------------------ ``` **なんかそんな感じ。若かりし頃に書いたコードでしょぼいのであまりお見せできませんorz** **近々大改装予定!** # まとめ * ゲームではなるべく動的アロケーションは使わない方が吉ですよ!どれ位必要かぐらいはプログラマなんだからちゃんと把握しておきましょう\(^o^)/ * どうしても動的アロケーションが必要な場合コストを十分に理解して使ってね!簡単に沼にハマるよ! * STLは初心者が内部実装理解せず気軽に使うものではないよ! [**EffectiveSTL**](http://www.amazon.co.jp/dp/4894714108) とかちゃんと読んで内部実装理解してからね!(できれば、自前で簡易的なSTLを実装できるようになるまでは。。。) * ゲーム向けに最適化&使いやすくなったSTLをEAが作ったりしてるので見てみると良いかも。[EASTL@Github](https://github.com/paulhodge/EASTL) ## その他、動的アロケーションのコストを下げる方法など * ゲーム開始時やステージ開始時にまとめてアロケーションして置いてその中から使う。<BR> →メモリプールという仕組みを使う手もある。自前でも簡単に作れるけど、もっと高機能な、Boost.Poolってのがあるらしいよ!僕、boostよく知らないけど! <BR> * STLの場合、予め領域を確保しておける<BR> → std::vector::reserve()など。 ## その他STL関連おまけ * 生固定配列だと、サイズオーバでメモリ破壊とか怖いなという時は、生配列より安全だけど、生配列と同じ処理コストで使える(メモリ使用量は生配列と同じで、デバッグ処理を無効にすれば処理コストもゼロ)std::arrayとかもあったりします。 **アロケーションは用法・容量に注意して正しく使いましょう\(^o^)/** 終わり。 |
|
| 834位 |
|
|||
|
15:19:03 |
(株式会社ミクシィ 所属) |
|
オフィスのメールサービスや予定管理システムが外部サービスになっているなかIRCなどチャットツールも外部サービスを使えるようにしていけると良いと思ってHipChatの使い方をまとめてみました。
 # 公式サイト https://www.hipchat.com/ # What's HipChat? > HipChatは企業やチームのためのホスティング型プライベートチャットサービスのことで、継続的に使えるチャットルーム、チャット履歴の保存、外部サービスとの連携に便利なAPI、などの特徴を持ちます。 http://blog.qnyp.com/2013/05/27/hipchat-driven-development/ # 登録 https://www.hipchat.com/sign_up # ブラウザとアプリでの利用が可能 ログイン後の最初の画面でダウンロード可能  # IRCと基本は一緒 # 通知の方法  http://help.hipchat.com/knowledgebase/articles/64429-how-do-mentions-work- まず@を入力すると人の候補が出てくるので選択するか、名前をそのまま入力し発言すると相手に通知が飛びます ## @mentions 特定の人に通知をしたいときに利用 ## @all そのルームにいる全員に通知をする ## @here ルームにいるメンバーの内アクティブなステータスなユーザーだけに通知をする # mentionだけの通知をポップアップで受け取るようにする 設定から以下の項目をオフにする   # コマンド一覧 ## ルーム | コマンド | 引数 | 動作内容 | |:-----------|:-----------|:-----------| | /join | room name | foom nameのルームに入る | | /part | | 今いる部屋を退室する | | /topic | new topic | 部屋のトピック名を変更する | ## ステータス | コマンド | 引数 | 動作内容 | |:-----------|:-----------|:-----------| | /available | message | 着席、ステータスアイコンが緑になる | | /away | message | 一時退席、ステータスアイコンが黄になる | | /dnd | message | 取り込み中、ステータスアイコンが赤になる | ## メッセージ書式 | コマンド | 引数 | 動作内容 | |:-----------|:-----------|:-----------| | /code | message | コードの引用表示 | | /quote | message | 引用表示 | | /clear | | 現在のタブのチャット履歴をクリア | | | #fe6464 | hexカラー指定で色表示 | |
|
| 835位 |
|
|||
|
22:15:27 |
|
|
参考: [Custom Transitions Using View Controllers - WWDC2013 session 218](https://developer.apple.com/wwdc/videos/) サンプル: [github/335g/CustomTransitionSamples](https://github.com/335g/CustomTransitionSamples) [iOS Advent Calendar 2013](http://qiita.com/advent-calendar/2013/ios) 13日目担当の[@335g](https://twitter.com/335g)です。 個人的事情で11月ぐらいからようやく触り始めたiOS7。色々新しい事は増えてるみたいですが、WWDCの時から気になってたのが今回のテーマ「Custom Transitions」です。ベータ版のカレンダーをいじりながらどうやるんだろうなんて妄想したものです。 はじめに ---------------------------------- まずはざっくり説明します。その後、いじっててはまった落とし穴的ビックリ!ポイントをシェアします。これからチャレンジする方がこの落とし穴にはまらず進めるようになれば幸いです。触り始めて少ししか経ってないので間違いがあるかもしれません。アドバイス等あれば本記事修正しますのでコメントかTwitter宛にmention下さい。よろしくお願いします。 Custom Transitions ---------------------------------- Custom Transitionsには4種類あります。 1. Presentations & Dismissals 2. UITabBarController 3. UINavigationController 4. UICollectionViewController layout-to-layout transitions まだ1と3しかいじれてないので、2と4は割愛。基本的には1や3と一緒ですよね、きっと。 (1) Animation ------------------------ アニメーションをカスタムするだけなら簡単です。あらかじめdelegate設定しておいてpresentやらpushします。例えば、present(or dismiss)であればiOS7から追加された`transitioningDelegate`を使います。 ```Objective-c:MyViewController.m UIViewController *vc; vc.modalPresentationStyle = UIModalPresentationCustom; vc.transitioningDelegate = self; [self presentViewController:vc animated:YES completion:nil]; ``` `UINavigaitonController`の場合は`UINavigationControllerDelegate`を設定してpushします。 ```Objective-C:MyViewController.m UIViewController *vc; self.navigationController.delegate = self; [self.navigationController pushViewController:vc animated:YES]; ``` `transitioningDelegate`には`UIViewControllerTransitioningDelegate`プロトコルを採用しているオブジェクトを指定します。delegateメソッドとしてこれらを実装します。 ```Objective-C:UIViewControllerTransitioningDelegate - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source; - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed; ``` `UINavigationControllerDelegate`ではこれらを実装します。 ```Objective-C;UINavigationControllerDelegate - (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC; ``` どちらの場合も`UIViewControllerAnimatedTransitioning`プロトコルを採用しているAnimatorオブジェクトを返します。このAnimatorオブジェクトは画面遷移時のアニメーションを定義しているオブジェクトです。`UIViewControllerAnimatedTransitioning`プロトコルのメソッド(required)としてはこれらがあります。 ```Objective-C:UIViewControllerAnimatedTransitioning // アニメーション時間 - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext; // アニメーション処理 - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext; ``` ここまで実装すればOKです。文章に起こすと面倒な感じがしますし色々なプロトコルが出てくるので理解しづらいですが、ちょっといじってみれば難しくない事がわかると思います。 (2) Interactive Transitions ------------------------------ Interactiveに遷移するためにはこれらに加えて以下が必要です。 まず、present(or dismiss)の場合は`transitioningDelegate`オブジェクトに以下を実装します。 ```Objective-C:UIViewControllerTransitioningDelegate - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id<UIViewControllerAnimatedTransitioning>)animator; - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator; ``` `UINavigationControllerDelegate`の場合は以下を実装します。 ```Objective-C:UINavigationControllerDelegate - (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>)animationController; ``` 先ほどは`UIViewControllerAnimatedTransitioning`プロトコルオブジェクトを返すメソッドを実装しましたが、今回は`UIViewControllerInteractiveTransitioning`プロトコルオブジェクトを返します。独自に`UIViewControllerInteractiveTransitioning`プロトコルを満たすオブジェクトを用意しても良いのでしょうが、`UIPercentDrivenInteractiveTransition`を使うのが楽でしょう。 ```Objective-C:UIpercentDrivenInteractiveTransition // 0.0〜1.0の値を代入する事で今どの状態なのかを指定する - (void)updateInteractiveTransition:(CGFloat)percentComplete // 終了orキャンセル時にはこれを - (void)finishInteractiveTransition; - (void)cancelInteractiveTransition; ``` 基本的には以上です。 (3) 個人的ビックリ!ポイント ----------------------------------------- 1. UINavigationController.viewにUIScreenEdgePanGestureRecognizerが勝手に追加されている 2. UINavigationItemのアニメーション 3. interactive transition したら 解放しましょう 4. view.transform (landscape) 5. topLayoutGuide ### 1. UINavigationController.viewにUIScreenEdgePanGestureRecognizerが勝手に追加されている UINavigationControllerを使う場合 **何も設定してなくても** UIRectEdgeLeftから右にpanしていくとpopできるようになっています。  例えばviewController.viewを下にドラッグしていきpopさせたいような場合、UIRectEdgeLeftから右にpanするとviewが下に動いていくというなんとも残念な感じになります。使わない場合はenabled=NOにしておく方が良いと思います。 ### 2. UINavigationItemのアニメーション 上記一連の流れを読むとわかると思うのですが、UINavigationBarに関しては何もしていません。画面遷移の際のUINavigationItemのアニメーション制御はiOSおまかせになります(opacity fadeIn fadeOut)。viewと連動して制御したい場合、自前でnavigationBarを作るしかなさそうです。 ||Default|Custom| |:--:|:--:|:--:| |Push||| |Pop||| ### 3. interactive transition したら 解放しましょう 調査不足。サンプルでは`UIPercentDrivenInteractiveTransition`オブジェクトを保持しており、スワイプの`UIGestureRecognizerStateChanged`の度に`updateInteractiveTransition:`を読んでやり状態更新しています。しかし、interactive transitionでpresentした後dismissした際、`UIPercentDrivenInteractiveTransition`オブジェクトを保持し続けていると再びpresentできなくなります。おそらく`percentComplete`が1.0の状態では開始できないのかと思います。 **interactive transition終わった後はnil解放しましょう**。終わったタイミングで破棄するのは自然といえば自然ですし。 ### 4. view.transform (landscape) デバイスの回転はview.transformが変化することで表現されます。pushされたviewControllerがviewDidAppearしたタイミングで`NSStringFromCGAffineTransform(view.transform)`を確認すると、以下のようになっています。 ||Default|Custom| |:--:|:--:|:--:| |presenting|[1, 0, 0, 1, 0, 0]|[0, 1, -1, 0, 0, 0]| |presented|[0, 1, -1, 0, 0, 0]|[0, 1, -1, 0, 0, 0]| interactive transitionしてる最中はpresentingもpresentedも両方表示されているので、両viewControllerとも回転状態にあるのは自然といえば自然です。ただし、これが原因なのかわかりませんが、 **presentedの座標系が(x軸が上下でy軸が左右に)変わっています**。何が言いたいかというと、presentedViewController.viewをアニメーションするわけですが、思わぬ方向に動いてしまうわけです。(*透明度によるpresentサンプルではこれを確認するため、わざと座標いじらず透明度のみ変化させています) ### 5. topLayoutGuide `UIViewControllerAnimatedTransitioning`プロトコルの`- (void)animateTransition:`でアニメーション部分を実装しますが、その際 containerView に viewController.view を addSubview する必要があります。しかし、addSubviewする前にviewの座標を上下に(portraitの時はy座標を、landscapeの時はx座標を)ずらして指定すると **topLayoutGuide.lengthが0** になってしまうようです。 ```Objective-c:EEHorizontalAnimator.m - (void)anitmateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { . . . UIVIewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; // 座標変更部 CGFloat deltaX, deltaY; switch (fromVC.interfaceOrientation) { case UIInterfaceOrientationPortrait: deltaX = fromVC.view.bounds.size.width; deltaY = 0.0; // <--- これを少しでもずらしてからaddSubviewすると topLayoutGuideが0.0に break; case UIInterfaceOrientationLandscapeRight: deltaX = 0.0; // <--- これを少しでもずらしてからaddSubviewすると topLayoutGuideが0.0に deltaY = fromVC.view.bounds.size.width; break; case UIInterfaceOrientationLandscapeLeft: deltaX = 0.0; // <--- これを少しでもずらしてからaddSubviewすると topLayoutGuideが0.0に deltaY = - fromVC.view.bounds.size.width; break; case UIInterfaceOrientationPortraitUpsideDown: deltaX = - fromVC.view.bounds.size.width; deltaY = 0.0; // <--- これを少しでもずらしてからaddSubviewすると topLayoutGuideが0.0に break; } toVC.view.center = CGPointMake(toCenter.x + deltaX, toCenter.y + deltaY); [containerView addSubview:toVC.view]; . . . } ``` ちなみにaddSubviewした後であれば問題無いようです。 最後に ----------------------------------------- 本記事を読むと、custom transitionは非常に扱いづらそうに思われるかもしれません。しかし、custom transition は viewController毎の関連性をわかりやすくするための必須技術だと思います。早めに地雷ポイントをつぶし、差別化を図るための武器に変えたいものです。「こんなのがあったよ〜」等あったら共有したいので教えてください。よろしくお願いします。 追記 ----------------------------------------- 2014.2.22 『5.topLayoutGuide』に関しては、addSubview:とinsertSubview:aboveSubview:で挙動変わりそうです。時間ができたらまたまとめます。 |
|
| 836位 |
|
|||
|
15:35:18 |
|
|
EC2のサーバ監視にCloudWatchを初めて使ってみました。 カスタムメトリクスを作って、CPU使用率、HttpStatus、Memory使用率、LoadAverageの監視を行うようにしたので、手順をまとめておきます。 CloudWatchは無料枠で10メトリクス、10アラームまで設定できるとのことなので嬉しいですね。 以下、[公式サイト](http://aws.amazon.com/jp/cloudwatch/)より引用 >無料利用枠* > >Amazon CloudWatch は無料で始めることができます。アプリケーションの多くは、これらの無料利用枠内で利用できるはずです。 >Amazon EBS ボリューム、Elastic Load Balancers、Amazon RDS DB インスタンスのすべてのメトリックス同様、Amazon EC2 インスタンスの基本モニタリングのメトリックス(5分間隔)は無料でご利用いただけます。 >新規および既存のお客様は、10メトリックス(Amazon EC2 インスタンスまたはカスタムメトリックスの詳細モニタリングに適用)、10アラーム、および100万の API リクエストを追加料金なしでご利用いただけます。 ## 1. 標準メトリクスの設定 CloudWatchのデフォルトで用意されている監視項目には以下のようなものがあります。 * CPU Utilization(CPU使用率) * Disk Reads(ディスク読み込み状況) * Disk Writes(ディスク書き込み状況) * Network In,Out(ネットワーク状況) * Status Check Failed(インスタンスのステータスチェック失敗) ここでは、CPU使用率のアラート設定をしてみます。 [AWSマネージメントコンソール](https://console.aws.amazon.com/console/home)からEC2メニューにアクセスし、「Instances」からアラート設定したいインスタンスを選択します。 下部にタブメニューが表示されるので「Monitoring」を選択。  「Create Alarm」をクリックして、アラートを作成します。 今回は以下のようにして、CPU使用率の最大値が80%を超えたら通知するように設定しました。 ``` Send a notification to(通知名) : CPUUtilization With these recipients (通知先メールアドレス): your_email@example.com Whenever(条件):Maximum of CPU Utilization Is : >= 80 Percent For at least : 1 consecutive period(s) of 5 minutes ```  アラート設定が完了すると「AWS Notification - Subscription Confirmation」というメールが指定したアドレスに届くので、メールのリンクをクリックして通知を有効化しておきます。 ## 2. カスタムメトリクスの作成 標準メトリクスでは、Memory使用率、LoadAverage等の情報は監視することができません。 このような場合は、カスタムメトリクスを作成してインスタンスから必要データを取得するようにします。 監視データの送信には aws-apitools-mon というツールを使いますが、Amazon Linuxの場合は、デフォルトで組み込まれています。 以下、[公式ドキュメント](http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/AmazonLinuxAMIBasics.html)より引用 >Amazon Linux AMI またはリポジトリには、AWS の統合および使用のための、次のよく使用されるコマンドラインツールが組み込まれています。 >aws-amitools-ec2 >aws-apitools-as >aws-apitools-cfn >aws-apitools-common >aws-apitools-ec2 >aws-apitools-elb >aws-apitools-iam >aws-apitools-mon >aws-apitools-rds >aws-cfn-bootstrap >aws-scripts-ses >aws-apitools-mon mon-put-data というコマンドを実行して、コマンドが見つからないという表示が出た場合は、 ``` yum install aws-apitools-mon ``` でインストールを行ってください。 ### 2-1. 監視用スクリプト作成の事前準備 以下はec2-userでログインしている状態で実行します。 監視用スクリプトを置くディレクトリを作成し、credentialファイルを設置します。 ``` mkdir ~/cloudwatch sudo cp /opt/aws/credential-file-path.template ~/cloudwatch/credential sudo chown ec2-user. ~/cloudwatch/credential chmod 600 cloudwatch/credential ``` 次に、AWSマネージメントコンソールから[IAM](https://console.aws.amazon.com/iam/home)メニューにアクセスし、cloudwatch用のユーザを作成します。 ユーザ作成時に表示される Access Key ID と Secret Access Key をメモしておきます。 Permissions は 「Policy Generator」 を選択し、CloudWatchの「PutMetricData」を許可するようにします。(下図参照)   ユーザ作成が完了したら、credential にAccess Key ID と Secret Access Key を記述します。 ```~/cloudwatch/credential AWSAccessKeyId=<Write your AWS access ID> AWSSecretKey=<Write your AWS secret key> ``` ### 2-2. 監視用シェルスクリプト作成 事前準備ができたので、監視用のシェルスクリプトを作成してHttpStatus、Memory使用率、LoadAverageの監視を行うようにします。 各監視項目の取得方法に関しては参考サイトのものを流用させていただきました。 まずは、引数に与えられたURLのHttpStatusを表示するシェルスクリプトを作成します。 ```~/cloudwatch/http_status_check.sh #!/bin/sh curl -s $1 -o /dev/null -w "%{http_code}" ``` 次に、監視データをCloudWatchに送信するシェルスクリプトを作成します。 ```~/cloudwatch/custom_metrics.sh #!/bin/bash export JAVA_HOME=/usr/lib/jvm/jre export AWS_CLOUDWATCH_HOME=/opt/aws/apitools/mon export EC2_REGION=ap-northeast-1 export AWS_CREDENTIAL_FILE=/home/ec2-user/cloudwatch/credential instanceid=i-**** # EC-2メニューの「Instances」から該当サーバのInstanceIDを取得して記述 # http status check status=`/home/ec2-user/cloudwatch/http_status_check.sh http://example.com` # 監視したいurlを記述 if [ $status -eq 200 ]; then Fail=0 else Fail=1 fi /opt/aws/bin/mon-put-data --metric-name "Http Status fail" --namespace "Custom Metrix" --dimensions "InstanceId=$instanceid" --value "$Fail" --unit "Count" # memory check memtotal=`free -m | grep 'Mem' | tr -s ' ' | cut -d ' ' -f 2` memfree=`free -m | grep 'buffers/cache' | tr -s ' ' | cut -d ' ' -f 4` let "memused=100-memfree*100/memtotal" /opt/aws/bin/mon-put-data --metric-name "FreeMemoryMBytes" --namespace "Custom Metrix" --dimensions "InstanceId=$instanceid" --value "$memfree" --unit "Megabytes" /opt/aws/bin/mon-put-data --metric-name "UsedMemoryPercent" --namespace "Custom Metrix" --dimensions "InstanceId=$instanceid" --value "$memused" --unit "Percent" # loadaverage check loadave1=`uptime | tr -s ' ' | cut -d ' ' -f 11 | cut -d ',' -f 1` /opt/aws/bin/mon-put-data --metric-name "LoadAverage" --namespace "Custom Metrix" --dimensions "InstanceId=$instanceid" --value "$loadave1" --unit "Count" ``` 各シェルスクリプトにcronからの実行権限を付与します。 ``` chmod 755 ~/cloudwatch/http_status_check.sh chmod 755 ~/cloudwatch/custom_metrics.sh ``` 5分おきに実行するようにcron設定をします。 ``` crontab -e */5 * * * * /home/ec2-user/cloudwatch/custom_metrics.sh ``` ## 2-3. アラート設定 cronが実行されると、AWSマネージメントコンソールの[CloudWatch](https://console.aws.amazon.com/cloudwatch/home)メニューの「All metrics」内に「Custom Metrix:InstanceId」項目が追加され、シェルスクリプトに設定した値がモニタされるようになります。 (モニタの反映までに多少時間がかかるようです。) アラート設定したい項目をクリックして、「CreateAlerm」を選択します。 HttpStatusチェックのアラートには5分間で1回以上ステータス200以外が返って来たらアラートが飛ぶように設定しました。 アラート作成画面1(下図参照)では、アラート名と説明、アラート条件を記述します。  アラート作成画面2(下図参照)では、アラート時に何をするかを設定できます。 アラート時に特定のメールアドレスに通知を行いたい場合は以下のようにします。 (複数メールアドレスに通知する場合は「,」で区切ります) ``` When Alarm state is : ALARM Take action : Send Notification Action details : Topic: 適当な通知名 Email(s): your_email@example.com ```  その他の監視項目も同様にアラート設定を行い、以下のような条件でアラート設定を行いました。 ``` CPUUtilization >= 80 for 5 minutes Http Status fail >= 1 for 5 minutes LoadAverage >= 2 for 5 minutes UsedMemoryPercent >= 80 for 5 minutes ``` Alert設定した項目はCloudWatchメニューの「Dashboard」に一覧表示されるようになります。 各アラートの条件に関しては適切な設定ができているか不安なところはありますが、これでCloudWatchのアラート設定ができました。 # 編集後記 記事を書きながら色々調べている時に見つけたのですが、[公式の監視スクリプト](http://aws.amazon.com/code/8720044071969977)が公開されているようです。 最初からこっちを使えばよかったですね。。。 次CloudWatchを設定する際はこちらも試してみることにします。 # 参考サイト * [Amazon CloudWatch](http://aws.amazon.com/jp/cloudwatch/) * [[AWS]CloudWatchを使ってEC2を監視する方法](http://thinking.ne.jp/2013/01/aws-cloudwatch-ec2/) * [CloudWatch 標準メトリクス(監視項目) 一覧](http://qiita.com/gohatk@github/items/d1cfcbd1a3c1f7e6b4d3) * [Amazon Cloud Watch で EC2 インスタンスのプロセス監視を行う](http://d.hatena.ne.jp/hiratake55/20130127/1359301757) * [Cloud Watchのカスタムメトリクスで特定のurlのhttp status codeを監視する](http://d.hatena.ne.jp/nekonokataomoi/20130701/1372657180) * [Amazon EC2編~EC2インスタンスを監視するには~](http://recipe.kc-cloud.jp/archives/258) * [Amazon CloudWatch編~監視スクリプトを使ってみよう!パート②~](http://recipe.kc-cloud.jp/archives/2145) * [CloudWatchのカスタムメトリクスでFreeMemoryMBytes、UsedMemoryPercent、LoadAverage、Stealを取得](http://dev.classmethod.jp/cloud/cloudwatch-custom-metrics-vmstat-free-uptime/) * [Awsをちゃんと使ってみた 監視編](http://sssslide.com/www.slideshare.net/rs_wisteria/aws-20006038) * [【AWS】CloudWatch入門/EC2のステータスチェックを行ってみよう](http://dev.classmethod.jp/etc/cloudwatch-metrics-for-ec2-status-checks/) * [ステップ 10: CloudWatch アラームを作成する](http://docs.aws.amazon.com/ja_jp/gettingstarted/latest/computebasics-linux/getting-started-create-cloudwatch.html) * [Amazon Linux AMI の基本](http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/AmazonLinuxAMIBasics.html) * [CloudWatch Monitoring ScriptでDiskUsageやMemoryUsage等を取得](http://dev.classmethod.jp/cloud/aws/cloudwatch-monitoring-script-for-linux/) |
|
| 837位 |
|
|||
|
18:54:21 |
|
|
Homebrewを利用してインストール ``` brew install redis ``` ``` ==> Downloading http://redis.googlecode.com/files/redis-2.6.10.tar.gz ######################################################################## 100.0% ==> make -C /private/tmp/redis-e09G/redis-2.6.10/src CC=cc ==> Caveats To have launchd start redis at login: ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents Then to load redis now: launchctl load ~/Library/LaunchAgents/homebrew.mxcl.redis.plist Or, if you don't want/need launchctl, you can just run: redis-server /usr/local/etc/redis.conf ==> Summary /usr/local/Cellar/redis/2.6.10: 9 files, 748K, built in 9 seconds ``` 起動 ``` redis-server /usr/local/etc/redis.conf ``` 接続 ``` redis-cli ``` 確認 ``` redis 127.0.0.1:6379> set key val OK redis 127.0.0.1:6379> get key "val" ``` GUIクライアント * http://redisdesktop.com/ * https://github.com/joeferner/redis-commander |
|
| 838位 |
|
|||
|
14:31:00 |
(Freelancer 所属) |
|
iOS 6 から、UILabel, UITextView, UITextFieldに、`attributedText` というNSAttributedString型のプロパティが追加されました。NSAttributedStringは文字列の属性を管理するクラスで、これを使用するとフォントや文字色、背景色、カーニング等の**属性をテキスト内の指定範囲に対して適用**することができるようになります。
 ##基本的な実装手順 基本的な手順は次の通りです。 ###1. NSMutableAttributedStringオブジェクトを生成 `initWithString:`メソッドでオブジェクトを生成します。 ```` NSString *str = @"NSAttributedString Sample"; NSMutableAttributedString *attrStr; attrStr = [[NSMutableAttributedString alloc] initWithString:str]; ```` 引数には対象となる文字列を渡します。 ###2. 属性をセット `addAttribute:value:range:`メソッドを用いて属性、値、適用範囲をセットします。 ```` // フォント [attrStr addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Futura-CondensedMedium" size:17.] range:NSMakeRange(0, [attrStr length])]; // 背景色 [attrStr addAttribute:NSBackgroundColorAttributeName value:[UIColor colorWithRed:1. green:1. blue:.0 alpha:1.] range:NSMakeRange(0, [attrStr length])]; // 打ち消し線 [attrStr addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInt:3] range:[str rangeOfString:@"Sample"]]; ```` ###3. UILabelにセット UILabelの`attributedText`プロパティに、作成したNSMutableAttributedStringオブジェクトをセットします。 ```` [self.label setAttributedText:attrStr]; ```` なお、サンプルではUILabelを用いていますが、UITextViewもUITextFieldもiOS6以上では同様の方法(attributedTextプロパティを使う)でテキストに装飾することができます。  ##属性と値について 上で載せたサンプルで`addAttribute:value:range:`メソッドに渡している属性は、それぞれ次のような効果があります。 - `NSFontAttributeName`:フォントを指定する - `NSBackgroundColorAttributeName`:背景色を指定する - `NSStrikethroughStyleAttributeName`:打ち消し線を指定する そして、**属性毎に引数valueに指定できる値の型は違ってきます。**たとえば、属性`NSFontAttributeName`に対して値はUIFontオブジェクトを渡しますが、属性`NSBackgroundColorAttributeName`に対しては値としてUIColorオブジェクトを渡します。 属性毎に指定できる値の詳細は、 [NSAttributedString UIKit Additions Reference](http://developer.apple.com/library/ios/#documentation/uikit/reference/NSAttributedString_UIKit_Additions/Reference/Reference.html) で確認することができます。 ##影と文字色 文字色を指定する場合は`NSForegroundColorAttributeName`を使用します。値は`NSBackgroundColorAttributeName`と同様にUIColorオブジェクトを渡します。 また影をつける場合は`NSShadowAttributeName`を使用します。値にはNSShadowオブジェクトを渡します。 ```` // NSShadowオブジェクト NSShadow *shadow = [[NSShadow alloc] init]; [shadow setShadowColor:[UIColor colorWithRed:0. green:0. blue:0. alpha:1.]]; [shadow setShadowBlurRadius:4.0]; [shadow setShadowOffset:CGSizeMake(2, 2)]; // 影 [attrStr addAttribute:NSShadowAttributeName value:shadow range:NSMakeRange(0, [attrStr length])]; // 文字色 [attrStr addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:1. green:1. blue:0. alpha:1.] range:NSMakeRange(0, [attrStr length])]; ````  ##中抜き文字、下線、カーニング `NSStrokeWidthAttributeName`を使用すると、中抜き文字にすることができます。値には線幅を指定します。中抜き文字の枠線の色を指定する場合は、`NSStrokeColorAttributeName`を使用します。 下線を引く場合は、`NSUnderlineStyleAttributeName`を使用します。値には整数のNSNumberオブジェクトを指定します。(ここでは定数`NSUnderlineStyleSingle`を使用しています。) カーニングは`NSKernAttributeName`を使用します。値には浮動小数点数のNSNumberオブジェクトを指定します。 ```` // 中抜き文字の線幅 [attrStr addAttribute:NSStrokeWidthAttributeName value:@5 range:NSMakeRange(0, [attrStr length])]; // 中抜き文字の枠線の色、下線、カーニングを一度に指定 NSDictionary *attributes = @{NSStrokeColorAttributeName: [UIColor magentaColor], NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), NSKernAttributeName: @1.5}; [attrStr addAttributes:attributes range:[str rangeOfString:@"Sample"]]; ```` また上記では`addAttributes:range:`メソッドを使用して**複数の属性と値を一度に指定**しています。同じ範囲に対して適用する場合等に便利です。  ##パラグラフスタイル `NSParagraphStyleAttributeName`を使用すると、パラグラフスタイル(NSParagraphStyleオブジェクト)を指定することができます。NSParagraphStyleはアライメントやハイフネーションを指定するためのプロパティを持っています。 ```` // NSMutableParagraphStyleオブジェクト NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init]; // アラインメント paragraph.alignment = NSTextAlignmentRight; // ハイフネーション paragraph.hyphenationFactor = 0.9; // パラグラフスタイル [attrStr addAttribute:NSParagraphStyleAttributeName value:paragraph range:NSMakeRange(0, [attrStr length])]; ```` `hyphenationFactor`には0.0〜1.0の値を指定することができ、0.0でハイフネーション(行末で切れる単語をハイフンで繋ぐ)なし、1.0で常にハイフネーションを試みる設定となります。  ##テキストエフェクト(iOS 7 or later) `NSTextEffectAttributeName` でテキストエフェクトを指定することができます。指定できる値としては今のところ `NSTextEffectLetterpressStyle` (実体は文字列)しかありません。 ```swift:swift let attributedStr = NSMutableAttributedString( string: string, attributes: [NSTextEffectAttributeName: NSTextEffectLetterpressStyle]) ``` こんな感じの効果が得られます。  > A graphical text effect giving glyphs the appearance of letterpress printing, in which type is pressed into the paper. ##リンク(iOS 7 or later) `NSLinkAttributeName` で文字列にリンクを貼れます。値は NSURL 型を推奨、NSString でもOK。 ```swift:swift let attributedStr = NSMutableAttributedString( string: string, attributes: [NSLinkAttributeName: NSURL(string: "https://github.com/shu223")!]) ```  ##カスタム絵文字(iOS 7 or later) `NSAttachmentAttributeName` を使用すると、文中に画像を挿入できます。つまり、**カスタム絵文字**的な使い方ができます。(Attachmentという名前なので、本当はもっと広い意味での用途を想定した機能だと思います) ```swift:swift let attachment = NSTextAttachment() attachment.image = UIImage(named: filename)! attachment.bounds = CGRectMake(2, 0, 16, 21) let attrStr = NSAttributedString(attachment: attachment) attributedStr.insertAttributedString(attrStr, atIndex: attributedStr.length) ```  実装方法は下記記事を参考にさせていただきました。 - [UILabelに画像を表示する](http://qiita.com/nagisawks/items/ff8232e92a2e9525ab0b) ##NSAttributedStringを使用可能なUIKitのクラス UILabel, UITextView, UITextFieldの、`attributedText` プロパティ以外にも、NSAttributedStringをセットできるプロパティ/メソッドがいくつかのUIKitのクラスに追加されています。 ※いずれも要iOS6以上 **UIButton** ```` - (void)setAttributedTitle:(NSAttributedString *)title forState:(UIControlState)state ```` **UIPickerView** ```` - (NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component ```` **UIRefreshControl** ```` @property (nonatomic, retain) NSAttributedString *attributedTitle ```` **UITextField** ```` @property(nonatomic,copy) NSAttributedString *attributedPlaceholder ```` ##(2015.12.22追記)属性一覧 現行バージョン(iOS 9.2)で利用可能な属性を NSAttributedString.h から抜き出して、iOSバージョンごとに分類しました。 また、**本記事で解説したものを太字に**しています。 ###iOS 6 以上で利用可能 - **NSFontAttributeName** - UIFont, default Helvetica(Neue) 12 - **NSParagraphStyleAttributeName** - NSParagraphStyle, default defaultParagraphStyle - **NSForegroundColorAttributeName** - UIColor, default blackColor - **NSBackgroundColorAttributeName** - UIColor, default nil: no background - NSLigatureAttributeName - NSNumber containing integer, default 1: default ligatures, 0: no ligatures - **NSKernAttributeName** - NSNumber containing floating point value, in points; amount to modify default kerning. 0 means kerning is disabled. - **NSStrikethroughStyleAttributeName** - NSNumber containing integer, default 0: no strikethrough - **NSUnderlineStyleAttributeName** - NSNumber containing integer, default 0: no underline - **NSStrokeColorAttributeName** - UIColor, default nil: same as foreground color - **NSStrokeWidthAttributeName** - NSNumber containing floating point value, in percent of font point size, default 0: no stroke; positive for stroke alone, negative for stroke and fill (a typical value for outlined text would be 3.0) - **NSShadowAttributeName** - NSShadow, default nil: no shadow - NSVerticalGlyphFormAttributeName - An NSNumber containing an integer value. 0 means horizontal text. 1 indicates vertical text. If not specified, it could follow higher-level vertical orientation settings. Currently on iOS, it's always horizontal. The behavior for any other value is undefined. ###iOS 7 以上で利用可能 - **NSTextEffectAttributeName** - NSString, default nil: no text effect - **NSAttachmentAttributeName** - NSTextAttachment, default nil - **NSLinkAttributeName** - NSURL (preferred) or NSString - NSBaselineOffsetAttributeName - NSNumber containing floating point value, in points; offset from baseline, default 0 - NSUnderlineColorAttributeName - UIColor, default nil: same as foreground color - NSStrikethroughColorAttributeName - UIColor, default nil: same as foreground color - NSObliquenessAttributeName - NSNumber containing floating point value; skew to be applied to glyphs, default 0: no skew - NSExpansionAttributeName - NSNumber containing floating point value; log of expansion factor to be applied to glyphs, default 0: no expansion - NSWritingDirectionAttributeName - NSArray of NSNumbers representing the nested levels of writing direction overrides as defined by Unicode LRE, RLE, LRO, and RLO characters. The control characters can be obtained by masking NSWritingDirection and NSTextWritingDirection values. LRE: NSWritingDirectionLeftToRight|NSWritingDirectionEmbedding, RLE: NSWritingDirectionRightToLeft|NSWritingDirectionEmbedding, LRO: NSWritingDirectionLeftToRight|NSWritingDirectionOverride, RLO: NSWritingDirectionRightToLeft|NSWritingDirectionOverride, iOS 8、9 では新規追加されなかったようです。 |
|
| 839位 |
|
|||
|
21:19:30 |
|
|
これは[Gitアドベントカレンダー](http://qiita.com/advent-calendar/git)の17日目のエントリーです。
このエントリーではEmacsのGitクライアントであるMagitの紹介を行います。ひよっこプログラマのエントリなので間違いがあれば指摘して下さい。逆に、ひよっこプログラマだからこそ出来る、これを見たら初心者でも出来るようなエントリを目指しました。 ##Magitって何? EmacsのGitクライアントです。Emacsでコード編集して、ターミナルに移動せずそのままEmacs上でGitの操作を行うことが出来ます。 ##インストール方法 ソースはgithubで公開されています。 https://github.com/magit/magit インストールします。 ```bash $ cd $ mkdir tmp $ cd tmp $ git clone git@github.com:magit/magit.git $ cd magit $ ./configure $ make $ sudo make install ``` で、.emacs.d/init.elに次の記述を追加します。 ```el (require 'magit) ``` これだけ! ##チュートリアル とりあえずgit管理下のファイルを開いている状態で、 `git status`に対応する`M-x magit-status`してみましょう。すると Remote: master @ origin (git@github.com:takc923/dotfiles.git) Local: master ~/dotfiles/ Head: 88b780e modified init.el and elscreen.el for emacs24 Changes: Modified .emacs.d/init.el こんな画面が現れます。カーソルを`Modified .emacs.d/init.el`の行に移動し、Tabキーを押すと Local: master ~/dotfiles/ Head: 88b780e modified init.el and elscreen.el for emacs24 Changes: Modified .emacs.d/init.el diff --git a/.emacs.d/init.el b/.emacs.d/init.el index b0db796..103a40f 100644 --- a/.emacs.d/init.el +++ b/.emacs.d/init.el @@ -1,5 +1,6 @@ ;; .emacs + ; path (setq load-path (cons "~/.emacs.d/site-lisp" load-path)) diffが見れます。変更が正しいことを確認したら、ファイルの行で`s`を押します。 Remote: master @ origin (git@github.com:takc923/dotfiles.git) Local: master ~/dotfiles/ Head: 88b780e modified init.el and elscreen.el for emacs24 Staged changes: Modified .emacs.d/init.el Stageされました(`git add .emacs.d/init.el`と同じ効果)。今は`Changes`なファイルが1つですが、複数ある時は`S`で全てStage出来ます。 次に`c`を押すと、新しくwindowが現れます。ここでcommitコメントを書いて`C-c C-c`を入力するとcommit出来ます。 Remote: master @ origin (git@github.com:takc923/dotfiles.git) Local: master ~/dotfiles/ Head: dd0f21c test Unpushed commits: dd0f21c * test commit出来ました。次に`git pull`を行います。`git pull`するにはgit-status画面で`F`を入力します。すると Switches -r: Rebase (--rebase) Actions F: Pull という画面が現れます。`git pull --rebase`するなら`-r`と入力します。すると`--rebase`の色が変わるので、その後`F`を押すとpull出来ます。pullする前にもう一度`-r`を入力すると`--rebase`の色が戻り普通のpullが出来ます。 すると、パスフレーズが聞かれるので入力します。 Password for '/home/takc923/.ssh/id_rsa': …………… pullが完了しました。 次にpushします。pushするにはmagit-status画面で`P`を押します。すると Switches -f: Force (--force) -d: Dry run (-n) -u: Set upstream (-u) Actions P: Push t: Push tags という画面が現れます。ここもpull同様、オプションをつけるなら`-f`,`-d`,`-u`してから`P`を入力します。やっぱりパスフレーズを聞かれるので入力するとpush出来ます。 チュートリアルは以上です。 ##終わりに magitのインストール方法と、簡単な使い方だけ紹介しました。これから使う人の入り口になれば幸いです。もちろんmagitでは他にも色んな操作が可能です。ここから先は以下の参考ページや、magitでぐぐって出てきたエントリを参考に使ってみてください。 参考 http://magit.github.com/magit/magit.html |
|
| 840位 |
|
|||
|
13:40:56 |
(M3, Inc. 所属) |
|
Ansible 1.5 が 2014/2/28 にリリースされました。
[Ansible 1.5 Released](http://blog.ansibleworks.com/2014/02/) 今回の目玉機能が [Ansible Vault](http://docs.ansible.com/playbooks_vault.html) です。Git などにそのまま登録したくないパスワードやAWSなどのAPIキーなどを暗号化することができます。 ansible-vault コマンドが追加されました。 * * * 2015/5/12 追記 Ansible 1.8 で view サブコマンドが追加されています。 (その後 1.8.3 で view のためのテンポラリファイルの権限問題が修正されているので最新版を使いましょう) * * * それでは試してみます。 ## テスト用 Playbook の作成 ひとまず、暗号なしでテスト用 Playbook を作成します。 ```:hosts localhost ansible_connection=local ``` ```yaml:site.yml --- - hosts: localhost gather_facts: no vars_files: - private.yml tasks: - debug: msg="password = {{ password }}" ``` ```yaml:private.yml --- password: 'hogehoge' ``` ## テスト用 Playbook の確認 ``` $ ansible-playbook -i hosts site.yml PLAY [localhost] ************************************************************** TASK: [debug msg="password = hogehoge"] *************************************** ok: [localhost] => { "msg": "password = hogehoge" } PLAY RECAP ******************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 ``` Playbook の動作確認はできました。 ## Vault で暗号化する それでは、ansible-vault で private.yml を暗号化します。暗号化のためのパスワードの入力を求められます。 ``` $ ansible-vault encrypt private.yml Vault password: Confirm Vault password: Encryption successful ``` 暗号化された private.yml の中身は次のようになっています。cipher は今のところ AES (1.5.1でAES256になりました)だけっぽいですが、将来は選べるようになりそう。 ```text:private.yml $ANSIBLE_VAULT;1.0;AES 53616c7465645f5fcc735bd59e5a55875bf96e5b184181ebc13c4ce1b6b813315fd7bfbe384038b1 4d93ece05905d8f9618080fb8e4f3ec04693e9592299f6347add4bf94e57e83d6615236a83dae0a9 5e4400964c944c90d99cba6bf587f168e4ab2dc0774e91b3beadf5c7160a9072 ``` ## 暗号化されたファイルを使った Playbook ファイルを暗号化した場合、同じコマンドで Playbook を実行するとパスワードを指定しろというエラーで実行できません。 ``` $ ansible-playbook -i hosts site.yml ERROR: A vault password must be specified to decrypt data ``` `--ask-vault-pass` でプロンプトを表示させるか `--vault-password-file=VAULT_PASSWORD_FILE` でパスワードの書かれたファイルを指定します。 ``` $ ansible-playbook -i hosts site.yml --ask-vault-pass Vault password: PLAY [localhost] ************************************************************** TASK: [debug msg="password = hogehoge"] *************************************** ok: [localhost] => { "msg": "password = hogehoge" } PLAY RECAP ******************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 ``` ひとつの Playbook 内で、複数の暗号化されたファイルを扱う場合、すべてのパスワードを同じに揃えておく必要があります。 ファイルをまるっと暗号化してしまうので、どんな変数が定義されているのかもわからなくなるのでちょっと不便な気もしますね。[Jan-Piet Mens :: My thoughts on Ansible's vault](http://jpmens.net/2014/02/22/my-thoughts-on-ansible-s-vault/) ## ansible-vault コマンド `ansible-vault` コマンドには `create`, `encrypt`, `decrypt`, `rekey`, `edit` というサブコマンドがあります。 * create * 新規に暗号化されたファイルを作成します * encrypt * 既存のファイルを暗号化します * decrypt * 暗号化されたファイルを復号化します * edit * 暗号化されたファイルをそのまま編集します * rekey * パスワードを変更します * view * 暗号化されたファイルを復号して PAGER で表示します (「復号化します」っていう表現は正しいのだろうか?とか書きながら思った) ## 追記 「[Ansible Vault を賢く使う](http://qiita.com/yteraoka/items/de9da64ca2d9261b0292)」も見てね |
|
| 841位 |
|
|||
|
00:49:58 |
|
|
## 作らないという選択肢
いきなりタイトルと矛盾しますが、Androidにおいてスプラッシュ画面は必要がないので **作るな** 。 大抵はiOSの真似でスプラッシュを表示させているのだと思うのだけど、iOSは体感起動時間を短縮させるという哲学のためにスプラッシュ画面を導入している。 * iOS7以降、スプラッシュ画面としてアプリの起動状態の画面と似せたものを利用することが推奨されている。(=機敏に起動していると錯覚させることが目的で、 **ブランディング目的の利用は非推奨** ) * iOS6以降の`ViewController`の復元機能においては、アプリがバックグラウンドに移行した瞬間の画面をPNGで保存し、次回起動時のスプラッシュとして表示することで、復元処理が行われていることを意識させないようにする、という凝った仕様だった。もっとも、この機能はその後のマイナーアップデートで消えたけど。 もちろん、プラットフォームが押し付けるやり方を鵜呑みにすることが常に正しいわけじゃない。iOSの良さを取り入れたいという考え方が間違っているとは思わない。ならば表層的な画面表示ではなく、体感起動時間を短したいという哲学を真似しよう。 その最適解は余計なスプラッシュを表示しないことです。 ## スプラッシュ画面の作り方 とはいっても、クライアント様のご意向でスプラッシュ画面を表示しなければならないということはよくある。すごくよくある。ありまくる。 作ること自体は簡単なのだけど、それゆえにストア上位のアプリでも間違った実装をしていることがある。 実装方法は様々あるが、ここでは一般的だと思われるスプラッシュ用の`Activity`を用意する方法について考える。恐らくそれはベストな選択肢ではない。ただクライアント様の意向を満たすという条件は達せられる。 (個人的には、`DialogFragment`を用いるのがベストなのではと思うが、実証する気もしない。) ## AndroidManifestを有効活用する `Activity`にはバックスタックの概念がある。スプラッシュを`Activity`として実装する場合、二度とスプラッシュの`Activity`を表示しないための処理を入れる必要がある。 真っ先に考え付くのは、画面遷移後に`finish()`することだろう。 しかしスプラッシュからの画面遷移の出口が一つだけになるとは限らない。初回起動時、バージョンアップ時には機能のチュートリアルへ遷移したいケースが出てくる。 全ての遷移で箇所で`finish()`を書くというのが一つの手だが、当該の`Activity`のマニフェストに`noHistory="true"`を記述するのがはるかに手っ取り早い。`noHistory="true"`を指定した`Activity`は、バックボタンによって戻ることはできなくなる。 ただし両者の挙動は異なる点に注意。 `finish()`による実装ではスプラッシュ画面を離れた時点でその`Activity`はスタックから取り除かれる。対してマニフェストに`noHistory="true"`を指定した場合には **その画面に戻ることはできないが、スタックには存在している** 状態となる。実際にスタックから取り除かれるタイミングは、タスクを切り替えた際などとなる。 よほど意味不明な処理をしない限り(例えば`noHistory="true"`を指定した`Activity`で巨大なリソースを管理しているとか、`singleTask`や`singleInstance`を指定する必要があるとか、親に指定して`NavUtils`でUpの遷移を行うとか)気にする必要はないはずだが…。 ## Handlerを使う時にはアプリ終了判定を入れよう スプラッシュを数秒間表示して、その後目的の`Activity`を表示する場合、`Handler`を使うことが多い。 よくある **ダメな実装例** が以下。 new Handler().postDelayed(new Runnable() { @Override public void run() { Intent intent = new Intent(SplashActivity.this, DestinationActivity.class); startActivity(intent); } }, 3000); この実装の問題は、ユーザーがスプラッシュ画面の時点でバックボタンを押し、`Activity`を終了させたつもりでも、問答無用で次の画面へ遷移すること。 お仕着せのブランディング画面を長々と見せ付けられた上に、終了させたはずのアプリから、ゾンビのように画面が立ち上がれば、自分なら即座にアンインストールする。 対策としては、 * `Handler`によって`Looper`に積まれた画面遷移のタスクを、何らかのタイミングで`removeCallBacks()`を用いてキャンセルする。タイミングとしては、`onPause`または`onStop`辺りが候補になると思われる。 * `isFinishing()`で`Activity`がまだ終了させられていないことを判定してから`startActivity`を呼び出す。 などがある。 スプラッシュからの遷移をキャンセルした場合、`postDelayed()`を再度呼ぶ必要がある。確実に一度だけ遷移することを保証するためには、`onResume()`で画面遷移のタスクを`Handler`に渡して、`onPause()`でキャンセルする前者の手法が推奨されると思われる。 しかしこれをやってないアプリはほんとに多い。マジで多い。 ## スプラッシュ画面に余計な処理を入れないこと スプラッシュの`Activity`には、メイン画面への遷移以外の特別な処理は一切させない、という認識であるべき。 なぜなら、必ずスプラッシュ画面から起動されるという保証がないからだ。 例えばアプリケーションの初期化処理をスプラッシュの`Activity`に書いたとしよう。開発した時点ではそれで問題がないかもしれない。しかし後から要件が追加され、スプラッシュ`Activity`以外の画面から起動するパターン生まれたらどうするべきなのだろうか? 最悪なのはコピペによって初期化処理が分裂することだ。気の利いたプログラマなら初期化処理を一箇所に集めて、そちらを呼び出すようにリファクタリングしてくれることが期待できる。 しかしアプリケーションスコープで必要なデータの管理は、カスタムな`Application`を作成し、アプリケーションのライフサイクルメソッドで制御することができる。アプリケーションで一度だけ行いたい処理があるならば、可能な限りカスタム`Application`を作成すること。 ただし`Activity`の`Context`と強く紐付く処理(`AsyncTask`の実行や、ダイアログの表示など)を既にスプラッシュに記述してしまっている場合、それらの処理を差し替えるのは非常に困難となる。 **スプラッシュ画面では余計な処理を実行しない** ことが強く推奨される。 |
|
| 842位 |
|
|||
|
14:52:24 |
(合同会社ユーキューブ 所属) |
|
## UbuntuなどDebian系
UbuntuなどDebian系は、[pg_config... no →libpq-dev いれとけ - 鶏肉がいいよね。](http://toriniku.hatenablog.com/entry/20111012/1318390958)によると、 ```bash $ sudo aptitude install libpq-dev ``` とするとよさげ。 ## CentOSなどRedhat系 CentOSなどRedhat系では、 ```bash $ sudo yum -y install postgresql-devel ``` でよさそう。 ## Mac OS X Mac OS Xでは、homebrewが入っていれば ```bash $ brew install postgresql ``` とする。そしたら `/usr/local/Cellar/postgresql/9.3.2/bin/pg_config` あたりにpg_configが生まれる。 ## pg_configがあるはずと思う場合 自分の場合はPostgreSQL 9.3をインストールしていて、デフォルトとは別の場所にpg_configファイルがあったので、以下のようにして場所を指定してやった。 ```bash $ gem install pg --with-pg-config=/usr/pgsql-9.3/bin/pg_config ``` また、bundle installの中でこのエラーが出るようなら、以下のようにしてbundle installの過程でインストールされるgemに引数を渡せる模様。 ```bash $ bundle config build.pg --with-pg-config=/usr/pgsql-9.3/bin/pg_config $ bundle install ``` ## 参考文献 * [pg_config... no →libpq-dev いれとけ - 鶏肉がいいよね。](http://toriniku.hatenablog.com/entry/20111012/1318390958) * [ruby on rails - How can I pass a parameter for gem installation when I run bundle install? - Stack Overflow](http://stackoverflow.com/questions/5167829/how-can-i-pass-a-parameter-for-gem-installation-when-i-run-bundle-install) |
|
| 843位 |
|
|||
|
11:28:07 |
(KAYAC Inc. 所属) |
|
どういうものかをざっと把握するために、GithubのREADMEの一部を訳してみた。 ちょっとまだどこまで理解できているのかわからないので、次はサンプルアプリものぞいてみる。 [参考:ReactiveCocoa](http://nshipster.com/reactivecocoa/) [Github:ReactiveCocoa/ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) ##Introduction **ReactiveCocoa**はリアクティブ・プログラミングの機能が実装されています。 **RAC(ReactiveCocoa)**は現在と未来の値をキャプチャした`RACSignal`を提供します。 `RAC`はソフトウェア側が継続的に値の監視を行う必要はありません。 `RAC`は`RACSignal`に反応したり、連鎖させたり、結合させたりすることによって、宣言的に記述することができます。 `RACSignal`はFuture,Promiseデザインパターンのように、非同期処理を記述することができます。 これによってネットワークのコードを含むソフトウェアを大幅に簡素化することができます。 また`RAC`を使う主な利点の一つとしては、callbackやBlocks、notification、KVO、さらにdelegateメソッドといったあらゆる非同期処理を単一のインターフェイスで扱うことができる点といえるでしょう。 ##Signals 次の例では、`self.username`に変更が合った場合、コンソールに新しい名前を表示させます。 `RACObserve(self, username)`によって現在の`self.username`の値を送信する`RACSignal`を新たに作成します。 `-subscribeNext: `:新しい値が送信されるたびにBlockが実行されます。 ``` [RACObserve(self, username) subscribeNext:^(NSString *newName) { NSLog(@"%@", newName); }]; ``` ##Chaining `RACSignal`ははKVOのnotificationとは異なり連鎖することができます。 次の例では、コンソールに表示させるのは`"j"`から始まる名前の場合とします。 `-filter:`:そのBlock内の実行結果がYESを返したときにのみ値を送信する新しいRACSignalを返します。 ``` [[RACObserve(self, username) filter:^(NSString *newName) { return [newName hasPrefix:@"j"]; }] subscribeNext:^(NSString *newName) { NSLog(@"%@", newName); }]; ``` ##State `Signal`は状態の導出にも用いることができます。 これまでのようにプロパティを監視し、その変化に応じて状態を示すための新たなプロパティを設定しなくとも、`RAC`によってSignalや操作そのものが状態の特性を示すことができるのです。 次の例では、パスワード入力欄とパスワード確認用の入力欄の値が同じであれば`createEnabled`を`True`とする一方向のバインディングを作成します。 `RAC()`はバインディングをいい感じに見せるためのマクロです。 `+combineLatest: reduce:`はSignalの配列を受け取り、各Signalの最新の値をもとにBlockを実行し、その実行結果に応じて新たに戻り値を送信する`RACSignal`を作成します。 ``` RAC(self, createEnabled) = [RACSignal combineLatest:@[ RACObserve(self, password),RACObserve(self, passwordConfirmation)] reduce:^(NSString *password, NSString *passwordConfirm) { return @([passwordConfirm isEqualToString:password]); }]; ``` ##Not just KVO `Signal`はただ単にKVOするだけではなく、どんなストリーム上にでもつくりだすことができます。 次の例では、ボタンを押したかどうかについても表すことができます。 `RACCommand`はUIのアクションを示す`RACSignal`のサブクラスになります。 `-rac_command`がNSButtonに追加されています。 ボタンが押されることで自分自身にコマンドを通知します。 ``` self.button.rac_command = [RACCommand command]; [self.button.rac_command subscribeNext:^(id _) { NSLog(@"button was pressed!"); }]; ``` ##Asynchronous network operations 次の例では、ネットワーク経由でログインするためのボタンをフックします。 ボタンが押される度に`loginCommand`が通知されます。 ``` self.loginCommand = [RACCommand command]; ``` `loginCommand`が値を送信するたびに、このBlockは、ログインプロセスを開始します。 `-addActionBlock:`コマンドが実行されるたびに、このブロック内で実行した結果を返します。 ``` self.loginSignals = [self.loginCommand addActionBlock:^(id sender) { //この`-logIn`メソッドはrequestが完了したときにsignalを返します。 return [client logIn]; }]; ``` ログインが成功したときのみ、ログメッセージを表示します。 ``` [self.loginSignals subscribeNext:^(RACSignal *loginSignal) { [loginSignal subscribeCompleted:^(id _) { NSLog(@"Logged in successfully!"); }]; }]; ``` ボタンをタップしたときにログインを実行するようにします。 ``` self.loginButton.rac_command = self.loginCommand; ``` またSignalsはタイマーや他のUIイベント、時間を通じて変化するものについてであれば何でも表現することができます。 非同期の処理にSignalを使う場合、Signalを変換したり連鎖したりすることで、さらに複雑な処理を構築することができます。 次の例では2つのネットワーク処理の両方が完了した時点にコンソールログを表示する処理になります。 `+merge:`:Signalの配列を受け取り、全てのSignalが完了した時点で新しい`RACSignal`を返します。 `-subscribeCompleted:`:Signalが完了した時点でBlockを実行します。 ``` [[RACSignal merge:@[ [client fetchUserRepos], [client fetchOrgRepos] ]] subscribeCompleted:^{ NSLog(@"They're both done!"); }]; ``` SignalはBlockのコールバックをネスティングする代わりに非同期処理を順次実行するために連鎖させることができます。 これはFuture,Promiseデザインパターンでよく用いられる方法に似ています。 次の例では、まずユーザーログインを行い、キャッシュされたメッセージをロードします。次にサーバーから残りのメッセージを受信し、全てが終わった時点でコンソールにログを表示します。 `-flattenMap:`:新しいSignalを受ける度にBlockを実行し、受け取ったすべてのSignalを単一のSignalにマージして新しいRACSignalを返します。 ``` [[[[client logInUser] //`-loginUser`は完了後にSignalを送る。 flattenMap:^(User *user) { // Return a signal that loads cached messages for the user. return [client loadCachedMessagesForUser:user]; }] flattenMap:^(NSArray *messages) { // Return a signal that fetches any remaining messages. return [client fetchMessagesAfterMessage:messages.lastObject]; }] subscribeNext:(NSArray *newMessages) { NSLog(@"New messages: %@", newMessages); } completed:^{ NSLog(@"Fetched all messages."); }]; ``` `RAC`は非同期処理の結果をバインドすることも容易になります。 次の例では、ユーザーの画像がダウンロードされたらすぐにself.imageViewのimageにセットするための一方向のバインディングをつくります。 `-deliverOn:`:他のキューに自分の仕事をさせる新しいSignalを作成します。この例ではメインスレッドに戻って、バックグラウンドキューに作業を移すために用いられています。 `-map:`:この例ではフェッチされたユーザーのBlockを呼び出し、Blockから返された値を送信する新しいRACSignalを返します。 ``` RAC(self.imageView, image) = [[[[client fetchUserWithUsername:@"joshaber"] deliverOn:[RACScheduler scheduler]] map:^(User *user) { // Download the avatar (this is done on a background queue). return [[NSImage alloc] initWithContentsOfURL:user.avatarURL]; }] // Now the assignment will be done on the main thread. deliverOn:RACScheduler.mainThreadScheduler]; ``` |
|
| 844位 |
|
|||
|
02:09:47 |
|
|
herokuで定期的な処理を行いたい場合、大体はaddonのheroku Schedulerを使うことになると思うので、簡単にまとめてみる。
## 特徴など * タスクは複数登録可能 * タスクの起動間隔は10分おき、1時間おき、1日おきで選べる。 * heroku schedulerはベストエフォート型のサービスなので、実行開始時間は多少の遅延したり、スキップされることがあるので注意する * 無償でできるのはプロセスの利用時間は1Dynos (= 750時間(/1ヶ月)) * WebのDynosを1つ利用していた場合、(1ヶ月が31日の場合) 24h * 31日 = 744hなので、残りの6h分のプロセスのみスケジュールタスクとしての処理時間として利用できます。 * それ以降は1時間あたり$0.05かかる(1ヶ月フル使用で$37.2) * 利用状況は https://dashboard.heroku.com/account のBilling のところで確認できる * herokuのアプリケーションをメンテナンスモードにしている場合、時間になってもタスクは実行 されない(スキップされる) ## 何はともあれaddonを追加 ### コンソールから ```shell: $ heroku addons:add scheduler:standard ``` またはaddonページから https://addons.heroku.com/scheduler ※アドオンは無償ですが、オーバーしたプロセスが課金対象になるのでクレジット登録になります。 ### スケジュール画面を開く https://heroku-scheduler.herokuapp.com/dashboard ```shell: $ heroku addons:open scheduler ``` ## タスクの登録 ### Railsの場合 `lib/tasks`の下にrakeファイルを作成 ```ruby:test.rake task :test_task => :environment do #処理 puts 'done.' end ``` スケジュール管理画面で`Add Job...`を選択 TASKに`rake test_task`を設定 FREQUENCYに起動間隔を設定。 ### Sinatraの場合 scheduler用の実行ファイルを作成 ```ruby:task.rb # 処理 puts 'done.' ``` スケジュール管理画面で`Add Job...`を選択 TASKに`bundle exec ruby task.rb`を設定 ##ログの確認 ``` $ heroku logs --ps scheduler.xxxx #(xxxxは実行時に割り当てられたプロセス番号) ``` ##公式Doc https://devcenter.heroku.com/articles/scheduler#defining-tasks |
|
| 845位 |
|
|||
|
22:21:07 |
(HiganWorks LLC. 所属) |
|
<!-- too_old -->
> **この記事は最終更新から1年以上経過しています。** 気をつけてね。 Chef,Puppetに代表される自動構築・構成管理ツールを使うと開発したサーバを検証用などの目的で簡単に再現可能になります。 ただ、漠然としたサーバ構築をしていると何をサービス提供しているのかという定義が曖昧になるため、Cucumber等を使ったテストを軸にテスト駆動でのサーバ構築をしてみましょう。 応用すれば既存のサーバをCucumberによってモデリングし、Chefによって繰り返し再現可能な状態に持っていけます。 このコンテンツで使ったコードはGithubの [https://github.com/higanworks/test_driven_infrastructure_example](https://github.com/higanworks/test_driven_infrastructure_example) で公開しています、参考にしてみたりフィードバックしてもらえると助かります。 ## ツール - **Cucumber**: "ふるまい"を自然言語のように記述し、結果をテストする - **ChefSpec**: ChefのCookbookのテストができるツール、目的のリソース定義がCookbookに記述されているかを確認する - **Foodcritic**: CookbookレシピのSyntaxほか、全体的な書き方のチェックをする、矛盾なども指摘してくれる - **Chef(Solo,Cookbook)**: CookBookを参照し、サーバの役割をコンバージェンスする ## モデル 練習用にこんなサーバを考えます。 - nginxをインストールして、HTTPをホスティングする - "www.example.com" のバーチャルホストを名前ベースで提供する 新規サーバならこれで良し、既存のサーバなら上記のようにモデリングしてみましょう。 ## Cucumber 上記モデルで"ふるまい"を定義するとはどういうことでしょう。 テストするなら - nginxのパッケージをインストールして、コンフィグを置いてあれこれパーミッションを確認してサービスをスタートして... と考えてしまいがちです。 ここはインフラ・サーバエンジニア脳ではなく、エンドユーザの視点で見ることが大事です。中で何が動いているかというのは、"ふるまい"の本質ではありません。 それを踏まえた"ふるまい"の定義はこうなります。 ・HTTPでTCP/80番をサービスしている ・`Host: www.example.com` ヘッダをつけたHTTPアクセスなら`www.example.com`のコンテンツを返す 足りない?そんなことはありません。 ### Cucumber-feature 先ほどの2つをCucumberでテストできるようにしていきます、こんなもんでしょうか。 ```cucumber:features/http_server.feature # language: ja 機能: httpサーバ webサーバを提供するために HTTPクライアントの立場から HTTPのレスポンスをチェックする シナリオ: HTTPクライアントリクエスト 前提: HTTPで localhost にリクエストする ならば: レスポンスのステータスが 200 だ シナリオ: 名前ベースでHTTPクライアントリクエスト 前提: Hostヘッダ に "www.example.com" をつけてHTTPで localhost にリクエストする ならば: レスポンスのステータスが 200 だ かつ: コンテンツに www.example.com が含まれている ``` サーバがこれらの機能を満たしていれば、構築(再現)は完了となるはずです。自分のサーバをレシピにしたい人はこういう形で書きだしてみましょう。 #### Cucumber実行 Cucumberはもう動きます、実行してみましょう。 ```shell:Shell-Out(Cucumber) $ cucumber # language: ja 機能: httpサーバ webサーバを提供するために HTTPクライアントの立場から HTTPのレスポンスをチェックする シナリオ: HTTPクライアントリクエスト # features/http_server.feature:9 前提: HTTPで localhost にリクエストする # features/http_server.feature:10 ならば: レスポンスのステータスが 200 だ # features/http_server.feature:11 シナリオ: 名前ベースでHTTPクライアントリクエスト # features/http_server.feature:13 前提: Hostヘッダ に "www.example.com" をつけてHTTPで localhost にリクエストする # features/http_server.feature:14 ならば: レスポンスのステータスが 200 だ # features/http_server.feature:15 かつ: コンテンツに www.example.com が含まれている # features/http_server.feature:16 2 scenarios (2 undefined) 5 steps (5 undefined) 0m0.006s You can implement step definitions for undefined steps with these snippets: 前提 /^: HTTPで (.*?) にリクエストする$/ do |arg1, arg2, arg3, arg4| pending # express the regexp above with the code you wish you had end ならば /^: レスポンスのステータスが (\d+) だ$/ do |arg1| pending # express the regexp above with the code you wish you had end 前提 /^: Hostヘッダ に "(.*?)" をつけてHTTPで (.*?) にリクエストする$/ do |arg1, arg2, arg3, arg4, arg5| pending # express the regexp above with the code you wish you had end ならば /^: コンテンツに www\.example\.com が含まれている$/ do pending # express the regexp above with the code you wish you had end If you want snippets in a different programming language, just make sure a file with the appropriate file extension exists where cucumber looks for step definitions. ``` シナリオ2つ、ステップが5つあることを検出しています。 同時にステップを実行するためのコードを定義するよう求めてきています、次はそれを元にステップを作ります。 ### Cucumber-Steps Featureに書かれている行をコードとして実行するのはStepです、Step実行結果がTrueならそのテストは成功という仕組みです。 先ほどcucumberを実行した際に出力されたStep定義を参考に、Stepファイルを作ります。 捕捉マッチを使って引数を拾い、コードを実行する仕組みなので多少変更します。 ```ruby:features/step_definitions/http_server_step.rb # encoding: UTF-8 前提 /^: HTTPで ([\w\.]+) にリクエストする$/ do |host| pending # express the regexp above with the code you wish you had end ならば /^: レスポンスのステータスが (\d+) だ$/ do |status| pending # express the regexp above with the code you wish you had end 前提 /^: Hostヘッダ に "([\w\.]+)" をつけてHTTPで ([\w\.]+) にリクエストする$/ do |host_header, host| pending # express the regexp above with the code you wish you had end ならば /^: コンテンツに ([\w\.]+) が含まれている$/ do |expect_string| pending # express the regexp above with the code you wish you had end ``` いまはすべてpendingですが、Cucumberの出力は変わります。 ```Shell:Shell-Out(Cucumber) 2 scenarios (2 pending) 5 steps (3 skipped, 2 pending) ``` 前提がPendingのテストは以降Skipされます、Pendingをなくしていきます。 #### Pending箇所をコードで記述 それではStepを埋めていきます、リソースの取得に手軽な`open-uri`と結果の評価にわりと便利な`RSpec`の`expectations`を使いましょう。 ```ruby:features/step_definitions/http_server_step.rb # encoding: UTF-8 require 'open-uri' require 'rspec/expectations' 前提 /^: HTTPで ([\w\.]+) にリクエストする$/ do |host| @resource = open("http://#{host}") end ならば /^: レスポンスのステータスが (\d+) だ$/ do |status| @resource.status[0].should == "200" end 前提 /^: Hostヘッダ に "([\w\.]+)" をつけてHTTPで ([\w\.]+) にリクエストする$/ do |host_header, host| @resource = open("http://#{host}","Host" => host_header) end ならば /^: コンテンツに ([\w\.]+) が含まれている$/ do |expect_string| @resource.read.should match(expect_string) end ``` Cucumberの出力がまた変わります、PendingをなくしたのでStepが本当に実行されFailとなります。 ```Shell:Shell-Out(Cucumber) 2 scenarios (2 failed) 5 steps (2 failed, 3 skipped) ``` ちなみにスタックトレースが出ますが、例外は `Connection refused - connect(2) (Errno::ECONNREFUSED)` です。 HTTPサーバが動いてないので当たり前ですね、これは同時に対象のサーバが**HTTPサーバという"ふるまい"をしているかどうかをCucumberによってテストできるようになった**ということです。 ではその"ふるまい"テストを満たすようにChefのレシピを書いて行きます。 ## ChefSpec 早速レシピを記述する・・・にはまだ早いのです。"ふるまい"確認の次はCookbookレシピの"仕様"を定義し、満たすかどうかをテストするためChefSpecを使います。 Chef-Client(及びSolo)はコンバージェンスツールです、要求する仕様が問題なく定義してあればサーバの役割はそのように収束します。 ChefSpecはレシピに期待するリソース設定を記述して、Chef-Clientを実行した際にレシピがそのように書けているかをテストすることができます。 ### spec/ の作成 `chefspec`を導入していると`knife cookbook`がちょっとだけ拡張されます。 ``` knife cookbook create_specs http_server -o cookbooks/ ``` 既存のCookbookに対して`create_specs`とすると、レシピの数だけ`spec/`以下にspecファイルが作られます。 中身は例によってペンディングです。 ```ruby:spec/default_spec.rb require 'chefspec' describe 'http_server::default' do let (:chef_run) { ChefSpec::ChefRunner.new.converge 'http_server::default' } it 'should do something' do pending 'Your recipe examples go here.' end end ``` `rspec`を実行してみます。 ```shell:Shell-Out(RSpec) $ rspec -fd http_server::default should do something (PENDING: Your recipe examples go here.) Pending: http_server::default should do something # Your recipe examples go here. # ./spec/default_spec.rb:5 Finished in 0.00109 seconds 1 example, 0 failures, 1 pending ``` このPendingを埋めて行きましょう。 ### chefspecの記述 まずレシピに期待する事をまとめていきます。 - WebサーバにはNginxを使いましょう、パッケージからでOK - 仮想ホスト用のコンフィグをインクルード用に設置 - 追加のコンフィグを設置したらnginxをリスタートする ChefSpecは[Githubのドキュメント](https://github.com/acrmp/chefspec)を参考に書きます、Chefのリソースプロバイダを動的に取得することができるのでドキュメントに無いリソースと思っても名称さえ合わせればチェック出来ます。 #### nginxをインストールする仕様にする ChefRunnerが走った際に、`パッケージ[nginx]`がインストールされているように記述するには`install_package`マッチャを使用します。 ```ruby:spec/default_spec.rb require 'chefspec' describe 'http_server::default' do let (:chef_run) { ChefSpec::ChefRunner.new.converge 'http_server::default' } it 'should install nginx' do chef_run.should install_package 'nginx' end end ``` `chef_run.should install_package 'nginx'`があると`RSpec`実行結果はこうなります。 ```shell:Shell-Out(RSpec) $ rspec -fd http_server::default should install nginx (FAILED - 1) Failures: 1) http_server::default should install nginx Failure/Error: chef_run.should install_package 'nginx' No package resource named 'nginx' with action :install found. # ./spec/default_spec.rb:6:in `block (2 levels) in <top (required)>' Finished in 0.00575 seconds 1 example, 1 failure Failed examples: rspec ./spec/default_spec.rb:5 # http_server::default should install nginx ``` PendingはFailに変わり、内容が **`No package resource named 'nginx' with action :install found.`** となりました。 ### Specに合わせてCookbookレシピを記述する そろそろCookbookレシピの作成にかかります、Specは一つ増やして2つに。 #### Spec nginxサービスをスタートさせる仕様を追加したら、ネームベース部分は後回しにして一旦ここまでのレシピを書いてみます。 ```ruby:spec/default_spec.rb require 'chefspec' describe 'http_server::default' do let (:chef_run) { ChefSpec::ChefRunner.new.converge 'http_server::default' } it 'should install nginx' do chef_run.should start_service 'nginx' end it 'should start nginx' do chef_run.should start_service 'nginx' end end ``` 仕様が決まってきたので、いよいよCookbookを書き始めることができます。 #### Recipe::default 先ほどのSpecを満たすためのレシピはこう。 ```ruby:recipes/default.rb package "nginx" do action :install end service "nginx" do action [:enable, :start] end ``` 単純ですが、Cucumberの要求、ChefSpecの仕様を決めるという段取りを経ているので余裕を持って取り組めます。 このレシピを書いたらSpecの出力も変わります。 ```shell:Shell-Out(RSpec) $ rspec -fd --color http_server::default should install nginx should start nginx Finished in 0.00755 seconds 2 examples, 0 failures ``` これでオールグリーンです、同時にCucumber1つ目のFeatureを満たしていることでしょう。 ## 2つめのFeature 次のFeatureはヴァーチャルホストの設定です、今までの要領でSpecを追加したものがこちら。`describe 'create virtualhosts'`から以降が追加分ですね。 必要なディレクトリ、ついでに`index.html`。それとnginxに読み込ませるコンフィグを設置してからNginxをリスタートする仕様にしました。 ### Spec ```ruby:spec/default_spec.rb require 'chefspec' describe 'http_server::default' do let (:chef_run) { ChefSpec::ChefRunner.new.converge 'http_server::default' } it 'should install nginx' do chef_run.should start_service 'nginx' end it 'should start nginx' do chef_run.should start_service 'nginx' end it 'should create virtualhost base dir' do dir = chef_run.node['nginx']['vhost_dir'] chef_run.should create_directory dir chef_run.directory(dir).should be_owned_by('www-data', 'www-data') chef_run.directory(dir).mode.should == "0750" end describe 'create virtualhosts' do site = 'www.example.com' it "create vhost directory" do dir = ::File.join(chef_run.node['nginx']['vhost_dir'], site) chef_run.should create_directory dir chef_run.directory(dir).should be_owned_by('www-data', 'www-data') chef_run.directory(dir).mode.should == "0750" end it "create vhost index.html" do file = ::File.join(chef_run.node['nginx']['vhost_dir'], site, "index.html") chef_run.should create_file_with_content file, site chef_run.file(file).should be_owned_by('www-data', 'www-data') chef_run.file(file).mode.should == "0640" end it "create vhost nginx config" do file = ::File.join(chef_run.node['nginx']['site_avail_dir'], "#{site}.conf",) chef_run.template(file).should be_owned_by("root", "root") chef_run.template(file).mode.should == "0640" chef_run.template(file).should notify("service[nginx]", :restart) end it "create vhost nginx config link" do file = ::File.join(chef_run.node['nginx']['site_avail_dir'], "#{site}.conf") link = ::File.join(chef_run.node['nginx']['site_enable_dir'], "#{site}.conf") chef_run.should create_link link chef_run.link(link).to.should == file end end end ``` ### Recipe & Attributes Specを元に作成したレシピです、さすがにベタ書きは辛くなってきたので少しAttributesに逃がして定義しました。 ```ruby:recipes/default.rb # # Cookbook Name:: http_server # Recipe:: default # package "nginx" do action :install end service "nginx" do action [:enable, :start] end # vhosts directory node['nginx']['vhost_dir'] do action :create owner "www-data" group "www-data" mode "0750" recursive true end ["www.example.com"].each do |site| directory ::File.join(node['nginx']['vhost_dir'], site) do action :create owner "www-data" group "www-data" mode "0750" end file ::File.join(node['nginx']['vhost_dir'], site, "index.html") do action :create content site owner "www-data" group "www-data" mode "0640" end template ::File.join(node['nginx']['site_avail_dir'], "#{site}.conf") do source "vhost.conf.erb" variables({ :server_name => site, :vhost_root => ::File.join(node['nginx']['vhost_dir'], site) }) owner "root" group "root" mode "0640" notifies :restart, "service[nginx]" end link ::File.join(node['nginx']['site_enable_dir'], "#{site}.conf") do action :create to ::File.join(node['nginx']['site_avail_dir'], "#{site}.conf") end end ``` ```ruby:attributes/default.rb default['nginx']['site_avail_dir'] = "/etc/nginx/sites-available" default['nginx']['site_enable_dir'] = "/etc/nginx/sites-enabled" default['nginx']['vhost_dir'] = "/var/www/vhosts" ``` ## ChefSpec通過、そしてFoodcriticへ 7つのサンプルを全て通過しました、ChefSpecに作成した仕様を満たすレシピの完成です。 ```shell::Shell-Out(RSpec) $ rspec -fd --color http_server::default should install nginx should start nginx should create virtualhost base dir create virtualhosts create vhost directory create vhost index.html create vhost nginx config create vhost nginx config link Finished in 0.07299 seconds 7 examples, 0 failures ``` さあいよいよChef-Client(Solo)を実行に移しましょうか? その前にもう一つツールを叩いてみましょう、**FoodCritic**です。 ### Foodcritic Foodcriticは実際にレシピを適用した際のチェックと読みやすいレシピにするための指摘を行うツールです。 指摘される内容はドキュメントを参照しながら直していきます。 [http://acrmp.github.com/foodcritic/](http://acrmp.github.com/foodcritic/) #### Foodcritic実行 Cookbookのルートで実行してみます。 ```shell:Shell-Out(Foodcritic) $ foodcritic ./ FC033: Missing template: ./recipes/default.rb:45 ``` `FC033: Missing template` をいただきました、テンプレート元のファイルが無いようですね。 このまま実行していてはエラーで失敗するところでした、これはうっかり。 nginxのコンフィグ元ファイルを適当に作ります、一部Chefによってリプレースするため`erb`形式にしてあります。 ```text:templates/default/vhost.conf.erb server { listen 80; server_name <%= @server_name %>; root <%= @vhost_root %>; index index.html; location / { try_files $uri $uri/ =404; } } ``` これで`Foodcritic`のチェックもクリアしました。 ## Chef-Recipeの実行 お気づきとは思いますが、**ここまでChefは一度も実行されず、サーバに何も変更はありません。**(※ChefSpecのRunner等は除く) ### Solo実行ファイルの用意 ではChef-Soloで今回のレシピを適用してみましょう、実行のためのコンフィグとランリストをファイルに書きます。 ```ruby:solo.rb file_cache_path File.expand_path('tmp_chef/cache', File.dirname(__FILE__)) file_backup_path File.expand_path('tmp_chef/backup', File.dirname(__FILE__)) cookbook_path File.expand_path('cookbooks', File.dirname(__FILE__)) log_location 'chef.log' ``` ```json::dna.json { "run_list" : "recipe[http_server::default]" } ``` ### Solo実行 では満を持してChefを実行し、レシピを適用する。コマンドはコンフィグとランリストを含むJsonを渡すと実行される。 `chef-solo -c solo.rb -j dna.json`。 ```shell:Shell-Out(Chef-Solo) $ chef-solo -c solo.rb -j dna.json INFO: *** Chef 10.18.2 *** INFO: Setting the run_list to "recipe[http_server::default]" from JSON INFO: Run List is [recipe[http_server::default]] INFO: Run List expands to [http_server::default] INFO: Starting Chef Run for localhost INFO: Running start handlers INFO: Start handlers complete. INFO: Processing package[nginx] action install (http_server::default line 10) INFO: Processing service[nginx] action enable (http_server::default line 14) INFO: Processing service[nginx] action start (http_server::default line 14) INFO: service[nginx] started INFO: Processing directory[/var/www/vhosts] action create (http_server::default line 20) INFO: directory[/var/www/vhosts] created directory /var/www/vhosts INFO: directory[/var/www/vhosts] owner changed to 33 INFO: directory[/var/www/vhosts] group changed to 33 INFO: directory[/var/www/vhosts] mode changed to 750 INFO: Processing directory[/var/www/vhosts/www.example.com] action create (http_server::default line 30) INFO: directory[/var/www/vhosts/www.example.com] created directory /var/www/vhosts/www.example.com INFO: directory[/var/www/vhosts/www.example.com] owner changed to 33 INFO: directory[/var/www/vhosts/www.example.com] group changed to 33 INFO: directory[/var/www/vhosts/www.example.com] mode changed to 750 INFO: Processing file[/var/www/vhosts/www.example.com/index.html] action create (http_server::default line 37) INFO: entered create INFO: file[/var/www/vhosts/www.example.com/index.html] owner changed to 33 INFO: file[/var/www/vhosts/www.example.com/index.html] group changed to 33 INFO: file[/var/www/vhosts/www.example.com/index.html] mode changed to 640 INFO: file[/var/www/vhosts/www.example.com/index.html] created file /var/www/vhosts/www.example.com/index.html INFO: Processing template[/etc/nginx/sites-available/www.example.com.conf] action create (http_server::default line 46) INFO: template[/etc/nginx/sites-available/www.example.com.conf] updated content INFO: template[/etc/nginx/sites-available/www.example.com.conf] owner changed to 0 INFO: template[/etc/nginx/sites-available/www.example.com.conf] group changed to 0 INFO: template[/etc/nginx/sites-available/www.example.com.conf] mode changed to 640 INFO: Processing link[/etc/nginx/sites-enabled/www.example.com.conf] action create (http_server::default line 58) INFO: link[/etc/nginx/sites-enabled/www.example.com.conf] created INFO: template[/etc/nginx/sites-available/www.example.com.conf] sending restart action to service[nginx] (delayed) INFO: Processing service[nginx] action restart (http_server::default line 14) INFO: service[nginx] restarted INFO: Chef Run complete in 5.2259668 seconds INFO: Running report handlers INFO: Report handlers complete ``` 特にエラーもなく終わったようだ。 つまりレシピに書いた通りにコンバージェンスが行われているので、特に確認の必要もない。 ## 帰ってきたCucumber これで冒頭で作成したCucumberをついに正常終了させることが出来る。 ```shell:Shell-Out(Cucumber) $ cucumber # language: ja 機能: httpサーバ webサーバを提供するために HTTPクライアントの立場から HTTPのレスポンスをチェックする シナリオ: HTTPクライアントリクエスト # features/http_server.feature:9 前提: HTTPで localhost にリクエストする # features/step_definitions/http_server_step.rb:7 ならば: レスポンスのステータスが 200 だ # features/step_definitions/http_server_step.rb:11 シナリオ: 名前ベースでHTTPクライアントリクエスト # features/http_server.feature:13 前提: Hostヘッダ に "www.example.com" をつけてHTTPで localhost にリクエストする # features/step_definitions/http_server_step.rb:15 ならば: レスポンスのステータスが 200 だ # features/step_definitions/http_server_step.rb:11 かつ: コンテンツに www.example.com が含まれている # features/step_definitions/http_server_step.rb:19 2 scenarios (2 passed) 5 steps (5 passed) 0m0.044s ``` これで **Cucumber、オールグリーン** だ! ### 終わりに ここまでくればひととおり、`infrastructure as code`における **テスト駆動サーバ構築** に触れることが出来た。 おそらくインフラ・サーバエンジニアの大半は『面倒臭い』『難しい』と感じたと思う。 しかしCucumberからchef-solo実行の流れは、要件定義・仕様書・手順書のコード化と実行者の自動化いうことに気付くだろうか。 それらを全てコードベースに置くことで管理可能になることのもたらす恩恵は、この手法を身につける努力に対する見返りとして十分なものになるはずです。 |
|
| 846位 |
|
|||
|
04:04:35 |
(Wantedly, Inc. 所属) |
|
サービスを運営していると、 * Admin以下のページだけちょっとヘッダーを変えたい * ABテストのために2種類のレイアウトを試してみたい * キャンペーン用にある期間だけ別のロゴを使いたい * ユーザー登録ページだけ簡易レイアウトにしたい などなどの理由で、`app/views/layouts/application.html.erb`と大体同じだけど、ちょっと違うというレイアウトが欲しくなります。ただ、コピペでいろんなレイアウトを作ってしまうと後の変更が困難になります。Layoutでもデフォルト値や継承を使って再利用性の高いものを作りましょう。 ## 方法1 Default content_for あるyieldに対応するcontent_forがセットされなかった時のデフォルト値を定める方法です。 yieldは普通の文字列を返す関数で、値がセットされていなければ`""`が返ります。なので`empty?`を使って値がセットされているか調べることができます。 ```ruby:app/views/layouts/application.html.erb <header> <% if yield(:header).empty? %> <%= render 'header' %> <% else %> <%= yield :header %> <% end %> </header> ``` ## 方法2 Template Inheritance Controllerの継承関係がViewのTemplateでも使えるという話です。 とりあえず以下のRails Castを見るのがいいと思います。 http://railscasts.com/episodes/269-template-inheritance 要約すると、共有したいpartial templateは`app/views/shared`等ではなく`app/views/application`下に置きます。例えば`_header.rb`という名前のpartialだとすると、以下のようになります。 ```ruby:app/views/layouts/application.html.erb <%= render 'header' %> ``` ```ruby:app/views/application/_header.html.erb <div class="head">Normal Page</div> ``` ここでproducts以下のページだけheader部分の内容を変えたいとしたら、以下のように`products`ディレクトリの下に同名の`_header.html.erb`を作り内容を上書きます。 ```ruby:app/views/products/_header.html.erb <div class="head">Production Page</div> ``` お分かりかと思いますが、これは、以下のようにProductsControllerがApplicationControllerを継承している事を利用しています。 ```ruby:app/controllers/products_controller.rb class ProductsController < ApplicationController ``` この方法は綺麗ですが、LayoutをControllerごとにしか変えられないという問題があります。 ## 方法3 Parent Layout layoutのテンプレートを親layoutを使って設定する方法です。各yieldの値が設定済みのlayoutを作れます。 基本的には以下のリンクを参照するといいです。 http://m.onkey.org/nested-layouts-in-rails-3 ただこれはrails3.0の方法を示していて、rails3.2では動きません。これを、http://stackoverflow.com/questions/9185344/ を参照して直したのが以下です。 ```ruby:app/helpers/application_helper.rb module ApplicationHelper def parent_layout(layout) @view_flow.append(:layout, self.output_buffer) self.output_buffer = render(file: "layouts/#{layout}") end end ``` これを使って、 ```ruby:app/views/layouts/admin.html.erb <h1>Admin Panel</h1> <div class="admin_navigation">Home | Posts | Users | Assets</div> <%= yield %> <% content_for :header do %> <%= link_to image_tag('logo.png'), admin_root_path %> <% end %> <% parent_layout 'application' %> ``` のようにすれば、`layout/application.html.erb`をベースに`layout/admin.html.erb`を作れます。 ## 3つを併用した使用例 これらの方法はどれかを選ぶというよりかは併用すると良いです。 ```ruby:app/views/layouts/application.html.erb <!DOCTYPE html> <html> <head> <title>App Title</title> <%= stylesheet_link_tag "application", :media => "all" %> <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> </head> <body> <div id="wrapper"> <header> <% if yield(:header).empty? %> <%= render 'header' %> <% else %> <%= yield :header %> <% end %> </header> <article> <%= yield %> </article> <footer> <% if yield(:footer).empty? %> <%= render 'footer' %> <% else %> <%= yield :footer %> <% end %> </footer> </div> </body> </html> ``` ```ruby:app/views/application/_header.html.erb <%= link_to image_tag('logo.png'), root_path %> ``` ```ruby:app/views/admin/base/_header.html.erb <%= link_to image_tag('logo.png'), admin_root_path %> ``` ```ruby:app/views/application/_footer.html.erb © <%= Date.today.year %> Company Name. All rights reserved. ``` ```ruby:app/layout/no_header_footer.html.erb <% content_for :header do %> <% end %> <% content_for :footer do %> <% end %> <% parent_layout 'application' %> ``` |
|
| 847位 |
|
|||
|
18:52:49 |
(フリープログラマ 所属) |
|
(13.12.10 改訂) ## 前書き ぶっちゃけると、この記事が書きたかっただけです。 その勢いでAdvent Calendarを作ってしまいました。 とはいえ、もう少し「ドキュメントを作る」ことに注目が集まってもいいのではないかと思います。 Markdownが流行っていることですし、LaTeXユーザもたくさんいますし。 というわけで、ドキュメント作成Tips Advent Calendarの第1回では、 「マインドマップであらゆる文書を書く方法」について、 自作ツールの宣伝も兼ねてお送りします。 ## あらすじ ### マインドマップを使った文書作成フロー この画像を順に追う形で説明していきます。  ### 誰に読んでほしいか * 報告書やブログ記事・プレゼン資料など、章立てやしっかりとした論理構成をもつ文書を作りたい全ての人 * 白紙のWordやエディタを前にして、呆然としたことのある人 * とにかく文書作成で楽したい人 ## マインドマップ作成ソフト XMind ### そもそもマインドマップとは マインドマップとは、元々はトニー・ブザン(Tony Buzan)が提唱した思考・発想法の一つです。 表現したい概念の中心となるキーワードやイメージを中央に置き、 そこから放射状にキーワードやイメージを広げ、つなげていきます。 このようにマップを広げることで、効率的かつ創造的なブレインストリーミングが可能になります。 マインドマップには、ブザンが考案したオリジナル・本家のマインドマップと、その派生があります: * オリジナルの方(ブザンのマインドマップ)は、描き方や色の使い方など細かいルールがある * そこから派生した(広義の)マインドマップは、アウトラインの一形式として広まっている * 次に紹介するXMindは、どちらかといえば派生版マインドマップです ### XMind XMindは、(広義の)マインドマップを作成するソフトです。 XMindの詳しい情報・ダウンロードは以下を参照してください: [日本語版公式サイト](http://jp.xmind.net/) * 【注意】最新版 (XMind2012) は日本語入力のバグがあるので、前バージョンのXMind3.2の使用を推奨します ### スクリーンショット 今書いているこの文書(書きかけ)のスクリーンショットはこんな感じです。  ### XMindの良さ 類似のソフトとしてFreeMindなどがありますが、私があえてXMindを好んで使っているのには、いくつか理由があります。 #### マップがカラフルで美しい ブザンのマインドマップでは、カラフルであることが重要です。 なぜなら、色は右脳に訴えかけ、想像力を豊かにするからです。 Xmindはカラフルなテーマが付属しています。見た目も美しく、気持ちよく使用できます。 また、備え付けマーカー(アイコン)がカラフルで使いやすいのも特徴です(必要であれば、自分でマーカーを追加することも可能です)。 一方、FreeMindはグラフィカルな面で貧弱な点が否めません。 #### アウトラインエディタとして優れている これはFreeMindでもあてはまりますが、 ドラッグ&ドロップでブランチ(枝)の配置を直感的に変えられるのが最大の魅力です。 この機能は文章の章立てを考えるときに便利で、文書の構造を大胆に変えられるのは大きなメリットです。 例えば、一度Word文書に書いたものについて、節単位の入れ替えなど大幅な修正をする場面を想像してみてください。 「はじめに」という節に書いた文を「おわりに」という節に移すだけでも、Wordだと一苦労だと思います。 マインドマップ上では、項目をドラッグ&ドロップするだけで簡単に修正できます。 #### マルチプラットフォーム XMindは、Windows, Mac, Linux(Debian/Ubuntu)という、現代で使われるPC環境のほとんどをカバーしています。 Javaで動作しているので、Debian系以外のLinuxユーザでも使えます。 #### 基本機能は無料 普段使うような機能については、無料で使用できます。 PDFエクスポートなど一部有料の機能がありますが、画像エクスポートは無料のため十分使えます。 ## XMinDoc (旧:XMorgDown) しかし、XMindのエクスポート機能は正直言って貧弱です。 HTMLについてはH3タグまでしかエクスポートできず、それ以下はべた書きになってしまいます。 そこで、XMinDocという自作ツールをRubyで書きました。 (以前はXMorgDownという名前で配布していましたが、gem化に伴い名称変更しました。) このツールは、XMindファイルを色々なフォーマットに変換します。 * 出力フォーマット: Markdown, Org-mode, LaTeX, HTML, etc... このツールは、実はPandocという神のようなツールの助けを借りています。 PandocはHTMLやMarkdown・LaTeXなどの文書を、多種多様なフォーマットに変換するHaskell製ツールです。 開発も活発に行われており、今後注目されるべき激アツなツールの一つです。 詳しくはこちらを参照してください: * [About pandoc](http://johnmacfarlane.net/pandoc/index.html) ### XMinDocの中でやっていること:XSLT + Pandoc XMinDocは以下のような処理を行っています: 1. XMindのファイルからXMLを抽出 2. XSLT (XML Stylesheet Language Transformations) でXMLをHTMLに変換 3. PandocにHTMLを食わせて、色々なフォーマットに変換 ### 使い方・インストール方法 詳しい使い方・インストール方法は以下をご覧ください: [XMinDoc: XMindのマインドマップをMarkdownなどに変換できるスクリプトをgem化した - Qiita [キータ]](http://qiita.com/sky_y/items/782cb656ce8b35bc2c9f) ## Edit & Publish マインドマップから生成した文書は、まだ骨格のみの状態です。 次の段階では、この骨格に対して、MarkdownやOrg-modeなどで文書の肉付けしていきます。 最後に、肉付けした文書を適当な形式にエクスポートし、体裁を整えて完成です! ### Markdown Markdownは、GithubやQiitaで採用されている軽量マークアップ言語です。もはや説明不要ですが、一応。 * Pandocオプション: -t markdown #### 出力 ##### HTML PandocやMarkdownエディタ(Mouなど)でHTMLに変換できるので便利です。 私はMac用Markdownエディタの[Mou](http://mouapp.com/)で編集した後、 プレビュー画面のHTMLをコピーしてEvernoteに貼り付けたりしています。 ##### WordPress WordPressのプラグインを使うと、Markdownで記事が書けます。 私が使っているのは、このプラグインです(管理画面でプラグインを検索したら出てくると思います)。 * Markdown on Save Improved * Markdown QuickTags ##### HatenaDiary markdown2hatena (@joker1007 さん作) を使用すると、Markdownをはてな記法に変換出来ます。 * [(ツール紹介)Markdown記法をはてな記法に変換するツール - 雲行きそらゆきココロイキ](http://d.hatena.ne.jp/sky-y/20120830/1346329634) ### Org-mode Org-modeはEmacs公式の文書作成・TODO管理モードです。 Markdownによく似た(しかし遥かに高機能な)Org-mode用の記法があります。 ドキュメントの日本語訳が追いつかないほど、多機能で奧の深いツールです。 個人的には、編集機能が強力なのでMarkdownよりも好きです。 見出し表示をまとめる機能があり、Org-mode単独でアウトラインエディタとして十分使えます。 まだ開発段階ですが、Vimにも一応Orgクローンがあるらしいです。 * [VimOrganizer : An Emacs' Org-mode clone for Vim](http://www.vim.org/scripts/script.php?script_id=3342) Pandocのオプションは以下の通りです: * Pandocオプション: -t org #### 出力 エクスポート形式が豊富なのも、Org-modeの特徴です。 エクスポートは C-c C-e で選択用バッファが開きます。 ##### LaTeX Org-modeはLaTeXと非常に相性がよく、強力な連携機能(RefTeX、数式埋め込みなど)も魅力です。 プレゼン用スタイルファイルBeamerにも対応しています。 ##### HTML HTMLエクスポートにも対応しています。 適当なCSSを自分で指定すれば、きれいに表示できます。 ##### OpenDocument形式 OpenDocumentはLibreOffice/OpenOffice.orgのファイル形式です。 Org-modeではWriter形式に対応しています。 アウトライン構造まで忠実にエクスポートしてくれるのが、非常にうれしいです。 さらに、LibreOfficeを用いてMS Word形式にエクスポート可能なので、 事務や共同作業の都合でWord形式の提出を迫られても安心です。 ### LaTeX 実はPandoc(XMorgDown)の段階で、直接LaTeXを出力できます。 * Pandocオプション: -t latex Beamerにも対応しています。 * Pandocオプション: -t beamer つまり、以下のうちから好みで選べることになります: * Org文書を編集してからLaTeX出力する * Pandocで直接LaTeX出力して編集する ### その他 その他、Pandocに対応しているものは何でもOKです。 詳しくは以下のサイトを見てください。 * [Pandoc User’s Guide](http://johnmacfarlane.net/pandoc/README.html) > It can read markdown and (subsets of) Textile, reStructuredText, HTML, LaTeX, and DocBook XML; > > and it can write plain text, markdown, reStructuredText, XHTML, HTML 5, LaTeX (including beamer slide shows), ConTeXt, RTF, DocBook XML, OpenDocument XML, ODT, Word docx, GNU Texinfo, MediaWiki markup, EPUB, Textile, groff man pages, Emacs Org-Mode, AsciiDoc, and Slidy, Slideous, DZSlides, or S5 HTML slide shows. It can also produce PDF output on systems where LaTeX is installed. ## 終わりに 最後に、ここまで説明した文書作成フローをおさらいしましょう。  私はいつもあらゆる文書をこのフローで作成しています。 もちろん、この記事も同じフローに乗っています。 セットアップ(特にXMorgDownのインストール)が大変だと思いますが、 そこを乗り越えると非常に快適なWriting Lifeが待っています。 もちろん、そこまで行かなくても、紹介したツールや言語のどれかに興味を持ってもらえれば幸いです。 (特に、Pandocはもっと知ってもらいたいです!) ぜひ興味を持ったツールを試してみてください! |
|
| 848位 |
|
|||
|
12:51:18 |
|
|
# 書いてる人
プログラミング学習サービスやら、ペットサロン予約サービス、風俗検索サービスなど色々とやっている「かずきち」です。 ■運営サービス一部 http://crazy-wp.com/ http://webukatu.com/ 新宿のホストから不動産・保険の営業を経て、HTMLって何?という状態から3ヶ月独学でプログラミングやデザインを学び、IT業界で1年間実務経験を積んで年収は1本超え。現在は起業家としてサービス運営やら不動産運営をしています。 Qiita内にそれ系の記事も書いてます。 <a href="http://qiita.com/kazukichi/items/7379b75fba2f90d3cf45" target="_blank">エンジニアで稼ぐために大切な13のコト</a> <a href="http://qiita.com/kazukichi/items/aeba286c2a750081e5c0" target="_blank">WEBサービスで起業したい人に読んで欲しい18のコト</a> #<a name="first">はじめに</a> ##CakePHPってなに? PHPで効率的にWEBサービスやシステムを作るために開発されたフレームワーク(骨組み)。 ##フレームワークってなに? HTMLにそのままPHPを書いていると同じ記述が増えて非効率だったり、「サイトに表示する部分」と「内部の処理」とがごっちゃになってしまい分かりにくい。 しかも、複数人で開発をしていると同じ処理でも記述の仕方が人それぞれ違ったりするので統一性がない。 なので、何行もある同じ記述を1行で済むようにしたり、処理と表示を分けて考える「MVCモデル」という設計手法(考え方)に基づいて作られたのがフレームワーク。 「アプリケーションを作る土台」みたいなもの。 ##MVCってなに? PHPなどでプログラムをそのままゴリゴリ書いているものを3つに分類して分けて記述していく考え方。 ###モデル(model) アプリケーションが扱うデータやビジネスロジックを担当。 データーベースに対する処理やそれ以外の計算や処理をモデルの中に実装し、コントローラーやビューにその結果を提供する。 ###ビュー(view) ユーザに対しての見た目を担当。 Webアプリケーションの場合はHTMLやJavaScriptなどブラウザに対するユーザインタフェースを提供する部分で、HTMLやJSに関する処理は極力ビューで行う。 ###コントローラー(controller) ユーザからの入力を受け取り、処理を行う部分。webアプリケーションにおいてはユーザからの入力に応じてモデルを呼び出し、その結果をビューを呼び出してユーザに表示するという司令塔の役割。 #導入の仕方 googleで検索する。 #<a name="howto">実際の使い方</a> モデル、ビュー、コントローラーのファイルをそれぞれ作って使う。 ##コントローラーの作り方 コントローラファイルを作る場所: `app/Controller` ファイル名: `アプリケーション名Controller.php` ※アプリケーション名は適当に「Site」とかで。 コントローラーにクラスを定義する。以下のような形で記述する。 ```php <?php //CakePHPに用意されているライブラリをロードする。 //CakePHPではさまざまな機能が用意されているので、それらを利用する場合にApp:usesを使う。 //書き方はApp::uses( クラス名 , パッケージ名 ); //AppControllerを継承するためにControllerパッケージを読み込む App::uses('AppController', 'Controller'); class コントローラー名 extends AppController { //AppControllerを継承して使う public function アクション名1() { ……ここに処理を書く…… } public function アクション名2() { ……ここに処理を書く…… } } ``` アクション1つが、WEBページの1ページになる。以降で、そのアクションに対応するViewを作成する。 ページを増やす時にはアクションを1つ増やしてあげる。 `$this->set("title_for_layout","サンプルサイト");` はサイトタイトルを設定するもの。第2引数にサイト名を入れる。 記述したら保存し、Webブラウザから以下のアドレスにアクセスしたら見れる。 ``` http://localhost/[cakephpディレクトリ]/sample/ ``` ※普通ならindexアクションにアクセスするには「〇〇/sample/index」となるはずが、 CakePHPではindexがデフォルトのアクションに設定されてるので、 アクション名が省略されると自動的にindexアクションにアクセスしたものとみなされる。 URLの中身は以下のようになっている。 ``` http://ドメイン名/cakeのフォルダ名/コントローラー名/アクション名/ ``` ##ビューファイルの作り方 ###1.app/Viewフォルダ内に○○フォルダを作成する ○○の部分は「コントローラー名と同じ名前」にする。 SampleControllerなら「Sample」という名前のフォルダを作る。 ###2.作ったフォルダの中に「○○.ctp」ファイルを作成する ○○の部分は「アクション名と同じ名前」にする。 indexなら「index.ctp」というファイルを作る。 この「.ctp」ファイルが各アクションに対応付けられたビューになる。 ###3.index.ctpに普通にhtmlを記述 ```html <html> <head> <titile>Sample</titile> </head> <body> <h1>sample Page</h1> <p>test sample page</p> </body> </html> ``` ##モデルファイルの作り方 ###1.データベースを作成する データベース、テーブル、カラムを普通通り作成。 データベース名はアッパーキャメルケース(「SampleList」など単語ごとに先頭大文字にする記法)で。 テーブル名は「sample_tables」などとスネークケースでさらに複数形にする。 ###2.データベース設定ファイルに追記する データベースアクセスに関しての基本情報が入っている `app/Config/database.php` を開き MySQLであれば以下のように記述する。 ```php <?php class DATABASE_CONFIG { public $default = array( 'datasource' => 'Database/Mysql', //MySQL使うならこう書く 'persistent' => false, //持続接続のための設定。通常はfalseでいい 'host' => 'localhost', //ローカル環境ならlocalhost 'login' => 'root', //ユーザ名(今回の場合は管理者) 'password' => '', //パスワード 'database' => 'MySampleData', //作成したDB名 'encoding' => 'utf8' ); } ?> ``` ※設定内容を連想配列で記述していく `http://localhost/CakePHPフォルダ名/` を開き一番下の項目に「Cake is able to connect to the database.」と緑色で表示されていれば、接続OK。 ###3.モデルファイルを作成する `app/Model` の中にファイルを作成する。 ファイル名:テーブル名と同じ(アッパーキャメルにして、単数形へ).php `sample_tables`なら`SampleTable.php`にする ファイル名と同じ空のクラスを作って終わり。 ```php <?php App::uses('AppModel', 'Model'); class SampleTable extends AppModel { } ?> ``` ###4.データベースへアクセスするコントローラーを作成する 作成ファイル場所:app/Controller 作成ファイル名:モデルファイル名の複数形+Controller.php(MySampleDatasController.php) ``` $datas = $this->MySampleData->find('all'); $this->set('datas',$datas); ``` ※あとはビューでコントローラーから渡された変数datasをforeachで取り出す。 データ構造:$data['テーブル名(アッパーキャメル、単数形)']['カラム名']; #<a name="send">コントローラーとビューの間で値を受け渡す</a> コントローラーのアクション内に以下を記述。 ``` $this->set( 変数名 , 値 ); ``` ```:例 $this->set('test','これはテストです'); ``` そうすると、ビュー側に自動的に指定した変数(今回だと「test」)が用意され、そこに値が入っているので ビュー側で「$test」を使って色々やればいい。 #<a name="helper">便利な機能「ヘルパー」</a> cakephpにはWEBページを作る上での便利な機能(ヘルパー)がいろいろある。 ##フォームヘルパー htmlのformを簡単に作れる機能。 ```php:viewファイルへの記述例 <h1>いま何時?</h1> <?php //createでフォームを宣言 echo $this->Form->create('Aisatsu',array( 'type' => 'post', //type属性を指定 'url' => 'show' //↑アクションを指定。submitが押されるとこのアクションへ値が渡される。今回の場合、showアクションへ値が渡る。 )); //inputでフォーム作成。type属性を指定すればいろいろなフォームが作れる。無指定だとtextになる。 echo $this->Form->input('Aisatsu.zikan',array( 'label' => '時間' )); //submitボタン作成 echo $this->Form->submit(); //フォーム宣言終わり。書かなくてもいい。 echo $this->Form->end(); ?> ``` ※このままだとフォームごとに改行されて並ぶので、 フォームを横並びにしたいなら横並びにしたいformのarrayに `‘div’=> falseと’label’=> false` を入れてあげる。 ###モデルで値を判断する App/Modelフォルダに適当な名前のphpファイルを作る。 今回は「Sample.php」 ```php:Sample.php <?php class Sample extends Model{ public $name = 'Sample'; //クラス名を指定 //DBのどのテーブルを使用するかを指定。使わないならfalseにしておく。 public $useTable = false; public function judge($time){ if($time == '朝'){ $msg = 'おはよう'; }elseif($time == '夜'){ $msg = 'こんばんは'; } return $msg; } } ?> ``` ###コントローラーで値を受け渡す ビューから入力された値をまずコントローラーで受け取り、値を判断したのち、指定のアクションへ渡す。 CakePHPではフォームデータは $_POST ではなく $this->request->data という形で受け取る。 ```php:コントローラーファイルの記述例 function show(){ //POSTが送信されたかどうか if($this->request->is('POST')){ $time = $this->request->data['Aisatsu']['zikan']; //送信データを受け取る $msg = $this->Sample->judge($time); //モデルのメソッドを記述。返り値を保存。 //ビューに値を渡す。引数1で受け渡した側で使える変数名を指定。引数2で受け渡す値を指定。 $this->set('say',$msg); //今回だとビューファイルでは$sayという変数に値が入って渡される。 } } ``` #<a href="layout">レイアウト(layout)の切り替え</a> 個々のページにはデフォルトのレイアウトが適用されている。 レイアウトは、画面にヘッダー・コンテンツ・フッターといった領域を設け、このコンテンツ部分に指定のビューをはめ込んで表示している。 このレイアウトを変更したい場合には以下のようにする。 ※デフォルトのレイアウトを修正してもいい 1.app/View/Layoutsフォルダ内に「適当な名前.ctp」というファイルを作成する 2.作ったファイル内に以下のように記述 ```html+php <!DOCTYPE html> <html> <head> <?php echo $this->Html->charset(); ?> <title> <?php echo $title_for_layout; ?> </title> <?php echo $this->Html->meta('icon'); echo $this->Html->css('Sample'); //適用するCSSファイル名を指定。今回の例だと「Sample.css」 echo $this->fetch('meta'); echo $this->fetch('css'); echo $this->fetch('script'); ?> </head> <body> <div id="container"> <div id="header"> <h1>My Board Application</h1> <h2>hello</h2> </div> <div id="content"> <?php echo $this->Session->flash(); ?> <?php echo $this->fetch('content'); ?> </div> <div id="footer"> </div> </div> <?php //echo $this->element('sql_dump'); ?> </body> </html> ``` 3.app/webroot/cssフォルダ内に2で指定したcssファイルを作る。 (今回の例だと「Sample.css」) 4.作ったCSSファイルの中を普通のCSSと同じ様に記述していく。 5.コントローラーに適用するスタイルを指定する ```php <?php class SampleController extends AppController{ public $name= "Sample"; public $components = array('DebugKit.Toolbar'); public $layout = "Sample"; //適用するスタイルを指定。今回だと「Sample.ctp」のスタイルを適用 public function index(){//アクション } } ``` #<a href="element">ビュー(View)ファイルを切り分ける「element」</a> 普段PHPでごりごり書く時にもヘッダー、フッターなどどのページでも同じ部分はファイルを分けて、そのファイルをincludeなどで読み込むが、同じ様にCakePHPではelementという機能でファイルを分けることができる。 切り分けは普通のPHPで書く時と同じように流用部分を分ける。 分けたファイルを読み込む時には ```php echo $this->element('読み込むviewファイル名',array('data' => $data, 'mes' => 'こんにちは')); ``` を記述。 arrayで読み込む先のファイルへデータ受け渡しが可能。 普通のphpだとincludeなどした場合には変数はどっちのファイルでも共通で使えるが、CakePHPの場合は引数で渡さないと使えない。 #CakePHPでのバージョンによる違い ##POST/GETの受け取り方の違い CakePHP1.~のものは ```php //GETの場合 $this->params['url']['data']; //POSTの場合 $this->params['form']['data']; ``` CakePHP2.~のものは ```php //GETの場合 $this->request->query['data']; //POSTの場合 $this->request->data['data']; ``` そのほか、1.~で `$this->data` として使ってたところは `$this->request->data` になった。 #<a name="redirect">リダイレクトとフォワード</a> リダイレクトは、AppControllerの「redirect」メソッドを使って行える。 リダイレクト先のアドレスを渡すだけ。 `$this->redirect( アドレス );` もし、密かに別のアクションに処理を送りたければ、「フォワード(サーバー内で別アドレスの内容を結果として出力する)」という操作をする。 ```php $this->setAction( アクション名 ); <?php App::uses('AppController', 'Controller'); class SampleController extends AppController { public function index() { $this -> autoRender = false; $this->redirect("./other/"); } public function other(){ $this -> autoRender = false; echo "<html><head></head><body>"; echo "<h1>サンプルページ</h1>"; echo "<p>これはもう1つのページです。</p>"; echo "</body></html>"; } } //別のアクションへのフォワード public function index() { $this -> autoRender = false; $this->setAction("other"); } ``` #<a name="sanitize">サニタイズ</a> サニタイズは、CakePHP2.0にライブラリとして用意されている。 サニタイズの方法は、クラスにいくつものメソッドとして用意されている。 ```php:例 <?php App::uses('AppController', 'Controller'); App::uses('Sanitize', 'Utility'); //サニタイズを使うためのライブラリ読み込み class SampleController extends AppController { public function index() {} public function form() { $text1 = $this -> data["text1"]; $check1 = isset($this -> data["check1"]) ? "On" : "Off"; $radio1 = $this -> data["radio1"]; $this -> set("text1", Sanitize::stripAll($text1)); //サニタイズする $this -> set("check1", $check1); $this -> set("radio1", $radio1); } } ``` ※`Sanitize::stripAll`を指定した場合、引数に渡したテキストからホワイトスペース、スクリプト、イメージ、スタイルシートなどの情報を全て取り除いて無効化する。 #画像ファイルを読み込ませる cssとほぼ同じ。例えば、test.jpgを読み込ませたい場合は、 ```php <?php echo $this->Html->image('logo.jpg',array('alt' =>'ZAKKRYトップページへ'); ?> ``` と記述。 画像は「/app/webroot/img/test.jpg」に配置する。 #画像読み込み+画像にリンクを付ける ```php <?php echo $this->Html->link($this->Html->image('logo.jpg',array('alt' =>'ZAKKRYトップページへ')), '', array('escape' => false)); ?> ``` `‘escape’ => false` をしないと、imgタグの < や > が&lt;、&gt;に変換されてしまい、画像が表示されない。 #外部jsファイルを読み込む ##1.jsファイルを置く jsファイルの置き場所:/app/webroot/js ##2.Controllerに1行記述 使いたいactionに以下の1行記述 ```php var $helpers = array('Javascript'); ``` ##3.Viewに1行記述 link('')内にjsファイル名を記述する ```php <?php echo $javascript->link('test'); ?> ``` ...続く #WEBプログラミングから起業までを一貫して学べるオンライン動画総合学習サービス『ウェブカツ!!』 様々なプログラミング学習サイトやサービスでは正直な所、自分でWEBサービスを作れるようにはならないため、「自分でWEBサービスを作れるようになる!」をゴールとしたオンライン動画総合学習サービス『ウェブカツ!!』を立ち上げました。ご興味ある方は登録よろしくお願いいたします。 http://webukatu.com/ |
|
| 849位 |
|
|||
|
20:18:27 |
|
|
Xcode 5 と iOS 7 SDK で、static library (静的ライブラリ)を作る手順をメモりました。 今回はRunScriptを利用して複数アーキテクチャ (armv7,armv7s,arm64含む)まで対応出来るものを作ってみます。 ##目次 **1.プロジェクトの新規作成 2.Target追加 3.Run Scriptを追加 4.Copy Fileの追加 5.Architecturesの追加 6.Schemeの編集 7.実行 8.実装について** ##1.プロジェクトの新規作成 Menu > File > New > Project... iOS > Other > "cocoa Touch static library" を選択 ここでは例として Product Name を 「Hoge」 とするとします。 ##2.Target追加 RunScriptを追加するためのTargetを追加します。 Menu > Editor > Add Taget… iOS > Other > "Aggregate" を選択 ここでは例として Product Name を 「Hoge-Universal」 とするとします。 ##3.Run Scriptを追加 2.で作成した、"Hoge-Universal" の Build Phase を編集します。 Menu > Editor > Add Build Phase > Add Run Script Build Phase. Scriptを追加します。 こんな感じ ``` xcodebuild_hoge() { xcodebuild \ -target "${PROJECT_NAME}" \ -project ${PROJECT_NAME}.xcodeproj \ -configuration ${CONFIGURATION} \ -sdk $1 clean build } BUILD_DIR="${SRCROOT}/build" LIB_DIR="${SRCROOT}/library" LIB_NAME="lib${PROJECT_NAME}.a" IOS_LIB="${BUILD_DIR}/${CONFIGURATION}-iphoneos/${LIB_NAME}" SIM_LIB="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${LIB_NAME}" DEST_LIB="${LIB_DIR}/${LIB_NAME}" # remove dir rm -rf "${BUILD_DIR}" rm -rf "${LIB_DIR}" # 1. xcodebuild xcodebuild_hoge iphoneos xcodebuild_hoge iphonesimulator # mkdir mkdir -p "${LIB_DIR}" # 2. lipo lipo "${IOS_LIB}" "${SIM_LIB}" -create -output "${DEST_LIB}" ``` xcodebuild で、Architectureごとにlibraryを作っています。 lipo で 全部まとめて 1つのlibHoge.aにしています。 ※細かい設定は必要に応じて変更してください。 ##4.Copy Fileの追加 .aの中に含まず外に出すを指定 ヘッダファイル等をあえて外出しにしたい場合なんかはここで指定しておきます。 今回は例として 3.での出力ディレクトリと同じ場所にHoge.hを出力するように指定しています。 Menu > Editor > Add Build Phase > Add Copy files Build Phase. **Destination** Absolute Path を選択 **Path** ${SRCROOT}/library を指定 ##5.Architecturesの追加 このままだと、有効なアーキテクチャ(iphoneosで一つiphonesimulatorで一つ)のみ対応するライブラリしか出来ないので、複数のArchitectureに対応出来るように Build Settings の設定を変更します。 ###Architectures **Architectures** "standard architectures (including64-bit)" を選択 **Build Active Architecture Only** Release側がNOになっている事を確認 ->これがYESだと、iphoneos / iphonesimulator それぞれ1つずつの有効な Architecture しか反映されません。 **Valid Architectures** armv7 armv7s arm64 の他に Simulator用の i386 x86_64 を追加 ###Deployment **iOS Deployment Target** デフォルトを最低iOSバージョン、arm64 Architecture に 7.0を指定 詳細:ライブラリをiOS7.0未満を対象とするアプリで利用する場合は、その最低バージョンをデフォルトに設定します。今回は64bitに対応させたいので、arm64についてはiOS7.0以上となるよう項目を追加して設定します。 **ex. 最低 64bit対応 かつ iOS5.0以降で使えるようにしたい場合の設定** >Deployment Target: Debug 5.0 arm64 architecture: 7.0 Release 5.0 arm64 architecture: 7.0 ##6.Schemeの編集 release buildでライブラリを生成するように設定を変更します。 Schemeの "Hoge-Universal" を選択後 EditScheme を選択 Run の Build Configuration を"release"へ変更 ##7.実行 以上の設定をした後に build すると、 プロジェクト/library/ 下にlibHoge.a と Hoge.h が生成されています。 ``` lipo -info Hoge.a ``` lipoコマンドでlibHoge.aの中身を確認すると ``` Architectures in the fat file: /hoge/library/libHoge.a are: armv7 (cputype (12) cpusubtype (11)) i386 x86_64 (cputype (16777228) cpusubtype (0)) ``` armv7,armv7s,arm64,i386,x86_64のアーキテクチャが確認出来ます。 ##8.実装について 先ほどのライブラリは中身が空なので、ソースをちょろっと追加 ```Hoge.h #import <Foundation/Foundation.h> @interface Hoge : NSObject { } -(void)hoge; -(void)fuga; -(void)bye; @end ``` ```Hoge.m #import "Hoge.h" @implementation hoge -(void)hoge { NSLog(@"hoge!"); } -(void)fuga { NSLog(@"fuga!"); } -(void)bye { NSLog(@"...bye (;_;)"); } @end ``` 呼び出し側でHogeをimportして、こんな感じで。 ```example.m #import "Hoge.h" @implementation example -(void)test { // : // : Hoge * hoge = [[Hoge alloc] init]; [hoge hoge]; [hoge fuga]; [hoge bye]; } @end ``` **実行結果** ``` hoge! fuga! ...bye (;_;) ``` |
|
| 850位 |
|
|||
|
23:08:11 |
|
|
おはようございますの人もいれば、こんにちはの人もいて、こんばんはの人もいれば、スラマッパギの人もいますね。ということで本日はどうも、まとめてスラマッパギ。
えーと、今日はちょっとした発表があります。といっても本当にちょっとしたものなので、ちょっとだけ耳を傾けてお聞きください。ええ、JavaScriptの代入についてです。そう、値渡しだの参照渡しだの猫ダマシだの「村長だ、ワシ」だの、そんな話です。 ということで、ちょっとこれから始めようかなと、ちょっと思います。 #さっそくだが、見よ!この値渡しを! ```javascript:array_sample1 var a, b; a = ["hoge", "fuga"]; b = a; // 値渡し b; // => ["hoge", "fuga"] b[0] = "hogera"; b; // => ["hogera", "fuga"] a; // => ["hogera", "fuga"] 値渡し! ``` ```javascript:object_sample1 var a, b; a = {hoge: "hogehoge", fuga: "fugafuga"}; b = a; // 値渡し b; // => {hoge: "hogehoge", fuga: "fugafuga"} b.hogera = "hogerahogera"; b; // => {hoge: "hogehoge", fuga: "fugafuga", hogera: "hogerahogera"} a; // => {hoge: "hogehoge", fuga: "fugafuga", hogera: "hogerahogera"} 値渡し! ``` どうですか、見事な値渡しでしょう。 お、すばらしいですか。ありがとうございます。 最近練習した甲斐がありました。 #「おいおい、それどう見ても参照渡しだろう。」 あ!あなたは!またあなたですか! いつもしつこい人だ…そして今日も出会ってしまうとは…運の悪い…! いや?むしろ今日は少し都合がいい。どうせならちょっとこのコードについて、説明してもらいましょうか! ```javascript:array_sample2 var a, b; a = ["hoge", "fuga"]; b = a; // 参照渡し? b = []; b; // => [] a; // => ["hoge", "fuga"] ←!?!!?!???!?!? ``` ```javascript:object_sample2 var a, b; a = {hoge: "hogehoge", fuga: "fugafuga"}; b = a; // 参照渡し? b = {}; b; // => {} a; // => {hoge: "hogehoge", fuga: "fugafuga"} ←!??!??!?!?!!?!?!?!?!? ``` 参照渡しって言うならこれはどういうことなんですか!?!? あ、どうせまた「仕様だ」とか言うんでしょう! いつもそうやってごまかして、今日はそこらへんしっかり答えてもらいますからってあの、どこ行くんですか! まだ質問終わってないんですけど!あのー! #ほら、値渡しじゃないですか! 取り乱しました。本題に戻ります。 結果を見てもらうと分かると思います。 言った通り、JSは基本的に __参照の値渡し__ なんです!!! #つ、つまり、どういうことだってばよ…? まあ、JSの変数って、もはやポインタみたいなものなんですよね。 値がそのまま入っているわけじゃないんですよ。 何が入っているかって、参照先のアドレスなんですね。 つまり、 ```js a = [0, 1, 2]; ``` この1文、なにをしているかって、 (1) 配列[0, 1, 2]を生成して、メモリのどこかに置く。 (2) そのメモリのアドレスをaに入れる。 ということなんですよ! だから、 ```js b = a; ``` この1文がなにをしているかって、 (1) aに保存されているアドレスをbに入れる。 ですよね。簡単ですね。 さて、それでは今、aとbが等価であるかと言われたら、等価なんですけど等価でないと言いたいところです。 "=="演算子なり"==="演算子で比較したらtrueを返しますが、それはあくまで __aに保存されているアドレスを参照してみて見つかったオブジェクトと、bに保存されているアドレスを参照してみて見つかったオブジェクトが等価だった__ だけであって、 __aとbが同じオブジェクトである__ ということではないと思うのです。 まあ、JSの実装を調べてないので詳しくは知らないんですけれども(小声) だから、あくまで ```js b = []; ``` というのは、 (1) 配列[]を生成して、メモリのどこかに置く。 (2) そのメモリのアドレスをbに入れる。 というだけのことであって、この動作の中で __bに保存されているアドレスを参照する__ ということ起きていません。起きているのはbへの代入だけです。 bへの代入ですから、もちろん __aへの代入をしているわけでもない__ ですし、 __bに保存されているアドレスは参照されていないのでそのアドレスに存在するオブジェクトが変更されることもない__ わけです。 よって、この文のせいでaまで影響が及ぶことはないということになります。 ただ、 ```js b[0] = "hogera"; ``` みたいな場合はちょっと違います。 まず (1) 文字列"hogera"を生成して、メモリのどこかに置く。 というのは同様なんですが、次のステップが違います。 何が違うかって、今までbへの代入は、単純に変数bへの代入でしたが、今回の場合は、 __bに保存されているアドレスを参照してみて見つかったオブジェクトの、添字0に対応する部分__ への代入となります。 つまり、今まで起きていなかった __bに保存されているアドレスを参照する__ という動作が行われるわけです。そして参照先のオブジェクトの一部に対して代入を行います。 bに保存されているアドレスとaに保存されているアドレスは同一のものなので、この動作によって初めて __aの参照先のオブジェクトにも変更が及ぶ__ ことになります。 変数aからしたら知ったこっちゃないですね。aはアドレスを持ってるだけで、そのアドレスが書き換えられるわけでもないですからね。 「変数aさん!出番よ!」と呼ばれたときに「あ〜、はいはい」とか内心思いながら自分の体に刻まれたアドレスを見ながらそのアドレスまで出かけて、「俺を何回同じとこ行かせたら気が済むんだよ、マジで」とかぼやきながらやっと着いたと思ったら「この前となんか違うじゃねえか!」みたいな。誰だよこいつをいつの間に変えやがってとか思ってるんでしょうね。犯人はbですよ!aさん! #だから、こんなことも! 謎は解けましたか、皆さん。 ここで、さらにマジックを見せてあげましょう。ハイッ! ```js:magic_preparation var a, b; a = 0; b = a; // 値渡し! b.hoge = "hoge"; ``` さて、何が起こるでしょうか、皆さん! きっと皆さんならお分かりですよね!それでは結果を見てみましょう! ```js:magic_result a.hoge; // => undefined ``` あ、あれ? ```js:magic? b.hoge; // => undefined ``` え、ええ!?!?!?ゑゑゑゑ?!?!???!?? あ、いや、ちょっと、まあ、場の空気をね、和ませようかなって、ははっ。 いやいや、まあ、僕別に実装自体は何も調べてないし結果から考察しただけなので、まあ、この結果からすればまあプリミティブは単純な値渡しなんじゃないでしょうか? それか動的にメンバを定義できないとかなんじゃないですかね! あ、そうそう、仕様ですよ!そう、うん。 仕様です。 それではーーーー!!!! |
|
| 851位 |
|
|||
|
23:00:40 |
(freee 所属) |
|
[Marionette.jsまとめ その1 Application, Controller, AppRouter](http://qiita.com/tohashi/items/6fdb16367e39afa41d8c)
[Marionette.jsまとめ その2 View, ItemView, CollectionView](http://qiita.com/tohashi/items/85475b2dbbea164179e5) [Marionette.jsまとめ その3 CompositeView, Layout, Region](http://qiita.com/tohashi/items/b5d1bac3f32d324527ce) # Marionette.jsとは [Marionette.js](http://marionettejs.com/)はBackbone.jsに下記等の機能を追加するライブラリです。 - `CollectionView`, `ItemView`などViewタイプの追加 - `Region`と`Layout`によるDOMの制御 - モジュール機構 - デストラクタ(elの削除、インスタンスの除去、オブザーバの解除) - コントローラ 自分用に整理したいというのもあって 基本的な機能やAPIを数回に分けて列挙していきます。 使用しているMarionette.jsのバージョンは1.2.3です。 [公式ドキュメント](https://github.com/marionettejs/backbone.marionette/tree/master/docs) [TodoMVC](http://todomvc.com/labs/architecture-examples/backbone_marionette/) ## Marionette.Application Marionette.jsアプリケーションの中核となるオブジェクトです。 このインスタンスをハブとして各オブジェクトを定義していきます。 ```js var App = new Marionette.Application(); ``` #### addRegions `Region`を追加します。 `Region`は関連するDOM内のレンダリングやViewの破棄などを管理するオブジェクトです。 `Application`や後述する`Layout`の子要素として定義します。 ```js:Regionを追加 App.addRegions({ 'header': 'header', 'main': '#main', 'footer': 'footer' }); App.header; // => Marionette.Regionのインスタンス ``` #### addInitializer アプリケーション初期化時のコールバックを追加します。 ```js App.addInitializer(function() { var router = new App.Route.Router(); }); ``` #### Events `listenTo`や`on`を使ってオブザーバを設定することができます。 またMarionette.jsのイベントは全て`Marionette.triggerMethod`を介して行われ、 イベント発火時にそのオブジェクトに`on + イベント名`のメソッドが定義されていれば実行します。 ```js // どちらも初期化完了後に呼び出される App.listenTo(App, 'initialize:after', function() { Backbone.History.start(); }); App.onInitializeAfter = function() { Backbone.History.start(); }; ``` #### module `Marionette.Application`のインスタンス配下にモジュールを定義します。 ```javascript App.module('Hoge', function(Hoge, App, Backbone, Marionette, $, _) { Hoge.Fuga = Backbone.View.extend({ initialize: function() { console.log('Foo'); } }); }); var fugaView = new App.Hoge.Fuga(); // サブモジュールまでまとめて定義可能 App.module('blur.blur.blur'); ``` #### start `Marionette.Application`の初期化処理を下記の順番で行います。 1. `initialize:before`イベント発火 2. addInitializerで登録した関数呼び出し、各モジュールの初期化処理 3. `initialize:after`イベント発火 4. `start`イベント発火 ```js $(function() { App.start(); }); ``` ## Marionette.Controller Backbone.jsには無かったコントローラです。 特に固有の機能はなく、定義されているメソッドは `triggerMethod`, `extend`, `close`と`Backbone.Events`関連です。 ```js var Controller = Marionette.Controller.extend({ initialize: function() { this.todos = new App.Todo.Collection(); }, showLayouts: function() { App.main.show(new App.Layout.HeaderView); } }); ``` ## Marionette.AppRouter `Backbone.Router`を拡張したオブジェクトです。 #### appRoutes ルーティングを設定します。 通常の`Backbone.Router`と違い、 呼び出すのは`controller`に指定したオブジェクトのメソッドです。 ```javascript var Router = Marionette.AppRouter.extend({ appRoutes: { 'top': 'hoge', '*splat': 'fuga' } }); var Controller = Marionette.Controller.extend({ hoge: function() { //... }, fuga: function() { //... } }); App.addInitializer(function() { router = new Router({ controller: new Controller() }); }); ``` |
|
| 852位 |
|
|||
|
00:19:20 |
|
|
## 美しい数式は目の保養になります
Qiita で数式を美しく[書けるようになった](http://qiita.com/Qiita/items/c686397e4a0f4f11683d?utm_source=Qiita+Newsletter+Users&utm_campaign=5f4e1fc624-Qiita_newsletter_74_16_10_2013&utm_medium=email&utm_term=0_e44feaa081-5f4e1fc624-10310533#2-9)そうなので、テストを兼ねて投稿。Euler の等式は、 $ e^{i \pi} = -1 $ 。 ```math \exp x = \sum_{n=0}^{\infty}\frac{1}{n!}x^{n} ``` 表示されているかな? 内部では [MathJax](http://www.mathjax.org/) というライブラリを使っています(そうですよね?)。MathJax は、LaTeX 記法や MathML で記述された数式をウェブブラウザ上でレンダリングしてくれる JS のライブラリです。[MathOverflow](http://mathoverflow.net/) や [Project Euclid](http://projecteuclid.org/DPubS?Service=UI&version=1.0&verb=Display&handle=euclid) にも採用されるなど、ウェブ上で数式を表示するためのデファクトスタンダードになりつつあり、スポンサーもいっぱいついてます。個人的にも応援しています。美しい数式は、目の保養になります。そのため、僕は Qiita の MathJax 対応を大変嬉しく感じているのです。 __みんなどんどん数式を書こう!!!__ ## 以下サンプルテスト ### ローレンツ方程式 ```math \begin{aligned} \dot{x} & = \sigma(y-x) \\ \dot{y} & = \rho x - y - xz \\ \dot{z} & = -\beta z + xy \end{aligned} ``` ```latex \begin{aligned} \dot{x} & = \sigma(y-x) \\ \dot{y} & = \rho x - y - xz \\ \dot{z} & = -\beta z + xy \end{aligned} ``` ### 外積 ```math \mathbf{V}_1 \times \mathbf{V}_2 = \begin{vmatrix} \mathbf{i} & \mathbf{j} & \mathbf{k} \\ \frac{\partial X}{\partial u} & \frac{\partial Y}{\partial u} & 0 \\ \frac{\partial X}{\partial v} & \frac{\partial Y}{\partial v} & 0 \end{vmatrix} ``` ```latex \mathbf{V}_1 \times \mathbf{V}_2 = \begin{vmatrix} \mathbf{i} & \mathbf{j} & \mathbf{k} \\ \frac{\partial X}{\partial u} & \frac{\partial Y}{\partial u} & 0 \\ \frac{\partial X}{\partial v} & \frac{\partial Y}{\partial v} & 0 \end{vmatrix} ``` ### Ramanujan の恒等式のひとつ ```math \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\ldots} } } } ``` ```latex \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\ldots} } } } ``` ### Maxwell 方程式 ```math \begin{aligned} \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\ \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\ \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\ \nabla \cdot \vec{\mathbf{B}} & = 0 \end{aligned} ``` ```latex \begin{aligned} \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\ \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\ \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\ \nabla \cdot \vec{\mathbf{B}} & = 0 \end{aligned} ``` ### LaTeX ```math \rm\LaTeX ``` ```latex \rm\latex ``` ## 数式記法の使い方 [Qiita MarkDown チートシート](http://qiita.com/Qiita/items/c686397e4a0f4f11683d?utm_source=Qiita+Newsletter+Users&utm_campaign=5f4e1fc624-Qiita_newsletter_74_16_10_2013&utm_medium=email&utm_term=0_e44feaa081-5f4e1fc624-10310533#2-9)を見てください。 |
|
| 853位 |
|
|||
|
15:59:38 |
(XTREME DESIGN Inc. 所属) |
|
お久しぶりです。<br>
~~とあるゲーム会社で、お仕事を始めました。~~ _※とあるゲーム会社でお仕事していた頃の記事です。_ 私事ですが、組み込み業界から飛び出て 1年半、<br> やっとたどり着いたこの素敵なチームで、技術がっつり手に入れていきます。 さて、さて。 今回ご紹介するのは、私たちが採用している **爆速** のフレームワーク! **Phalcon PHP Framework** 技術陣全員が口をそろえてこう言います。 「web フレームワークの集大成だ…!」 そんな Phalcon . われらが Phalcon ! 手始めに、概要をまとめてみました。 ぜひご覧ください~ ('◇')ゞ Prologue ======================= さて、今回ご紹介するのは、私たちが採用している **爆速** のフレームワークです! **[Phalcon PHP Framework](http://phalconphp.com/)** 技術陣全員が口をそろえてこう言います。 「web フレームワークの集大成だ…!」 そんな Phalcon についてご紹介します。<br> ---- Slideshare -------------------- 尚、slideshare にも同資料を掲載しておりますので、併せてご覧頂ければ幸いです。 [Meet ♡ Phalcon | slideshare](http://www.slideshare.net/dzeyelid/meet-phalcon) ---- Phalcon PHP Framework ======================= Phalcon とは? -------------------- - PHP の **爆速** フレームワーク - 今までの MVC フレームワークの **集大成** のような出来! - 初心者でもわかりやすいシンプルなフレームワーク - Rails を経験していれば、さらにとっつきやすい - Windows, Linux/Unix/Mac 対応 - 親しみやすいテンプレートエンジン Volt - NoSQL と相性が良い - 便利な scaffold のGUIツール あり 爆速 PHP フレームワーク ------------------------- - 内側が C言語 で実装されている - PHPのエクステンションとして実装されている  シンプルで柔軟な、集大成フレームワーク ------------------------- - 単一/複数のMVCモジュールを扱える → [Full MVC applications](http://docs.phalconphp.com/en/latest/reference/applications.html) - シンプルなREST API も実装しやすい → [Micro Applications](http://docs.phalconphp.com/en/latest/reference/micro.html) - [DIコンテナ](http://docs.phalconphp.com/en/latest/reference/di.html) を利用できる - Rails の経験を活かせる、さらに便利 - Rails では gem で提供されていたページネイションなど、標準装備 → ex. [Pagination](http://docs.phalconphp.com/en/latest/reference/pagination.html) - Twitter Bootstrap を利用 - シンプルな [Model](http://docs.phalconphp.com/en/latest/reference/models.html) - NoSQL と相性が良い → [ODM (Object-Document Mapper)](http://docs.phalconphp.com/en/latest/reference/odm.html) - 使いやすいテンプレートエンジン [Volt](http://docs.phalconphp.com/en/latest/reference/volt.html) - 便利な scaffold の GUIツール あり → [Developer Tools](http://docs.phalconphp.com/en/latest/reference/tools.html) テンプレートエンジン Volt --------------------------------- - シンタックス - {% ... %} : PHPのステートメントとして置き換えられる - {{ ... }} : <? echo … ; ?> として置き換えられる - これは、既存のフレームワークでも **よく採用されている** シンタックス - インスパイアを受けた Python の テンプレートエンジン Jinja - Symfony のテンプレートエンジンも同じようなシンタックス - **気配り** が効いていて、使って気持ちがいい ODM for MongoDB ------------------------------------ - ODM = Object-Document Mapper - ドキュメントをオブジェクトに変換するマッパー - Collection クラスをオーバーライドすることで、Model クラスと同様に **NoSQL のデータを扱える** - **CRUD** (Create, Read, Update, Delete) - find(), save(), delete() - find() で利用できるパラメータ - conditions, fields, sort, limit, skip - validate() で利用できるパラメータ - Email, ExclusionIn, InclusionIn, Numericality, Regex, StringLength  Phalcon Development Tools ------------------------------------------ - Controllers - Controller の名前を入力して生成 - Models - 指定した Model を生成(全テーブルも指定可能) - Scaffold - 指定したテーブルから Model, View, Controller を生成 - Migrations - テーブル単位でマイグレーション可能  さいごに ----------------------------------- Phalcon PHP Framework は、[new BSD license](https://github.com/phalcon/cphalcon/blob/master/docs/LICENSE.md) に基づきリリースされています。 尚、本資料における Phalcon からの引用、および画像については、 [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) に基づき、使用しています。 ----------- Epilogue ========================== このフレームワークを使って、お仕事したい方は! **[RODEO.inc](http://www.rodeo.jp.net/)** で一緒に作りませんか! お待ちしております! ヾ(・ω・)ノ ---- 以上です、読んでくださってありがとうございました! |
|
| 854位 |
|
|||
|
05:00:56 |
|
|
## サーバ側作業
### ユーザー作成 ``` adduser gitrepo usermod -a -G gitrepo nekogeruge ``` ### リポジトリ作成 ``` su - gitrepo mkdir testproject.git cd testproject.git git --bare init --shared ``` ## クライアント側作業 ### クライアント側で作成したプロジェクトをpushする ``` mkdir testproject cd testproject git init echo "git test" > readme git add . git commit -m "first commit" git remote add origin ssh://configのHostに記述した名前/home/gitrepo/testproject.git git push origin master ``` ### cloneして変更を加えてpushしてみる ``` git clone ssh://configのHostに記述した名前/home/gitrepo/testproject.git echo "oeeee" >> readme git add . git commit -m "change test" git push origin master ``` ``` git pull cat readme ``` |
|
| 855位 |
|
|||
|
11:42:03 |
(株式会社リクルートホールディングス MTL 所属) |
|
みなさんは、.gitignoreどのようなのを使われていますか? 僕のやつを公開するので、みなさんも教えて下さい! CocoaPodも使うので、Pods/*も入れています。(このフォルダ以下は `pod install`するとPodfileやPodfile.lockを参考に、installされ、自動的Fetchされ、つくられるので、共有する必要ありません) PodFileは.gitignoreしないとして、Podfile.lockも.gitignoreに入れないほうが良いと思います。(理由は以下追記) これが現状の.gitignoreです。 ```` # Mac .DS_Store # Xcode build/* *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 *.xcworkspace !default.xcworkspace xcuserdata profile *.moved-aside #CocoaPod Pods/* ```` # Podfile.lockについて Podfile.lockは ``` PODS: - AFNetworking (1.3.4) DEPENDENCIES: - AFNetworking (~> 1.3.4) SPEC CHECKSUMS: AFNetworking: cf8e418e16f0c9c7e5c3150d019a3c679d015018 ``` のように、PODSがライブラリが実際に使われているバージョン番号が固定で、DEPENDENCIESにはPodfileに書いてある指定の方法で、SPEC CHECKSUMSにはgitのハッシュが書かれています。 これを共有しておくことで、どの環境で`pod install`しても同じバージョンのライブラリがインストールされることになるので、.gitignoreしないほうが良いと思います 共同で開発している時に、ライブラリのバージョンが違うことによって、片方で動かないみたいなことがないようにしましょう! |
|
| 856位 |
|
|||
|
16:55:13 |
(Freelancer 所属) |
|
Xcodeでクラスファイルを新規作成した後、毎回同じようなコードを書いていて面倒だなと感じることがあります。たとえば、
- UIViewControllerを継承したクラスにカスタムセルのUITableViewを持たせる - NSObjectを継承したクラスをシングルトンにする といった実装はアプリ開発ではよくありますが、手順は大体毎回同じです。 こういった実装をファイルテンプレート化しておけば、クラスファイルの新規作成時にそのコードが生成されるので、何度も同じコードを書く手間が省けとても便利です。 以下に、NSObjectを継承したシングルトンクラスのファイルテンプレートを例にとり、作成手順を説明していきます。 ##1. デフォルトテンプレートをコピーしてくる デフォルトテンプレートはXcode.appのパッケージの中身をたどって行った先にあります。 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates この直下にある、Cocoa Touchフォルダをコピーして、 ~/Library/Developer/Xcode/Templates/File Templates にペーストします。(上記フォルダがなければ、作成してください) 上記フォルダが**カスタムファイルテンプレートの置き場**となります。 ##2. フォルダ名をリネームする コピーしてきたCocoa Touchというフォルダ名を"My Cocoa Touch"といった名前にリネームします。 ここで、試しにXcodeから新規ファイル作成画面を開いてみてください。テンプレートの選択肢に、コピーしてきてリネームしたフォルダ名が入っていることが確認できます。 ※以下、フォルダ名をMy Cocoa Touchにリネームしたものとして説明します。 ##3. TemplateInfo.plistを編集する My Cocoa Touch/Objective-C class.xctemplateフォルダ配下に、"TemplateInfo.plist"というファイルがあります。 このファイルをXcodeやテキストエディタ等で開き、"Options"という要素に次の項目を**追加**します。 ````TemplateInfo.plist <dict> <key>Default</key> <string>false</string> <key>Identifier</key> <string>Singleton</string> <key>Name</key> <string>Singleton</string> <key>RequiredOptions</key> <dict> <key>cocoaTouchSubclass</key> <array> <string>NSObject</string> </array> </dict> <key>Type</key> <string>checkbox</string> </dict> ```` Xcodeから新規ファイル作成 > My Cocoa Touch > Objective-Cを選択 > Nextと進み、SubclassにNSObjectを選ぶとSingletonというチェックボックスが有効になることが確認できます。 ##4. テンプレート用クラスファイルを作成する TemplateInfo.plistと同階層にある"NSObject"というフォルダを複製して、"NSObjectSingleton"というフォルダ名にリネームします。 このフォルダ名は、手順2でTemplateInfo.plistに追記した項目のうち、Identifierと、RequiredOptionsに関係しています。 RequiredOptionsに指定してあるNSObjectをクラス生成時に親クラスとして指定し、Singletonのチェックボックスにチェックを入れると、{cocoaTouchSubclass名}+{チェックボックス項目のIdentifier}、すなわち"NSObjectSingleton"という名前がついたフォルダにあるテンプレートファイルを参照するようになります。 ##5. テンプレート用クラスファイルを編集する 次に、NSObjectSingletonフォルダ配下にある、"\_\_\_FILEBASENAME\_\_\_.h"と、"\_\_\_FILEBASENAME\_\_\_.m"を編集します。 ````___FILEBASENAME___.h ___IMPORTHEADER_cocoaTouchSubclass___ @interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___ + (id)sharedInstance; @end ```` ````___FILEBASENAME___.m #import "___FILEBASENAME___.h" @implementation ___FILEBASENAMEASIDENTIFIER___ static id instance = nil; + (id)sharedInstance { @synchronized(self) { if (!instance) { instance = [[self alloc] init]; } } return instance; } @end ```` 以上でNSObjectSingletonテンプレートは完成です。 |
|
| 857位 |
|
|||
|
23:21:43 |
|
|
最近下のように書いてる。変遷としては、若干サーバーサイドJavaScriptを意識しつつ徐々に離れていった感じ。 1. 即時関数を2重にして、トップには何も書かない 2. 外側の即時関数の中には、`'use strict'`を書く - 非strictモードのコードが含まれているとどっちのモードになるかわからないため。負の遺産が含まれ(ry - だいたいjQuery使うので、外側の即時関数の書き方はjQuery boilerplateのを参考にしている(jQuery使わない場合は、第二引数を抜かす) 3. 内側の即時関数に実際の宣言を書く - app.jsか何か設定用のスクリプトでアプリケーション用名前空間としてグローバルオブジェクトを宣言しておく(`window.App = {};`など) - 即時関数内で宣言されたプロパティやメソッド、関数などを返り値にして、グローバルオブジェクトに展開する ```functions.js /** * functions.js - Utility functions. */ ;(function(global, $, undefined) { 'use strict'; var App = global.App; // 名前空間は宣言済み(app.jsのような設定用のスクリプトか何かで宣言する) App.functions = (function() { // プライベートな関数や変数が必要な場合は、返り値(module)に含まない様に、この辺に書く // […] var module = { /** * compact array by removing 0, false, null, '' and undefined. * * @param {Array} array An source array. * @return {Array} The compacted array. */ compact: function(array) { var arr = $.isArray(array) ? array : [array]; return $.grep(arr, function(val, i) { return !!val; }); } }; return module; // この例ではオブジェクトリテラルだが、コンストラクタを使っている場合は、コンストラクタを返すようにする。 }()); }(this, jQuery)); ``` ```app.js /*! * app.js - Application root. */ ;(function(global, undefined) { 'use strict'; global.App = { DEFAULT_KEYWORD: '@iphone_dev_jp' }; }(this)); ``` ```bootstrap.js /*! * bootstrap.js - Setup and Load application. */ ;(function(global, $, undefined) { 'use strict'; var App = global.App; // Mixin helper functions to View. $.extend(App.View.prototype, App.viewHelpers); $(function() { var view = App.View.getInstance(); view.initialize({collection: new App.Tweets()}); view.registerEvents().search(App.DEFAULT_KEYWORD); }); }(this, jQuery)); ``` ##参考 - https://github.com/roothybrid7/twtestapp - [demo](http://roothybrid7.github.com/twtestapp/) |
|
| 858位 |
|
|||
|
17:01:38 |
|
|
GitHubでフォークした後にフォーク元の差分を取り込む時の手順です。
ローカル環境で該当プロジェクトのディレクトリに移行して、フォーク元のリポジトリを登録します。 ``` $ git remote add upstream git://github.com/octocat/Spoon-Knife.git ``` `git://github.com/octocat/Spoon-Knife.git`はフォーク元のURLになります。 フォーク元からの内容を取得して、自分のmasterにマージします。 ``` $ git fetch upstream $ git merge upstream/master ``` |
|
| 859位 |
|
|||
|
02:29:51 |
|
|
## デモ
 早すぎます。こんなタイピング早くないです。 後でこの調整方法も載せます。 ## ttyrecのインストール * Mac ```bash $ brew install ttyrec ``` * Debian,Ubuntu ```bash $ sudo aptitude install ttyrec ``` ## ttygifのビルド * Debian/Ubuntu ```bash $ sudo aptitude install imagemagick ttyrec gcc $ git clone https://github.com/icholy/ttygif.git $ cd ttygif $ make ``` * Mac ```bash $ brew install imagemagick $ git clone https://github.com/icholy/ttygif.git $ cd ttygif $ make ``` ## 使ってみる 1. `ttyrec`による録画 2. `ttygif`による画像化 3. `concat`によるgif化 の3段構成 録画前にttygifディレクトリ内にcdしといてください。 ```bash $ ttyrec rec_name ``` rec_nameを省略した場合ttyrecordでファイルが作成されます。 この後、録画したい部分の動作をやります。 `Ctrl-D`押下か`exit`入力まで撮り続けます。 ```bash $ ./ttygif rec_name ``` ttyrecにより作成されたファイルからコマ送り画像を作成します。 0001_0538のようなファイルがたくさんできますが順番_遅延時間(ミリ秒)になっています。 打ち間違えたり、考えていて手が止まっていたりした部分の要らないファイルを消したり、遅延時間の部分を調整してgifの再生速度を制御できます。 遅延時間は通常の入力(アルファベット1文字あたり)を50-150msとか、ちょっと止まっておきたい時は1000msを設定しておけばいいかと思います。 Macで最後に`-f`オプションを付けるとターミナルの外観(ウィンドウ枠)が表示されます。まぁ要らないです。 最後にコマ送り画像をgifに変換します。 * Debian,Ubuntu ```bash $ ./concat.sh output.gif ``` * Mac ```bash $ ./concat_osx.sh output.gif ``` outputの部分にファイル名を指定して終了です。 最初に紹介したデモのようなgif画像が出来上がります。 ## 参考 * [ttygif](https://github.com/icholy/ttygif) - 本家です。 ## 追記 LinuxやMacOSなら[asciinema](https://asciinema.org/)というのもあって、こっちはgifにしたり細かい調整はできませんが、より手軽にターミナルを動画として提供できます |
|
| 860位 |
|
|||
|
01:30:17 |
|
|
下のgif画像みたいな挙動をどうやって実装するかという話です <img src="http://gifzo.net/BCUJT7mT45v.gif" width=200 /> [動画(youtube)](https://www.youtube.com/watch?v=YK_jr6Rf7p0&feature=youtu.be) ネタ元 : [Android: show/hide a view using an animation(stackoverflow)](http://stackoverflow.com/questions/9252740/android-show-hide-a-view-using-an-animation) ポイントは * View.StartAnimationを使う * View.setVisibilityでビューの表示/非表示を切り替える サンプルを書いてみた ```activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_marginTop="100dp" > <!-- このTextViewをピヨっとさせたい --> <TextView android:id="@+id/childview" android:layout_width="match_parent" android:layout_height="50dp" android:orientation="vertical" android:text="child view here!!" android:fontFamily= "sans-serif-light" android:textSize="30sp" android:textColor="#e2f2f8" android:gravity="center" android:visibility="gone" android:background="#17abe1"/> </LinearLayout> ``` 最小限の実装がこんな感じになる ```MainActivity.java public class MainActivity extends ActionBarActivity { View containerView; View childView; Animation inAnimation; Animation outAnimation; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); containerView = findViewById(R.id.container); childView = findViewById(R.id.childview); inAnimation = (Animation) AnimationUtils.loadAnimation(this, R.anim.in_animation); outAnimation= (Animation) AnimationUtils.loadAnimation(this, R.anim.out_animation); containerView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // ビューが表示されてるか判定 if(childView.getVisibility() == View.GONE){ // アニメーションしながらViewを表示 childView.startAnimation(inAnimation); childView.setVisibility(View.VISIBLE); } else{ // アニメーションしながらViewを隠す childView.startAnimation(outAnimation); childView.setVisibility(View.GONE); } } }); } } ``` アニメーションの定義はネタ元にも載ってるけどY座標を上下させるように記述をするとピヨっとした感じになる ```in_animation.xml <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:fillAfter="false" android:fromXDelta="0" android:fromYDelta="-20%" android:toXDelta="0" android:toYDelta="0" /> ``` outのアニメーションもfromとtoを入れ替えた感じにすれば問題ないです あとたぶん思った感じにならないのでアニメーションさせるViewに対してMarginは取らないほうがいいです こういうコード毎回書くの面倒なので,ネタ元ではRelativeLayoutを継承してAnimatingRelativeLayoutというクラスにまとめています [Android: show/hide a view using an animation(stackoverflow)](http://stackoverflow.com/questions/9252740/android-show-hide-a-view-using-an-animation) |
|
| 861位 |
|
|||
|
23:58:37 |
(bexide 所属) |
|
# ディレクトリ構成 apt-get等のパッケージ管理ソフトで入れた場合にはjenkinsのホームディレクトリは`/var/lib/jenkins`になります。そのパスはjenkinsの実行時に使用出来、`$JENKINS_HOME`の変数名で閲覧できる。 ジョブの設定ファイルは`$JENKINS_HOME/jobs/*/config.xml`に記述される。このファイルを別サーバーのjenkinsに設置するだけで移行する事ができる(らしい) ジョブでgitなどのバージョン管理ツールからソースをcloneすると`$JENKINS_HOME/workspace/*`に展開される。jenkinsで設定したビルド手順はworkspace下でcloneされたディレクトリ内で実行されている。 # jenkinsユーザーになる。 最初のビルドの設定時、失敗する事が多々あると思われる。 問題把握の為にはjenkinsユーザーになり、実際にビルド手順を手打ちした方が手っ取り早い。 sudo vi /etc/passwd ``` jenkins:x:106:65534::/var/lib/jenkins:/bin/false ``` `/bin/false`を`/bin/bash`に変更する sudo su jenkins cd cd workspace/{ジョブ名} でビルド手順を実行してみる。 作業終了後には`/bin/false`に戻す事を忘れずに! # プラグンの導入 jenkinsはデフォルトではgitに対応していないので`Git Plugin`の導入をする必要がある。 `Git Plugin`では導入をしたあと、`jenkinsの管理`➡`システムの設定`でgitのユーザー名、メールアドレスの設定が必要。 rubyプロジェクトのビルド、rubyスクリプトの定期実行でのrubyのバージョン管理は`rbenv plugin`が簡単便利。 # ディスク容量 jenkinsを定期実行してしばらくすると失敗するようになります(というか、なりました)。原因はビルド後のログが貯まってディスク容量がいっぱいになっている事です。 対策としてはジョブ毎の設定で`古いビルドの破棄`をしておく。 これをする事によってビルド後のログを破棄してくれるようになります。 ビルドの保存日数、ビルドの保存最大数が設定出来ます。 保存日数`30` 保存最大数`100` 程度に設定しておけば大抵は大丈夫かと思われます。 |
|
| 862位 |
|
|||
|
17:24:58 |
|
|
# 書いてる人
プログラミング学習サービスやら、ペットサロン予約サービス、風俗検索サービスなど色々とやっている「かずきち」です。 ■運営サービス一部 http://crazy-wp.com/ http://webukatu.com/ 新宿のホストから不動産・保険の営業を経て、HTMLって何?という状態から3ヶ月独学でプログラミングやデザインを学び、IT業界で1年間実務経験を積んで年収は1本超え。現在は起業家としてサービス運営やら不動産運営をしています。 Qiita内にそれ系の記事も書いてます。 <a href="http://qiita.com/kazukichi/items/7379b75fba2f90d3cf45" target="_blank">エンジニアで稼ぐために大切な13のコト</a> <a href="http://qiita.com/kazukichi/items/aeba286c2a750081e5c0" target="_blank">WEBサービスで起業したい人に読んで欲しい18のコト</a> #JSPってなに? JSP(Java Server Pages)と言って、PHPなどと同じようにタグを使ってhtml内にそのまま記述出来るもの。 同じくサーブレットというものがあるが、それをもっと使いやすくした感じ。 Javaのお手軽版的な。 #サーブレットって? 簡単に言うと、サーバーで動的なコンテンツを生成するプログラムを作るための技術。 Javaサーバーで動く小さなプログラムで、JSPの原型。 JSPはJavaサーバーがJSPのコードを読み込み、それをサーブレットのソースコードに変換。 HTMLのタグなど、すべてprintlnで書きだすように変換される。 そして、生成されたサーブレットのソースコードをコンパイルし、サーブレットを生成してそれを呼び出す。 ``` JSPファイル ⇒ サーブレットファイル ⇒ クラスファイル ⇒ 実行 ``` ※サーブレットはバージョンがあり、サーバーによって対応していなかったりするので注意 #サーブレット ##サーブレットとは? サーブレットは、Javaを使ってサーバサイドプログラムを作るための技術。 WEBサービスなどをJavaで作る場合には、必須のもの。 サーブレットや同じ感じのJSPという技術を使うには、JavaSEではなくJavaEEの技術が必要。 (JavaEEをPCにインストールする必要がある) サーブレットはブラウザからリクエストがあるとアプリケーション・サーバーがサーブレットクラスのインスタンスを生成する。 毎回リクエストの度にインスタンスを生成するのはサーバーに負荷がかかるため、一回目作ったインスタンスをリクエスト応答後も破棄せずに次のリクエストでも再利用する。 (サーバーが終了するなどの時にインスタンスがサーバーによって破棄される) ##サーブレットの書き方 ```java:Sample.java //①お決まりのインポート文(Eclipseでサーブレットクラスを作成すると自動で書いてくれる) import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; //②サーブレットクラスを作るにはサーブレットクラスの元になるHttpServletクラスを継承する必要がある。 public class Sample extends HttpServlet{ //③サーブレットクラスがGETで呼ばれた場合のdoGetメソッドをオーバーライドする。書き方は基本このお決まりの書き方になる。 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ //処理を書く } //④サーブレットクラスがPOSTで呼ばれた場合のdoPostメソッドをオーバーライドする。書き方は基本このお決まりの書き方になる。 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ //処理を書く } } ``` ブラウザからリクエストを受けるとアプリケーションサーバーがサーブレットクラスのdoGet()を呼び出す。 その時に引数として、ブラウザから届いたリクエスト情報の入った「HttpServletRequest」とサーバーから送るレスポンスに関係する情報や機能をもつ「HttpServletResponse」を渡している。 HttpServletRequestインスタンスに格納された情報を元に処理を行い、HttpServletResponseインスタンスを使ってブラウザへ結果を返している。 ##サーブレットからHTMLを出力する ```java:Sample.java protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.setContentType("text/html; charset=UTF-8"); //①ブラウザへ渡す情報の文字コードを指定 //②htmlを出力 PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); out.println("</head>"); out.println("<body>"); out.println("<p>Hellooooo!</p>"); out.println("</body>"); out.println("</html>"); } ``` ##サーブレットクラスのコンパイル サーブレットクラスのソースファイル → コンパイル → サーブレットクラスのクラスファイルが生成される → ブラウザがリクエスト → サーブレットクラスのクラスファイルが実行される → サーブレットクラスのクラスファイルからインスタンス化 → サーブレットクラスのインスタンスが実行される ##サーブレットクラスとURL サーブレットを呼び出す際のURLは以下の仕組みになっている。 ```txt: http://サーバー名/アプリケーション名/URLパターン ``` URLパターンというのはサーブレットクラスでつける「アダ名」のようなもの。 サーブレットクラスでURLパターンを設定しておくことで、それをURLとして呼び出すことができる。 このURLをhtmlのaタグのhref属性やformのaction属性に設定すれば、サーブレットへ飛ばせる。 ###URLパターンの設定 URLパターンをサーブレットクラスで設定するには、「@WebServletアノテーション」を使う。 ```servlet:サーブレットファイル:sample.java import javax.servlet.annotation.WebServlet; //①アノテーションを使うために読み込んどく ・・・省略 @WebServlet("/sample") //②URLパターンを「sample」として設定 public class Sample extends ・・・省略 ``` URLパターンをスラッシュで区切って階層を増やすことも出来る。 ※URLパターンの設定方法には、アノテーションの他に`web.xml`という設定ファイルを使用する方法がある ※Eclipseでは、サーブレットクラスを作ると自動的にアノテーションに「/クラス名」と設定される。 ##サーブレットの注意 サーブレットクラスのコードを修正しても、すぐには反映されない。 Eclipseでは、修正後にしばらく(数秒から数十秒ほど)待つとコンソールビューに「再ロードが完了しました」と出る。そうすれb反映されてる。 もしくは、サーバーを再起動させる。 #JSP ##JSPファイルから作成されるサーブレットクラスの場所 Eclipseを使っている場合、ワークスペースで指定した場所内の以下の場所にある ``` 〜ワークスペース/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/work/Catalina/localhost/プロジェクト名/org/apache/jsp ``` jspを作成して実行した場合、上記の場所にjavaファイルとclassファイルが作成される。 たまにjspファイルを修正されても反映されない場合は、このディレクトリにあるjavaファイルやclassファイルが古いままの場合があるので、削除して実行しなおす。 ##JSPファイルのURL jspファイルはhtmlファイルと同じ扱いで、WebContentフォルダの中に配置し、jspファイル名がURLになる。 実際のURLは下記の通り。 ``` http://サーバ名/アプリケーション名/WebContentからのパス ``` ##JSPの注意 Eclipseでjspを書く場合、文法エラーがある場合はコードの左側に☓印とコード内に波線が出て表示してくれるが、 エラーがない場合でもこれが出てしまう場合がある。 なので、そういう場合にはエラーが出たら一回切り取りして貼り付けし、上書き保存をすると直る。 直らなければ本当にエラーがあるってこと。 #JSP&サーブレットを使う ##JSPからサーブレットへの値の受け渡し方法 ブラウザからGETやPOSTで送られたリクエストパラメータはHttpServletRequestインスタンスに格納され、送信先のJSPやサーブレットへ送られる。 ```jsp:doPostやdoGetメソッド内で書く //リクエストパラメータの文字コードを指定 request.setCharacterEncoding("UTF-8"); //リクエストパラメータの取得 String name = request.getParameter("name"); //getParameter("属性名"); String gender = request.getParameter("gender"); ``` ##リクエストパラメータをJSPファイルで受け取る ```jsp:jspファイル内で書く <% //リクエストパラメータの文字コードを指定 request.setCharacterEncoding("UTF-8"); //リクエストパラメータの取得 String name = request.getParameter("name"); //getParameter("属性名"); String gender = request.getParameter("gender"); %> ``` requestなどは暗黙オブジェクトのため、宣言せずに使える。 ##MVCモデル ``` コントローラ ・・・ ブラウザからのリクエストを受け取る役割(サーブレット) ビュー ・・・ レスポンスとして返すファイル(JSPファイル) モデル ・・・ 実際の処理を行う役割(普通のjava) ``` ##フォワードとリダイレクト ###フォワード サーバー側で他のファイルの処理へ移すことができる。 ブラウザ側のURLが変わらずに画面など変えられる。 ####サーブレットからJSPファイルへフォワードさせる `javax.servlet.RequestDispatcher`をインポートした上で下記のように書く ``` RequestDispatcher dispatcher = request.getRequestDispatcher("フォワード先のパス"); dispatcher.forward(request,response); ``` フォワード先のパスはJSPファイルなら`/WebContentからのパス` サーブレットなら`/URLパターン`となる。 ####JSPファイル配置とリクエスト JSPファイルを以下に配置するとブラウザからは直接リクエストできないようになる。 JSPファイルは基本サーブレットが呼び出してブラウザへ返すものなので、以下に配置する。 ```java: /WebContent |___ /WEB-INF |___ /jsp |___この中にフォワードするjspを配置しとく ``` ###リダイレクト サーバー側で一旦「こっちのファイルを呼んで!」とレスポンスを返し、 受け取ったブラウザ側は再度そっちのファイルへリクエストを送るもの。 ####リダイレクト方法 ``` response.sendRedirect("リダイレクト先のURL"); ``` リダイレクト先は同じサーバー内の場合以下のようにも指定できる ``` サーブレットの場合 → /アプリケーション名/URLパターン JSPファイルの場合 → /アプリケーション名/WebContentからのパス ``` ##スコープ(画面間でのデータの引き継ぎ方法) Java内で値を持ち回すにはJavaBeansを使い、インスタンスを持ち回す。 インスタンスを保持できる範囲・期間によって、4種類のスコープがある。 ``` ・ページスコープ ・リクエストスコープ ・セッションスコープ ・アプリケーションスコープ ``` ###ページスコープ 1画面内でしか使いまわすことはできない。 基本、意識する必要はないものなので省略。 ###リクエストスコープ クライアントからリクエストを投げて、サーバーがレスポンスを返すまでの間だけ保持される。 なので、リダイレクトすると消える。が、フォワードの場合は情報は消えない。 ```java:サーブレット内でのリクエストスコープへのインスタンス保存と取り出し protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ //リクエストスコープへ保存するインスタンス生成 Human human = new Human("かずきち", 29); //リクエストスコープにインスタンス保存 request.setAttribute("human",human); //第一引数は「属性名」、第二引数はインスタンス //リクエストスコープからインスタンス取得 human h = (Human) request.getAttribute("human"); //取得したインスタンスはObject型になっているので元の型へキャスト(型変換)する必要がある } ``` ```jsp:JSP内でのリクエストスコープからインスタンス取り出し <%@ page import = "bean.Human" %> //取得したいインスタンスのクラスをインポートする <% //リクエストスコープからインスタンスを取得する Human h = (Human) request.getAttribute("human"); %> <%= h.getName() %>さんの年齢は<%= h.getAge() %>歳です。 //インスタンスのプロパティにアクセスし、値を取り出す ``` ###セッションスコープ いわゆる、「セッション」と呼ばれるもの。 セッションスコープに保存した値は、インスタンスを削除するかブラウザを閉じるまで利用できる。(なので、リダイレクト先でも値を引き継いで利用できる) Cookieという仕組みを使って値を保存しておく。セッションはCookieに関しての説明は面倒なので省略します。 セッションス コープを使うには、`javax.servlet.ttp.HttpSession`型のインスタンスを使う。 ```java:サーブレットでセッションスコープを利用する例 Human human = new Human(); human.setName("かずきち"); human.setAge(29); //HttpSessionインスタンスの取得(javax.servlet.ttp.HttpSessionをインポートしておく必要がある) HttpSession session = request.getSession(); //セッションスコープにインスタンスを保存 session.setAttribute("human", human); //セッションスコープからインスタンスを取得 Human h = (Human) session.getAttribute("human"); //セッションスコープからインスタンスを削除 session.removeAttribute("human"); //セッションスコープ自体を削除したい場合 session.invalidate(); //スコープ自体が削除され、保存していたすべてのインスタンスが破棄される。ショッピングサイトなどで、ユーザーがログアウトを行った時などに利用する ``` ###アプリケーションスコープ WEBアプリケーションが終了するまでずっと値が保持される。 なので、サーバーを再起動するまでずっと保持しておけるってこと。 データベースに保存するまでもない情報などはアプリケーションスコープで保存しておくといい。 ```java:サーブレットでアプリケーションを利用する例 Human human = new Human("かずきち",29); //ServletContextインスタンスの取得 ServletContext app = this.getServletContext(); //javax.servlet.http.ServletContextをインポートする必要がある //アプリケーションスコープにインスタンスを保存 app.setAttriute("human", human); //アプリケーションスコープからインスタンスを取得 Human h = (Human) app.getAttribute("human"); //アプリケーションスコープからインスタンスを削除 app.removeAttribute("human"); ``` ```jsp:jspファイルでアプリケーションスコープを利用する <%@ page import = "bean.Human" %> <% //アプリケーションスコープからインスタンスを取得 Human h = (Human) app.getAttribute("human"); %> <%= h.getName() %>さんの年齢は<%= getAge() %>際です ``` ##フィルタ(同じ処理はまとめて実行!) サーブレット内などで毎回書く`request.setCharacterEncoding("UTF-8");`やログインしていないと見れないページなどで毎回ログインしているかをチェックする処理を書く際など、フィルタという仕組みを使うことで毎回書く必要がなくなる。 ```java:フィルタのサンプル @WebFilter("/*") //① public class FilterSample implements Filter { public void init(FilterConfig fConfig) throws ServletException{ } //フィルタがインスタンス化された直後に行いたい処理を書く場所 //設定したサーブレットクラスが呼ばれた(リクエストされた)時に行いたい処理を書く場所 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{ request.setCharacterEncoding("UTF-8"); chain.doFilter(request, response); } public void destroy(){ } //フィルタのインスタンスが破棄される直前に行いたい処理を書く場所 } ``` ① フィルタには@WebFilterアノテーションをつける。アノテーションをつけることで、フィルタをどこまでの範囲に適用するかを指定する。 ```java:@WebFilterの書き方 @WebFilter("/フィルタを設定するサーブレットクラスのURLパターン") ※javax.servlet.annotation.WebFilterをインポートする必要がある ``` ```java:@WebFilterの例 @WebFilter("/Sample") //Sampleのサーブレットクラスに設定 @WebFilter("/Sample/*") //Sample以下のサーブレットクラスに設定 @WebFilter("/*") //全てのサーブレットクラスに設定 ``` ##jspファイルを分割してインクルードする jspファイルでもヘッダーやフッターなど共通な部分がある。 そういった時に毎回jspファイルにヘッダーフッターを記述するのは面倒だし、ヘッダー部分に修正があった場合、全てのjspファイルに対して修正をしないといけないので面倒。 なので、ヘッダーやフッターなどを共通化してそれぞれjspファイルにし、そのjspファイルを読み込むようにする。 ```jsp:jspファイル内で他のjspを読み込む(サーブレット内でも使える) <% RequestDispatcher dpc = request.getRequestDispatcher("インクルード先"); dpc.include(request, response); %> ``` もしくは、jspなら下記のようにアクションタグを使って読み込むこともできる ```jsp: <jsp:include page="インクルード先" /> ``` インクルード先の指定方法はフォワードの時と同じ。 アクションタグには最初から用意されているものがある。それを「標準アクションタグ」という。 ##標準アクションタグ 主に以下のようなものがある ``` <jsp:useBean> ・・・ スコープからJavaBeansインスタンスを取得。取得できない場合は、インスタンスを新規作成してスコープに保存する。 <jsp:setProperty> ・・・ JavaBeanのプロパティの値を設定 <jsp:getProperty> ・・・ JavaBeanのプロパティの値を取得 <jsp:include> ・・・ インクルードできる <jsp:forward> ・・・ フォワードできる ``` htmlのタグのように書けるが、大文字小文字は区別されるので注意。 ##動的インクルードと静的インクルード インクルードには2種類ある。 動的インクルードは、ファイルを実行中にインクルードで他のjspファイルなど呼び出し、そのファイルの処理を実行し、結果を元のファイルに戻すというもの。 静的インクルードは、ファイルを実行中にインクルードで他のjspファイルなどの処理をそのまま自身のファイルへ取り込み、自身のファイルとして実行するもの。 動的インクルードだと別のファイルとして実行しているため、別ファイルで定義した変数や生成したインスタンス、インポートしたクラスやタグライブラリなどなどは自身のファイルで使えない。 静的インクルードは、一つのファイルとして実行されるので、別ファイルで定義した変数や生成したインスタンス、インポートしたクラスやタグライブラリなどは自身のファイル内で使える。(が、変数名などバッティングしないように気をつける必要がある) ###静的インクルードの方法 includeディレクティブを使う ```jsp:jspファイル内で <%@ include file="インクルード先" @> ``` ※インクルード先は/WebContentからのパスを指定 ※サーブレットクラスはインクルードできないので注意 ※インクルード先のファイルを更新したら、インクルード元のファイルも更新しないと実行結果に反映されないので注意 #EL式 EL式を使うとスコープに保存された値を取得するのが楽になる。 インポートとスコープからの取得が不要に。 ```jsp:普通に取得する場合 <%@ page import="model.Sample" @> <@ Sample sp = *Smaple) session.getATtribute("sample"); @> <@= sp.getName() @> ``` ```jsp:EL式を使った場合 ${ sp.name } ${ sp[2].name } //リストから取得もできる ``` ##EL式の書き方 ```jsp: //スコープに保存されているインスタンスを取得する ${属性名} //スコープに保存されているインスタンスのプロパティを取得する ${属性名.プロパティ} //※指定したプロパティのgetterが自動で実行される。 ``` ※EL式は指定した属性名のインスタンスを下記の順序で探す ``` ページスコープ → リクエストスコープ → セッションスコープ → アプリケーションスコープ ``` ※何も見つからなくても例外はスローされず、ただそこには表示されないだけ。 もし、セッションスコープに保存されているインスタンスを利用したいなどスコープを指定する場合は下記の通り ```jsp:セッションスコープから取得したい場合 ${ sessionScope.msg } ``` EL式は後述のスクリプトレットやスクリプト式内で使うことが出来ない。なので、if文やfor文の中に使いたい場合は後述のJSTLを使う必要がある。 #JSTL カスタムタグと言われるもので、開発者が独自に開発したアクションタグのことをいう。 jspでは`<jsp:forward>`のように標準で用意されているアクションタグのほか、独自で作成もできる。 独自作成したアクションタグは`タグライブラリ`にまとめて、他の人に配布できる。(タグのプラグイン的な) JSTLは5つのタグライブラリで構成されている。 DB操作や条件分岐、繰り返しなど色々なタグがある。 ##JSTLの利用方法 JSTLを使うにはネットからJSTLのJARファイルをダウンロードし、`WEB-INF/lib`に配置する。 JSPでタグライブラリを使うには、下記のようにtaglibディレクティブで使用するタグライブラリを指定する。 ```jsp:JSP内でのタグライブラリ指定方法 <%@ tablib prefix="接頭辞" url="使用するタグライブラリのURI" %> ``` ##Coreタグライブラリ 条件分岐などできるライブラリ。使い方は以下の通り。 ```jsp:例 <c:out value="変数名"> //変数の値を出力。エスケープ処理も行ってくれる。 <c:out value="${sample.name}"> //EL式も使える //分岐1 <c:if test="条件式"> trueの場合の処理 </c:if> //分岐1(EL式使った場合) <c:if test="${sample.age == 20}"> //trueの場合の処理 </c:if> //分岐2(if-elseにしたい場合にはこっちを使う) <c:choose> <c:when test="条件式"> //trueの場合の処理 </c:when> <c:otherwise> //falseの場合の処理 </c:otherwise> </c:choose> //繰り返し <c:forEach var="i" bigin="0" end="9" step="1" > //繰り返したい処理 </c:forEach> //繰り返し(拡張for文) <c:forEach var="変数名" items="配列など"> //繰り返したい処理。varに指定した変数名を使って色々できる。 </c:forEach> ``` #JSPのタグの種類 主なタグは5種類 ``` アクションタグ ・・・ <jsp: 内容 /> ``` ``` 1.アクションタグ ・・・ 定型的な処理をタグ形式で記述 2.ディレクティブタグ ・・・ ページ単位での処理方法を指定する。htmlでいうmetaタグ的な。 3.宣言部 ・・・ 変数やメソッドの定義のみ書くもの 4.スクリプトレット ・・・ ページの処理方法を記述。Javaコードなんでも書いてOK 5.式 ・・・System.out.println(変数)と同じ機能。println書くならこっち使った方が楽。 ``` ##ディレクティブ ```text:基本構文 <%@ ディレクティブ名 属性名="属性値1" 属性名2="属性値2" … %> ``` ※属性値は必ずダブルクウォートで囲む ```text:主なディレクティブ @page ・・・ 基本的な処理方法を決める @include ・・・ 外部ファイルをインクルードする @taglib ・・・ ページ内で使用するタグライブラリを宣言 ``` ```text:@pageディレクティブの主な属性 contentType ・・・ ページの出力コンテンツタイプ(文字コードなど)を設定。基本は「text/html」でいい。 画像や動画を出力する場合は「image/jpeg」「video/mpeg」などを値に指定。 pageEncoding ・・・ jspファイルの文字コード import ・・・ インポートするパッケージやクラスを指定。「java.util.Date」などと値を指定。 errorPage ・・・ 例外発生時に表示するエラーページのパス ``` ##スクリプトレット ```text:基本構文 <% 内容 %> ``` ```jsp:例 <% for(int i= 0; i<5; i++){ out.prinln("テストです。"); } %> ``` ※暗黙オブジェクト(インスタンス化とかしないでいきなり使えるやつ)も使える ※ここで宣言した変数はページ処理終わるたびに破棄される。 ```jsp:例 <% for(int i= 0; i<5; i++){ %> テストです。<br> <% } %> ``` ※phpと同じように挟んでも書ける ##宣言部 ```text:基本構文 <%! 内容 %> ``` ```jsp:例 <%! public String repeat(String msg, int n){ String rst = null; for(int i=0; i<n; i++){ rst+= msg; } return rst; } %> ``` ※スクリプトレットのような処理の記述はできない。メソッド定義や変数定義のみ。 ※暗黙オブジェクトはこの中では使えない ※ここで宣言した変数はApacheなどのWebサーバーが起動してる間ずっと残る。 ※ブラウザがリロードしても、ほかのPCから見ても、変数の中身は共有。 (なので、読み取り専用の変数だけを定義した方がいい) ##式 ```text:基本構文 <%=変数 %> ``` ```jsp:例 <%=result %> ``` ※変数を複数記述したり、計算式をそのまま記述はできない。メソッドはok。 #実際の使い方の流れ ##1.ファイルを作成する JSPを使うにはファイル名の拡張子をjspにする。 ``` ファイル名.jsp ``` ※「httpd」「htdocs」的なフォルダ内に置くこと ##2.文字コードを設定する(基本utf8で) ```jsp: <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> ``` ※jspファイルの中でさらにincludeして他のjspファイル(子ファイル)を読み込む場合、子ファイルにはcharset属性はなしにする。 ##3.パッケージを取り込む ```jsp: <%@ page import="クラスの指定" %> ``` ##4.メソッドや変数を宣言する `<%!` と `%>` の間に、メソッドの定義や変数の宣言を記述すると他の`<% %>`内でも使いまわせる。 ``` <%! 変数宣言やメソッド定義 %> ``` ```jsp:例 <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>テストタイトル</title> </head> <body> <% for(int i = 0; i<10 ; i++) out.println("テストテキスト"); %> </body> </html> ``` #ページ間で値を受け渡しする ##aタグやGET経由で渡す クエリ情報には「?」「&」「%」や空白、マルチバイト文字(日本語)はそのままだと渡せない。 だから、渡す前にURLエンコードして渡せる様にする必要がある。 (フォーム経由の場合は自動でエンコードされるので不要) ※クエリ情報での受け渡しはIEの場合は2083文字まで。 ###送る側処理 ```html: <a href=”link2.jsp?keyword=<%=URLEncoder.encode(“クエリ情報(&&%)”,”UTF-8”)%>情報を送る</a> ``` ※「?キー値=値」で送る ※import=”java.net.*”をしておく ※受け取る側がgetParameterした時に自動的にデコードされる 日本語が文字化けするなら、`%CATALINA_HOME%/conf`フォルダ内`server.xml`の72行目あたりを以下のように編集する ``` <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" useBodyEncodingForURI="true" /> ``` ###受け取る側処理 JSPではGET、POSTを気にすることなく扱える設計になっている。 GETで送られてきたものでも、POSTで送られてきたものでも、下記のように`getParameter("name属性名")` 受け取れる。 ```jsp: request.getParameter(“keyword”); ``` ※キー値で取得できる。 ##色々なフォームからPOSTで値を受け取る ```java:例 String inpt = request.getParameter("input"); //name属性がinput String chk = request.getParameter("check"); //チェックボックス(name属性がcheckの場合) String rd = request.getParameter("radio"); //ラジオボタン(name属性がradioの場合) //複数選択可能なセレクトボックス(name属性がselectの場合) String[] slt = request.getParameterValues("select"); String rst = null; if (slt != null){ for(String rst : slt){ out.println(rst); } } ``` ※値を受け取るときには必ず文字エンコードすること ```java: request.setCharacterEncoding("UTF-8"); //クライアントから送信されたリクエスト情報の文字コードを宣言 ``` ※getParameterする前に必ず上の1行を記述すること。 ※「JISAutoDetect」という値にすると自動で文字コードを判別してくれるが、絶対正しい訳じゃないので使わない #リダイレクトさせる ```jsp: <% response.sendRedirect("http://www.google.com/"); %> ``` #エスケープ処理 フォームなどから受け取った値は何が入っているかわからない。<script>タグなどを入れられてるとブラウザで動いてしまって変な処理をされたりとセキュリティ的によくないので、そういう特殊な文字を変換(エスケープ)して影響のない文字にする。 JSPでは `getEscapedString` というメソッドが用意されているので、そのメソッドを定義して中にエスケープしたい文字を追加する。 ```jsp //エスケープ処理メソッド <%!public String getEscapedString(String s){ String str = s; str = str.replace("&","&"); str = str.replace("<","<"); str = str.replace(">",">"); str = str.replace("\"","""); return str; }%> //実際にエスケープする際 <% String input = request.getParameter("input"); String str = getEscapedString(input); %> //html内で以下のような記述をすればエスケープされた文字が表示される <%=str %> ``` #リクエストパスを取得する 色々な種類がある。 getRequestURIで取得したリクエストパスを物理パスに変換してファイル読み書きしたりする。 #リダイレクトする ```jsp response.sendRedirect(“http://www.yahoo.co.jp/”); ``` #Cookieを使う ##Cookieの流れ ``` (初回)ブラウザがサーバへアクセス ➡ サーバがCookie生成して返す ➡ (次回以降)ブラウザがサーバへアクセス時に持っているCookie送信 ➡ サーバがもらったCookieを読み込み ``` ##Cookieの使い方 ``` //クッキーのインスタンス生成 Cookie 変数 = new Cookie( 名前 , 値 ); //有効期限設定 クッキーインスタンス名.setMaxAge( 秒数 ); //クッキー保存 response.addCookie( クッキーインスタンス名 ); //クッキー読み込み Cookie[] 変数 = request.getCookies(); ``` ※JSPでは指定したクッキーだけを読み込むということができないので、サイトで保存されているすべてのクッキーをCookie配列として取り出す ※有効期限を設定しない場合、ブラウザ閉じた段階でクッキーが破棄される ``` //クッキーの名前を取得 String 変数 = クッキーインスタンス名.getName(); //クッキーの値(中身)を取得 String 変数 = クッキーインスタンス名.getValue(); ``` ##URLエンコード Cookieには日本語は保存できないので、URLエンコードする必要がある。(java.net.*をimportして使う) ``` //テキストをURLエンコードする String 変数 = URLEncoder.encode( 値 , エンコード名 ); //テキストをURLデコードする String 変数 = URLDecoder.decode( 値 , エンコード名 ); ``` ##テキストエンコード方式を設定 日本語を表示する際など文字化けするので、文頭に追記しておく ```jsp request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); ``` 例)フォームに入力した内容を保持する ```jsp:test1.jsp //■入力するページ <% page contentType="text\html; charset=UTF-8" %> <% //エンコード設定 request.setCharacterEncoding("utf-8"); //フォームデータ送信時の文字コード //クッキーを取得 String email=""; Cookie[] cookie = request.getCookies(); if(cookie != null){ //取得したクッキーに何か入っていた場合 for(Cookie c : cookie){ //インスタンス変数cookieをインスタンス変数cへ移し if(c.getName().equals("email")){ //インスタンス変数cの中の名前がemalと等しいか1個ずつ確認 //デコードする email = URLDecoder.decode(c.getValue();,"UTF-8"); } } } %> <!DOCTYPE html> <html><head><meta charset="UTF-8" /><title>クッキーテスト</title></head> <body> <form method="POST" action="cookie.jsp"> <!--cookie.jspへ遷移--> <input type="text" size="10" name="email" value="<%= email %>"/> <inpu type="submit" value="送信" /> </form> </body> </html> ``` ```jsp:cookie.jsp //■Cookieを生成してブラウザへ渡す(保存)ページ <% page contentType="text\html; charset=UTF-8" %> <html> <head> <meta charset="UTF-8" /> <title>クッキー保存処理</title> </head> <body> <% request.setCharacterEncoding("UTF-8"); //文字コードを設定 String value = URLEncoder.encode(request.getParameter("email"),"UTF-8"); //フォームの値を取得 Cookie cookie = new Cookie("email",value); //クッキー生成し、セット。引数はクッキー名、値の順。 cookie.setMaxAge(60*60*24*7); //クッキーの有効期限を設定(今回は1週間)(引数を0にするとクッキー削除) response.addCookie(cookie); //クッキーを発行 %> <p>クッキーを保存しました</p> </body> </html> ``` #Sessionを使う セッションID、キー値、値の3つで管理される。 ##流れ ``` フォーム送信 ➡ セッション保存。ID発行。 ➡ セッションID送信 ➡ セッションID読み込む ``` セッションはオブジェクトも保存できる。日本語は文字化けする。 新規のアクセスならセッションIDを発行する。 Cookieがオフになってると、セッションは使えない。(その代わり、URLでセッションIDを発行するURLリライティングを使う) ##使い方 sessionはインスタンス生成する必要はない。 ```jsp //値を保管する session.setAttribute( 名前 , 値 ); //値を取得する Object 変数 = session.getAttribute( 名前 ); //セッションを開始した時刻を得る long 変数 = session.getCreationTime(); //最終アクセス時間を得る long 変数 = session.getLastAccessedTime(); //セッションを無効にする session.invalidate(); ``` ※セッションを無効にした場合、それ以降のsessionメソッドは使えない ##例 ```jsp ■入力するページ <% //セッション読み込み、オブジェクト型なのでString型へキャスト String email = (String)session.getAttribute("email"); %> //emailがnullだったら、空の文字を返す <input type="text" size="10" name="email" value="<%= (email == null) ?"": email %>"/> ■sessionを保存するページ <% // session.setMaxInactiveInterval(60*60*24*7); //サーバー側のセッション有効期限を決める(秒単位)(デフォルトは30分) session.setAttribute("email",request.getParameter("email")); %> ``` セッションは確実に破棄しておかないと、どんどん溜まって行って負荷が増える。(ブラウザ側のセッションIDは閉じたら破棄される) 破棄の方法は3通り。 ① session.invalidate();で現在のセッションとその関連のオブジェクトを破棄。 ② セッションの有効期限を決める ③ セッションの有効期限を設定ファイル(web.xml)で設定する。 ```xml //WEB-INFフォルダ内のweb.xml <session-config> <session-timeout>10</session-timeout> <!--分単位で指定する--> </session-config> ``` ##セッションの主なメソッド ``` removeAttribute(key) ・・・ 指定セッションを削除 getCreationTime() ・・・ セッション生成時刻を取得(タイムスタンプで) getLastAccessedTime() ・・・ セッションの最終アクセス時間を取得(タイムスタンプで) getId() ・・・ セッションIDを取得 isNew() ・・・ 新規に生成されたセッションかを調べる ``` #MySQLデータベースへ保存、読み込みする。 DBへのやり取りには、JDBCを使う。 本来、DBごとにやり取りが違うものをJDBCを挟むことで同じ方法でやり取りが出来る。 実際にはDB種類ごとのJDBCドライバをインストールし、そのドライバが間を繋いでくれてる。 ユーザーごとに毎回毎回DBに接続して~とやるのは、負荷がかかるので「コネクションプーリング」を使い 接続をいくつか保持して、DBを使いたいユーザーへ都度貸し出す様な仕組みをとる。 ##コネクションプーリングとデータソース コネクションプーリングを管理しているのはデータソース(javax.sql.DataSource) データソースの設定はMETA-INFフォルダ内の`context.xml`で行う ※「DB接続時のユーザID・PW」「プールする最大接続数」や「待機時に最低維持する接続数」などが設定できる ```jsp:接続例 <%@ page contentType="text/html; charset=UTF-8" import="java.sql.*,javax.naming.*,javax.sql.*" %> <!DOCTYPE html> <head><meta charset="UTF-8" /><title>DB接続テスト</title></head> <body> <% Connection db = null; PreparedStatement pstm = null; try{ Context context = new InitialContext(); //データソース(DB接続のための基本情報)を取得。返り値はオブジェクト型なのでキャストする DataSource ds = (DataSource)context.lookup("java:comp/env/jdbc/データソース名(DB名)"); Connection db = ds.getConnection(); db.setAutoCommit(false); //トランザクション処理をする場合は記述。自動コミットをoffにする //プリペアードステートメントで値をセット pstm = db.preparedStatement("INSERT INTO user(id,name) VALUE(?,?)"); pstm.setInt(1,request.getParameter("id")); pstm.setString(2,request.getParameter("name")); //他にもsetDateやsetTimestamp、setBooleanなどがある pstm.executeUpdate(); //SQL実行。挿入、更新、削除時には返り値に成功した件数が返る db.commit(); //トランザクション処理の場合には最後にコミットする }catch(Exception e){ throw new ServletException(e); }finally{ try{ if(db != null){ db.close(); //プールへ接続を戻す。必ず記述しないと一定時間保持し続けたままになってしまう。 } if(pstm != null){ pstm.close(); } db.rollback(); //トランザクション処理の場合に記述。エラー時はコミット前にDBを戻す。 }catch(Exception e){} } response.sendRedirect("success.jsp"); //登録後に任意のページへリダイレクト %> データベース処理成功。 </body> </html> ``` ```jsp:検索系のDB処理の場合 ResultSet rs = null; try{ //接続らへんは同じ rs = pstm.executeQuery(); //検索系のクエリーを実行 while(rs.next()){ //一つずつ取得していく %> <p><%=rs.getString("name") %></p> <% } %> <% //その他は同じくclose処理を記述 %> ``` ##ログイン処理 ```java <%@page contentType="text/html" pageEncoding="UTF-8" %> <%@page import="java.sql.* " %> <% //------------------------- //フォームから送られてきた内容を取得 //------------------------- String name = request.getParameter("name"); String psss = request.getParameter("pass"); //------------------------- //DBに接続して検索する //------------------------- //nameもパスもちゃんと入っていたら処理開始 if(name != null && pass != null){ Class.forName("com.mysql.jdbc.Driver").newInstance(); String url = "jdbc:mysql://localhost/mydb?characterEncoding=UTF-8"; Connection conn = DriverManager.getConnection(url, "test", "pass"); //nameとsha1で暗号化したパスがDBのと一致するか PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM user WHERE name = ? AND pass = SHA1(?)"); //さっきのクエリーの?部分に値を入れる pstmt.setString(1,name); pstmt.setString(2,pass); ResultSet rs = pstmt.executeQuery(); //ログイン成功なら if(rs.next()){ //セッションを一度破棄 session.invalidate(); //セッション再生成 session = request.getSession(); //セッションへ保存 session.setAttribute("name",name); //画面遷移 response.sendRedirect("secretform.jsp"); } //DBを明示的に切断 pstmt.close(); conn.close(); } %> 以下、htmlのログインフォーム ``` ##認証が必要なページでのログインチェック ログインしてないと見れないページでのログイン認証 ```java <%@page contentType="text/html" pageEncoding="UTF-8" %> <%@page import="java.sql.* " %> <% //------------------------- //セッションからnameを取り出す。なかったら、ログイン画面へリダイレクト //------------------------- String name = (String)session.getAttribute("name"); if(name == null){ response.sendRedirect("login.jsp"); } //------------------------- //送信データがあるなら、データベースに接続して登録 //------------------------- //文字コードセット request.setCharacterEncoding("UTF-8"); //フォームよりテキストを取得 String message = request.getParameter("message"); if(message != null){ //DB接続 Class.forName("com.mysql.jdbc.Driver").newInstance(); String url = "jdbc:mysql://localhost/mydb?characterEncoding=UTF-8"; Connection conn = DriverManager.getConnection(url, "test", "pass"); //クエリー作成(フォームのテキストをmessageテーブルへ保存する) PreparedStatement pstmt = conn.PreparedStatement("INSERT INTO message (name,body) VALUES (?,?)"); //?に値セット pstmt.setString(1,name); pstmt.setString(2,name); //実行 pstmt.executeUpdate(); //DB明示的に切断 pstmt.close(); conn.close(); } %> 以下、テキストフォームなど ``` ##ログアウト ```java session.invalidate(); response.sendRedirect("login.jsp"); ``` ##フォームの内容を記憶する ```java <%@page contentType="text/html" pageEncoding="UTF-8" %> <%@page import="org.apache.commons.lang.*" %> <% //------------------------- //テキストボックスに入力された値を復元 //------------------------- //フォーム送信された値を取得 String paramq = request.getParameter("q"); //取得した値がnullじゃない(送信されてる)なら if(paramq != null){ //サニタイズして格納(htmlへ出力するので) paramq = StringEscapeUtils.escapeXml(paramq); }else{ paramq = ""; } %> <html> <head> <title>郵便番号から住所を検索するフォーム</title> </head> <body> <form action="" method="get"> <imput type="text" name="q" value='<%= paramq %>' /> <input type="submit" value="search" /> </form> <!-- 郵便番号から住所を検索する処理ページを読み込み --> <jsp:include page="zips.jsp" /> </body> </html> <% ``` #サーブレット ##動作手順 1..javaファイルをコンパイルする 2.出来た.classファイルを`/WEB-INF/classes`フォルダ内にパッケージ構造に則した形で置く ※パッケージが`jp.co.test.sample01`だったら、`/WEB-INF/classes/jp/co/test/sample01`フォルダ内に置く ##基本の書き方 doGetメソッドとdoPostメソッドを定義する。 doGetはGETでアクセスされた時の処理。 doPostはPOSTでアクセスされた時の処理。 ```java:基本構文 package jp.co.test.sample01; import java.io.IOException; //以下6つのパッケージはインポート必須(jspでは自動でインポートされてた) import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; //ブラウザでサーブレットを呼び出すためのパスを宣言。アプリケーションルートからの絶対パスで。 //以下の場合、http://localhost:8080/アプリケーション名/Test で呼び出せる @WebServlet("/Test") public class TestServlet extends HttpServlet { //HttpServletクラスの機能を引き継いで使う @Override //オーバーライドしていることを明示し、メソッド名や引数など誤りをコンパイラが通知 //doGetメソッドをオーバーライド。 //引数にはクライアントから送信された入力情報を管理するHttpServletRequestオブジェクトと //クライアントへ送信する出力情報を管理するHttpServletResponseオブジェクトを指定する protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); //ブラウザへ渡すコンテンツのタイプを指定 request.setCharacterEncoding("UTF-8"); //ブラウザから受け取ったデータの文字コード指定 response.setCharacterEncoding("UTF-8"); //ブラウザへ送るデータの文字コード指定 PrintWriter out = response.getWriter(); //クライアントへ表示するためのインスタンス生成 //htmlをゴリゴリ記述 out.println("<html><head></head><body><h1>こんにちは!</h1><p>このページはサンプルです。</p></body></html>"); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { //POSTでアクセスされた時の処理を記述 } } ``` ※サーブレット2.5(Tomcat6.0)以前のものは@WebServletは使えないので`/WEB-INF`フォルダ内のweb.xmlにサーブレットを登録する。 ※サーブレットクラスはリクエストのたびにインスタンス化されるのではなく、1回インスタンス化されたものを複数のユーザーで共有している。 ##フォームの値を受け取る JSPと同じような記述で受け取れる。 name属性「email」のフォームからpostで送られたものを受け取る場合 ```html:フォーム画面(post.jsp) <form method="POST" action="TestServlet"> //普通にフォームを記述 </form> ``` ```java:受け取るサーブレット(TestServlet.java) package jp.co.test.sample01; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/Test") public class TestServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE html><html><head><meta charset='UTF-8'>"); out.println("</head><body>"); String email = request.getParameter("email"); if(email.equals("") out.println("空です"); out.println("</body></html>"); } } ``` ```text:フォルダ構成 /Test(アプリケーション名) |_post.jsp(フォーム画面) |_/WEB-INF |_classes |_/jp |_/co |_/test |_/sample01 |_TestServlet.java(コンパイル用javaファイル) |_TestServlet.class(コンパイル済みのもの) ``` ##フォワードを使って、JSPに表示を切り替える フォワードは同じURL(「sample.jsp」など)のままでサーブレットなど違う処理ファイルへ移動し、そのままのURLで結果を取得できるもの。 ただし、リロードした際にはdoGetの内容が表示されてしまう。 1. ServletContextを取得する ```java ServletContext app = this.getServletContext(); ``` ServletContextは、現在動いているWebアプリケーションを管理するためのもの。 このインスタンス内に、Webアプリケーションに関する重要な機能がまとめられている。 2. RequestDispatcherを取得する ```java RequestDispatcher dispatcher = app.getRequestDispatcher("/helo.jsp"); ``` ServletContextから「RequestDispatcher」というインスタンスを取得。 これは、リクエストをディスパッチ(別のものに掛け替える)するためのもの。 フォワードがメソッドとしてあると思えばいい。 3. forwardメソッドでフォワードする ```java try { dispatcher.forward(request, response); } catch (ServletException e) { out.println(e); } ``` ##ファイルをアップロードする ```jsp:upload.jsp <%@ page contentType="text/html;charset=UTF-8" %> <!DOCTYPE html> <html><head><meta charset="UTF-8"><title>ファイルアップ</title></head> <body> <form method="POST" enctype="multipart/form-data" action="UploadServlet"> <input type="file" name="file" size="80"><br> <input type="submit" value="アップロード"> </form> </body></html> ``` ```java:UploadServlet.java package jp.co.test.sample01; import java.io.*; import javax.servlet.*; @MultipartConfig(location="C:/tmp/") //①アップロードの設定をする(今回は一時保存先を指定) @WebServlet("/UploadServlet") public class TestServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { Part part = request.getPart("file"); //②getPartでファイルを取得(戻り値はPartオブジェクト) String name = this.getFileName(part); //③アップされたファイル名を取り出す if(this.isValidFile(name)){ part.write{ //④part.writeメソッドで指定のパスへファイルを保存 getServletContext().getRealPath("/WEB-INF/data")+"/"+name); //仮想パスを絶対パスへ変換 response.sendRedirect("Upload.jsp"); }else{ response.getWriter().println("アップロードエラー"); } } } private String getFileName(Part part){ //ファイル名を取得 String result = null; for(String disp : part.getHeader("Content-Disposition").split(";")){ disp = disp.trim(); if(disp.startsWith("filename")){ result = disp.substring(disp.indexOf("=")+1).trim(); result = result.replace("¥","").replace("¥¥","/"); int pos = result.lastIndexOf("/"); if(pos >= 0){ result = result.substring(post+1); } break; } } return result; } private boolean isValidFile(String name){ //拡張子チェック if(name != null){ String[] perms = { "fig","jpg","jpeg","png"}; String[] names = name.split("¥¥."); for(String perm: perms){ if(perm.equals(names[names.length-1])){ return true; } } } return false; } } ``` ##文字コードなどを一括で設定する 毎回毎回、文字コードやパスを設定するのは面倒&変更した時の修正が面倒なので、設定ファイル`web.xml`に定義して、それを読み込むようにする。 ```java:受け取るサーブレット(TestServlet.java) import javax.servlet.annotation.WebInitParam; //initメソッドを使うため読み込み @WebServlet("/Test") public class TestServlet extends HttpServlet { private String path = null; @Override //initメソッドはサーブレットクラスが初めて呼び出された時だけ実行される public void init(ServletConfig config) throws ServletException{ super.init(confg); //初期化パラメータの値をインスタンス変数pathにセットし、保存先パスなどに使う this.path = config.getInitParameter("path"); } //・・・ } ``` ```xml:web.xml <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/sml/ns/javaee/we-app_3_0.xsd" version="3.0"> //・・・ <servlet> <servlet-name>TestServlet</sevlet-name> <servlet-class>jp.co.test.sample01.TestServlet</servlet-class> <init-param> <param-name>path</param-name> //変数定義 <para-value>/WE-INF/img/</param-value> //変数内の値を定義 </init-param> </servlet> //・・・ ``` ※<servlet>要素内で宣言された初期化パラメータは指定したサーブレットクラスでしか参照できないので、全サーブレットで参照できるようにするには<context-param>要素で宣言する。 #いくつかのURLから1つのサーブレットを呼び出す いくつかのリクエストを1つのサーブレットで受け取るものを「フロントコントローラ」という。 例えば、URLが`~/FrontServlet/mypage1`や`~/FrontServlet/mypage2`でも、同じサーブレットを呼び出し、そのサーブレットの中で処理を切り分ける。 ``` package jp.co.test.sample01; import java.io.*; import javax.servlet.*; @WebServlet("/FrontServlet/*") //アスタリスク以下のURLが何指定されてもこのサーブレットを呼び出す public class FrontServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ response.setContentType("text/html:charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<pre>"); out.println("リクエストされたURLは"+request.getRequestURL()); out.println("リクエストされたURIは"+request.getRequestURI()); out.println("サーブレットパスは"+request.getServletPath()); out.println("拡張パスは"+request.getPathInfo()); //クエリ情報に相当する部分 out.println("<?pre>"); } } ``` ※拡張パスとして入力された情報(「list/1023」などスラッシュで区切られたもの)をサーブレット側で解析し、処理の判断をする。 ※クエリ情報で動的に変化させるページだと検索エンジンがクローリングしてくれない場合があるので、SEOとしても動的に切り分けるページの場合は拡張パスでやったほうがいい #JSPとサーブレットの連携 入力画面jspからサーブレットへ値を送り、サーブレットから出力画面jspへ値を送る。 サーブレットからjspへ値を送るにはsetAttributeを使う。 setAttributeは「リクエスト属性」に指定の変数を登録するもの。 リクエスト属性とは、「クライアントがリクエスト要求してから、クライアントへ応答を返すまで」の間だけ保持される変数のこと。 ```jsp:bmi.jsp <%@ page contentType="text/html; charset=UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>テスト連携</title> </head> <body> <form method="POST" action="TestServlet"> <label name="height">身長:<input type="text" name="height">m</label> <label name="weight">体重:<input type="text" name="weight">kg</label> <input type="submit" value="計算する"> </form> </body> </html> ``` ```java:TestServlet.java package jp.co.test.sample01; import java.io.*; import javax.servlet.*; @WebServlet("/TestServlet") public class FrontServlet extends HttpServlet{ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ double h = Double.parseDouble(request.getParameter("height")); double w = Double.parseDouble(request.getParameter("weight")); double bmi = w/(h*h); request.setAttribute("bmi",bmi); //setAttributeでjspへ渡す変数名と値を設定 if(bmi < 18.5){ request.setAttribute("rank","やせ"); }elseif(bmi < 25){ request.setAttribute("rank","標準"); }else{ request.setAttribute("rank","肥満"); } //jspへ渡すため、RequestDspatcher.forwardメソッドを呼び出す。 //RequestDispatcherオブジェクトを取得するにはServletContext.getRequestDispathcerメソッドを使う this.getSevletContext().getRequestDispatcher("/bmi_result.jsp").forward(request,response); } } ``` ```jsp:bmi_result.jsp <%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>テスト連携</title> </head> <body> <p>身長:${param['height']}</p> //フォワードを使うことで、フォームの値をそのまま使用できる <p>体重:${param['weight']}</p> <p>BMI:${requestScope['bmi']}</p> <p>結果:${requestScope['rank']}</p> </form> </body> </html> ``` ```text:フォルダ構成 /Test(アプリケーション名) |_bmi.jsp(フォーム画面) |_bmi_result.jsp(結果画面) |_/WEB-INF |_classes |_/jp |_/co |_/test |_/sample01 |_TestServlet.java(コンパイル用javaファイル) |_TestServlet.class(コンパイル済みのもの) ``` ##リダイレクトとフォワードの違い 1.リダイレクトはサーバー側がクライアントへ強制的にページBをリクエストさせるので、内部的に2往復の通信をしている。 ```tex ■リダイレクト クライアントがページA要求 → サーバーがリダイレクトを応答 → クライアントがページB要求 → サーバーがページBを応答 ■フォワード クライアントがページA要求 → サーバー内でページAからBへ処理を転送 → サーバーがページBを返す ``` 2.リダイレクトだとクライアントがページBを要求した場合、そのリクエストは他のリクエストの最後尾になるので、トラフィックが多い場合、ページBが表示されるまで時間がかかる。 3.リダイレクトだと内部的に2往復しているので、ポストデータやクエリ情報、リクエスト属性は引き継げないが、フォワードであれば1つのリクエスト・レスポンス内なので、引き継げる。 4.フォワードは外部のサーバーへは使えない。(http://~などと指定しなければいけない場合) #複数のユーザー間で値を共有したい場合 ##JSPでやる場合 ```jsp <% application.setAttribute(属性名,値); //変数を共有したい場合 application.getAttribute(属性名); //共有している変数を受け取る場合 %> ``` ##サーブレットでやる場合 ```java ServletContext app = this.getServletContext(); //ServletContextオブジェクトを明示的に取得 ``` #JavaBeansを使う JavaBeansは部品化できるようにルールづけしたクラスのことをそう呼んでいるだけ。 今までは、JSPで画面を出力し、サーブレットで内部処理(ビジネスロジック)をしていたが、 サーブレット内には○○のname属性の値を取得して~○○の変数で出力して~と本来のデータ処理のほかに「データ受け取り・受け渡し」の部分などが含まれている。 そういった処理はどのページでも共通にも関わらず、name属性やレイアウトが変わっただけで使いまわせなくなってしまう。 なので、切り離して再利用性を高めた方がいい。こういう設計方法をMVCモデルという。 JavaBeansはMVCモデルでいうところの、モデル部分。 JavaBeansでモデルを記述し、JSPでViewを実装、サーブレットでコントローラーとして制御する。 ##JavaBeansクラスのルール ###1.Serializableインターフェースを実装していること オブジェクトはフィールドとメソッドを持つ構造だが、そのオブジェクトをそのままDBやファイルへ保存することはできないので、Serializableを実装することで「直列化(バイト列へ変換)」し保存することが出来る。 ###2.引数のないコンストラクタがあること ###3.アクセサメソッド(ゲッター、セッター)を定義すること。いわゆるカプセル化。 アクセサメソッドを使うことで、変数に書き込みだけ出来るようにしたいなどの制約をさせたり、 メソッド内で処理が記述できるので、書き込む内容を制限(整数だけとか)したり出来る。 ```java:TestServlet.java package jp.co.test.sample01; import java.io.*; import javax.servlet.*; @WebServlet("/TestServlet") public class FrontServlet extends HttpServlet{ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ Bmi bmi = new Bmi(); //Bmiクラスをインスタンス化 bmi.setHeight(Double.parseDouble(request.getParameter("height"))); bmi.setWeight(Double.parseDouble(request.getParameter("weight"))); bmi.calculate(); request.setAttribute("bmi",bmi); //bmiオブジェクトをリクエスト属性bmiにセット //jspへ渡すため、RequestDspatcher.forwardメソッドを呼び出す。 //RequestDispatcherオブジェクトを取得するにはServletContext.getRequestDispathcerメソッドを使う this.getSevletContext().getRequestDispatcher("/bmi_result.jsp").forward(request,response); } } ``` ```java:Bmi.java package jp.co.test.sample01; import java.io.*; public class Bmi implements Serializable{ //ルール1:Serializableを実装 //ルール3:カプセル化のためにprivateで変数への外部からのアクセスを制限 private double height = 0; private double weight = 0; private double bmi = 0; public Bmi(){} //ルール2:引数のないコンストラクタを定義する。中身が空なら定義しなくてもいい。 //ルール3:アクササメソッドを定義 public double getHeight(){ return this.height; } public double getWeight(){ return this.weight; } public double getValue(){ return this.bmi; } public String getRank(){ if(this.bmi < 18.5){ return "やせ"; } } public void setHeight(double height){ if(height > ){ this.height = height; } } public void setWeight(double weight){ if(height > ){ this.weight = weight; } } public void calculate(){ this.bmi = this.weight / (thiw.height * this.height); } ``` ```jsp:bmi_result.jsp <p>身長:${param['height']}</p> <!-- フォワードを使うことで、フォームの値をそのまま使用できる --> <p>体重:${param['weight']}</p> <p>BMI:${requestScope['bmi'].value}</p> <!-- getValueメソッドを呼び出してる --> <p>結果:${requestScope['bmi'].rank}</p> <!-- getRankメソッドを呼び出してる --> ``` ※`${requestScope['bmi'].rank}`は`${bmi.rank}`と書いてもいい #デプロイメントディスクリプタ JSP/サーブレットの標準設定ファイルのこと。 Webアプリケーションの配置情報を記述したXML形式のファイル。 使用しているコンテナによってファイル名が違ったりする。(Tomcatではweb.xml) サーブレットの登録やセッション情報、初期化パラメータやウェルカムページ、エラーページ、認証の定義など設定(定義)できる。 ##web.xmlの配置場所 レベルによって2種類ある。 全アプリケーションの設定・・・`%CATALINA_HOME%/conf/web.xml` 現在のアプリケーションのみの設定・・・'(アプリケーションルート)/WEB-INF/web.xml' ※基本/conf/web.xmlは触らないこと ##web.xmlの最低限の構成 ```xml: <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <!-- 要素を指定していく --> </web-app> ``` ##<web-app>要素内に記述できる主な設定要素 ```text: 要素名 概要 <icon> GUIツールに表示するアイコン画像 <display-name> GUIツールに表示するアプリケーション名 <description> GUIツールに表示する備考情報 <content-param>※ 初期化パラメータ <filter>※ フィルタクラスを登録 <fileter-mapping>※ フィルタの適用範囲 <listener>※ リスなクラスを登録 <servlet>※ サーブレットクラスを登録 <servlet-mapping>※ サーブレット呼び出しのためのURI <session-config> セッション情報(有効期限) <mime-mapping>※ MIMEタイプ <welcome-file-list> ウェルカムページ <error-page>※ エラーページ <jsp-config> JSPページの設定情報 <security-constraint>※ アクセス制限の適用範囲 <login-config> ログイン方法 <security-role>※ 認証に使用するロール名を登録 ``` ※の要素は複数記述できる #XML文書の書き方 1.「要素」と「属性」で構成されている ```text: <要素名 属性名="属性値" ・・・> データ </要素名> ``` ※要素名は大文字小文字が区別される ※属性値はダブルクウォートもしくはシングルクウォートで囲む 2.XML文書だということを宣言する ```xml: <?xml version="1.0" encoding="UTF-8" ?> ``` ##xml内要素を定義する ```xml:web.xml <?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <!--全サーブレット共通のパラメータを定義--> <context-param> <param-name>sample.title</param-name> <param-value>テストタイトル</param-value> </context-param> <!--エラーページを定義。今回の場合、java.lang.Exception例外に対して/ErrorServletを表示する--> <error-page> <exception-tpe>java.lang.Exception</exception-type> <location>/ErrorServlet</location> </error-page> <!--エラーページを定義。エラーコードごとにも定義できる--> <error-page> <error-code>404</error-code> <location>/404error.jsp</location> </error-page> </web-app> ``` ```jsp:jspでパラメータを受け取る場合 ${initParam['sample.title']} ``` ```java:サーブレットでパラメータを受け取る場合 ServletContext application = this.getServletContext(); application.getInitParameter("sample.title"); ``` ```ErrorServlet.java package jp.co.test.sample01 import java.io.*; import javax.servlet.*; @WebServlet("/ErrorServlet") public class ErrorServlet extends HttpServlet{ @override protected void doGet(HttpServletRequest request.HttpservletResponse response) throws ServletException, IOException{ ServletContext app = this.getServletContext(); StringBuffer sb = new StringBuffer(); sb.append(new java.util.Date()); //エラー発生日時を記録 sb.append(System.getProperty("line.separator")); //改行 sb.append(request.getAttribute("javax.servlet.error.status_code")); //HTTP応答ステータスコード sb.append(System.getProperty("line.separator")); //改行 sb.append(request.getAttribute("javax.servlet.error.request_uri")); //エラー場所 sb.append(System.getProperty("line.separator")); //改行 sb.append(request.getAttribute("javax.servlet.error.message")); //エラーメッセージ app.log(sb.toString()); //エラー情報をデフォルトのログファイルに記録 response.sendRedirect(request.getContextPath()+"/error.jsp"); } } ``` ※ログは外部から見えない様に/WEB-INFフォルダ内か`%CATALINA_HOME%/webapps`フォルダの外に配置する ```text:error.jsp 時間を空けて再度アクセスしてください。的な内容を表示する。 ``` #JARファイルの配置場所 `%CATALINA_HOME%/webapps/<アプリケーション名>/WEB-INF/lib` #WEBプログラミングから起業までを一貫して学べるオンライン動画総合学習サービス『ウェブカツ!!』 様々なプログラミング学習サイトやサービスでは正直な所、自分でWEBサービスを作れるようにはならないため、「自分でWEBサービスを作れるようになる!」をゴールとしたオンライン動画総合学習サービス『ウェブカツ!!』を立ち上げました。興味ある方は登録よろしくお願いいたします。 #Javaアプリケーションのファイル構成 ``` プロジェクトフォルダ |_ Javaリソース |_ src |_servlet ←パッケージ |_サーブレットファイルが入っている |_model ←パッケージ |_普通のjavaファイル(クラス)を置く場所 |_WebContent |_静的なHTMLファイルや動的なJSPファイルを置く場所 |_WEB-INF |_ブラウザからリクエストできないが、フォワードで使いたいJSPファイルを置く場所 |_lib |_JARファイルを置く場所 ``` |
|
| 863位 |
|
|||
|
21:29:46 |
|
|
Adventカレンダーのネタに困ってたのだけど、ちょうどObjective-CとJavaScriptの連携ではまったので、備忘録がてらやらせて頂きます。
概要 - objective-cからwebView内のjavascriptを実行 - javascriptからObjective-Cに通知してフック ##Objective-CからWebViewに対してJavaScriptを実行する 以下のhtmlの ``` javascriptfunction ``` を実行するコード。 ```main.html <html> <body> hoge <script> var msg = “hello javascript”; function javascriptfunction(){ alert(msg); } </script> ``` ```hogeController.m - (void)viewDidLoad{ [super viewDidLoad]; NSString *html = @“<html> <body> hoge <script> var msg = ‘hello javascript’; function javascriptfunction(){ alert(msg); } </script> </html>“; // webviewに読み込み [_video_view loadHTMLString:html baseURL:[[NSBundle mainBundle] resourceURL]]; } ``` これでセットアップ完了 なんかのアクションに貼り付けてみれば、Objective-Cから実行できて、Alertするはず。 ```hoge.m [_webView stringByEvaluatingJavaScriptFromString:@“javascriptfunction();”]; ``` 以上でObj-CからJSはおk ##JavaScriptからObjective-Cにフック 通知側のJavaScript ```main.js function somefunction(){ // schemeは適当でおk open("native://js”); } ``` ```hogeController.m /** ** webViewがリクエスト送った時のDelegate **/ -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *) request navigationType:(UIWebViewNavigationType)navigationType { // schemeがnative だった場合 if ([ request.URL.scheme isEqualToString:@"native" ]) { // 関数を呼べ! [ self invokeNativeMethod: request ]; // WebViewの読み込みは中断する。 return NO; } // 通常のschemeの場合は、フックせずそのまま処理を続ける else { return YES; } } // Native処理を呼び出す -(void)invokeNativeMethod: (NSURLRequest *)request { // native://closeWebViewが指定された場合 if ([request.URL.host isEqualToString:@“js”]) { NSLog(@“js from objs”); } } ``` 以上でJSからObjective-Cに通知してフックできます。 ##感想 自分は開発がほぼ終わりそうな段階で知ったのですが、もっと早く知ってれば無駄なことしなくてよかったのになぁ…とか思ってます。 しょうもない小ネタみたいなかんじで、申し訳ないですが以上になります! ##追記 面白そうな資料見つけたので、後で追記します https://developer.apple.com/library/mac/documentation/AppleApplications/Conceptual/SafariJSProgTopics/Tasks/ObjCFromJavaScript.html |
|
| 864位 |
|
|||
|
22:53:58 |
|
|
「半年前の自分は他人」という自戒の元、丁寧に書くのを癖にしたい今日この頃。 ####Node.js [node.js とは何か](http://d.hatena.ne.jp/badatmath/20101020/1287587240)によると、「JavaScriptを用いたNon-blocking I/O環境」だそうです。 バックエンドで動作。 ####Express Sinatra ライクな Node.js 用Webアプリケーションフレームワーク、軽量アプリケーション向け。 Node.js + Express で最低限のWebページが作れます。 ちなみに Sinatra は Ruby です。 ####forever Node.js をデーモン化して、プロセスを監視してくれます。 最近は pm2 という更に高機能な監視ツールもあるのですが、移行は今後考えるとして日本語文献の多い forever を使う。 今回は、nginx から pm2 で動かした Node.js サービスに流すまでを目指します。 ##環境 ゲスト: CentOS 6.2 ホスト: MacBookAir MacOS 10.8 VM: Parallels Desktop 9 for mac ゲストを対象とし、IPアドレスは 10.211.55.2 とする。 ##Node.js インストール [CentOSでndenvをシステムワイドにインストールした時のメモ](http://qiita.com/ryurock/items/572169174e16bc669a7b)を参考に ndenv で Node.js のバージョン管理を行います。作者[riywo](http://blog.riywo.com/2013/06/21/152736)さんの元記事です。ありがとうございます。 必要であれば適時`sudo`してください。 記述通りのバージョン、構成にしました。 /usr/local/ndenv /usr/local/ndenv/plugins/node-build ```bash: $ node -v v0.10.20 ``` 別のバージョンを入れたい場合は、リストを見て検討しましょう。 ```bash: $ ndenv install --list ``` ##Express インストール npm を使用して、Express をグローバルインストールします。 ```bash: $ sudo npm install express -g ``` ここで`express --help`を実行してみたら express が見つからないよと言われたので`ndenv rehash`しようとしたのだが、 ```bash: $ sudo ndenv rehash ``` ではダメだった。仕方なく、 ```bash: $ sudo -s $ ndenv rehash $ exit ``` で一部をrootユーザーとして実行したら成功したのだが、まだ原因は解っていない。 とりあえず、以降はこれで`rehash`していく。 一応確認も。 ```bash: $ which express /usr/local/ndenv/shims/express ``` ##forever インストール 同じく npm を使用して、forever をグローバルインストールします。 ```bash: $ sudo npm install forever -g $ sudo -s $ ndenv rehash $ exit $ which forever /usr/local/ndenv/shims/forever ``` ##サンプルアプリを作って走らせる HOME直下に作って、必要なライブラリをローカルインストールします。 ```bash: $ cd $ express -e SampleApp $ cd SampleApp $ npm install ``` そのまま forever で起動。 [node.jsスクリプトをforeverでデーモン化する](http://onlineconsultant.jp/pukiwiki/?node.js%20node.jsスクリプトをforeverでデーモン化する)の通りに実行しました。 ちゃんと理解してないのですが、app.js が根幹って事でしょうか。 ```bash: $ forever start app.js ``` 止めたい時と、再起動したい時は単純にこれ。 ```bash: $ forever stop app.js $ forever restart app.js ``` ##nginx でリバースプロキシ app.js の中身を見ると、このサンプルアプリはポート3000で動作しているので、ホストOSから http://10.211.55.2:3000/ にアクセスすれば表示されるのですが、これでまたいちいち`iptables`編集するのもなってのと、実際はサブドメイン切ってリバースプロキシあてがうだろうなって事で、ローカル環境でもそれに似た状況を作った。 既にあった /etc/nginx/conf.d/default.conf と、[nginxでリバースプロキシ。](http://d.hatena.ne.jp/mkouhei/20100121/1264086004)を参考に、このようにしました。 ```bash:/etc/nginx/conf.d/node-app.conf upstream node-sampleapp { server localhost:3000; } server { listen 80; server_name 10.211.55.2; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; location / { proxy_pass http://node-sampleapp/; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` 再読み込み。 ```bash: $ sudo service nginx reload ``` ホストから http://10.211.55.2/ にアクセス。 ##Welcome to Express コングラッチュレーション…! ##感謝 参考にさせていただいたサイト管理人の皆様、誠にありがとうございました。 |
|
| 865位 |
|
|||
|
16:30:57 |
(Photosynth Inc. 所属) |
|
最近まで別のチケット管理システムを使っていたのですが、試しにbitbucketのIssueを使い始めました。
で、コミットメッセージとIssueをリンク・操作できると知らなかったのでそのメモです。 ## Issue Hookを追加 リポジトリの設定画面で左カラムのHookより「Issue」を追加してください。 以下みたいな感じになってるなら大丈夫です。  ## コミットメッセージでIssueをリンク化 登録したIssueにはそれぞれ番号が振ってあると思いますが、 その番号の前に#を追加してコミットメッセージに書くとリンク化されます。 ``` #<Issueの番号> 何かしらのコミットメッセージ ``` 例) #15 文字をなんとか〜  こんな感じでリンク化されます。 ## Issueをコミットメッセージから操作 コミットメッセージを ``` <コマンド> #<Issueの番号> ``` という感じにすれば操作が可能です。前後に文字が入るのは大丈夫だと思います。 コマンド一覧は以下です。 | 説明 | コマンド(どの単語を使っても同じです) | 例 | |--------|-----------------|----------| | link to a changeset for the issue | addresses re references ref refs see | see #34 and #456 | | mark an issue invalid | invalidate invalidates invalidated invalidating | invalidates #45 | | mark an issue on hold | hold holds holding | holds #123 | | mark an issue wontfix | wontfix | wontfix #12 | | reopen an issue | reopen reopens reopening | reopen #746 | | resolve an issue | close closes closed closing fix fixed fixes fixing resolve resolves resolved resolving | close #845 | pull requestをmergeするときのコミットメッセージに```fixes #12 #16```などと書くと対応するIssueがまとめてResolveされているのでとても便利でした。 ## 参考リンク [Resolve issues automatically when users push code](https://confluence.atlassian.com/display/BITBUCKET/Resolve+issues+automatically+when+users+push+code) |
|
| 866位 |
|
|||
|
12:38:32 |
(Yahoo! Japan Corporation 所属) |
|
# 背景
ある程度中規模のアプリケーションを作成する前提で、[公式サイトの「Getting Started」](http://www.sinatrarb.com/intro-jp.html)とは構成を変える。Viewは別ファイルへ分ける。Modelはクラスを分ける。サーバー実行コードをアプリケーションコードから分ける。ライブラリはbundlerで管理する。Viewはhaml, sass, coffeescriptを使用する。テストにはrspecを使用する。 # 実装 ## ディレクトリ作成 ``` $ mkdir app $ cd app/ ``` ## bundlerの準備 ### Gemfileの作成とインストール先のDir作成 ``` $ bundle init $ mkdir -p vender/bundle $ vim Gemfile ``` ### 必要なライブラリを書く jsにはcoffee-script, cssにはsass, htmlテンプレートにはhamlを使用する。 開発環境での実行にはshotgunを使用する。 テストフレームワークにはrspecを使用する。 ```ruby:Gemfile source "https://rubygems.org" gem 'sinatra' gem "sass" gem "haml" gem "coffee-script" gem "shotgun" group :test do gem 'rspec' end ``` ### ライブラリのインストール インストール先にvender/bundleを指定する。 ``` $ bundle install --path vender/bundle ``` ## アプリケーションを構成 1. ルーティングを行うためのapp.rbを作成する。 2. アプリケーション起動のためのconfig.ruを作成する 3. sass, coffee-scriptのファイルを置いておくためのassets/を作成する 4. hamlを置いておくためのviews/を作成する 5. modelファイルを置いておくためのmodels/を作成する 6. modelをロードするためのinit.rbを作成する。 7. テストを置いておくためのspec/を作成する ``` $ touch app.rb $ touch config.ru $ mkdir -p assets/js assets/css $ mkdir views $ touch views/index.haml $ mkdir models $ touch models/init.rb $ mkdir spec ``` ### ルーティングを行う ルーティングとコントローラをまとめて書く ```ruby:app.rb require 'sinatra/base' require 'haml' require 'sass' require 'coffee-script' require_relative 'models/init' class Server < Sinatra::Base get '/' do haml :index end end ``` ### サーバー設定を行う サーバーの設定を別ファイルへ分ける。 ```ruby:config.ru root = ::File.dirname(__FILE__) require ::File.join(root, 'app') run Server ``` ### Viewを作成する ```haml:index.haml %h1 This is the application root. ``` ### アプリケーションを実行してみる development環境でアプリケーションを実行する。 ``` $ bundle exec shotgun ``` ## Modelを作成する 1. Rakefileを作成して、テストの実行環境を整える 1. Specファイルを作成しテストを書く 2. Modelのクラスを作成する 3. テストが意図したとおり通らないことを確認する 4. Modelへテストを通すために必要なコードを追加する 5. テストが意図したとおり通ることを確認する ### テストの実行環境を整える コマンドで全てのテストファイルが一括実行されるようにする。 ``` $ touch Rakefile ``` ```ruby:Rakefile require 'rspec/core/rake_task' desc "run spec" task :default => [:spec] RSpec::Core::RakeTask.new(:spec) do |spec| spec.pattern = 'spec/*_spec.rb' spec.rspec_opts = %w(--color --format progress) end ``` RSpecの設定を行う。全てのspecファイルでこのspec_helperをロードする。 ``` $ touch spec/spec_helper.rb ``` ```ruby:spec/spec_helper.rb ENV['RACK_ENV'] = 'test' require File.join(File.dirname(__FILE__), '..', 'app.rb') require 'rspec' require 'rack/test' set :environment, :test set :run, false set :raise_errors, true set :logging, false RSpec.configure do |conf| conf.include Rack::Test::Methods end def app Sinatra::Application end ``` ### Specファイルを作成する models/以下に***_spec.rbのパターンで、モデルのテストを作成する。 ```spec/models/hoge_spec.rb require 'spec_helper' describe Hoge do it "should print String" do expect(Hoge.new.puts).to be_instance_of String end end ``` ### Modelクラスを作成する まずはテストが通らないことを確認するためになにも書かない。 ```ruby:models/hoge.rb class Hoge end ``` ### アプリケーションに読み込ませる ```ruby:models/init.rb require_relative 'hoge' ``` ### テストを行う テストが通らないことを確認する。 ``` $ bundle exec rake spec ``` ### Modelクラスを編集する テストが通るように編集する。 ```ruby:models/hoge.rb class Hoge def puts 'Hello' end end ``` ### テストを行う テストが通ることを確認する。 ``` $ bundle exec rake spec ``` あとはapp.rbの中で作成したmodelを利用する。 # 参考資料 ※ あとで |
|
| 867位 |
|
|||
|
02:39:09 |
(フリーランス 所属) |
|
(※以下の記事はもう古く、Rails4.1時代に追従できていません。他の先輩諸氏の記事を参考にしてください)
## はじめに懺悔します 最強とかウソです。釣りです。すみませんすみませんすみません。 これくらい、みなさんやってますよね。でも、慣れてない人とかいると思いますし、なにがしかの参考になればと思って私の設定を晒します。 ## 導入する項目 本記事で導入するテスト関連のGemは以下の通りです。フフフ、圧倒的じゃないか、我が軍は。 * RSpec (BDD環境) * Cucumber (受け入れテスト環境) * Guard (ファイル変更を監視して、テストを自動化) * Spring (テスト高速化。テスト以外にもrails gとかrails consoleとか色々高速化してくれる) * Factory Girl (標準のフィクスチャに不満を感じる全ての方に) * Database Cleaner (テストのたびにDBをキレイに) * Capybara (Webブラウザによるアクセスをシミュレートするテストを実現) * coffee-rails-source-maps (CoffeeScriptのソースマップを出力する) ## 以下、手順 ### rbenvでRuby2.0をインストールする。 詳しい手順は、私の書いた[この記事](http://qiita.com/emadurandal/items/a60886152a4c99ce1017)を参考にしてください。 ### Rails4.0のローカルインストール 同じく、私の書いた[この記事](http://qiita.com/emadurandal/items/a60886152a4c99ce1017)を参考にしてください。 ```bash $ cat << EOS > Gemfile source "http://rubygems.org" gem "rails", "4.0.0" EOS ``` ```bash bundle install --path vendor/bundle ``` ### プロジェクト生成 ```bash $ bundle exec rails new MyProject -d postgresql --skip-test-unit --skip-bundle ``` `--skip-test-unit`をつけることで、Rails標準のTest::Unit 関連の設定やファイルの生成を行わないようにします。 また、`--skip-bundle`によって、プロジェクト生成時の`bundle install`の発動を抑止します(後に`bundle install --path vendor/bundle`したいため)。 -dオプションは、使用するデータベースを指定するものです。MySQLの場合は`-d mysql`。何もつけなければ標準のSQLiteです。ここらへんはお好みで。 ### Gemfileの編集 以下に全文を晒しますが、`group :development do ~ end` と `group :development, :test do ~ end` のところだけ追記すればOKです。 ```rb:Gemfile source 'https://rubygems.org' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '4.0.0' # Use postgresql as the database for Active Record gem 'pg' # Use SCSS for stylesheets gem 'sass-rails', '~> 4.0.0' # Use Uglifier as compressor for JavaScript assets gem 'uglifier', '>= 1.3.0' # Use CoffeeScript for .js.coffee assets and views gem 'coffee-rails', '~> 4.0.0' # See https://github.com/sstephenson/execjs#readme for more supported runtimes # gem 'therubyracer', platforms: :ruby # Use jquery as the JavaScript library gem 'jquery-rails' # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks gem 'turbolinks' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder gem 'jbuilder', '~> 1.2' group :doc do # bundle exec rake doc:rails generates the API under doc/api. gem 'sdoc', require: false end group :development do gem 'coffee-rails-source-maps' # CoffeeScriptのソースマップを出力するために必要 end group :development, :test do gem 'spring' gem 'rspec-rails' gem 'cucumber-rails', require: false, git: 'https://github.com/cucumber/cucumber-rails.git' gem 'factory_girl_rails' gem 'database_cleaner', github: 'bmabey/database_cleaner' gem 'rb-fsevent', :require => false if RUBY_PLATFORM =~ /darwin/i # OSXの場合のみ、ファイル変更検知のため(それ以外の環境ではポーリングになる) gem 'guard-rspec' gem 'guard-cucumber' gem 'guard-coffeescript' # app/assets以下はRailsが自動的にコンパイルしてくれるが、テスト用コードは対象外なので、これを入れる gem 'capybara', git: 'git://github.com/jnicklas/capybara.git' end # Use ActiveModel has_secure_password # gem 'bcrypt-ruby', '~> 3.0.0' # Use unicorn as the app server # gem 'unicorn' # Use Capistrano for deployment # gem 'capistrano', group: :development # Use debugger # gem 'debugger', group: [:development, :test] ``` ところどころgit指定しているのは、通常のバージョンではまだ互換性の問題を抱えていたりして、現時点ではエラーが起きるためです。そこでgitリポジトリから問題が解決されているであろう最新のmasterの内容を取ってきて、対応します。 そのうち、git指定なくても良くなるかもしれませんね。 ### Gemfileで指定したgemのインストール `--path vendor/bundle` を指定することで、プロジェクト内に閉じた形でGemをインストールできます。たまにこれをやってない人がいますが、今や常識ですので覚えましょう。 ```bash $ bundle install --path vendor/bundle ``` なお、現時点では`nokogiri`というGemがおそらくバージョン1.6.0以降のものがインストールされると思いますが、このインストール処理で、「固まったか!?」というくらい待たされます。 原因については[こちらの記事](http://qiita.com/suu_g/items/fcf549e16c797a9d7dc0)を参照してください。まぁ、辛抱強く待つか記事中の回避策をとられるかはお任せします。 ### テスト環境のセットアップ #### RSpecのインストール ```bash $ bundle exec spring rails g rspec:install ``` エラーが出ないか確認 ```bash $ bundle exec spring rspec No examples found. Finished in 0.0001 seconds 0 examples, 0 failures ``` rails g コマンドでモデルやコントローラを作成したときに、同時にRSpecのファイルも生成されるように設定します。 config/application.rb に次の記述を追加します。 ```rb config.generators do |g| g.test_framework = "rspec" end ``` 念のために、config/application.rbの全文を晒しましょうか。 ```rb:config/application.rb require File.expand_path('../boot', __FILE__) require 'rails/all' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(:default, Rails.env) module MyProject class Application < Rails::Application # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. # config.time_zone = 'Central Time (US & Canada)' # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de config.generators do |g| g.test_framework = "rspec" end end end ``` #### cucumberのインストール ```bash $ bundle exec spring rails g cucumber:install ``` エラーが出ないか確認 ```bash $ bundle exec spring cucumber Using the default profile... 0 scenarios 0 steps 0m0.000s Run options: --seed 28489 # Running tests: Finished tests in 0.002694s, 0.0000 tests/s, 0.0000 assertions/s. ``` #### Guardの初期設定 Guardfile生成 ```bash $ bundle exec guard init ```` Guardfileにspringを使うために、記述を修正する。 ```rb guard 'cucumber', command_prefix: 'spring', bundler: false do ``` ```rb guard 'rspec', spring: true do ``` また、テスト用CoffeeScriptコード用の設定を記述する すでに記述されている `guard 'coffeescript', :input => 'app/assets/javascripts'` はコメントアウトする。 ``` # guard 'coffeescript', :input => 'app/assets/javascripts' guard 'coffeescript', output: 'spec/javascripts/compiled' do watch(%r{^spec/javascripts/(.*).coffee}) end ``` #### Factory Girlの設定 spec/spec_helperに以下を追加。 ```rb require 'factory_girl_rails' ``` features/support/env.rbに以下を追記。 ```rb require 'factory_girl_rails' ``` テストの際にテストデータがちゃんと読み込まれるように、 spec_helper.rb に以下を追記。 ```rb config.before(:all) do FactoryGirl.reload end ``` これで設定は終わりです。お疲れ様でした。 ## じゃ、テストするぞー。 次のように、Guardを起動すれば……。 ```bash $ bundle exec guard 05:54:31 - INFO - Guard is using TerminalTitle to send notifications. 05:54:31 - INFO - Running all features Disabling profiles... 0 scenarios 0 steps 0m0.000s 05:54:36 - INFO - Guard::RSpec is running 05:54:36 - INFO - Guard is now watching at '/Users/smith/dropbox/Program/MyProjects/' [1] guard(main)> ``` 途中でエラー(スタックトレースなど)が出ずに、最後に、`guard(main)>` というプロンプトが出ればOKです。 もし、エラーが出てしまった場合は、guardやspring、cucumber、RSpecなどのバージョン間の互換性がとれていない場合があります。ググると大抵、各プロジェクトのgithubのIssueとかで似たようなトラブル報告が引っかかるので、それを参考に対処しましょう。 さて、無事にプロンプトが出たと仮定しますが、使い方が分からない場合は、とりあえず`help`と入力しましょう。また、何も入力せずにリターンキー(エンターキー)を押すと、全てのテストを強制実行します。Guardを停止したい場合は`quit`と入力します。 Guardの起動中は、Guardがコンソールを占有するので、他にコマンドラインで作業したい場合は別のコンソールを立ち上げましょう。 さて、これでRSpecやらCucumberやらのファイルを追加・修正するたびにテストが走ります。もうやめられませんね。これは。 ## CoffeeScriptによるJavaScriptテストについて 今回は人の好みがあると思ってあえて紹介しませんでしたが、JavaScriptのテストも重要ですね。 JasmineとかMochaとか。ググればRails用のGemの紹介記事が見つかると思います。 で、JavaScriptのテストコードですが、やはりCoffeeScriptでスッキリ書きたいものですね。 先ほどの以下の設定により、CoffeeScriptで書いたテストファイルの修正をGuardが検知して再コンパイルしてくれます。 ちなみに以下の設定はJasmine用ですが、Mochaの場合とか、必要に応じて書き換えてください。 ```rb guard "coffeescript", output: "spec/javascripts/compiled" do watch(%r{^spec/javascripts/(.*).coffee}) end ``` あと、テストというよりデバッグですが、coffee-rails-source-mapsを入れているおかげで、Development環境では常にCoffeeScriptのソースマップが生成されます。Chromeなどでソースマップを有効にしていれば、CoffeeScriptのままステップ実行したりできます。 ## FactoryGirlの使い方について 一番は[公式の情報](https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md)を読むことでしょう。一番詳しく書いてあります。が、ちょっと説明が簡素すぎますね。 こちらの[サイト様](http://blog.livedoor.jp/sasata299/)の記事でも分かりやすく解説されていますね。 [factory_girl で最低限知っておきたい4つの使い方](http://blog.livedoor.jp/sasata299/archives/51931175.html) また、テストの時だけでなく、開発時にデータ投入したい場合にもFactoryGirlを使うことができます。 Railsコンソールを起動して、 ```bash bundle exec spring rails console ``` FactoryGirlの機能を呼べば良いのです。 ```irb irb(main):1:0> FactoryGirl.reload irb(main):2:0> 10.times { FactoryGirl.create(:foo) } ``` 便利ですね。しかし、FactoryGirl……工場娘ですか。なかなか良い響きですね(何が ## その他トラブルシューティング ### プロジェクトのフォルダ名やパスを変更するとRspecでnokogiri関連のエラーが発生する。 →フォルダ名・パスを元に戻すか、vendor/bundleディレクトリを削除して、bundle updateしなおす。 ### $ bundle exec spring rspecした際に、以下のようなエラーが起きた場合。 ``` /Users/smith/dropbox/Program/RailsProjects/MyProject/01_Development/xxxx/yyyy/zzzz/MyProject/vendor/bundle/ruby/2.0.0/gems/spring-0.0.10/lib/spring/server.rb:40:in `initialize': too long unix socket path (117bytes given but 104bytes max) (ArgumentError) ``` プロジェクトの配置場所(パス)が深すぎるので、もっと浅い所に置くか、浅い所にここへのシンボリックリンクを作って、そのシンボリックリンクからプロジェクトを利用する。 ## ふっ、きさまらではわたしに…グッ…! こ、このわたしにかたひざをつかせるとはっっ! いかがでしたか。 「うまく動かないよ!」という方はコメントください。 また、「さいきょうを名乗るなど片腹痛いわ! お前よりオレの方が強いっ!」的なノウハウをお持ちの方もぜひコメントいただけると嬉しいです。 本記事をこまめにアップデートして、つねにさいきょうのおとこをめざしたいとおもいます。 |
|
| 868位 |
|
|||
|
11:39:27 |
(The University of Tokyo 所属) |
|
_この記事は2013年の情報オリンピック夏季セミナーの発表のために
## 動機 Haskellには競技プログラミングに使えるCoolな機能が沢山あります。 パターンマッチやプレースホルダ、パターンガード、柔軟な関数合成などです。 下はAtCoder Regular Contest #14のB問題に対する回答です。 再帰や場合分けがかなりシンプルに書けます。 ```haskell:ARC14B import Control.Monad import Control.Applicative import Data.List main :: IO () main = do n <- readLn (w:ws) <- replicateM n getLine putStrLn $ case check [w] ws True of Nothing -> "DRAW" Just True -> "WIN" Just False -> "LOSE" check _ [] _ = Nothing check dict@(la:_) (w:ws) b | last la /= head w = Just b | w `elem` dict = Just b | otherwise = check (w:dict) ws (not b) ``` このコードを見た人は早速Haskellで問題を解いてみたくなってしまうに違いありません。 そして書き始めたとき、ある問題に気づきます。 『入力の受け取り方が分からない(^_^;)』 名著『すごいHaskell楽しく学ぼう』でも入力が出来るようになるのは全15章中の8章とかなり後ろの方です。 これには勿論理由があり、Haskellの素ん晴らしい性質の一つである__参照透過性__と、IOによる副作用を共存させる仕組みとしてモナドというものが用いられますが、こいつがそれなりにややこしく理解に時間が掛かるからなのです。 競技プログラミングに使う分にはモナドを理解する必要は全くなく、いくつかある入力の方法を覚えてしまえばそれで終わりなので、この記事ではそのような方法を紹介したいと思います。 ## do記法 まずHaskellでの対話環境であるghciを起動して行入力関数であるgetLine関数の型を調べてみましょう。 ``` ghci> :t getLine -- :t はgetLineの型を返す。 getLine :: IO String -- getLineの型は「IO String」である。 ``` これは`IO String`型であって`String`型ではないので`String`型を取る関数にこれを渡しても型エラーが出てしまいます。do記法を使うことで`IO String`型から`String`を取り出すことができます。 ```haskell:do記法による入力 main = do 進捗 <- getLine -- input: 進捗 putStrLn (進捗 ++ "ダメです!") -- output: 進捗ダメです! ``` 一行に空白区切りの複数の入力がある場合にはwords関数を用いて文字列の配列に変換出来ます。Haskellの関数合成は`.`を用いて`func2 . func1`と表すことが出来ますが、IOなどに包まれた型の中身をIOから取り出して関数を作用させた後またIOにしまうには`<$>`を用います。わかりにくいですが、`IO String`の`String`の部分だけに関数を適用し、また`IO なんとか`に戻すことが出来るということです。`<$>`と`words`を使うことで`IO String`を`IO [String]`に変換出来ます。 ```haskell:空白区切りの入力 import Control.Applicative main = do ss <- words <$> getLine -- input: hoge fuga piyo putStrLn (head ss) -- output: hoge ``` ```haskell:複数の数字が区切られている時 import Control.Applicative main = do ss <- (map read . words) <$> getLine -- input: 123 456 789 -- ss = [123, 456, 789] putStrLn $ show $ foldl (+) 0 ss -- output: 1368 ``` `putStrLn $ show $ foldl (+) 0 ss`というのは、foldlに(+)と0を部分適用した関数とshowという関数とputStrLnという関数を関数合成した関数に`ss`という引数を与えるという意味です。 `<-`はパターンマッチを利用してリストの中身を個別の変数に束縛することが出来ます。 ```haskell:パターンマッチ import Control.Applicative main = do [a,b] <- words <$> getLine -- 空白区切り文字列の1番目と2番目をa,bに束縛する (c:cs) <- getLine -- 入力文字列の最初の文字と後ろの文字とそれに続く文字列をc,csに束縛する puts a -- input: ちなつ あかり, output: ちなつ puts cs -- input: コモナド, output: モナド ``` パターンマッチに失敗すると実行時エラーが出るので一般的なプログラムを書くときは注意が必要です。競技プログラミングで入力フォーマットが保障されている時などは気にしなくてよいでしょう。 ## モナド型クラスメソッド do記法は実はモナドの合成のための専用構文です。この専用構文を用いないと前述のコードは下のようになります。 `>>=`の型は以下のとおりで、`getLine >>= putStrLn . func`は`func`に入力を渡して変換した文字列を出力できます。 ``` (>>=) :: (Monad m) => m a -> (a -> m b) -> m b ``` ```haskell:普通の入力 main = getLine >>= putStrLn . (++ "ダメです!") -- input: 進捗 -- output: 進捗ダメです! ``` ```haskell:区切られた入力 main = getLine >>= putStrLn . (unwords . map reverse) . words -- input: hoge fuga piyo -- output: egoh aguf oyip ``` ```haskell:複数の数字が区切られている時 main = getLine >>= putStrLn . solve . map read . words solve :: [Int] -> String solve = show . foldl (+) 0 -- input: 123 456 789 -- output: 1368 ``` ```haskell:テンプレート main = getLine >>= putStrLn . solve solve :: String -> String ``` 競技プログラミングの範囲だとdo記法よりも簡潔で綺麗になる場合が多いので、出来れば使えるようになりたいテクニックです。 ## 便利関数 ghciの`:t`を使うと関数の型がわかります。関数の機能と使い方は型と名前からなんとなく推測してください。 ### putStr 文字列を末尾に改行を付加しないで出力します。 ### putChar 文字を末尾に改行を付加しないで出力します。 Showのインスタンス型を受け取りshowした結果を表示します。 (GHCiで実行した時表示される文字列になります。) ### when BoolとIOアクションを受け取り、Bool値がTrueの場合にIOアクションを実行します。 ```haskell:when ghci Control.Monad> when (1 > 0) $ do putStrLn "Tlue" ``` ### interact `String`を引数に取り`String`を返す関数を渡すと、入力をその関数で変換した結果の文字列を出力してくれます。interactは終端文字が与えられるまで関数を繰り返しますが、遅延評価により入力文字列が必要になった時に入力を要求されます。 ```haskell:interact main = interact $ unwords . map reverse . words -- input: hoge fuga piyo -- output: egoh aguf oyip ``` ### readLn 入力の型を推論します。 ```readLn main = do n <- readLn -- input: 55 putStrLn . show $ n * n -- output: 3025 ``` ### getContents 終端文字までの全ての文字列(改行含む)を得られます。 ```haskell:getContents main = do s <- getContents -- input: 1234\n5678\n90 putStrLn . show . foldl (+) 0 . map read . lines $ s -- output: 7002 ``` ### mapM_ リストの要素に対してIOアクションを実行できます。 ```haskell:mapM ghci> mapM_ print [1, 2, 3] 1 2 3 ``` ### replicateM N行の入力を取るのに便利です。 ```haskell:replicateM import Control.Monad main = do s <- replicateM 3 getLine -- input: 123\n456\n789 -- s == ["123", "456", "789"] putStrLn . show . foldl (+) 0 . map read $ s -- output:1368 ``` ## 具体例 現在(2013/8/30)でHaskellが使えるオンラインコンテストジャッジはAtCoderとCodeforcesだけです。英語は読めないのでAtCoderの問題を使って入力を受け取ってみましょう。 ### ARC14A 1. 1行目にはたこ焼きの番号を表す整数 N(1≦N≦1000) が与えられる。 ```plain:入力例 N ``` ```haskell:実装例 main = do n <- readLn putStrLn if even n then "Red" else "Blue" ``` ### ARC14B 1. 1行目には整数 N(1≦N≦100) が与えられる。 2. 2行目からN+1行までのN行では、文字列Wiが与えられる。 ```plain:入力例 N W_1 W_2 : W_N ``` ```haskell:実装例 import Control.Monad main = do n <- readLn ss <- repliciateM n getLine putStrLn $ solve n ss solve :: Int -> [String] -> String solve = undefined ``` ### ARC14D 1. 1行目には空白で区切られた整数all,N,Mが与えられる。 2. 2 行目からN+1行までのN行では整数Liが与えられる。 3. N+2行目からN+M+1行までのM行では整数xi,yiが与えられる。 ```plain:入力例 all N M L1 L2 : LN x1 y1 x2 y2 : xM yM ``` ```haskell:実装例 import Control.Monad import Control.Applicative main = do [all,n,m] <- map read . words <$> getLine l <- replicateM n readLn xy <- map (map read . words) <$> replicateM m readLn putStrLn $ solve all l xy solve :: Int -> [Int] -> [[Int]] -> [Int] solve = undefined ``` ## まとめ - Haskellは強い - でもIOが難しい - do記法が手続きっぽくて書きやすい - モナドを使うとシンプルに書けて格好良い - モナドが使えなくても入出力用の関数を使える場合がある - 競技プログラマで使うようなパターンは限られているので覚えてしまおう |
|
| 869位 |
|
|||
|
23:07:21 |
(フリーランス 所属) |
|
※動作確認は不十分のため、動作しない or 挙動がおかしい場合は公式ドキュメントなどを見て実装することをお勧めします。
# Tweetボタン ## URL形式 ``` https://twitter.com/intent/tweet?url=<URL>&text=<テキスト>&via=<ユーザーID>&hashtags=<タグ名>&related=<ユーザーID> ``` ## URL例 <https://twitter.com/intent/tweet?url=https%3A%2F%2Fparse.com&text=テストテスト&via=bluesbuger&hashtags=テスト,hogehoge&related=bluesbuger> ## クエリパラメータ一覧 | パラメータ名 | 説明 | データ形式 | |:-----------|:-------------|:---| | url | 付与するURL | URL | | via | 付与するアカウント名 | アカウント名 | | text | ツイートするテキスト | 文字列 | | related | 投稿後にフォローさせたいユーザーID | アカウント名 | | hashtags | 付与するハッシュタグ。[カンマで複数指定可] | 文字列 | # Facebookシェア ## URL形式 ``` https://www.facebook.com/sharer/sharer.php?u=<URL> ``` ## URL例 <https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fparse.com> ## クエリパラメータ一覧 | パラメータ名 | 説明 | データ形式 | |:-----------|:-------------|:---| | u | シェアするURL | URL | # google plus共有 ## URL形式 ``` https://plus.google.com/share?url=<URL> ``` ## URL例 <https://plus.google.com/share?url=https%3A%2F%2Fparse.com> ## クエリパラメータ一覧 | パラメータ名 | 説明 | データ形式 | |:-----------|:-------------|:---| | url | シェアするURL | URL | # LINEで送る ## ■WEBブラウザを開く ### URL形式 ``` http://line.me/R/msg/<CONTENT TYPE>/?<CONTENT KEY> ``` ### URL例 <http://line.me/R/msg/text/?てすてすてす> ### クエリパラメータ一覧 | パラメータ名 | 説明 | 値 | |:-----------|:-------------|:---| | <CONTENT TYPE> | テキスト情報を送るときに指定します。 | text | | <CONTENT KEY> | パーセントエンコーディング(utf-8)したテキスト情報の値を指定します。原則、ページタイトル、ページURLの指定が可能です。ページに関係ない情報の指定はガイドラインで禁止されています | [任意の値] | ## ■アプリをダイレクトに開く ### URL形式 ``` line://msg/<CONTENT TYPE>/<CONTENT KEY> ``` ### URL形式 <line://msg/text/てすてすてす> ### クエリパラメータ一覧 | パラメータ名 | 説明 | 値 | |:-----------|:-------------|:---| | <CONTENT TYPE> | テキスト情報を送るときに指定します。 | text or image | | <CONTENT KEY> | パーセントエンコーディング(utf-8)したテキスト情報の値を指定します。原則、ページタイトル、ページURLの指定が可能です。ページに関係ない情報の指定はガイドラインで禁止されています | [任意の値] | # mixiボイス ## URL形式 ``` https://mixi.jp/simplepost/voice?status=<テキスト> ``` ## URL例 <https://mixi.jp/simplepost/voice?status=テストテスト> ## クエリパラメータ一覧 | パラメータ名 | 説明 | データ形式 | |:-----------|:-------------|:---| | status | ボイス本文 | 文字列・全角150文字まで | # メールで共有 ### !! 注意 mailto:によるメール送信で文中に日本語がある場合、Windowsでメールクライアントによっては文字化けする。全メールクライアントに対応するのはできないので、文字化けせずにメールで共有させたい場合はメールフォームを使うなどを考える。 参考:http://maruta.be/intfloat_staff/107 ## URL形式 ``` mailto:<メールアドレス>?subject=<タイトル>&body=<メール本文>&cc=<メールアドレス>&bcc=<メールアドレス> ``` ## URL例 <a href="mailto:?subject=動画を共有します&body=この動画マジおすすめ!%0D%0Ahttps://www.youtube.com/watch?v=ngOHl2aGcvQ">mailto:?subject=動画を共有します&body=この動画マジおすすめ!%0D%0Ahttps://www.youtube.com/watch?v=ngOHl2aGcvQ</a> ※メールで共有の場合は宛先のメールアドレスを空にする ## クエリパラメータ一覧 | パラメータ名 | 説明 | データ形式 | |:-----------|:-------------|:---| | subject | タイトル | 文字列 | | body | 本文 | 文字列 | | cc |カーボンコピー[カンマで複数指定可]|メールアドレス| | bcc |ブラインド・カーボンコピー[カンマで複数指定可]|メールアドレス| #参考 Tweetボタン <https://dev.twitter.com/docs/tweet-button> Facebookシェア <https://developers.facebook.com/docs/plugins/share-button/#faqdialog> LINEで送る <http://media.line.me/howto/ja/> mixiボイス <http://developer.mixi.co.jp/connect/mixi_plugin/simplepost/spec_of_simplepost/#toc-3> mailto Protocol <http://msdn.microsoft.com/en-us/library/aa767737%28v=vs.85%29.aspx> |
|
| 870位 |
|
|||
|
12:29:43 |
(ヤフー株式会社 所属) |
|
Mavericksで標準インストールされているVimではクリップボード連携ができません。 クリップボード連携できるようになるとVimで書いたプログラムなどをコピーしてその他のエディターなどに貼り付けることができるようになります。 特に何もしていないVimでは以下のように表示されると思います。 ```Bash $ vim --version | grep clipboard -clientserver -clipboard +cmdline_compl +cmdline_hist +cmdline_info +comments -xterm_clipboard -xterm_save #-clipboard=クリップボードが無効 ``` **おすすめのサービス:[MyBooks](http://mybooks.strikingly.com/)** ## 手順 Homebrewを使って新しいVimを入れます。 ```Bash $ brew update $ brew install vim ``` インストールされたことを確認します。 ```Bash $ brew list vim /usr/local/Cellar/vim/7.4.161/bin/xxd /usr/local/Cellar/vim/7.4.161/bin/vimtutor /usr/local/Cellar/vim/7.4.161/bin/vimdiff /usr/local/Cellar/vim/7.4.161/bin/vim /usr/local/Cellar/vim/7.4.161/bin/view /usr/local/Cellar/vim/7.4.161/bin/rvim /usr/local/Cellar/vim/7.4.161/bin/rview /usr/local/Cellar/vim/7.4.161/bin/ex /usr/local/Cellar/vim/7.4.161/share/man/ (113 files) /usr/local/Cellar/vim/7.4.161/share/vim/ (1496 files) ``` インストールしたvimがclipboard連携できるか確認します。 ```Bash $ /usr/local/Cellar/vim/7.4.161/bin/vim --version | grep clipboard +clipboard +iconv +path_extra -toolbar +eval +mouse_dec +startuptime -xterm_clipboard # +clipboardになっているのでOK ``` 既存のvimを置き換えます。 ```Bash $ sudo mv /usr/bin/vim /usr/bin/old_vim $ sudo ln /usr/local/Cellar/vim/7.4.161/bin/vim /usr/bin/ ``` きちんと配置できたことを確認します。 ```Bash $ vim --version | grep clipboard +clipboard +iconv +path_extra -toolbar +eval +mouse_dec +startuptime -xterm_clipboard # +clipboardになっていることを確認 ``` ここまでできたらVimの設定です。 `~/.vimrc`に以下の設定を追加しましょう。 ``` set clipboard+=unnamed ``` これで終了です。 vimでコピーした文字列がOSレベルのクリップボードで保持できるようになります。 tmux内で使おうとしたらエラーが出ました。[次の記事](http://qiita.com/shoma2da/items/853074f05445722b496e)で書きました。 [shoma2daをフォロー](https://twitter.com/intent/user?screen_name=shoma2da) |
|
| 871位 |
|
|||
|
00:54:32 |
|
|
## はじめに
JavaScriptでオブジェクト指向言語の継承に相当する概念を実装する方法は、大きく分けて4つあります。実務上はライブラリを使ったり、TypeScriptを使ったりと、直接意識する必要があることは少ないわけですが、それだといつまでたっても「JavaScriptにおける継承」を理解できません。ES6で、シンタックスシュガーとしてのclass / extendも導入される可能性も高そうですが、そんな今だからこそ「本来はどう書くのか?」を整理してみることにしました。 1) コンストラクタを使いprototypeチェーンを使って継承する 2) コンストラクタは使うが、prototypeチェーンは使わず、prototypeを直接拡張する 3) コンストラクタは使わず、prototypeチェーンを使って継承する 4) コンストラクタもprototypeも使わず、直接オブジェクトを拡張する JavaScriptという言語は、prototypeを使って継承するように作られていて、クラスを使って継承する言語ではありません。ただ、良く知られているようにコンストラクタ関数からnew演算子を使ってインスタンスを生成することはでき、この場合、クラスベースの言語の「クラス - インスタンス」と同等のことをすることはできます。そして、コンストラクタ(クラスベース言語のクラスに相当)を継承したコンストラクタを作ることで、クラスベース言語の継承と同等のことを行うこともできます。(1のパターン) ただ、JavaScriptではオブジェクトを継承をするのに、コンストラクタは必須ではなく、オブジェクトから別のオブジェクトを直接継承することができるのです(3のパターン)。しかも、上述の1のパターンのおいて、コンストラクタ(のprototype)の継承は、3のパターンと同等の仕組みが使われているわけで、その意味で、JavaScriptの継承は基本的には3のパターンであり、1はそこから派生した機能と言うことができるのです。 1), 3)のようにprototypeチェーンを使うことのメリットは、継承先オブジェクト生成後に継承元オブジェクトを変更したときに、リアルタイムで継承したオブジェクトに反映されること、無駄なメモリを使わないことなどがあります。 ただし、この方法では直線的な継承しかできず、クラスベース言語の多重継承のようなことはできません。また、オブジェクトをHashMap的に使おうとした時に、prototypeの継承が含まれると、プロパティを列挙するのが面倒という問題があります。そこで、しばしばprototypeチェーンによる継承の代わりに、「拡張」を使う方法がとられます( 2と4のパターン)。ここで「拡張」というのは、JavaScriptの言語仕様上の用語ではないのですが、jQuery.extendやangular.extendのように、オブジェクトのプロパティをコピーする手法のことを言っています。サンプルプログラムでは、$.extendという記述にしていますが、他のライブラリのextendでも同じです。 ちなみに、プログラム中に出てくるobjectFromProtoの定義については、最後に説明します。 ## 1) コンストラクタを使いprototypeチェーンを使ってクラス継承する ```js function inheritConstructor(Parent, Constructor, prop){ Parent = Parent || Object; Constructor.prototype = $.extend(objectFromProto(Parent.prototype), prop, { __super: Parent.prototype, constructor: Constructor }); return Constructor; } Child = inheritConstructor(Super, function Child(){}, {}); ``` ## 2) コンストラクタは使うが、prototypeチェーンは使わず、prototypeを直接拡張する ```js function extendConstructor(Parent, Constructor, prop){ var Parent = Parent || Object; $.extend(Constructor.prototype, Parent.prototype, prop, { __super: Parent.prototype, constructor: Constructor }); return Constructor; } Child = extendConstructor(Super, function Child(){}, {}); ``` ## 3) コンストラクタは使わず、prototypeチェーンを使って継承する ```js function inheritObject(parent, prop){ return $.extend(objectFromProto(parent), prop); } child = inheritObject(parent, {}); child = inheritObject(Parent.prototype, {}); ``` ## 4) コンストラクタもprototypeも使わず、直接オブジェクトを拡張する ```js function extendObject(parent, prop){ return $.extend({}, parent, prop); } child = extendObject(parent, {}); child = extendObject(Parent.prototype, {}); ``` ちなみに、objectFromProtoは、以下のように定義されているものとします。 ```js if(typeof Object.create === 'function'){ var objectFromProto = function(proto){ return Object.create(proto); } }else{ var objectFromProto = function(proto){ var Temp = function(){}; Temp.prototype = proto; return new Temp(); } } ``` この関数は、protoというオブジェクトがあったときに、x.__proto__ = protoであるような新しいオブジェクトxを作るものです。xは、protoを継承し、x.aが独自に定義されていなければ、x.aで、proto.aにアクセスすることができます。JavaScriptの継承のコアとなる関数です。 今、ほとんどの処理系ではObject.createが定義されているので、これを使えば良いのですが、古い処理系では、コンストラクタを使った実装が使われます。一見してトリッキーな実装ですが、要するにJavaScriptでは、コンストラクタからのインスタンスの生成にも、prototypeチェーンが使われているわけであり、これを使った実装ということになります。クラスベース言語では、継承とインスタンス生成は全く違う仕組みであるわけですが、これが同じ仕組みで実現されているところが、JavaScriptの味わい深いところではないでしょうか。 余談ですが、ES6でclass / extendsキーワードが導入される予定です。これは本質的には、1)のシンタックスシュガーであり、このため、もはや1)や3)のパターンで「クラス」が使われていると言っても差し支えないと思うのですが、現状では混乱を招くため、JavaScriptにはクラスという概念はないものとして説明しています。 |
|
| 872位 |
|
|||
|
18:20:37 |
(フリープログラマ 所属) |
|
ドキュメント変換ツール[Pandoc](http://johnmacfarlane.net/pandoc/)を用いてMarkdownやLaTeX文書などからWord文書を作成する方法を紹介します。 以下の手順はターミナル(コマンドプロンプト)の操作ができることを前提とします。 PandocはMarkdown, LaTeXの他にもreStructuredText, HTMLなども入力できます。 詳細は [Pandocユーザーズガイド日本語版](http://sky-y.github.io/site-pandoc-jp/users-guide) をご覧ください。 ## 1. Pandocをインストールする Windows/Macの場合は以下からインストーラをダウンロードできます: * [Downloads - pandoc - general markup converter - Google Project Hosting](https://code.google.com/p/pandoc/downloads/list) Linuxの場合は、Debian/Ubuntuの場合は下記でインストールできるようです(ただしバージョンが古いです): sudo apt-get install pandoc その他の場合は、Haskell Platformに含まれるcabalというパッケージマネージャを使ってインストールできます。詳細は下記をご覧ください: * [HTML - 多様なフォーマットに対応!ドキュメント変換ツールPandocを知ろう - Qiita](http://qiita.com/sky_y/items/80bcd0f353ef5b8980ee) ## 2. テンプレートを用意する PandocにおけるWord文書用デフォルトテンプレートはヘッダに色が付いていて使いにくいので、テンプレートを変えます。 本来は自分でテンプレートを作る必要がありますが、3分クッキングよろしく予め用意したテンプレートを使いましょう。 (このテンプレートは、デフォルトテンプレートのヘッダの色を黒にしただけのものです。) 下記からテンプレート`reference_simple.docx`を直接ダウンロードできるので、保存します(以下、`~/Downloads`フォルダに入れたとします)。 * <https://github.com/sky-y/pandoc-templete/raw/master/docx/reference_simple.docx> これを、 * Mac/Linuxの場合は`~/.pandoc`に * Windows7/8の場合は`C:\Users\USERNAME\AppData\Roaming\pandoc`に 入れ、`reference.docx`にリネームする必要があります。以下、Macの場合を例にしてコマンドを示します。 mkdir ~/.pandoc # まだ作っていない場合 mv ~/Downloads/reference_simple.docx ~/.pandoc/reference.docx 以上で、準備が整いました。 ### 14.04.27 追記 自分で作成する場合は、 1. 下記のコマンドを実行してreference.docxを取得する pandoc --print-default-data-file reference.docx > reference.docx 2. Word上でreference.docxを編集する という手順でreference.docxを得ます。 ## 3. Markdown/LaTeX文書を作成する お好きなMarkdown/LaTeX文書を作成します。 ここでは、例として、[Pandocユーザーズガイド日本語版](http://sky-y.github.io/site-pandoc-jp/users-guide)の元となっているMarkdown文書をダウンロードします。 curl -O https://github.com/sky-y/pandoc/raw/master/doc-jp/README-JP.md ついでに、後の例のためにLaTeX文書をここで生成しておきます(注意:`-s`オプションを付けないと、完全なLaTeX文書が生成されません)。 pandoc -s README-JP.md -o README-JP.tex ## 4. Markdown/LaTeX文書を変換する いよいよ、Pandocを用いて文書を変換します。 ### Markdownの場合 pandoc README-JP.md -o README-JP.docx ### LaTeXの場合 pandoc README-JP.tex -o README-JP.docx 以上です。簡単ですね。 仕上がりを画像で示します。  なかなか良い仕上がりですね。日本語も正しく変換されているようです。 ## まとめ Pandocを使ってMarkdownやLaTeXなどの文書をWord文書に変換する方法を見てきました。 他にPandocで何ができるかを知りたいは、以下のリンクを参照してみてください。 * [Pandocユーザーズガイド日本語版](http://sky-y.github.io/site-pandoc-jp/users-guide) * [HTML - 多様なフォーマットに対応!ドキュメント変換ツールPandocを知ろう - Qiita](http://qiita.com/sky_y/items/80bcd0f353ef5b8980ee) |
|
| 873位 |
|
|||
|
10:27:57 |
(esa LLC 所属) |
|
最近Mac+Heorku+PostgreSQLでアプリを作ることが多いので、GUIクライアントをいくつか試しました。
なお、他のDBでは - MySQL: [Sequel Pro](http://www.sequelpro.com/) - SQLite 3: [Base 2](http://menial.co.uk/base/) を使っています。 # [PG Commander, a PostgreSQL client for Mac](https://eggerapps.at/pgcommander/)   - 今のところ一番使いやすい - 有料(今日現在: ¥5,200) - もうちょっと使ったら払ってしまおうかと思っている - 無料でも使える - 接続先の保存は5件まで - 複数ウィンドウを開けない - (`PG Commander.app`を複製することで回避可能?) - HerokuのPostgreSQLに接続する方法 - `heroku config`して、`DATABASE_URL`をクリップボードにコピー(Command+C) - PG Commanderを起動して、`New Favorite`を押すとDB情報が入力済になっている - あとはNicknameを付けて接続するだけ # [Induction; A Polyglot Database Client For Mac OS X](http://inductionapp.com/) - オープンソース - まだαリリースで不安定 - "Induction supports PostgreSQL, MySQL, SQLite, Redis, and MongoDB" - [hangs when connect to heroku postgres · Issue #67 · Induction/Induction](https://github.com/Induction/Induction/issues/67) - 最新版だとherokuのPostgreSQLにつなぐとフリーズするので、ここで別のビルドが配布されてる - HerokuのPostgreSQLに接続する方法 - `DATABASE_URL`を直接入力可能 # [pgAdmin: PostgreSQL administration and management tools](http://www.pgadmin.org/)  - UIがアレ - HerokuのPostgreSQLに接続する方法 - 地道にusername等を入力 - 大量のDBから自分のDBを探すのが面倒 # [DBeaver - Universal Database Manager](http://dbeaver.jkiss.org/)  - データのロードが遅い - データの編集方法がカラムごとにポップアップが出て面倒 - HerokuのPostgreSQLに接続する方法 - 地道にusername等を入力 - driverでSSLオプションを選択する必要がある # [Tuples - PostgreSQL database management tool for Mac OS X](http://www.tuplesapp.com/) - 有料(30USD, 30日のトライアルあり) - "first native client for PostgreSQL for Mac OSX"らしい。 - データをちょっと確認したい時に、毎回SQL書く必要があって面倒 # See also: - [Community Guide to PostgreSQL GUI Tools - PostgreSQL wiki](http://wiki.postgresql.org/wiki/Community_Guide_to_PostgreSQL_GUI_Tools#PG_Commander) |
|
| 874位 |
|
|||
|
13:18:34 |
(フリーランス 所属) |
|
初投稿。iOSアプリ開発ビギナーで至らないところもあるかと思いますがよろしくお願いします(ツッコミとか)。 ---- データの永続化を行うにあたり、iOSではフラットファイル、SQLite、CoreDataを使うことが出来ます。前者2つはウェブ・アプリケーションで多少馴染みがあるものの、Cocoaプラットフォームでのデータ永続化はさっぱりわからずCoreDataって何?という状態からスタートしました。 このドキュメントでカバーすること: * CoreData, mogenerator, MagicalRecordがどんなものであるかとその使い方 このカバーしないこと: * Xcodeの基本的な使い方 * CoreData, mogenerator, MagicalRecord以外のライブラリのインストール方法 ---- # 登場人物 * [CoreData](https://developer.apple.com/library/mac/documentation/cocoa/conceptual/coredata/cdProgrammingGuide.html) * [mogenerator](http://rentzsch.github.io/mogenerator/) * [MagicalRecord](https://github.com/magicalpanda/MagicalRecord) ## CoreData CoreDataプログラミングガイドより。 >Core Dataフレームワークは、オブジェクトの永続性をはじめ、ライフサイクルやオブジェクトグラフの管理に関連して、頻繁に必要となるさまざまな処理機能を、汎用化、自動化して提供します。 Appleは **CoreDataはORMではないと明言しています** が、実際のところデータベースの一種(関連データベース=Relational Databaseではない)であり、プログラムからモデルクラスを通して操作する点はORMの概念と似通っています。 例えばPython / Djangoではモデルの作成を次のように行いますが、 ```python bike = Bike() bike.brand = u"Ravanello" bike.name = u"SAT" bike.save() ``` CoreData + MagicalRecordでは次のように行います。 ```objectivec Bike *bike = [Bike MR_createEntity]; bike.brand = @"Ravanello"; bike.name = @"SAT"; [[NSManagedObjectContext MR_context] MR_saveToPersistentStoreAndWait]; // この辺ちょっとうろ覚え ``` 個人的にはほとんど同じ概念で扱えるんじゃないかと思ってます。 ## mogenerator Xcode上でのモデル定義は、モデルエディタによりGUIで行い、NSManagementObjectのサブクラスとして、ヘッダ、実装ファイルに出力することが出来ます。この機能は一見便利なのですが、出力したファイルは次の問題があるように思います。 * Xcodeからファイルを出力すると既存のファイルが上書きされる * 既存のファイルに実装があった場合、上書きで削除される * 結果として、モデルクラスに独自の実装を追加することが出来ない(カテゴリ作って拡張したけど悪手すぎた) mogeneratorはカスタマイズ可能なモデルクラスを作成することを目的として開発されており、Xcodeで作成したモデル定義を元に次の2つのクラスを作成します。 * mogenerator独自の内部クラス * カスタマイズ用のサブクラス カスタマイズ用のサブクラスには任意の実装を追加することが出来て、mogeneratorでファイルの再出力を行っても上書きされません。つまり、mogeneratorを使用することでカスタマイズの柔軟性を確保することが出来ます。 ## MagicalRecord [MagicalRecord本家サイト](https://github.com/magicalpanda/MagicalRecord)によれば、Ruby on RailsのActiveRecordにインスパイアされたCoreDataラッパーライブラリである、となります。 このライブラリを使用すると、CoreDataの初期化手順、モデルのフェッチなどを短い記述で行えるようになり、要はCoreDataの面倒くさい手続きを代わってくれるライブラリです(と個人的には考えています)。 # インストール ## mogenerator 公式の配布物からインストールするか、[homebrew](http://brew.sh/)からインストールする。  brewの場合はbrewコマンドからどうぞ。 <pre><code>brew install mogenerator</code></pre> ## MagicalRecord CocoaPodsからインストールする。Podfileに以下を書きます。 ```ruby pod 'MagicalRecord' ``` pod installします。 ```bash /usr/bin/pod install ``` インストールすると.pchファイルに次の行が追加されているので確認します。 ```objectivec #import "CoreData+MagicalRecord.h" ``` # 使い方 ## モデルの定義 ## mogeneratorによるヘッダ、実装ファイル出力 mogeneratorによるファイル出力は、入力にXcodeで作成したモデルファイルを、出力先に適当なディレクトリを指定します。 こんな感じ。 ```bash /usr/bin/mogenerator \ --template-var arc=true \ -m ./example/Model/Model.xcdatamodeld \ -O ./example/Model/ ``` オプションを1つずつ超簡単に解説します。 ```bash --template-var arc=true ``` ARC環境で利用するには`--template-var arc=true`を指定します(これ以上のことは調べていません!)。 ```bash -m ./example/Model/Model.xcdatamodeld ``` Xcodeで作成したモデルファイルを指定します。 ```bash -O ./example/Model/ ``` ヘッダ、実装ファイルの出力先を指定します。 mogeneratorは機械生成によるmogenerator内部クラスを **machine file**, カスタマイズするためのサブクラスファイルを **human file** と呼んでいます。machine fileはファイル名のプリフィクスに`_`が付いていて、内部クラスとして認識出来る形になっています。一方human fileにはXcodeで定義したモデルクラス名がそのまま使用されています。 例えばモデル定義が`Bike`だとしたら、 * machine file: `_Bike.h`, `_Bike.m` * human file: `Bike.h`, `Bike.m` となります。 なお、ファイル管理の都合でmachine file, human fileの出力先を分けたい場合には、`--machine-dir`と`--human-dir`オプションを利用してください。 mogeneratorの実行タイミングは用途によりけりですが、mogeneratorからのファイル出力はbuild phaseの1つとして加えても良いですし、必要なタイミングで都度コマンドラインから実行しても良いと思います。 ## MagicalRecordによるCoreDataの初期化 CoreDataの初期化は`application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions`でMagicalRecordのセットアップメソッドを呼び出します。`setupCoreDataStackWithStoreNamed:`メソッドでは、CoreDataのバックエンドとしてSQLiteを使用し、任意のデータベースファイル(ここではdb.sqlite)を作成することを意味します。 ```objectivec - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // initialize coredata [MagicalRecord setupCoreDataStackWithStoreNamed:@"db.sqlite"]; // (snip) } ``` 次に、アプリケーション終了時に正しくデータを永続化するため`applicationWillTerminate:(UIApplication *)application`内でMagicalRecordの`cleanUp`メソッドを呼び出します。 ```objectivec - (void)applicationWillTerminate:(UIApplication *)application { [MagicalRecord cleanUp]; } ``` ## モデルの作成、読み出し、更新、削除 ```objectivec // create Bike *bike = [Bike MR_createEntity]; // retrieve Bike *bike = [Bike MR_findFirstByAttribute:@"id" withValue:@1]; // update Bike *bike = [Bike MR_findFirstByAttribute:@"id" withValue:@1]; bike.name = @"Colnago"; [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait]; // この辺ちょっとうろ覚え // delete [bike MR_deleteEntity]; [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait]; // この辺ちょっとうろ覚え ``` ## モデルのカスタマイズ モデルをカスタマイズする場合には、mogeneratorが出力した **human file** を編集してください。次のファイルは某アプリの写真管理部分のソースコードです。 通常のObjective-Cプログラミングと同様に、ヘッダに宣言をして実装を行います。 Photo.h ```objectivec #import "_Photo.h" typedef NS_ENUM(NSUInteger, kMyBikePhotoSize) { kMyBikePhotoSizeSmall, kMyBikePhotoSizeMedium, kMyBikePhotoSizeLarge, kMyBikePhotoSizeOriginal }; @interface Photo : _Photo {} - (NSURL *)getThumbnailUrlWithSize:(enum kMyBikePhotoSize)photoSize; @end ``` Photo.m ```objectivec #import "Photo.h" @interface Photo () // Private interface goes here. @end @implementation Photo - (NSURL *)getThumbnailUrlWithSize:(enum kMyBikePhotoSize)photoSize { NSString *path = [NSString stringWithFormat:kMyBikeApiPhotoDetail, @"bike_thumbnail", self.id]; return [self urlWithPath:path photoSize:photoSize]; } @end ``` ---- というわけで私的メモ的なCoreData, MagicalRecord, mogeneratorの導入話はおしまいです。何か得ることがあったらまたアップデートすると思います。 |
|
| 875位 |
|
|||
|
20:12:11 |
|
|
# はじめに
 「[Capistrano](https://github.com/capistrano/capistrano) のバージョンが上がったので使おうとしたら丸一日ハマっていた。」 「な… 何を言ってるのか わからねーと思うが (ry」 ### Table of Contents 1. Gemfile 2. cap install 3. Capfile 4. config/deploy.rb 5. config/deploy/production.rb | staging.rb 6. 参考文献 基本的な使い方などは[README](https://github.com/capistrano/capistrano/blob/master/README.md)を読んでください。 まあ、読んでてもアレだったんですけどね。 # Gemfile Capistranoはどのような言語やフレームワークからでも利用可能」by [公式サイト](http://www.capistranorb.com/)とのことで、もはやRailsだけのものではありません(以前からですけど)。 よって、本体とは別に、プラグイン形式でGemを追加していきます。 ``` ruby group :development do # ... gem 'capistrano' gem 'capistrano-rails' gem 'capistrano-rbenv' gem 'capistrano-bundler' # ... end ``` `capistrano-rails`に気づかずにエラーと戦っている場合があるので、気をつけてください。 capistrano 2.x時代に`capistrano_colors`をもし使っているなら、もう必要ないので、削除してください。 # cap install `cap install`により以下のファイルが生成されますが、上書き保存されてしまうので、それぞれ別名で保存しておくことをおすすめします。 - Capfile - config/deploy.rb - config/deploy/production.rb - config/deploy/staging.rb # Capfile なんとなく存在していた`Capfile`ですが、今回のアップデートでは中身がガラッと変更されました。 以下がデフォで生成されます(2013-10-10現在) ``` ruby # Load DSL and Setup Up Stages require 'capistrano/setup' # Includes default deployment tasks require 'capistrano/deploy' # Includes tasks from other gems included in your Gemfile # # For documentation on these, see for example: # # https://github.com/capistrano/rvm # https://github.com/capistrano/rbenv # https://github.com/capistrano/chruby # https://github.com/capistrano/bundler # https://github.com/capistrano/rails/tree/master/assets # https://github.com/capistrano/rails/tree/master/migrations # # require 'capistrano/rvm' # require 'capistrano/rbenv' # require 'capistrano/chruby' # require 'capistrano/bundler' # require 'capistrano/rails/assets' # require 'capistrano/rails/migrations' # Loads custom tasks from `lib/capistrano/tasks' if you have any defined. Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r } ``` これを私は以下のように変更しました。 ``` ruby # Load DSL and Setup Up Stages require 'capistrano/setup' # Includes default deployment tasks require 'capistrano/deploy' require 'capistrano/rbenv' set :rbenv_type, :user # or :system, depends on your rbenv setup set :rbenv_ruby, '2.0.0-p247' require 'capistrano/bundler' require 'capistrano/rails' # Loads custom tasks from `lib/capistrano/tasks' if you have any defined. Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r } ``` # config/deploy.rb 2.x時代のdeploy.rb を使いまわすことは出来ません。 cap install にて生成されるものをベースに組み立てるのが吉です。 微妙に設定項目名が変更されているので、古いものと見比べながら設定して行きます。 このとき、一点、重要な変更が。 以前は`set :application`したものをローカル変数`application`として使えたのですが、今回からは`fetch(:application)`という形で呼び出します。 コレ非常に重要。 ### passengerユーザの場合 ``` ruby namespace :deploy do desc 'Restart application' task :restart do on roles(:app), in: :sequence, wait: 5 do # Your restart mechanism here, for example: execute :mkdir, '-p', release_path.join('tmp') # <= これを追加する execute :touch, release_path.join('tmp/restart.txt') end end # ... end ``` デプロイ時に、なぜか`tmp`ディレクトリを生成しないので、(誰かのプルリクエストが適用されるまでは)tmpディレクトリを作成するように追記しておくと良いです。 # config/deploy/(production.rb|staging.rb) これも生成されたファイルをよく読んでください。 難しくありません。ただ、`fetch(:user)`など、変数の呼び出しに注意してください。 # 参考文献 - [公式サイト](http://www.capistranorb.com/) - [Capistrano/capistrano - github](https://github.com/capistrano/capistrano) # 最後に capistranoはrailsのものだけじゃない、ということをよく理解させてくれるようなアップデートでした。 `capistrano-rails`というカタチで外出しされているのはとても良いことですね。 `database.yml`のsymlinkとか、migrationとかをサラッとやってくれるので助かります。 皆さんもぜひ、capistranoをv3.0にアップデートしましょうね! |
|
| 876位 |
|
|||
|
21:59:24 |
|
|
出来るとは聞いていたけど、やってみたら簡単さに感動したのでメモw
詳細は以下に書いてある。 [QA1176 Getting a Packet Trace](https://developer.apple.com/library/ios/#qa/qa1176/_index.html) まとめると、以下 1. Macを用意する 1. Xcode(4.2以降)をインストールする 1. iPhoneやiPadなどの端末(iOS5以降)をMacに接続する 1. iPhoneデバイスの仮想インターフェイス(Remote Virtual Interface)を作成する 1. 仮想インターフェイスをキャプチャする 1. 仮想インターフェイスを破棄する # 以下appleサイトからの引用 ## 仮想インターフェイス作成 >$ # First get the current list of interfaces. >$ ifconfig -l >lo0 gif0 stf0 en0 en1 p2p0 fw0 ppp0 utun0 >$ # Then run the tool with the UDID of the device. >$ rvictl -s 74bd53c647548234ddcef0ee3abee616005051ed > >Starting device 74bd53c647548234ddcef0ee3abee616005051ed [SUCCEEDED] > >$ # Get the list of interfaces again, and you can see the new virtual >$ # network interface, rvi0, added by the previous command. >$ ifconfig -l >lo0 gif0 stf0 en0 en1 p2p0 fw0 ppp0 utun0 rvi0 * -sで指定しているのは端末のidentifier * 色々確認方法はあると思うが、今回はOrganizerで端末のidentifierを確認 ## キャプチャ開始 >$ sudo tcpdump -i rvi0 -n * 後で、wiresharkで読み込んだりするのなら、-wでファイルに書き出すと良いかも * buffer sizeを変更してないとイケナイ場合は、-Bとか * パケットサイズが大きい時とか! * 個人的には結局以下のコマンドを叩いた ``` $ sudo tcpdump -i rvi0 -n -w hoge.pcap ``` ## 仮想インターフェイスの破棄 >$ rvictl -x 74bd53c647548234ddcef0ee3abee616005051ed > >Stopping device 74bd53c647548234ddcef0ee3abee616005051ed [SUCCEEDED] # まとめ * すごい便利(´ω` * そして、これが出来るようになったら以下のような事を検索する事になるのかな〜?(=w= * [tcpdump + wireshark で https な通信を解読する](http://qiita.com/ywatai@github/items/baddcf6988ec1f6ac014) 僕は、未だに解読出来る事の方が少ないですけどね(^q^; |
|
| 877位 |
|
|||
|
01:46:31 |
(JUBILEE WORKS, Inc. 所属) |
|
再利用可能なカスタムビュークラスを、Nib(xibファイル)を使って作る方法を考えます。例としてViewの上にLabelとButtonがひとつずつ乗っているカスタムビューを作ります。 サンプルコードが[こちら](https://github.com/gonsee/NibViewSample)にあります。  ## ルートのViewにカスタムクラスを指定する方法 - CustomView1.h, CustomView1.m, CustomView1.xibを作る - Interface Builderで図のようにView, Label, Buttonを配置する - ルートのViewのClassを`CustomView1`にする - File's Ownerはなし - LabelのoutletとButtonのactionをソースコード側とつなぐ ソースコードは以下のように実装します。 ```objc // CustomView1.h @interface CustomView1 : UIView @property (weak, nonatomic) IBOutlet UILabel *label; + (instancetype)view; @end ``` ```objc // CustomView1.m #import "CustomView1.h" @implementation CustomView1 - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { } return self; } - (void)awakeFromNib { [super awakeFromNib]; // 初期化 self.label.text = NSStringFromClass([self class]); } + (instancetype)view { NSString *className = NSStringFromClass([self class]); return [[[NSBundle mainBundle] loadNibNamed:className owner:nil options:0] firstObject]; } - (IBAction)buttonTapped:(id)sender { NSLog(@"tapped."); } ``` 使う側のクラスでは以下のようにします。 ``` CustomView1 *cv1 = [CustomView1 view]; [self.view addSubview:cv1]; ``` `-[NSBundle loadNibNamed:owner:options]`を呼ぶと`initWithCoder:`と`awakeFromNib`が呼ばれます。初期化の処理はoutlet等がインスタンス化された後に呼ばれる`awakeFromNib`の中でやるのが良いでしょう。 `[[CustomView1 alloc] init]`のように使いたい場合は指定イニシャライザの`initWithFrame:`をオーバーライドする必要があります。 ``` - (id)initWithFrame:(CGRect)frame { self = [[self class] view]; return self; } ``` この方法はNibから直接欲しいカスタムビュークラスをインスタンス化していてわかりやすいですが、カスタムビューを使う側はコードで書かなければならず、他のNib内で使う事ができません。 ## File's Ownerにカスタムクラスを指定する方法 - CustomView2.h, CustomView2.m, CustomView2.xibを作る - CustomView1と同様にInterface BuilderでView, Label, Buttonを配置する - File's OwnerにCustomView2を指定する - IB上のViewはCustomView2の`contentView`というoutletにつなぐ - Label, ButtonはCustomView1と同様につなぐ ソースコードは以下のように実装します。 ```objc // CustomView2.h @interface CustomView2 : UIView @property (weak, nonatomic) IBOutlet UIView *contentView; @property (weak, nonatomic) IBOutlet UILabel *label; @end ``` ```objc // CustomView2.m #import "CustomView2.h" @implementation CustomView2 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self p_commonInit]; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { [self p_commonInit]; } return self; } - (void)p_commonInit { NSString *className = NSStringFromClass([self class]); [[NSBundle mainBundle] loadNibNamed:className owner:self options:0]; self.contentView.frame = self.bounds; [self addSubview:self.contentView]; self.label.text = className; } - (IBAction)buttonTapped:(id)sender { NSLog(@"tapped."); } ``` Nibからインスタンス化したViewをselfにaddSubviewしているので、よけいなviewの階層がひとつ増えてしまいます。 しかし使う側は`[[CustomView alloc] init]`のようにコードから使う事もできますし、Interface Builderで配置したViewのクラスに`CustomeView2`を指定して利用する事もできます。前者の場合`initWithFrame:`が、後者の場合`initWithCoder:`が呼ばれるので、必要な処理は共通の初期化メソッドの中に書きます。 |
|
| 878位 |
|
|||
|
15:49:13 |
(株式会社ずんシステム 所属) |
|
多分現在主流のRSSリーダーであるFeedlyが少し前にようやくAPIを公開してくれたので、オレオレリーダーを作る為の下調べをしてみた。最初戸惑ったが色々API叩いてるうちに大体わかってきたのでメモる。
APIドキュメント: http://developer.feedly.com/v3/ #IDの種類と形式 - userId - `user/:userId` - feedId - `feed/:feedUri` - categoryId - `:userId/category/:categoryName` - 特殊カテゴリ: `global.all`, `global.uncategorized` - tagId - `:userId/tag/:tagName` - 特殊タグ: `global.saved` #認証手順 - 普通のOAuth2.0だが現在は`redirect_uri`が制限されているためサイト間認証は使えない。 - `http://localhost`や`urn:ietf:wg:oauth:2.0:oob`は許可されてるのでローカルアプリで使う分にはとりあえず問題ない。 - OAuthのプロセス中に出てくるclient_idだのclient_secretだのは全部Sandbox用の固定値を使えばとりあえず自分のアクセストークンを取る分には問題ない。 ## やってみる 1. https://cloud.feedly.com/v3/auth/auth?client_id=feedly&redirect_uri=http://localhost&scope=https://cloud.feedly.com/subscriptions&response_type=code&provider=google&migrate=false をブラウザで開く。(パラメータは全部固定でOK) 2. 認証後 `http://localhost/?code=XXX&state=` に飛ばされるのでアドレスバーから `XXX` の部分をコピーする。 3. https://cloud.feedly.com/v3/auth/token へ `client_id=feedly&client_secret=0XP4XQ07VVMDWBKUHTJM4WUQ&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fwww.feedly.com%2Ffeedly.html&code=XXX` の最後のXXX部分を先ほど取得したcodeの値で置き換えたものをPOSTする。こんな感じ>`$ curl -s -d 'client_id=feedly&client_secret=0XP4XQ07VVMDWBKUHTJM4WUQ&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fwww.feedly.com%2Ffeedly.html&code=XXX' https://cloud.feedly.com/v3/auth/token` 4. おなじみのaccess_tokenとrefresh_tokenがとれるので以降のAPIリクエストにはここで取得した`access_foken`の値をヘッダに付けて行うようにする。 5. 使用例はこんな感じ>`$ curl -s -H 'Authorization: Bearer ACCESS_TOKEN' https://cloud.feedly.com/v3/profile` (ACCESS_TOKENには3で取得したaccess_tokenの値で置き換え) #Profile - ユーザプロファイルを取得する https://cloud.feedly.com/v3/profile ```json: { "fullName": "Yoshiaki Kawazu", "locale": "ja", "picture": "https://lh3.googleusercontent.com/-ECJgMVZ8twg/AAAAAAAAAAI/AAAAAAAAAVM/vdZM4-BFTMs/photo.jpg", "gender": "male", "email": "kawazzz@gmail.com", "google": "107778016735353586604", "givenName": "Yoshiaki", "familyName": "Kawazu", "client": "feedly", "id": "43d020e7-87a7-4e7f-a8e9-cacb54acd1b5", "wave": "2013.11", "windowsLiveConnected": false, "wordPressConnected": false, "facebookConnected": false, "reader": "01581900768824391990", "evernoteConnected": false, "pocketConnected": false, "twitterConnected": false } ``` #Markers ##markers/counts - フィードの未読数が取得できる - 登録してるfeedが全て取得できるっぽい - フィードカテゴリやALLなどの特殊なidのやつも返ってくる https://cloud.feedly.com/v3/markers/counts ```json: { "unreadcounts": [ { "updated": 1394598427423, "count": 18, "id": "feed/http://feedproxy.google.com/hatena/b/hotentry" }, { "updated": 1394137265230, "count": 0, "id": "feed/http://aws.amazon.com/rss/whats-new.rss" }, { "updated": 1394599287257, "count": 24, "id": "user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/global.all" }, { "updated": 1394599287257, "count": 0, "id": "user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/知り合い" }, { "updated": 1394582230100, "count": 0, "id": "user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/エロ" }, { "updated": 1394599287257, "count": 24, "id": "user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/global.uncategorized" } ] } ``` # Stream ## streams/ids - ストリームIDにぶら下がったエントリのID一覧を取得する。 - 取得済みエントリIDはキャッシュしておき新着チェックに使うのが目的のAPIと思われ。 - コンテンツまで一発で取得したい場合は`streams/contents`を使えば良い。 - エントリIDからコンテンツを取得するには`entries/:entryId`もしくは`entries/.mget`を使う。 - streamIdに指定できるのは`categoryId` `feedId` `tagId`のどれか。 - `streams/:streamId/ids`というURLも使えるとドキュメントにあったがクエリで渡しても良い。 - `:streamId`の部分に埋める場合は必ず、ID中の`/`も含めてURLエンコードしておく必要がある。 https://cloud.feedly.com/v3/streams/ids?streamId=user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/global.all&count=5 または https://cloud.feedly.com/v3/streams/user%2F43d020e7-87a7-4e7f-a8e9-cacb54acd1b5%2Fcategory%2Fglobal.all/ids?count=5 ```json: { "ids": [ "vd6/vZ0gPFJR0iUi25Q38MITxKq1RRhf/pxYrAJpSMQ=_144b479e861:7f050e:5f76ba1", "RkLxQmvOp/nc7DIgnc7qmGmho0aOcNwtk5qewRZKloo=_144b47765db:78700b:57158c7e", "SzCknK5yCfKi0NrRCLzY3clkPWypMPSgdsVAzzPiIEk=_144b472c945:1f479:3a1395db", "gHDibUzBXqLZr4i7DfbTJIWTMbq4x72ysmvnyHLsh54=_144b46fc344:768b12:57158c7e", "gHDibUzBXqLZr4i7DfbTJIWTMbq4x72ysmvnyHLsh54=_144b46fc344:768b11:57158c7e" ], "continuation": "144b46fc344:768b11:57158c7e" } ``` ## streams/contents - ストリームIDにぶら下がったエントリのコンテンツ一覧を取得する。 - streamIdに指定できるのは`categoryId` `feedId` `tagId`のどれか。 - `streams/:streamId/contents`というURLも使えるとドキュメントにあったがクエリで渡しても良い。 - `:streamId`の部分に埋める場合は必ず、ID中の`/`も含めてURLエンコードしておく必要がある。 https://cloud.feedly.com/v3/streams/contents?streamId=user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/global.all&count=2 または https://cloud.feedly.com/v3/streams/user%2F43d020e7-87a7-4e7f-a8e9-cacb54acd1b5%2Fcategory%2Fglobal.all/contents?count=2 ```json: { "items": [ { "engagementRate": 0.36, "engagement": 5, "crawled": 1394598626006, "author": "jniino", "summary": { "direction": "ltr", "content": "光ネットワークや無線ネットワーク、パケット通信など種類の異なる複数のネットワーク..." }, "content": { "direction": "ltr", "content": "光ネットワークや無線ネットワーク、パケット通信など種類の異なる複数のネットワークの情報を統一的に定義し、上位レイヤから簡単に扱えるようにしたSoftware-Defined Networkingの技術を、NTT、NTTコム、富士通、日立、NECが共同で発表しました。 これにより通信事業者などが広域にわたる複数のネットワークを柔軟に組み合わせて仮想ネットワークを構築しつつ、全体の状況を把握したり、各レイヤのネットワーク装置を連係させることで、要求に応じたネットワークの設計、構築……" }, "id": "/4r8BU2nbsgJrgfRKK3amXnaOxpprjCJvWjoCKjAPwY=_144b48e42d6:852591:5f76ba1", "originId": "tag:www.publickey1.jp,2014://2.2804", "keywords": [ "blog", "Software-Defined Network", "ネットワーク" ], "fingerprint": "9703a9db", "alternate": [ { "type": "text/html", "href": "http://www.publickey1.jp/blog/14/software-defined_networkingnttnttnec.html" } ], "title": "Software-Defined Networkingで広域ネットワークを総合的に扱う基本技術を確立。NTT、NTTコム、富士通、日立、NECが共同で発表", "updated": 1394597056000, "published": 1394596984000, "origin": { "streamId": "feed/http://www.publickey1.jp/atom.xml", "title": "Publickey", "htmlUrl": "http://www.publickey1.jp/" }, "sid": "+d0QLpjFyPQ0+bv2Zs0tZe8M5+yaZXD0xyM5xDZrw6I=", "visual": { "url": "none" }, "unread": true }, { "unread": true, "visual": { "url": "none" }, "origin": { "streamId": "feed/http://kernel.org/kdist/rss.xml", "title": "Latest Linux Kernel Versions", "htmlUrl": "http://www.kernel.org" }, "fingerprint": "1870aafa", "originId": "kernel.org,linux-next,next-20140311,2014-03-12", "id": "3n4rP10OBRjEEdn8ddzpt3a6rmsfO/6fqgXkJR2bJ5s=_144b48dda3b:850d01:5f76ba1", "summary": { "direction": "ltr", "content": "<table>\n<tr><th align=\"right\">Version:</th><td><strong>next-20140311</strong> (linux-next)</td></tr>\n<tr><th align=\"right\">Released:</th><td>2014-03-12</td></tr>\n</table>" }, "crawled": 1394598599227, "alternate": [ { "type": "text/html", "href": "http://www.kernel.org/" } ], "title": "next-20140311: linux-next", "published": 1394595471000 }, ], "continuation": "144b48b3b1f:8441c8:5f76ba1", "updated": 1394598626006, "id": "user/43d020e7-87a7-4e7f-a8e9-cacb54acd1b5/category/global.all" } ``` # まとめ ↑とりあえずこれだけ例を上げとけば普通のRSSリーダは作れそうだな。 最初ALLの内容をどうやって取るのかが分からんくて非常に悩んだ。global.allカテゴリのコンテンツをEntryかStreamから取れば良いまで分かれば後はどうとでもなる。 あとは既読マークとかは Markers に適当にPOSTしとけば良い。 |
|
| 879位 |
|
|||
|
10:31:18 |
(A.I. Squared, Inc. 所属) |
|
#nilとは何か?
## nilとNULLの由来 どちらも、「何もない」という意味 * NULLの由来は、ラテン語で「無」を意味するnullus * nilの由来は、ラテン語で「無」を意味するnihil ## Objective-Cにおける、nilとNilとNULL いずれも「ポインタが何も指していない」ことを示し、実体は0です。 それぞれ、想定されている型が異なるため、コンテクストに応じて使い分けるべきです。 * nil : (id)0 -> オブジェクトを何も指していない * Nil : (Class)0 -> クラスを何も指していない * NULL : void * -> ポインタを何も指していない ```objc id nullObj = nil; // 空じゃない場合に実行したい!! if(nullObj){ //OK } if(nullObj != nil){ //OK } if(nullObj != 0){ //OKだけど、コードとして分かりにくいのでオススメしない } ``` ## NSNull NSNullは「何もない」という情報をもったオブジェクトであり、本質的にnilとは異なります。 NSArray等のコレクションはnilを収納できないので、「空である」という状態を保持したい場合にはNSNullを使用します。 ```objc id nullObj = [NSNull null]; // 空じゃない場合に実行したい!! if(nullObj){ //ダメ。オブジェクトとしては存在する } if(nullObj != nil){ //ダメ。NSNullオブジェクトは、nilじゃない } if(![nullObj isEqual: [NSNull null]]){ //OK NSObject的な比較なので問題ない } if(nullObj != [NSNull null]){ //OK シングルトンなので、ポインタ比較でも問題ない } ``` ## NSNumberの0と、NSIntegerの0 NSNumberの0(@0)は、0ではなく、0という値を持つNSNumberのインスタンスです。 NSIntegerの0は、nilと同じ0です。 # コーディングの中でのnil ## nilに対するメソッド呼び出し結果はnilである Objective-C的な記法は、このnil呼び出し仕様に強く依存しています。 たとえば、一番基本的なalloc + initという書き方も、allocが失敗してnilになっても、initが問題なく処理されることを前提としています。 ```objc [[NSObject alloc]init] ``` これはObjective-Cの特徴であり文化なので、これを生かしたコーディングをすることで、よりObjective-Cらしいコードになります。 ```objc if(self.label != nil){ self.label.text = @""; } //nilチェックはシンプルにかける if(self.label){ self.label.text = @""; } //そもそもチェックしなくても良い self.label.text = @""; ``` ただし、他の多くの言語(Java等)では、nil(に相当するNULL等)に対して呼び出しを行うとエラーになります。 Objective-Cが特別と思っておかないと、他の言語を使った時にNull Pointer Exceptionを見て涙することにになります。 ## nilに対するブロック呼び出しは落ちる ブロックはNSObjectに近い扱いができますが、NSObjectではありません。ブロックがnilの場合、それに対する呼び出しを行うと落ちるので要注意です。 [ブロックの安全な呼び出し方](http://qiita.com/tomohisaota/items/ca09bd1bee0f3c5395a3) ## 安全 != 意図通り nilに対する呼び出しは安全ですが、必ずしもプログラマの意図通りの動作をするとは限りません。 nilで落ちないからこそ、nilでも正しく動くコーディングスタイルが必要になります。 ```objc if(obj.state.isOK){ //ここに入るのは、(obj != nil) && (obj.state != nil) && (obj.state.isOK == YES) } if(!obj.state.isOK){ //ここに入るのは、(obj == nil) || (obj.state == nil) || (obj.state.isOK == NO) } ``` # ARCとnil ## ARCとは何か ARCはObjective Cで使用するメモリ管理の方式で、従来プログラマが明示的に行っていた参照数管理をコンパイラが自動的に行う物です。リファレンスカウンティングGCに近いですが、頑張るのがランタイムではなく、コンパイラな点が異なります。(詳しく知りたい人はぐぐるといいと思うよ) Automatic Reference Coutingという名前のとおり、「参照数のカウント」を自動的に行い、参照数が0になったオブジェクトを自動的に解放してくれます。(このカウントアップをretain、カウントダウンをreleaseと呼びます) この「参照している」という状態の反対が、「何も指していない」というnilです。 ## 強参照(strong pointer) 普通に参照を定義すると、それは強参照となり対象をretainします。 ARCは強参照が一つでもある限りオブジェクトを解放しないので、不要なオブジェクトを指すポインタには明示的にnilを設定して解放する必要があります。実際には変数のスコープとオブジェクトグラフの構造により、あまり意識せずに解放は行われます。 ## 弱参照(weak pointer) __weakを指定して作成した参照は、弱参照となり対象をretainしません。 ARCは強参照が0になると弱参照があってもオブジェクトを解放し、weak pointerにnilを設定します。これは、nilに対する呼び出しが安全に行えるObjective-Cだからこそ生きる機能です。 ## 弱参照(unsafe_unretained pointer) もう一つの弱参照がunsafe_unretainedで、弱参照となり対象をretainしません。 ARCは強参照が0になると弱参照があってもオブジェクトを解放し、unsafe_unretainedには何もしません。。。呼ぶと危険なのでunsafeです。delegateや、Core Fundation系で遭遇します。 ## 循環参照(retain cycle) ### オブジェクト間の循環参照 二つのオブジェクトが相互に強参照をもってしまった場合、そのオブジェクトをARCが解放することはありません。ガベージコレタクのように孤立した循環参照を見つけて解放とかは一切ないので、プログラマの責任で循環参照を回避する必要があります。 基本的な対策として、相互に参照するオブジェクトには常に主従関係を考え、従->主の参照はweakポインタにしておきます。もし、主従関係を考えるのが難しい場合には、解放したいタイミングで参照ポインタをnilにする必要があります。 ### Blockによる循環参照 Blockはスコープ内で必要な変数への参照を持ちます。そのため、ブロック内で不用意にブロックの所有者を参照してしまうと循環参照となります。 対策は、、、一言で書くのがちょっと難しいです。よくあるケースとしてブロック内でselfを参照してしまうケースについては、weakなselfを作ることで対応できます。ただ、これは本当にケースバイケースです。 |
|
| 880位 |
|
|||
|
23:10:44 |
(Hazel Research Institute, LLC 所属) |
|
### Qiita > URLのリクエストパラメタに?token=YOURTOKENのように付加してリクエストを送る. [Qiita APIドキュメント - Qiita:Developer](http://qiita.com/docs) なお, ユーザ名+パスワードを`/api/v1/auth`にpostすればtokenを取得できる. ### GitHub * Basic認証, OAuth2トークン(ヘッダー or パラメータ)の3種類が使える. ```bash $ curl -u "username" https://api.github.com ``` ```bash $ curl -H "Authorization: token OAUTH-TOKEN" https://api.github.com ``` ```bash $ curl https://api.github.com/?access_token=OAUTH-TOKEN ``` [OAuth | GitHub API](http://developer.github.com/v3/oauth/) ### webpay * curlに-uとして渡し, ベーシック認証を利用 [curl APIドキュメント | WebPay: 開発者向けクレジットカード決済サービス](https://webpay.jp/docs/api) > リクエスト例 ``` $curl "https://api.webpay.jp/v1/charges" \ -u "test_secret_eHn4TTgsGguBcW764a2KA8Yd:" ``` > curlコマンドは、-uオプションによってベーシック認証に必要なデータの受け渡しを行います。(APIキーの後にコロン":"を付けることで、パスワードを求められることを回避できます。) ### NewRelics * ヘッダー > From the command line, you can use ``` curl -gH "x-api-key:REPLACE_WITH_YOUR_API_KEY" '<URL>' ``` [Getting started with the New Relic REST API - New Relic Documentation](https://docs.newrelic.com/docs/features/getting-started-with-the-new-relic-rest-api) ### Box * ヘッダー ``` curl https://api.box.com/2.0/folders/FOLDER_ID \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` [Box Platform Developer Documentation](http://developers.box.com/get-started/) ### rackspace まずcurlで"認証" * POSTのパラメータとしてauth: { "keyxxxx" : { ... ```bash curl -k -X POST https://identity.api.rackspacecloud.com/v2.0/tokens -d '{ "auth":{ "RAX-KSKEY:apiKeyCredentials":{ "username":"<username>", "apiKey":"<apikey>" } } }' -H "Content-type: application/json" ``` [8.2. Authentication with cURL - Cloud Files™ Developer Guide - API v1](http://docs.rackspace.com/files/api/v1/cf-devguide/content/Authentication-d1e2929.html) * その後はヘッダーに. ```bash $ curl -X HEAD -D - \ -H "X-Auth-Token: fc81aaa6-98a1-9ab0-94ba-aba9a89aa9ae" \ https://storage.clouddrive.com/v1/CF_xer7_343 ``` ### MongoHQ * 普通にパラメータとして ``` curl -X GET "https://api.mongohq.com/databases/vehicles/collections/rockets/documents?_apikey=12345&limit=5" ``` ### さくらクラウド * Basic/Digest認証 > さくらのクラウドAPIはAPIキーとシークレットトークンを利用したBasic認証またはDigest認証を使用します。 [さくらのクラウド API v1.1 ドキュメント](http://developer.sakura.ad.jp/cloud/api/1.1/) ### Ebook Glue * 普通にパラメータ ``` curl -o converted.epub \ https://ebookglue.com/convert?token=your-api-key ``` [Ebook Conversion API Documentation | Ebook Glue](https://ebookglue.com/docs) ### Dropbox * ヘッダー ``` $ curl https://api.dropbox.com/1/account/info -H “Authorization: Bearer <access token>” ``` [Using OAuth 2.0 with the Core API - Developer Blog - Dropbox](https://www.dropbox.com/developers/blog/45/using-oauth-20-with-the-core-api) AccessTokeの取得までもcurlで進めることが出来る. ### Crocodoc * パラメータ ``` $ curl "https://crocodoc.com/api/v2/document/upload" --data "token=${API_TOKEN}&url=http://web.crocodoc.com/files/test-simple.pdf" ``` [API Reference | Crocodoc](https://crocodoc.com/docs/api/) |
|
| 881位 |
|
|||
|
17:59:42 |
|
|
```sh
$ cd $ mkdir .rbenv/plugins $ cd .rbenv/plugins $ git clone git://github.com/ianheggie/rbenv-binstubs.git ``` - rbenvにはpluginsという仕組みがあってrbenvに機能を追加できる。ruby-buildもその一つ。「ひとつのことをうまくやる」思想っぽい。 - そのpluginのひとつにbundlerとの連携をうまくやってくれる[rbenv-bundler](https://github.com/carsomyr/rbenv-bundler)というのがあるんだけど、rbenvはこれを[非推奨](https://github.com/sstephenson/rbenv/wiki/Plugins#bundler-integration)といってる。パフォーマンスが悪くなったり、バグが多いみたい。 - 同じようなpluginを探したところ、[rbenv-binstubs](https://github.com/ianheggie/rbenv-binstubs)というものを見つけた。[thoughtbotのブログ](http://robots.thoughtbot.com/post/47273164981/using-rbenv-to-manage-rubies-and-gems)で紹介されていた。 ### Rails 3 ```sh $ cd path/to/project $ bundle install --binstubs $ rbenv rehash $ rails s ``` - `--path=vendor/bundle`を指定するとうまくいかなかった。 ### Rails 4 ```sh $ cd path/to/project $ bundle install --binstubs=bundle_bin $ rbenv rehash $ rails s ``` - Rails 4からはbinディレクトリの扱いが若干変わるので要注意。詳しくは[こちら](http://qiita.com/items/01c41578e611b038da6e)。 - binstubsが実行ファイルを生成するディレクトリを指定する。指定してもちゃんと動く。 |
|
| 882位 |
|
|||
|
13:45:27 |
|
|
##まえおき
AngularUI RouterはAngularJS用のルーティングモジュールで、標準のngRouteより高機能。 詳しくは[AngularUI Routerの使い方 - ばかおもちゃ本店](http://b.denkizakana.com/2014/02/angularui-router.html)にも書いた。 実働サンプルもあるよ!(宣伝) ##ngRouteより高機能な部分。 以下のようなことがngRouteより高機能。 - 複数の部分にviewを表示できる - URLではなくて階層化したstateで表示状態を管理する - stateに結び付けられるURLは正規表現で指定できる - ui-srefでURL構造に依存しないでリンクを貼れる(URL変更時とかに何もしなくていい) ##サンプル ```js angular.module('app',['ui.router','ListCtrl'], function($stateProvider,$urlRouterProvider){ $urlRouterProvider.otherwise('/emp'); $stateProvider .state('emp',{ url:"/emp" ,templateUrl:"list.html" ,controller:"ListCtrl" }) .state('emp.detail',{ url:"/detail/{empID:[0-9]{5}}" ,templateUrl:"detail.html" ,controller:function($stateParams,$scope){ $scope.params = $stateParams; } }) .state('emp.detail.pictures',{ url:"/pictures" ,views:{ "@":{ templateUrl:"pictures.html" ,controller:function($stateParams,$scope){ $scope.params = $stateParams; } } } }); }]); ``` このサンプルは自サイトの実働サンプルまま。 この設定で、 - 社員一覧みたいなのを表示 - 一覧から一人クリックすると、リストの下の詳細ビューに詳細が表示される - 詳細内のリンクをクリックすると、一番上の階層のビューに写真一覧が表示される みたいな感じの動作になる。 ##ngRouteとの設定の違い + routeProvider -> stateProvider + otherwiseは$urlRouterProviderに設定する + 第一引数がURLではなくてstate + stateはドット区切りで階層化できる(parentプロパティに設定でも可) + urlはオプション内のプロパティに指定する + 正規表現は"/detail/{empID:[0-9]{5}}"こんな感じ + urlは親のurl以下に続くが、'^/hoge'みたいにすると一番上から指定しなおせる + viewを指定するときは直接controllerやtemplateを設定するのではなく、viewsに設定する + viewsのプロパティ名で表示するview名@state名を指定できる + @書かなきゃ親state下、view書かなきゃ無名view、@書いてstate書かなきゃ最上位state下 ##リンク側 ```html <a ui-sref=".detail({empID:e.id})">{{e.name}}さんの詳細</a> ``` みたいな感じで書く。 + ドットで始まってる場合、現在のstate以下のstateを選ぶ + ^で始まってる場合、親state以下のstate(siblings) + 何もなしで始まる場合、一番上の階層下のstate 記号は違うけど、ファイルパスの指定なんかと似たような感じ。 以上。 |
|
| 883位 |
|
|||
|
15:27:09 |
|
|
# --verbose
--verboseオプションをつけるとリクエストヘッダ、レスポンスヘッダ、httpsならTLS handshakeの様子等が出力されるようになります。なので、--verboseをつけるとヘッダが見れます。 また、この"verboseな"情報は標準エラー出力に出力されるので、ヘッダだけ見たい、と言う時は標準出力は/dev/nullに捨てちゃうといい感じに見やすくなります。 ``` $ curl --verbose http://increments.co.jp/ 1> /dev/null * About to connect() to increments.co.jp port 80 (#0) * Trying 75.101.145.87... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* connected * Connected to increments.co.jp (75.101.145.87) port 80 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8y zlib/1.2.5 > Host: increments.co.jp > Accept: */* > < HTTP/1.1 200 OK < Server: nginx < Date: Thu, 26 Dec 2013 18:00:31 GMT < Content-Type: text/html;charset=utf-8 < Content-Length: 6624 < Connection: keep-alive < X-Xss-Protection: 1; mode=block < X-Content-Type-Options: nosniff < X-Frame-Options: SAMEORIGIN < { [data not shown] 100 6624 100 6624 0 0 6838 0 --:--:-- --:--:-- --:--:-- 8406 * Connection #0 to host increments.co.jp left intact * Closing connection #0 ``` # --head httpメソッドに[HEAD](http://www.studyinghttp.net/cgi-bin/rfc.cgi?2616#Sec9.4)というのがあって、これはbodyを返しません。で、このリクエストを送るオプションが--headです。レスポンスヘッダだけ見たい場合はこれが一番お手軽かもしれません。 ``` $ curl --head http://increments.co.jp/ HTTP/1.1 200 OK Server: nginx Date: Thu, 26 Dec 2013 18:09:33 GMT Content-Type: text/html;charset=utf-8 Content-Length: 6624 Connection: keep-alive X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN ``` 何が見たいかによりますが、ほしい情報がGETやPOSTとは結果が異なることがあるので注意ですね。 # --include --includeオプションをつけると、レスポンスヘッダも出力されます。 ``` $ curl --include http://increments.co.jp/ HTTP/1.1 200 OK Server: nginx Date: Thu, 26 Dec 2013 18:11:22 GMT Content-Type: text/html;charset=utf-8 Content-Length: 6624 Connection: keep-alive X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN <!DOCTYPE html> <html xmlns:og='http://ogp.me/ns#'> <head> <meta charset='UTF-8'> <title>Increments株式会社</title> 略 ``` # --dump-header --dump-header FILENAME でFILENAMEファイルにレスポンスヘッダを出力します。 ``` $ curl --dump-header header.log http://increments.co.jp/ <!DOCTYPE html> <html xmlns:og='http://ogp.me/ns#'> <head> <meta charset='UTF-8'> <title>Increments株式会社</title> 略 $ cat header.log HTTP/1.1 200 OK Server: nginx Date: Thu, 26 Dec 2013 18:12:53 GMT Content-Type: text/html;charset=utf-8 Content-Length: 6624 Connection: keep-alive X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN ``` FILENAMEのところでハイフン(-)を指定すると標準出力へ出力されます(=--includeと同じ) # --trace --trace FILENAMEでtcpdumpのような通信内容がFILENAMEファイルに出力されます。ただ日本語には対応してないようです。 ``` $ curl --trace trace.log http://increments.co.jp/ > /dev/null % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 6624 100 6624 0 0 8023 0 --:--:-- --:--:-- --:--:-- 11420 $ head -50 trace.log == Info: About to connect() to increments.co.jp port 80 (#0) == Info: Trying 174.129.212.2... == Info: connected == Info: Connected to increments.co.jp (174.129.212.2) port 80 (#0) => Send header, 147 bytes (0x93) 0000: 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a GET / HTTP/1.1.. 0010: 55 73 65 72 2d 41 67 65 6e 74 3a 20 63 75 72 6c User-Agent: curl 0020: 2f 37 2e 32 34 2e 30 20 28 78 38 36 5f 36 34 2d /7.24.0 (x86_64- 0030: 61 70 70 6c 65 2d 64 61 72 77 69 6e 31 32 2e 30 apple-darwin12.0 0040: 29 20 6c 69 62 63 75 72 6c 2f 37 2e 32 34 2e 30 ) libcurl/7.24.0 0050: 20 4f 70 65 6e 53 53 4c 2f 30 2e 39 2e 38 79 20 OpenSSL/0.9.8y 0060: 7a 6c 69 62 2f 31 2e 32 2e 35 0d 0a 48 6f 73 74 zlib/1.2.5..Host 0070: 3a 20 69 6e 63 72 65 6d 65 6e 74 73 2e 63 6f 2e : increments.co. 0080: 6a 70 0d 0a 41 63 63 65 70 74 3a 20 2a 2f 2a 0d jp..Accept: */*. 0090: 0a 0d 0a ... <= Recv header, 18 bytes (0x12) 0000: 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 20 HTTP/1.1 200 OK 0010: 0d 0a .. <= Recv header, 15 bytes (0xf) 0000: 53 65 72 76 65 72 3a 20 6e 67 69 6e 78 0d 0a Server: nginx.. <= Recv header, 37 bytes (0x25) 0000: 44 61 74 65 3a 20 53 75 6e 2c 20 32 39 20 44 65 Date: Sun, 29 De 0010: 63 20 32 30 31 33 20 30 36 3a 31 36 3a 31 33 20 c 2013 06:16:13 0020: 47 4d 54 0d 0a GMT.. <= Recv header, 39 bytes (0x27) 0000: 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 74 65 Content-Type: te 0010: 78 74 2f 68 74 6d 6c 3b 63 68 61 72 73 65 74 3d xt/html;charset= 0020: 75 74 66 2d 38 0d 0a utf-8.. <= Recv header, 22 bytes (0x16) 0000: 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 Content-Length: 0010: 36 36 32 34 0d 0a 6624.. <= Recv header, 24 bytes (0x18) 0000: 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 70 Connection: keep 0010: 2d 61 6c 69 76 65 0d 0a -alive.. <= Recv header, 33 bytes (0x21) 0000: 58 2d 58 73 73 2d 50 72 6f 74 65 63 74 69 6f 6e X-Xss-Protection 0010: 3a 20 31 3b 20 6d 6f 64 65 3d 62 6c 6f 63 6b 0d : 1; mode=block. 0020: 0a . <= Recv header, 33 bytes (0x21) 0000: 58 2d 43 6f 6e 74 65 6e 74 2d 54 79 70 65 2d 4f X-Content-Type-O 0010: 70 74 69 6f 6e 73 3a 20 6e 6f 73 6e 69 66 66 0d ptions: nosniff. 0020: 0a . <= Recv header, 29 bytes (0x1d) 0000: 58 2d 46 72 61 6d 65 2d 4f 70 74 69 6f 6e 73 3a X-Frame-Options: 0010: 20 53 41 4d 45 4f 52 49 47 49 4e 0d 0a SAMEORIGIN.. <= Recv header, 2 bytes (0x2) 0000: 0d 0a .. <= Recv data, 1196 bytes (0x4ac) 0000: 3c 21 44 4f 43 54 59 50 45 20 68 74 6d 6c 3e 0a <!DOCTYPE html>. 0010: 3c 68 74 6d 6c 20 78 6d 6c 6e 73 3a 6f 67 3d 27 <html xmlns:og=' ``` # --trace-ascii 前項の--traceの16進数部分が邪魔、文字列化したやつだけ見せてくれ、って人は--trace-asciiオプションが良いです。同様に日本語は表示されません。 ``` $ curl --trace-ascii trace.log http://increments.co.jp/ > /dev/null % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 6624 100 6624 0 0 5611 0 0:00:01 0:00:01 --:--:-- 15622 $ head -35 trace.log == Info: About to connect() to increments.co.jp port 80 (#0) == Info: Trying 75.101.163.44... == Info: connected == Info: Connected to increments.co.jp (75.101.163.44) port 80 (#0) => Send header, 147 bytes (0x93) 0000: GET / HTTP/1.1 0010: User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 0050: OpenSSL/0.9.8y zlib/1.2.5 006c: Host: increments.co.jp 0084: Accept: */* 0091: <= Recv header, 18 bytes (0x12) 0000: HTTP/1.1 200 OK <= Recv header, 15 bytes (0xf) 0000: Server: nginx <= Recv header, 37 bytes (0x25) 0000: Date: Sun, 29 Dec 2013 06:20:51 GMT <= Recv header, 39 bytes (0x27) 0000: Content-Type: text/html;charset=utf-8 <= Recv header, 22 bytes (0x16) 0000: Content-Length: 6624 <= Recv header, 24 bytes (0x18) 0000: Connection: keep-alive <= Recv header, 33 bytes (0x21) 0000: X-Xss-Protection: 1; mode=block <= Recv header, 33 bytes (0x21) 0000: X-Content-Type-Options: nosniff <= Recv header, 29 bytes (0x1d) 0000: X-Frame-Options: SAMEORIGIN <= Recv header, 2 bytes (0x2) 0000: <= Recv data, 2644 bytes (0xa54) 0000: <!DOCTYPE html>.<html xmlns:og='http://ogp.me/ns#'>. <head>. 0040: <meta charset='UTF-8'>. <title>Increments............</title 0080: >. <meta content='Increments................................. ``` |
|
| 884位 |
|
|||
|
17:12:10 |
(Wasoo Inc. 所属) |
|
git のコマンド入力をサボったら、以下のようなメッセージが。
```shell-session $ git push warning: push.default is unset; its implicit value is changing in Git 2.0 from 'matching' to 'simple'. To squelch this message and maintain the current behavior after the default changes, use: git config --global push.default matching To squelch this message and adopt the new behavior now, use: git config --global push.default simple See 'git help config' and search for 'push.default' for further information. (the 'simple' mode was introduced in Git 1.7.11. Use the similar mode 'current' instead of 'simple' if you sometimes use older versions of Git) ``` 大抵は、フルで(refspec までは書かないけど) 入力していたけど、安全に楽できそうな気がしたので、設定値を調べてみた。 ## push.default の設定値 ### nothing サボれないようにするための設定。必ず指定する必要がある。 ### matching 現行バージョンのデフォルト。ローカルとリモートで同じ名前のブランチがあったら、根こそぎ push する。カレントだけではないので、意図しないものまで push されちゃう。 ### upstream(旧名 tracking) カレントブランチに追跡ブランチが設定されている場合に、追跡ブランチに対して push する。 ### simple Git 2.0 でデフォルトになる予定のもの。カレントブランチに追跡ブランチが設定されている、かつローカルとリモートのブランチ名が同じである場合に、追跡ブランチに対して push する。 ### current カレントブランチと同名のリモートブランチがあれば、そこに push する。 --- これまで事故は起きなかったけど、今後は追跡ブランチの設定をしようかな〜 と思う次第。追跡ブランチの設定方法は、幾つかあるので、現況に応じて使い分ける。 ### ローカルブランチ作成のタイミングで設定 ```shell-session $ git checkout -t origin hogehoge or $ git checkout --track origin hogehoge ``` ### リモートブランチ作成のタイミングで設定 ```shell-session $ git push -u origin hogehoge or $ git push --set-upstream origin hogehoge ``` ### 既に存在するブランチに設定する Git 1.7.x スタイル ```shell-session $ git branch --set-upstream hogehoge origin/hogehoge ``` Git 1.8.x スタイル カレントブランチが hogehoge の場合 ```shell-session $ git branch -u origin/hogehoge or $ git branch --set-upstream-to=origin/hogehoge ``` ローカルブランチも指定する場合、 ```shell-session $ git branch -u origin/hogehoge hogehoge or $ git branch --set-upstream-to=origin/hogehoge hogehoge ``` もっと前? ```shell-session $ git branch -t hogehoge origin/hogehoge or $ git branch --track hogehoge origin/hogehoge ``` --- といろいろあるけど、track という言葉は使わず upstream へ向かっているようなので、これから覚えるならそっちなんでしょうね。 |
|
| 885位 |
|
|||
|
21:32:05 |
|
|
[RFC5322](http://tools.ietf.org/html/rfc5322)で定義されているメールアドレスの書式を完全にサポートすることは簡単ではありませんが、適当な正規表現を紹介してOKとする記事があとを絶ちません。
HTML5には [input[type=email]](http://www.w3.org/TR/html5/forms.html#valid-e-mail-address) という要素があり、メールアドレスの書式チェックをクライアントサイドで行えるようになっています。このチェックでは、下記の(Perl5の記法にならった)正規表現を使っています。 ~~~~ /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ ~~~~ 注釈に > この要件は電子メールの構文を定義するRFC5322に対して **意図的に違反** (willful violation) している。 とあるように、仕様を完全に満足する正規表現ではありませんので、電子メールヘッダを解析するような本格的な用途に対しては不十分ですが、WEBサービスに対するユーザー入力を検証する程度であれば、この正規表現を採用しておくのが無難ではないでしょうか。 そして、せめて `+` をlocal-partに含むメールアドレスを拒否するサービスがこの世から絶滅しますように。 |
|
| 886位 |
|
|||
|
21:10:30 |
|
|
Helios は [これ](http://helios.io)。 単なる勉強ログなのでおそらくあまり参考にはなりません。 また、Sinatra 関係が多かったのでざっくり省略してあります。 ## 1. Helios を構成する iOS 連携ライブラリ群 すべて Heroku の[mattt (Mattt Thompson)](https://github.com/mattt)氏によるもの。 ### 1-1. cupertino [mattt/cupertino · GitHub](https://github.com/mattt/cupertino) *Automate administrative tasks that you would normally have to do through the Apple Dev Center websites. Life's too short to manage device identifiers by hand!* Ruby から Apple Developer Center にアクセスできる驚異のライブラリ。 ターミナルからデバイスの管理などが行えるコマンドラインツールも付属する。 ### 1-2. houston [mattt/houston · GitHub](https://github.com/mattt/houston) *Houston is a simple gem for sending Apple Push Notifications. Pass your credentials, construct your message, and send it.* iOS のプッシュ通知サービスを利用するためのライブラリ。 同様の機能を持つライブラリに APNS gem があるが、houston gem のほうが実用的な機能がまとまっている。 ### 1-3. rack-push-notification [mattt/rack-push-notification · GitHub](https://github.com/mattt/rack-push-notification) *This project is part of a series of open source libraries covering the mission-critical aspects of an iOS app's infrastructure.* Rack に iOS の APNS にデバイス登録するためのエンドポイントを用意するライブラリ。 iOS 側の [Orbiter](https://github.com/mattt/Orbiter) と連携して iOS と Rack アプリケーションのインフラ統合を担うシリーズの一端となる。 ### 1-4. shenzhen [mattt/shenzhen · GitHub](https://github.com/mattt/shenzhen) *Create .ipa files and then distribute them with TestFlight or HockeyApp, all from the command line!* Xcode の CLI ツールである xcodebuild の Ruby ラッパーを含む iOS プロジェクトのビルド・パッケージング用ライブラリ。 `.ipa` ファイル (iOS のアプリケーションバイナリ) 生成の煩わしさから解放してくれる。 アプリケーション配布・フィードバック収集サービスである TestFlight や HockeyApp へのデプロイ機構も持つ。 ### 1-5. venice [mattt/venice · GitHub](https://github.com/mattt/venice) *Venice is a simple gem for verifying Apple In-App Purchase receipts, and retrieving the information associated with receipt data.* iOS のアプリ内課金 (In-App Purchase) の Receipt を Ruby から検証・解析できるライブラリ。 [rack-in-app-purchase](https://github.com/mattt/rack-in-app-purchase) 内で使用され、Rack アプリケーションの In-App Purchase 対応を容易に行うことができる。 ### 1-6. rack-in-app-purchase [mattt/rack-in-app-purchase · GitHub](https://github.com/mattt/rack-in-app-purchase) *Rack::InAppPurchase is Rack middleware that manages products for in-app-purchases and verifies receipts.* iOS のアプリ内課金 (In-App Purchase) に対応する Rack のミドルウェア。 `venice` に依存する。 ### 1-7. dubai [mattt/dubai · GitHub](https://github.com/mattt/dubai) *Dubai makes it easy to generate .pkpass from a script or the command line, allowing you to rapidly iterate on the design and content of your passes, or generate one-offs on the fly.* iOS 6 の機能である Passbook を開発・パッケージングするためのユーティリティ。コマンドラインツールとして `tk` コマンドが提供される。 ターミナルから Passbook に関連するリソースを管理・ビルド・認証することが可能で、Passbook の配信サーバを構築することも可能。 ### 1-8. rack-passbook [mattt/rack-passbook · GitHub](https://github.com/mattt/rack-passbook) Rack アプリケーションを iOS の Passbook に容易に対応させるためのミドルウェア。 ### 1-9. rack-newsstand [mattt/rack-newsstand · GitHub](https://github.com/mattt/rack-newsstand) Rack アプリケーションを iOS の Newsstand に容易に対応させるためのミドルウェア。 ### 1-10. core_data [mattt/core_data · GitHub](https://github.com/mattt/core_data) iOS や OS X のデータベースフレームワークである CoreData のスキーマ定義ファイル `.xcdatamodel` をパースするライブラリ。 ### 1-11. rack-scaffold [mattt/rack-scaffold · GitHub](https://github.com/mattt/rack-scaffold) 上記 `core_data` gem を用いて CoreData のスキーマ定義ファイルからモデル群を生成する Rack のミドルウェア。 README より抜粋: ```ruby require 'sequel' require 'core_data' require 'rack/scaffold' DB = Sequel.connect(ENV['DATABASE_URL']) run Rack::Scaffold model: './Example.xcdatamodeld', only: [:create, :read] ``` ## 2. Heroku 関連 Helios は Heroku 上でも動作するため、Heroku 関連のライブラリもインストールされる。 ### 2-1. foreman [ddollar/foreman · GitHub](https://github.com/ddollar/foreman) Foreman は Heroku における `Procfile` の後方で動作するアプリケーションを管理するためのツール。 Procfile は、Heroku 内でプロセスのタイプ (`web`, `worker`, `urgentworker`, `clock` 等) を識別するためのメカニズムのこと。 ### 2-2. rails-database-url [glenngillen/rails-database-url · GitHub](https://github.com/glenngillen/rails-database-url) heroku で使用する DATABASE_URL を `database.yml` から読み取って設定してくれる小さなライブラリ。 ## 3. Rack 関連 ### 3-1. rack-contrib [rack/rack-contrib · GitHub](https://github.com/rack/rack-contrib) Rack のアドオンコンポーネント群。 ### 3-2. rack-protection [rkh/rack-protection · GitHub](https://github.com/rkh/rack-protection) Rack を XSS や CSRF, クリックジャッキングなどの脆弱性攻撃に対処できるようにするライブラリ。 ## 4. コマンドライン関連 ### 4-1. highline [JEG2/highline · GitHub](https://github.com/JEG2/highline) CLI からのユーザ入力を補助するライブラリ。 パスワードをエコーバックさせないようにしたりできる。 また、複数の選択肢からユーザに選択させるような使い方もできそう。 ドキュメントより抜粋: ```ruby choose do |menu| menu.prompt = "Please choose your favorite programming language? " menu.choice(:ruby) { say("Good choice!") } menu.choices(:python, :perl) { say("Not from around here, are you?") } end ``` ### 4-2. commander [visionmedia/commander · GitHub](https://github.com/visionmedia/commander) Thor のような感じで CLI のコマンドを簡単につくれるライブラリ。 依存 gem に上記 highline を持つ。 ### terminal-table [visionmedia/terminal-table · GitHub](https://github.com/visionmedia/terminal-table) ターミナルにテーブルを描画できる。 README より抜粋: ```ruby rows = [] rows << ['One', 1] rows << ['Two', 2] rows << ['Three', 3] table = Terminal::Table.new :rows => rows # > puts table # # +-------+---+ # | One | 1 | # | Two | 2 | # | Three | 3 | # +-------+---+ ``` ### 4-3. formatador [geemus/formatador · GitHub](https://github.com/geemus/formatador) CLI の標準出力をいい感じにフォーマットしてくれるライブラリ。 こちらも `terminal-table` と同じくテーブルの描画ができる。 また、プログレスバーの表示も可能。 ## 5. Webフロントエンド関連 ### 5-1. compass [chriseppstein/compass · GitHub](https://github.com/chriseppstein/compass) *Compass is an open-source CSS Authoring Framework.* CSS拡張メタ言語である SASS に乗っかって、ベンダープリフィクスの解決、クリアフィクス、CSS3 関連の機能などを網羅的に、簡潔に使えるようにするための CSS ユーティリティフレームワーク。 ### 5-2. zurb-foundation [zurb/foundation · GitHub](https://github.com/zurb/foundation) レスポンシブ・ウェブデザインに対応したウェブページを構築するための CSS フレームワーク。 ### 5-3. haml [haml/haml · GitHub](https://github.com/haml/haml) *HTML Abstraction Markup Language* 言わずと知れた HTML 生成のためのマークアップ言語。 ### 5-4. jsmin [rgrove/jsmin · GitHub](https://github.com/rgrove/jsmin/) JavaScript を minify することができる jsmin.c の Ruby 実装。 こちらの gem はもう長いことメンテされていない模様。 同様の gem では `Uglify` が有名。 ## 6. ツール群 ### 6-1. fog [fog/fog · GitHub](https://github.com/fog/fog) Ruby から各種クラウドサービスを簡単に扱うことができるライブラリ。 AWS の EC2 や S3 を始め、Rackspace や VMware vSphere などのサービスにも対応している。 ### 6-2. dotenv [bkeepers/dotenv · GitHub](https://github.com/bkeepers/dotenv) `.env` というファイルを読み込んで ENV を設定してくれるライブラリ。 APIのトークンやキー等を管理するのに使える。 ### 6-3. plist [bleything/plist · GitHub](https://github.com/bleything/plist) *Plist is a library to manipulate Property List files, also known as plists. It can parse plist files into native Ruby data structures as well as generating new plist files from your Ruby objects.* OS X や iOS でポピュラーなデータ形式である Property List をパース・ジェネレートできるライブラリ。 ### 6-4. backports [marcandre/backports · GitHub](https://github.com/marcandre/backports) *The goal of ‘backports’ is to make it easier to write ruby code that runs across different versions of Ruby.* 古い Ruby でも新しい Ruby の機能 (例えば `Array#sample` や `String#byteslice` など) を使えるようにすることで、特定のコードが Ruby のバージョンに関わらず実行できるようにすることを目標としたライブラリ。 ### 6-5. excon [geemus/excon · GitHub](https://github.com/geemus/excon) HTTP クライアントライブラリ。 数ある HTTP クライアントとしては[4位のシェア](https://www.ruby-toolbox.com/categories/http_clients#excon)を誇るらしいけど日本語の情報は全然見当たらず。 README より抜粋: ```ruby # Custom headers Excon.get('http://geemus.com', :headers => {'Authorization' => 'Basic 0123456789ABCDEF'}) ``` ### 6-6. chunky_png [wvanbergen/chunky_png · GitHub](https://github.com/wvanbergen/chunky_png) Pure Ruby で書かれた PNG 読み書き用のライブラリ。 メタデータをいじったりピクセルデータにアクセスしたりできる。 ### 6-7. fssm [ttilley/fssm · GitHub](https://github.com/ttilley/fssm) ファイルのモニタリングができるライブラリ。但し、*currently unmaintained* とのこと。 現在、同じ用途では `Guard` が主流。 ## 7. 説明不要っぽいものたち ### 7-1. sequel [jeremyevans/sequel · GitHub](https://github.com/jeremyevans/sequel) Ruby のためのデータベース・ツールキット。 SQL の組み立てをかっこいい感じにできる。 ### 7-2. eventmachine [eventmachine/eventmachine · GitHub](https://github.com/eventmachine/eventmachine) イベントドリブンI/Oを提供するフレームワーク。 ### 7-3. net-ssh [net-ssh/net-ssh · GitHub](https://github.com/net-ssh/net-ssh) Pure Ruby で実装された SSH クライアントライブラリ。 ### 7-4. net-scp [net-ssh/net-scp · GitHub](https://github.com/net-ssh/net-scp) セキュアなファイル転送プロトコルである SCP の Ruby 実装。 ### 7-5. ruby-hmac [ruby-hmac | RubyGems.org | your community gem host](http://rubygems.org/gems/ruby-hmac) 暗号ハッシュ関数を使用してメッセージ認証を行なう仕組みである HMAC の Ruby 実装。 |
|
| 887位 |
|
|||
|
19:42:26 |
|
|
フロントでSSL解いてリバースプロキシってのは割と良く作るのだけど、あれれ...?
nginx, ときどき httpd で出来てたことが出来なくて「えっ?」てなる。今後のためにメモ。 ```nginx.conf server { listen 80; server_name www.example.com; rewrite ^ https://$http_host$request_uri? permanent; } server { listen 443; server_name www.example.com; ssl on; ssl_certificate ... ssl_certificate_key ... : location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://backend.example.com/; proxy_redirect http:// https://; } } ``` ポイントは ```proxy_redirect http:// https://;``` の行。後ろが頭のいい奴(例:Rails)だと、X-Forwarded-Proto を見てよしなにヘッダを吐いてくれるんだけど、X-Forwarded-Proto 見てくれない奴が裏側にいる場合には、これで Location ヘッダなんかを強制的に https:// に書き換える必要がある。 apache httpd で言うところの ProxyPassReverse ですな。こっちの方が融通利くみたい。 ちなみに nginx の $http_host と $host は同じようでいて微妙に違うらしい > [ネタ元](http://forum.nginx.org/read.php?2,213799,213813#msg-213813) |
|
| 888位 |
|
|||
|
20:43:54 |
|
|
[Mina - 公式ドキュメント](http://nadarei.co/mina/) [Capistrano is dead - use Mina - weluse Blog](http://weluse.de/blog/capistrano-is-dead-use-mina.html) にて紹介されている、デプロイツール「Mina」の導入方法。 Mina は Capistrano のような、プロジェクトをリモートに配置する、いわばデプロイツールの一つだ。 ざっと調べてみた感じでは、 - Rake そのものを拡張している (Capistrano のように、構文を揃えているだけではない) - リモートで流すスクリプトを予め用意しておくことで、SSH接続を1回に抑え、オーバーヘッドを解消 - リモートで流されるコマンドをシェルスクリプトとして表示・確認することができる - Capistrano に比べてデプロイスピードが速く、マシンにも優しい - デフォルトで Capistrano の deploy_via :remote_cache のような挙動 (リポジトリの clone は最初だけ、次回以降は差分で更新) - まだ新しいプロジェクトなので、Capistrano に比べると拡張面で非力? - Capistrano より名前が可愛い (New!) といった印象だ。 公式ドキュメントは情報量が少ないながらも綺麗にまとまっている。 物は試しということで、実際に使ってみた。 ## 導入 ``` $ gem install mina ``` 例の如く gem でインストール。 ## セットアップ (ローカル) ``` $ mina init ``` カレントディレクトリに config/deploy.rb が作成される。 Capistrano の capify と同じ。 ## 設定ファイルの書き方 設定ファイルを編集する前に DSL を少しだけ頭に入れておく。 基本的には Rake の記法に則る。 (実際のところ、mina コマンドは rake のエイリアスであり、lib/mina/helpers.rb による追加の DSL とデフォルトのタスクを読み込むだけの様子) ``` ruby # set: キーに対して値を設定する (Mina の設定用) set :user, 'username' p user # => "username" (set したデータはメソッドとして呼び出せる) # desc: task の前に書いてタスクに説明を付ける desc "タスクの説明" # task: タスクを定義する task :mytask => [:subtask] do # subtask を前提タスクとして mytask を定義 # queue: リモートで "touch hoge.txt" を実行するコマンドを実行キューに追加 queue 'touch hoge.txt' # queue!: --verbose モードでコマンドが出力される以外は queue と同じ queue! 'rm -f hoge.txt' # invoke: タスク another を実行 invoke :another # commands: キューに入っているコマンドを配列で返す puts commands # > touch hoge.txt # > rm -f hoge.txt # run!: キューに入っているコマンドを全て実行する # キューの中身が消えたりはしない run! # run: リモートでコマンドを即座に実行する run "bundle install" # to: 特定の処理に名前をつける。ブロック内部ではキューが独立する。 # 同名で to 処理を二度書いた場合、ブロックが評価されるのは最初の一回だけである (上書きされない、但し実行はされる) to :barks do queue %[echo 'nyaoon'] p commands(:queue) # => ["echo 'nyaoon'"] run! # => nyaoon end # isolate: 明示的にキューを独立させる。 # (複数ドメインにデプロイするとき等に使う?) isolate do domains = ["a.example.com", "b.example.com"] domains.each do |domain| set :domain, domain Rake::Task[:deploy].reenable # 2回目以降でも実行する invoke :deploy run! end end # die: エラーを吐いて落ちる die "Bad Luck!" end ``` ## 設定 お好みのエディタで config/deploy.rb を編集。 一般的な Rails アプリケーションを想定した場合、こんな感じに。 ``` ruby:config/deploy.rb # 適宜必要な補助ライブラリを読み込む。 require 'mina/rails' require 'mina/git' require 'mina/bundler' # require 'mina/rbenv' # (Rbenv を使用する場合) # require 'mina/rvm' # (RVM を使用する場合) # デプロイユーザ、SSH接続先、デプロイ先のパス、Git のリポジトリ、ブランチ、RAILS_ENV set :user, 'www' set :domain, '10.0.1.1' set :deploy_to, '/Users/www/rails/waooon' set :repository, 'ssh://git@10.0.1.2/~/repos/waooon.git' set :branch, 'develop' set :rails_env, ENV['RAILS_ENV'] || 'production' # サーバ固有のファイルを設定する。 # #{deploy_to}/shared にファイルを置いておけば、deploy:link_shared_paths タスクによって本来の位置へとシンボリックリンクされる。 # config/database.yml を設定したい場合はサーバサイドで shared/config/database.yml を書いておけば OK set :shared_paths, ['config/database.yml', 'log'] # 環境設定用タスク。 # パスを張ったりプロキシの設定をするならここ? # 試しに .bashrc を読んであげたら上手く動いた。無いと bundle にパスが通ってなくてエラー task :environment do queue %[source ~/.bashrc] # Rbenv を使用する場合 # invoke :'rbenv:load' # RVM を使用する場合 # invoke :'rvm:use[ruby-1.9.3-p125@default]' end # リモートセットアップ時の挙動を設定する # (デフォルト) task :setup => :environment do queue! %[mkdir -p "#{deploy_to}/shared/log"] queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/log"] queue! %[mkdir -p "#{deploy_to}/shared/config"] queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/config"] queue! %[touch "#{deploy_to}/shared/config/database.yml"] end # デプロイ時の挙動を設定する desc "Deploys the current version to the server." task :deploy => :environment do deploy do # 一般的な Rails セットアップ用のタスク invoke :'git:clone' invoke :'deploy:link_shared_paths' invoke :'bundle:install' invoke :'rails:db_migrate' invoke :'rails:assets_precompile' to :launch do # 再起動用ファイルを作成 queue 'mkdir -p tmp' queue 'touch tmp/restart.txt' end end end ``` ## セットアップ (リモート) リモートのセットアップを行う。その前に、リモートにデプロイ先ディレクトリが無いなら作成しておく。 (11/8 13:00 追記) ディレクトリがない場合は自動で作成されるようだ。親ディレクトリに書き込み権限がない場合などは、当然自分で作ることになる。 ``` $ ssh www@10.0.1.1 "mkdir -p /Users/www/rails/waooon" ``` リモートのセットアップコマンドを実行。 ``` $ mina setup ``` セットアップ後のファイル構造は以下のようになった。 ``` ├── releases └── shared ├── config │ └── database.yml └── log ``` このタイミングでリモートの database.yml を適宜編集。 他に shared_paths 以下の設定ファイルがあれば編集しておく。 ## デプロイ (確認) --simulate オプションを使ってみる。所謂 Dry-Run? ``` $ mina deploy --simulate ``` タスク実行時にリモートで行われる予定のスクリプトがずらずらと羅列されていく。 リモートで実際にどんな処理が行われるかというのは気になるところだと思うので、これは確認しておこう。 といっても、僕は主にデバッグ用途に使っていた。 ## デプロイ ``` $ mina deploy --verbose --trace ``` --verbose, --trace は詳細表示とデバッグ用のオプション。 --verbose オプションを設定したとき、deploy.rb で queue! を使っていれば、実行前にコマンドが表示される。 (他のオプションは[こちら](http://nadarei.co/mina/command_line_options.html)) 下記のような結果が表示されれば成功。 ``` -----> Creating a temporary build path $ touch "deploy.lock" $ mkdir -p "$build_path" $ cd "$build_path" -----> Fetching new git commits $ (cd "/Users/www/rails/waooon/scm" && git fetch "ssh://git@10.0.1.2/~/repos/waooon.git" "master:master" --force) -----> Using git branch 'master' $ git clone "/Users/www/rails/waooon/scm" . --recursive --branch "master" Cloning into '.'... done. -----> Using this git commit $ git --no-pager log --format="%aN (%h):%n> %s" -n 1 AOKI Yuuto (xxxxxx): > initial commit $ rm -rf .git -----> Symlinking shared paths $ mkdir -p "./config" $ mkdir -p "." $ rm -rf "./config/database.yml" $ ln -s "/Users/www/rails/waooon/shared/config/database.yml" "./config/database.yml" $ rm -rf "./log" $ ln -s "/Users/www/rails/waooon/shared/log" "./log" -----> Installing gem dependencies using Bundler $ mkdir -p "/Users/www/rails/waooon/shared/bundle" $ mkdir -p "./vendor" $ ln -s "/Users/www/rails/waooon/shared/bundle" "./vendor/bundle" $ bundle install --without development:test --path "./vendor/bundle" --binstubs bin/ --deployment (省略) Your bundle is complete! It was installed into ./vendor/bundle -----> Migrating database $ RAILS_ENV="production" bundle exec rake db:migrate -----> Precompiling asset files $ RAILS_ENV="production" bundle exec rake assets:precompile RAILS_GROUPS=assets -----> Build finished -----> Moving build to releases/1 $ mv "$build_path" "$release_path" -----> Updating the current symlink $ ln -nfs "$release_path" "current" -----> Launching $ cd "$release_path" -----> Done. Deployed v1 Elapsed time: 16.00 seconds ``` ## 注意点 (今回のデプロイにあたって) - database.yml が正しく編集されていなかったりすると普通に mina deploy 内の db:migrate で落ちる (当たり前だけど) - デフォルトのままだと #{Rails.root}/tmp が作成されず 'touch tmp/restart.txt' が失敗する可能性がある - environment タスクで .bashrc を読んであげないと bundle が見つからずエラーになった。 (command not found: bundle) - isolate とループの組み合わせで繰り返し invoke する際、reenable でタスクを有効化する必要がある。呼び出したタスクで更に invoke が行われていた場合それも reenable しなければいけない為、現実的ではない? ## 覚えてたら便利そうなこと ``` $ mina tasks ``` 上記コマンドで使い方を確認。 ``` Basic usage: mina help # Show help. mina init # Creates a sample config file. mina tasks # Show all tasks. Server tasks: mina console # Starts an interactive console. mina deploy # Deploys the current version to the server. mina rails[arguments] # Execute a Rails command in the current deploy. mina rake[arguments] # Execute a Rake command in the current deploy. mina run[command] # Runs a command in the server. mina setup # Sets up a site. More tasks: mina bundle:install # Install gem dependencies using Bundler. mina deploy:cleanup # Clean up old releases. mina deploy:force_unlock # Forces a deploy unlock. mina deploy:link_shared_paths # Links paths set in :shared_paths. mina git:clone # Clones the Git repository to the release path. mina rails:assets_precompile # Precompiles assets (skips if nothing has changed since the last release). mina rails:assets_precompile:force # Precompiles assets. mina rails:db_migrate # Migrates the Rails database (skips if nothing has changed since the last release). mina rails:db_migrate:force # Migrates the Rails database. ``` deploy.rb 内で invoke していたタスクはそれぞれ切り出して個別に使える。 また、mina run なんかはサーバのログをちょっと確認したりするのに良いかもしれない。 ## Capistrano に取って代わるプロジェクトと成りうるか? 使い方次第だとは思うけれど、Capistrano と Mina でタスクに互換性がないのが難点。 僕は Cap 自体あまり突っ込んだ使い方をしていなかったので、Mina に移行しても不便はないかなと感じた。 冒頭で紹介した記事では「Capistrano は死んだ」なんて煽り気味のタイトルが踊っている訳だが、まだまだエンジニア次第ではないだろうか。 ## 参考になったページ - 公式 - [Mina](http://nadarei.co/mina/) - [デプロイツール mina を使ってみる | yukku++](http://blog.44uk.net/2012/11/03/use-deploy-tool-mina/) - [Mina – Getting started 和訳 | yukku++](http://blog.44uk.net/2012/11/03/mina-getting-started-ja/) |
|
| 889位 |
|
|||
|
12:26:03 |
|
|
Unityではシーンを切り替えるとGameObject等は全部破棄される。
でもゲームのスコアや、主人公キャラ等とっておきたいケースは多々ある。 そんな時に使える方法などなどのメモ書き。 ## static static付の変数は、アプリ終了時まで破棄されません。 (使い所は吟味する必要があるかと思いますが・・・ここでは割愛) ```c#:Hero.cs public static int score; void EnemyKill() { Hero.score += 100; } ``` ## GameObjectの場合は `Object.DontDestroyOnLoad`を使用。 引数に指定したGameObjectは破棄されなくなり、Scene切替時にそのまま引き継がれます。 ```c# public GameObject hero; void Awake() { // 特定のGameObjectだったり DontDestroyOnLoad(hero); // 自分自身だったり DontDestroyOnLoad(this); } ``` ## Sceneの合体 `Application.LoadLevelAdditive`でScene切り替えを行うと、 現在のSceneに切り替え後のSceneが合体されます。 一時的なSceneの呼び出し・・・例えば、ゲーム中のポーズ画面とかでしょうか。 ```c# Application.LoadLevelAdditive("PauseScene"); ``` ##以上を踏まえ応用編として、前Sceneを破棄せずに丸ごと保持しておく方法 1. EmptyなGameObjectを作る。 2. シーン内のGameObjectは、全て1で作成したGameObjectの子にする。 3. 1で作成したGameObjectを`DontDestroyOnLoad`で破棄しないようにするか、 次Sceneを`Application.LoadLevelAdditive`で合体する事により、 Scene内のGameObject全て、次のSceneに引き継ぎできる。 例えば1で作成したGameObjectを 非表示&一時停止`SetActive(false)`し、次Sceneを読み込み。 必要な箇所で有効`SetActive(true)`にする等々…色々応用が考えられるかと。 |
|
| 890位 |
|
|||
|
15:46:24 |
|
|
## 2014/10/07更に追記
iOS8でブラービューが標準機能として搭載されました。 別記事に投稿しましたのでそちらをご覧ください。 http://qiita.com/takabosoft/items/84e2039c19685d1c028b ## 2014/03/28追記 Twitterにて、@takatronix様より情報を頂きました。 このページの「自前BlurViewの作り方」でご紹介しているUIToolBarを使ったBlurViewを実装しますと、リジェクトになる可能性があります。 ご利用は自己責任でお願いいたします。 なお、Appleが公式で静止画にブラーを掛けるコードを提供していますので、動的ではなく静的なもので宜しければこちらを使っていただく方が確実です。 https://developer.apple.com/downloads/index.action `UIImageEffects`で検索すると出ます。 ## 概要 先日、ドット絵ツールの「EDGE touch」というアプリをリリースいたしまして、そこそこの評価を頂いているようなのですが(ステマです)、その時にiOS7の曇りガラス(磨りガラス)効果について多少経験値が溜まりましたので、知っている範囲でまとめておきたいと思います。 - Xcode 5.0.2 + SDK 7.0 + iPhone4S + iOS 7.0.4 (2014/02/07) - iOS6以前との互換性は一切考慮していません ## 標準の曇りガラスUIの挙動について ### 標準の曇りガラスUIの一般的な使い方 標準UIで曇りガラス効果が出る物は、`UINavigationBar`、`UIToolbar`、`UITabBar`ぐらいでしょうか。 SDK7を使えば勝手に曇りガラスになるので便利ですね。 UINavigationControllerを使った例がこちら。 ```objectivec:AppDelegate.mの一部 UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; self.window.rootViewController = navi; ``` ```objectivec:ViewController.m - (void)loadView { [super loadView]; self.view.backgroundColor = [UIColor whiteColor]; UIView *box = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; box.backgroundColor = [UIColor redColor]; [self.view addSubview:box]; } ``` 実行結果:  これだけでiOS7らしさが出るので楽しいですね。 ### 曇りガラス効果が得られない事案 ところが、一部の機種や設定によっては、曇りガラス効果が得られない事が判っています。 - 曇りガラス効果非対応の機種一覧情報が見つかりませんでした。ご存知の方はコメントをお願いします。 設定>一般>アクセシビリティ>コントラストを上げる...をオンに設定すると曇りガラス効果が無効になります。  実行結果:  ### 曇りガラスの色を変えるには さて、「コントラストを上げる」はいったんオフにしまして、これら標準UIの曇りガラスの色味を変えようと思ったら、何の疑いもせずに`barTintColor`プロパティを設定すると思いますが、iOS7.0.3以降は曇りガラス効果が上手い事得られません。 参考:[iOS 7.0.3からナビゲーションバーにtintColorを指定すると半透明じゃなくなった (yimajoさん)](http://qiita.com/yimajo/items/4781d5d2712e34677db2) ```objectivec:AppDelegate.mの一部 navi.navigationBar.barTintColor = [UIColor blueColor]; ``` 実行結果:  yimajoさんの記事にもありますが、アルファ値を下げると曇りガラス効果が何故か得られます。 ```objectivec:AppDelegate.mの一部 navi.navigationBar.barTintColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.2]; ``` 実行結果:  うまく曇るので、これで実装しちゃいがちなのですが、先にご紹介した「コントラストを上げる」をオンにすると... 実行結果:  ご覧の通りスッカスカになってしまって駄目です。 (余談ですが、開発の途中まではこのようにアルファ値を設定していました。一緒に開発をしていたデザイナーさんの端末の設定がたまたま「コントラストを上げる」になっており、「スカスカだよ?」と報告されるまで全く気がつきませんでした...危うくそのままリリースする所でした(^_^;) おそらく一般的な希望としてはデフォルトの動作と同じように、「コントラストを上げる」がオン(もしくは曇りガラス非対応機種)の状態ではバーは透過しないのが理想ではないでしょうか。 ではどうやってそれを実装するかですが、標準UIに色の薄いビュー(またはレイヤー)を被せるという手法を採用しました。 参考:http://stackoverflow.com/questions/18897485/achieving-bright-vivid-colors-for-an-ios-7-translucent-uinavigationbar ```objectivec:AppDelegate.mの一部(テストコードなのでいい加減です) UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, -20, 320, 44 + 20)]; view.backgroundColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.3]; [navi.navigationBar insertSubview:view atIndex:1]; ``` 実行結果:曇りガラスオン  実行結果:曇りガラスオフ  狙った色&ぼやけ方にするのは難しいですが、iOS7のマイナーバージョンによる挙動の違いに左右されませんし、端末/設定による曇りガラス効果に適した見た目になりますので、落としどころとしては無難なのではないでしょうか。 ### 標準曇りガラスでも重いものは重い 単純に上部にナビゲーションバー、下部にツールバーが出ているだけでも、コンテンツのフレームレートが著しく低下する現象を確認しています。iPhone5sなどではおそらく問題が無くても、iPhone4Sではカクカクになってしまう可能性があります。 見た目ではなく速度を重視する場合は素直に `toolbar.translucent = NO;` で曇りガラス効果を無効にしてしまいましょう。 ### 標準曇りガラスのUIImageViewへの影響について あんまりやらないとは思いますが、UIImageViewで画像を表示している時に、曇りガラス効果付きの標準UIを表示/非表示と切り替えると、UIImageViewで表示している画像が微妙に変化する現象を確認しています。 再現条件はよく判っていませんが、曇りガラスはある程度の大きさで、画像も補間が掛かった状態だと起きやすいようです。 ```objectivec:ViewController.m #import "ViewController.h" @implementation ViewController { UIToolbar *_tb; } - (void)loadView { [super loadView]; self.view.backgroundColor = [UIColor whiteColor]; UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"z33" ofType:@"jpg"]]; UIImageView *v = [[UIImageView alloc] initWithFrame:self.view.bounds]; v.contentMode = UIViewContentModeScaleAspectFit; v.image = img; [self.view addSubview:v]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if (_tb) { [_tb removeFromSuperview]; _tb = nil; } else { _tb = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 100)]; [self.view addSubview:_tb]; } } @end ``` タッチするとツールバーを表示したり消したりしてるだけのサンプルコードです。 画面の見た目は変わっているのですが、スクリーンショットには影響が出ないようでして、残念ながらここで比較画像を出す事ができませんでした。 ただ、曇りガラスが出ていると、補間が無効になるような感じになるので、だったら最初から補間処理を無効にしておけばいいじゃない!というわけで、UIImageViewを `v.layer.magnificationFilter = kCAFilterNearest;` という感じにセットしておけば、画像拡大時においては曇りガラスの表示/非表示に関わらず見た目が同じになる事を確認しています。 ところが画像縮小表示時に関しては、 `minificationFilter = kCAFilterNearest;` としても誤差が発生してしまいました。`kCAFilterTrilinear`が一番差が少なくて気がつかれにくいかもしれません。 ## カスタムUIで曇りガラス効果を得るには ### 公式でそういうビューがあるんじゃないの? iOS7になったので、せっかくならば自前のUIでも曇りガラス効果を使いたくなるかと思いますが、標準UIと同じ曇りガラス効果が得られるビューというのもは公式では存在していません。Appleの公式サンプルでは、UIImageをぼかすというものを見かけた事がありますが、開発者が期待するのはそんな物ではありませんよね。 というわけで、オープンソースをまとめて下さっている方がいらっしゃいましたのでご紹介。 参考:[オープンソースの曇りガラス風ビューをいろいろ試してみた(questbeatさん)](http://questbeat.hatenablog.jp/entry/2013/10/15/171900) 私も参考にさせてもらって、いくつか試してはみたのですが、組み込みやすく、使い勝手が良く、標準UIのような速度が出て、かつ端末やコントラスト設定によって曇ったり曇らなかったりするもの...というオープンソースが無さそうでしたので、最終的には`UIToolbar`の派生クラスを作って自前でBlurViewを用意しました。 将来UIToolbarの挙動が変わった時には対処が必要ではありますが、先の条件を満たすにはこの方法が一番だと判断しました。 ### 自前BlurViewの作り方 `UIToolbar`の派生クラスを作ります。 色の付け方は「曇りガラスの色を変えるには」でご紹介した方法と同様です。 セパレータが付いていますが、init内などで`clipToBounds = YES;`としてしまえば見えなくする事ができます。 と、まあ特に説明する事もないのですが、一点だけ変な挙動がありました。 自前BlurViewにラベル等をサブビューとして登録して`[UIView animate...]`でフェードイン/アウトを行うと、期待した結果にならない事がありました。こうした場合には、BlurViewとラベルを親子関係にするのではなく、兄弟関係にすると期待した結果に近づけられると思います。 (グループ的なフェードにはなりませんが。) ちなみに自作アプリではこのBlurViewを使ってコントロールセンターもどきのメニューを実装しました。  ## 最後に 結構いい加減な性格なので、まずい箇所などがありましたらコメントをお願いいたします。 |
|
| 891位 |
|
|||
|
03:32:35 |
|
|
## はじめに
ES6以降で使えるようになったgeneratorでJavaScriptの非同期処理を書く、という話をします。同様のトピックについてはすでに多くのブログで言及されているのですが、各所で異なった使われ方や解説がなされていることが多いように思われたので、ここではそれぞれの関係を明らかにしつつ、generatorのさまざまな使い方を整理するのが目的です。 ## ふつうのやり方 もっとも一般的なコールバックスタイルで書くとこうなる。コールバックの第1引数にErrorオブジェクト、第2引数に得られた結果を渡すのはnodeの慣習(エラー処理については今回は省略)。 ```javascript function sleep(cb) { setTimeout(function() { cb(null, 'I woke up at ' + new Date()); }, 1000); } sleep(function(err, res) { console.log(res); sleep(function(err, res) { console.log(res); sleep(function(err, res) { console.log(res); }); }); }); ``` こんな風にネストがどんどん深くなるのを、もっと「同期的に」書きたい、というのが問題の出発点。 ## 非同期処理を同期的に書く 「同期的に」とはどういうことか? 代表的な要件としては * ネストが入れ子にならない * 上から下に読んで実行フローが把握できる * 非同期処理の結果得られた値を次の処理に引き回せる * try/catchで素直に例外を捕捉できる などが挙げられる(もちろんこれら全てが必要不可欠なわけではない)。 代表的な方法を大別すると * deferred/promiseを使う方法 * generatorを使う方法 * その他([async.js](https://github.com/caolan/async)など)の方法 こう書くとgeneratorは他の方法の代替手段のようにみえるのだけれど、実はそうではない。むしろ両者を組み合わせることでより汎用的なインターフェースを提供できる。その代表例が[co](https://github.com/visionmedia/co)。はじめはここを全く勘違いしていて、coのソースコード自体は読めるのに、その意義がうまく理解できなかった。 ## 基本的な考え方 「`yield`を使えば関数の実行を途中で止められる」→「それならフロー制御の本体をgenerator関数で書けばよい?」→「`yield`で非同期処理が終わるのを待って、非同期処理が終わったら`g.next(val)`で処理を戻す」→「ネストなしで書けてうれしい」というのが、generatorによる非同期処理の基本的な発想。 ## 実装その1(素朴な方法) http://techblog.yahoo.co.jp/programming/js_callback/ ```javascript function sleep(g) { setTimeout(function() { g.next('I woke up at ' + new Date()); }, 1000); } var g = (function *() { console.log(yield sleep(g)); console.log(yield sleep(g)); console.log(yield sleep(g)); })(); g.next(); ``` * `sleep()`はgeneratorを受け取って、非同期処理が終わったら`g.next()`を呼び出す * ユーザはgenerator関数を実行してgeneratorを作成 * ユーザは`g.next()`を明示的に呼び出して、あとは順々に実行 ## 実装その2(generatorの抽象化) http://labs.cybozu.co.jp/blog/kazuho/archives/2007/05/coopthread.php ```javascript function runnable(generator) { var g; g = generator(function(val) { g.next(val); }); g.next(); } function sleep(cb) { setTimeout(function() { cb('I woke up at ' + new Date()); }, 1000); } runnable(function *(next) { console.log(yield sleep(next)); console.log(yield sleep(next)); console.log(yield sleep(next)); }); ``` 上から順に「フローの実行ライブラリ」「実行内容(とりあえずミドルウェアと呼んでおく)」「フロー本体」と、きれいに機能を分割できている。こうすると、フロー本体を書くユーザは、フローをgenerator関数でまとめて、内部で`yield`を書くと非同期で実行した値がもらえることさえ知っていればよい。またミドルウェアを書く人は「終ったら`cb(data)`を呼び出す」ことさえ知っていればよい。細かい実行タイミングの制御は`runnable()`がやってくれる。 ## 実装その3(coっぽい何か) ```javascript function naiveCo(generator) { var g = generator(); function next(data) { var ret = g.next(data); if (!ret.done) { ret.value(function(data) { next(data); }); } } next(); }; function sleep(next) { setTimeout(function() { next('I woke up at ' + new Date()); }, 1000); } naiveCo(function *() { console.log(yield sleep); console.log(yield sleep); console.log(yield sleep); }); ``` その2との大きな違いは、`yield`には関数を渡して、実行自体はライブラリの内部で行うようにした点。`g.next()`の戻り値のオブジェクト(の`value`プロパティ)から`sleep`を取り出して、コールバックを渡して実行している。その2までは「`g.next(val)`で入れた値を`yield`で取り出す」だけだったのが、今度は「`yield`で入れた値を`g.next()`の戻り値として取り出す」という逆向きのデータのやりとりが発生している。こうすると「yieldに何を入れるか」を抽象化できる。これがcoのドキュメントに書かれている[yieldable](https://github.com/visionmedia/co#yieldables)。 ## その4(ほんもののco) https://github.com/visionmedia/co ```javascript var co = require('co'); function sleep(cb) { setTimeout(function() { cb(null, 'I woke up at ' + new Date()); }, 1000); } co(function *() { yield function *() { console.log(yield sleep); console.log(yield sleep); console.log(yield sleep); }; console.log(yield sleep); console.log(yield sleep); })(); ``` ほんもののco。基本的な構造はその3と一緒なのだけれど、`yield`にyieldableなものたち、すなわちgenerator関数やpromise、配列やオブジェクトを渡したりできる。 実はここで`sleep()`の形式がとても重要で、必ずコールバックのみを引数に取る1引数の関数でなくてはならない。ところがnodeの非同期処理は、たとえば`fs.readFile()`がそうであるように、複数の引数の最後にコールバックを指定することが多い。この橋渡しを行うのが[node-thunkify](https://github.com/visionmedia/node-thunkify)で、これを使えば、元の関数を、コールバック以外の引数をあらかじめ部分適用できるような関数に変換できる。ここでは詳細な説明は省略する。`sleep()`をthunkifyする必要があるのは、下記のように休止時間を外から渡したい場合など。 ```javascript function sleep(interval) { return function(cb) { setTimeout(function() { cb(null, 'I woke up at ' + new Date()); }, interval); } } ``` ## まとめ 「generatorを使って非同期処理を同期的に書く」というテーマで、いくつかの利用パターンを整理してみました。その1からその4に進むに従って、抽象化と機能の分割が進んでいるのが見てとれます。 generatorと非同期処理については、すでに2006年頃、Firefox2.0が出た頃から話題になっていたのだけれど、去年の終わり頃、ちょうど[koa](https://github.com/koajs/koa)のリリースに前後するあたりから再び盛り上がりをみせているようです。 もともとJavaScriptの非同期処理をシンプルに書きたいというニーズは非常に高いのですが、(1) 実行フローをどれほど見通しよく書けるのか? (2) 既存のAPIからどのようにミドルウェアを生成するのか? という問いに対してのベストプラクティスはまだ確立していないように思います。それぞれのフロー制御のライブラリがどんなコンセプトのもとで実装されているかを考えてみると、コードを読む際にも理解が深まるかもしれません。 ## 参考 * http://labs.cybozu.co.jp/blog/kazuho/archives/2007/05/coopthread.php * http://techblog.yahoo.co.jp/programming/js_callback/ * http://tech.nitoyon.com/ja/blog/2013/06/27/node-yield/ * http://jxck.hatenablog.com/entry/2014-01-12/generator-screencaset * https://github.com/visionmedia/co * https://github.com/visionmedia/node-thunkify |
|
| 892位 |
|
|||
|
15:27:48 |
|
|
* 解説が不十分な部分ついては、補足(コピペで本文に取り込める体裁だとありがたい)を頂けると助かります!
* mysqlのバックアップ手法に関しては様々な方法がありますので随時追記していきたいと思います! * サーバー構成に関しても様々なパターンがありますので、随時追記していきたいと思います。 - - - mysql-server 5.1.8以降では、以前の手法ではWarningが発生するようになったため、 それ回避した上での手法を公開したいと思います。 まずmysql5.1.8以前のバージョンでのバックアップ手法 ``` mysqldump --opt --all-databases --default-character-set=binary -u root > /root/backup/mysql/all_db.sql ``` 以前はこれで問題なかったのですが、5.1.8以降では上記コマンドを実行すると下記のWaningが 発生するようになりました。 ``` -- Warning: Skipping the data of table mysql.event. Specify the --events option explicitly. ``` これはバージョン5.1.8からmysqlデータベースにeventsテーブルが追加されたことが原因です。 mysqldump コマンドに対して全てのデータベース内容をdumpする「`--all-databases`」のオプションを付与している場合、 これまでの手法では上記のeventsテーブルのみが対象外となってしまっているために発生しているWaningです。 この回避策としては、mysqldum コマンドに対して「`--all-databases`」のオプションを付与している場合は さらに「`--events`」 のオプションを追加する必要があります。 これらの内容を反映した上での、バックアップ取得スクリプトを作成してみました。 ## /bin/sh + mysqldump バージョン ###事前準備(バックアップ保存用ディレクトリの作成) ``` sudo mkdir /root/backup/mysql ``` ###スクリプト作成開始 ``` sudo vim /root/backup4mysql.sh ``` ```backup4mysql.sh #!/bin/sh # 他のユーザからバックアップを読み込めないようにする umask 077 # バックアップファイルを何日分残しておくか(一ヶ月分) period=31 # バックアップファイルを保存するディレクトリ dirpath='/root/backup/mysql' # ファイル名を定義(※ファイル名で日付がわかるようにしておきます) filename=`date +%y%m%d` # mysqldump実行(ファイルサイズ圧縮の為gzで圧縮しておきます。) mysqldump --opt --all-databases --events --default-character-set=binary -u root --password=パスワード | gzip > $dirpath/$filename.sql.gz # 古いバックアップファイルを削除 oldfile=`date --date "$period days ago" +%y%m%d` rm -f $dirpath/$oldfile.sql.gz ``` ###アクセス権限設定変更 このshellにはmysqlのrootのパスワードが丸々記載されているため、rootユーザ以外は内容を閲覧できなくしておく必要があります。 ``` sudo chmod 0700 /root/backup4mysql.sh ``` ###cron設定 最後にこのスクリプトをcronで定期実行することにより、定期的に全データベースのバックアップを取得することが可能となります。 ``` su - echo "0 3 * * * root /root/backup4mysql.sh" > /etc/cron.d/backup4mysql ``` 以上、ご参考になれば幸いです。 |
|
| 893位 |
|
|||
|
16:06:44 |
(あるけどいいたくない 所属) |
|
(追記:2016/01/16) ちゃんと追ってないですが、Vagrant 1.8からSnapshot機能が公式についたみたいなので、そこを先にチェックして見た方が今はいいと思います。 [Vagrant 1.8の公式サイトの紹介文](https://www.hashicorp.com/blog/vagrant-1-8.html) # vagrant-vbox-snapshotっていうVagrantのplugin、有用ですよ、という話 ネット上にvagrant-vbox-snapshotを使っている人が少ないみたいなので、紹介する価値があるかなと思って書いてみました。 Vagrantが何かとか、Vagrantのインストールの仕方とか基本的なことは書きません。ネット上に一杯溢れてますし。 で、このvagrant-vbox-snapshotは何かというと、Vagrantでsnapshotを作成するためのpluginです。詳しくは以下のリンクを見ると分かるかと。 - 公式サイト(githubリポジトリ) - https://github.com/dergachev/vagrant-vbox-snapshot - 日本語情報 - [Vagrant 1.1+ でスナップショット](http://hayajo.hatenablog.com/entry/2013/06/13/120258) - [仮想マシンのスナップショットとるならvagrant-vbox-snapshotが便利](http://blog.code-life.net/blog/2013/06/12/vagrant-vbox-snapshot/) VirtualBox以外でこのpluginを使用したことがないので他のソフトウェア(VMWareなど)を使用したときにも使えるかは分かりませんが、VirtualBox + Vagrant使用時ならかなり有用なpluginです。 #インストール 具体的な使い方は上記リンクを読んでみると大半書いてあるので改めて紹介する意味あるのかなと思いつつ書いていきます。 インストールは以下の通り。 `vagrant plugin install vagrant-vbox-snapshot` すると`vagrant snapshot`コマンドが使えるようになります。 `vagrant snapshot`と打って確認できるサブコマンド群はこんな感じです。 ``` $ vagrant snapshot Usage: vagrant snapshot <command> [<args>] Available subcommands: back delete go list take For help on any individual command run `vagrant snapshot <command> -h ``` 全部のコマンドは使ったことが無いので、普段使っているコマンドに絞って以下説明します。 # 使い方 ## スナップショットを作成する `vagrant snapshot take <スナップショット名>` ## スナップショットの一覧を出す `vagrant snapshot list` ## 指定したスナップショットの状態に移行する `vagrant snapshot go <スナップショット名>` ## スナップショットを削除する `vagrant snapshot delete <スナップショット名>` `vagrant snapshot back`は使ったことが無いのでよく分かりません。 # 何が便利なのか? 最近話題のChefやPuppet、Ansibleなどの構成管理ツールの実験をするときに各仮想マシンの状態を戻して実験する際に非常に役立ちます。というか無いとやってられません。 複数のスナップショットもこのpluginで作成できるので重宝してます。 |
|
| 894位 |
|
|||
|
21:28:43 |
(Picos LLC. 所属) |
|
**/tmp**と**/Caches**ディレクトリ内のファイルは、iTunesのバックアップ対象外でシステムにより自動で削除されます。 何となく使っていたら容量不足でアプリがクラッシュしてしまったので、2つの違いと運用方法について調べてみました。 # パスの取得 ```objectivec:Application_Home/tmp/ NSString *path = NSTemporaryDirectory(); ``` ```objectivec:Application_Home/Library/Caches/ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString *path = [paths objectAtIndex:0]; ``` # 違い |ディレクトリ|ドキュメント記載の使用例|アクティブ中のシステムによる削除|非アクティブ中のシステムによる削除| |---|---|:---:|:---:| |/tmp|次のアプリの起動で保持する必要のないファイル|×|◯| |/Caches|ダウンロードコンテンツ等|◯|◯| # 運用方法と注意 ## /tmp - アクティブ中にシステムによる削除を受けたくない一時ファイルに使用する。 - アクティブ中はシステムに削除されないため、ファイル容量に注意して管理しなければならない。 ##/Caches - アクティブ中にシステムが削除してもよい一時ファイルに使用する。 - アクティブ中にシステムによって削除された場合でも不具合が発生しない作りにしなければならない。 # まとめ 今回調べるきっかけになった問題は、画像の一時的な保存に**/tmp**を利用していて、システムによる削除が入るから大丈夫かなーって思って何も考えずにどんどん保存してたら10時間くらい後にアプリ容量が10GBくらいになり(iPhoneの空き容量が0GB)クラッシュしました。(適切な処理をしていればクラッシュしないかもしれません) あとドキュメントはちゃんと読みましょうということでした。 # ドキュメント [File System Programming Guide - About the iOS File System](http://developer.apple.com/library/ios/#documentation/FileManagement/Conceptual/FileSystemProgrammingGUide/FileSystemOverview/FileSystemOverview.html) |
|
| 895位 |
|
|||
|
01:05:41 |
(Picos LLC. 所属) |
|
## 前提知識 本投稿ではCoreTextの描画については記載してません。 基礎的な描画については[github](https://github.com/yusuga/CoreTextSample)の`0. 最小限の描画`などを参考にして下さい。 また別途、 [欧文書体の基礎知識](http://www.akibatec.net/wabunfont/study/basic/basic2.html) [CoreTextの日本語行間の問題(解決)](http://novis.jimdo.com/2011/07/02/coretextの日本語行間の問題-解決/) もお読み下さい。 ## 本題 ヒラギノフォントを使用した場合にでも正確に描画に必要なサイズを取得する方法です。 CoreTextでサイズを取得する場合は以下になります。 ```objectivec:CoreTextSample.m /* サイズを取得 */ // 属性 NSDictionary *attrDict = [NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)self.ctFont, kCTFontAttributeName, (__bridge id)self.ctParagraphStyle, kCTParagraphStyleAttributeName, nil]; // 属性文字列の作成 NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:self.text attributes:attrDict]; // CoreTextのフレームセッターの作成 CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attrStr); // 描画に必要なサイズを取得 CGSize contentSize = CTFramesetterSuggestFrameSizeWithConstraints( framesetter, CFRangeMake(0, attrStr.length), nil, CGSizeMake(self.bounds.size.width, CGFLOAT_MAX), nil); // リリース CFRelease(framesetter); ``` しかしヒラギノフォントを使用した日本語とアルファベットが混在する場合、上記だけでは正確に取得することが出来ません。 以降の説明では、サイズ取得は上記を使用します。 viewのサイズをわかりやすくするため self.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.4]; しています。 ## 横幅が足りない! テキストには `@"あいab"` を設定し、取得したサイズでviewのサイズを設定します。 ```objectivec: self.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.4]; self.text = @"あいab"; CGFloat fontSize = 50.f; self.ctFont = CTFontCreateWithName(CFSTR("HiraKakuProN-W3"), fontSize, NULL); //////////////// /* サイズを取得 */ //////////////// self.frame = CGRectMake(0.f, 0.f, contentSize.width, contentSize.height); NSLog(@"size %@", NSStringFromCGSize(contentSize)); // size {155.615, 60} ``` ***abが描画されてない***  横幅が足りない為 `ab` が描画されていません。 注目は取得した横幅が **155.615** になっている点です。 実際に描画を行なっている`- (void)drawRect:(CGRect)rect:`に渡されるRectはピクセルサイズに丸められるのでrectには`{{0, 0}, {155.5, 60}}`が渡されることになり、描画に必要な横幅が`0.115`足りなくなります。そのため描画されない文字が発生します。 これは切り上げればいいのでfloatを切り上げる関数の `float ceilf(float)` を使用して解決です。 ※ より厳密に行うなら0.5ポイント単位で切り上げれば良いかと思います。 self.frame = CGRectMake(0.f, 0.f, ceilf(contentSize.width), contentSize.height); ***abも描画された!***  ## 縦幅が足りない! テキストには `@"あいabgj"` を設定し、取得したサイズ(横幅は切り上げて)でviewのサイズを設定します。 ```objectivec: self.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.4]; self.text = @"あいab"; CGFloat fontSize = 50.f; self.ctFont = CTFontCreateWithName(CFSTR("HiraKakuProN-W3"), fontSize, NULL); //////////////// /* サイズを取得 */ //////////////// self.frame = CGRectMake(0.f, 0.f, contentSize.width, contentSize.height); NSLog(@"frame %@", NSStringFromCGRect(self.frame)); // frame {{0, 0}, {207, 50}} ``` ***gjが途切れている***  こんどは縦幅が足りません。 複数行ではこんな感じです。 ***3行目のgjが途切れている***  ここで注目は**1行あたりの縦幅が足りない**というわけではない点です。(1行あたりの縦幅が全部違う場合は、3行の場合全体でもっと足りなくなることになります) 何が足りないかというと最後の1行だけ `Descent` 分高さが足りなくなっています。(理由は不明。バグ?) ですので、`Descent`分高さを足せば解決です。 設定しているCTFontのDescentを加え、横幅の場合と同じように切り上げをします。 self.frame = CGRectMake( 0.f, 0.f, ceilf(contentSize.width), contentSize.height + ceilf(CTFontGetDescent(self.ctFont))); ***gjが途切れてない!***  ***解説用色付けした図***  赤色 Asent 黄色 Descent 黒色 足りないDescent ## まとめ - CTFramesetterSuggestFrameSizeWithConstraints()で取得したサイズは縦横共に小数点切り上げる - 高さには更に使用フォントのDecentを加える - CoreTextについてまとめたものは[github](https://github.com/yusuga/CoreTextSample)にまとめてあります。今回のサイズを求めるのは`14. sizeToFit`です。 ## その他 CTFontCreateWithName(CFSTR("Helvetica"), fontSize, NULL); などヒラギノフォントを使用しなければ上記の問題は発生しません。 しかし、[CoreTextの日本語行間の問題(解決)](http://novis.jimdo.com/2011/07/02/coretextの日本語行間の問題-解決/)の問題があるので、明示的にヒラギノフォントでCTParagraphStyleSettingを設定する必要があります。その場合はサイズを取得するために上記の対応が必要です。 その他の解決方法で1行ずつサイズを求める方法があります。 ちゃんとは試してないのですが、以下でCTParagraphStyleSettingで設定したleadingを毎行加えれば求めることが出来ると思います。 http://stackoverflow.com/questions/2707710/core-texts-ctframesettersuggestframesizewithconstraints-returns-incorrect-siz ```objectivec:CoreTextSample.m /* 以下は使用する場合はCTParagraphStyleSettingで設定したleadingを毎行足せば多分高さが求まる [注意] CTLineGetTypographicBounds() はCTParagraphStyleSettingが考慮されず値が返る */ +(CGFloat)heightForAttributedString:(NSAttributedString *)attrString forWidth:(CGFloat)inWidth { CGFloat H = 0; // Create the framesetter with the attributed string. CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString( (__bridge CFMutableAttributedStringRef) attrString); CGRect box = CGRectMake(0,0, inWidth, CGFLOAT_MAX); CFIndex startIndex = 0; CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path, NULL, box); // Create a frame for this column and draw it. CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(startIndex, 0), path, NULL); // Start the next frame at the first character not visible in this frame. //CFRange frameRange = CTFrameGetVisibleStringRange(frame); //startIndex += frameRange.length; CFArrayRef lineArray = CTFrameGetLines(frame); CFIndex j = 0, lineCount = CFArrayGetCount(lineArray); CGFloat h, ascent, descent, leading; NSLog(@"start line -> %ld", lineCount); for (j=0; j < lineCount; j++) { CTLineRef currentLine = (CTLineRef)CFArrayGetValueAtIndex(lineArray, j); CTLineGetTypographicBounds(currentLine, &ascent, &descent, &leading); h = ascent + descent;// + leading; NSLog(@"%f %f %f %f", ascent, descent, leading, h); H+=h; } CFRelease(frame); CFRelease(path); CFRelease(framesetter); return H; } ``` |
|
| 896位 |
|
|||
|
10:37:25 |
|
|
webアプリケーション開発者たるもの、時には https と戦わねばならぬこともあるのです。
打つ手がなくて泣きそうになっていたら同僚から救いの手が! (ただしサーバ側/クライアント側のいずれかで鍵交換アルゴリズムを変更できる場合に限る) まず、tcpdump でダンプしたものを wireshark で見るには `-w FILE` オプションをつけてダンプデータをファイルに書き出させます。環境によってはデフォルトで先頭数十バイトしかダンプしない (man によれば、CentOS 5.x で 68 bytes, 同じく Ubuntu 10.04 で 65535 bytes)ようなので、`-s 65535` とか `-s 0` などとダンプさせるサイズを指定してやるのが吉 今回は https なのでお手軽にポートで絞ることにして...こんな感じで叩けば良いかと。 ```shellscript tcpdump -s 65535 -w /tmp/dump.pcap -i eth0 dst port 443 or src port 443 ``` 出力されたファイルは普通に wireshark で開けます。もちろんこのままだとメッセージボディは暗号化されていて読めません。 次に wireshark で暗号化解きの設定をします。wireshark のメニューから "Preferences" > "Protocols" > "SSL" と開き、 "RSA keys list:" となっているところの "Edit" ボタンをクリック。 開いたウインドウで "New" ボタンをクリックし、以下のようにサーバの秘密鍵を登録します。 * IP Address: (サーバのアドレス) * Port: 443 * Key File: (サーバの秘密鍵、pem形式) * Password: (秘密鍵にパスフレーズを設定している場合、パスフレーズ) この辺りの操作は [WiresharkでSSL/TLSトラフィックを解読する方法@Citrix Knowledge Center](http://support.citrix.com/article/CTX116872) そのままですので、詳しくはそちらをご覧いただくのが良いかと。 これで成功していれば生データが表示されるペインに "Decrypted SSL Records" というタブが追加され、そこでほどいた中身が見えます...が、たいていうまくいきません。メッセージの暗号化に使う共有鍵がさくっと解けないような方式で交換されているせいとかなんとか。(DHアルゴリズム。この辺いろいろありますが、説明できるほど調べてないので適当にごまかす) 鍵交換/暗号化アルゴリズムはSSLのセッション確立時にサーバ-クライアント間で強度の高いものが選ばれるようになっていますので、サーバ側、クライアント側のいずれかでDHを使わないように絞ってあげないとだめな模様。 クライアントが openssl コマンドであれば、鍵交換/暗号化アルゴリズムを選択できますので ``openssl ciphers -v`` して出てくる中から ``Kx=RSA`` となっているものを選んで、``-cipher HOGE`` とオプションをつけて叩いてください。あまりしょぼいアルゴリズムだとサーバ側から蹴飛ばされることがあるので、AES256-SHA あたりが無難かと思います。 たとえば、こんな感じで。 ```shellscript cat /tmp/request.txt | openssl s_client -cipher AES256-SHA -connect localhost:443 ``` クライアントがブラウザの場合など、設定を自由にいじれない場合には[こちらの方法](http://qiita.com/items/1283c6c83cc72a218bb9)で生のリクエストのダンプを取得して、``openssl`` コマンドで投げつける (動作がクライアントの動作に依存する場合には使えません) か、サーバ側で設定してあげる必要があります。webサーバに Apache httpd を使用している場合、例えば以下のように設定することで RSA方式での鍵交換を強制できる...と思います (未検証) ```httpd.conf SSLCipherSuite kRSA ``` この状態でダンプしたデータであれば、wireshark でメッセージボディがほどけているはずなので、後は http として解読できます。 俺のデバッグはまだ始まったばかりだ! |
|
| 897位 |
|
|||
|
17:05:18 |
|
|
##選択された単数要素を取り出す
基本はselectタグの属性を指定してval()で値を取得すればよいが、表示文字列を取得したいときはoption:selectedを指定する必要がある。 ```html:html <select name='alphabet'> <option value='ABC'>えーびーしー</option> <option value='DEF'>でーいーえふ</option> <option value='GHI'>じーえいちあい</option> <option value='JKL'>じぇーけーえる</option> </select> ``` ```js:js $('[name=alphabet]').change(function() { // 選択されているvalue属性値を取り出す var val = $('[name=alphabet]').val(); console.log(val); // 出力:ABC // 選択されている表示文字列を取り出す var txt = $('[name=alphabet] option:selected').text(); console.log(txt); // 出力:えーびーしー }); ``` ##選択された複数要素を取り出す タグにmultiple属性が指定されており、複数要素を取り出す必要がある場合はどうなるか。 ```html:html <select name='alphabet' multiple='multiple'> <option value='ABC'>えーびーしー</option> <option value='DEF'>でーいーえふ</option> <option value='GHI'>じーえいちあい</option> <option value='JKL'>じぇーけーえる</option> </select> ``` ```js:js // 前述のコードと同じ $('[name=alphabet]').change(function() { var val = $('[name=alphabet]').val(); console.log(val); // 出力:['ABC', 'DEF'] var txt = $('[name=alphabet] option:selected').text(); console.log(txt); // 出力:えーびーしーでーいーえふ }); ``` 前述したコードでは、値を取り出した場合は配列として取得できるようだが、表示文字列はすべて連結された一文字列として取得される。 例えば、同じように配列として各表示文字列を取得したいのであれば、eachを使用して一つずつ配列にpushすればよい。 ```js:js // 複数選択された表示文字列を配列として取得 $('[name=alphabet]').change(function() { var array = []; $('[name=alphabet] option:selected').each(function() { array.push($(this).text()); }); console.log(array); // 出力:["えーびーしー", "でーいーえふ"] }); ``` |
|
| 898位 |
|
|||
|
18:22:14 |
(フリーランス 所属) |
|
Web サーバやアプリケーションサーバは、リクエスト数やメモリ使用量がある閾値を超えたらプロセスの再起動を行う仕組みが用意されています。
Apache にも IIS にもそのための設定項目が用意されていて、この仕組みを一般にリサイクルと呼ぶようです。 UA からのリクエストをさばくワーカープロセスが疲弊せず、パフォーマンスを維持するための仕組みですね。 Ruby で人気のアプリケーションサーバである Unicorn ですが、残念ながらこのリサイクルの機能を持っていません。 どうにかできないかなーと思って調べていたら、この短所を補完する Gem がリリースされていました。 [Unicorn Worker Killer | github](https://github.com/kzk/unicorn-worker-killer) そのまんまですね。 --------------------------------------- この Gem でできるのは、以下のようなリサイクルです。 ・一定のリクエスト数を超えたらランダムで再起動する(上限に達したら必ず再起動)。 ・一定のメモリ使用量を超えたらランダムで再起動する(上限に達したら必ず再起動)。 公式そのまま引用すると、以下のような設定を行うことになります。 ```rb:config.ru # Unicorn self-process killer require 'unicorn/worker_killer' # Max requests per worker use Unicorn::WorkerKiller::MaxRequests, 3072, 4096 # Max memory size (RSS) per worker use Unicorn::WorkerKiller::Oom, (192*(1024**2)), (256*(1024**2)) ``` リクエスト数もメモリ使用量も、値が二つあります。最初はなんで?と思いますよね。 公式の解説を読む限り、上限値ひとつにしなかったのは、複数のワーカープロセスの再起動タイミングの足並みが揃わないようにするため、ということらしいです。 実際に使用してみると、以下のようなログを吐くようになります。 ``` Jun 19 13:57:52 jobhub app/web.3: W, [2013-06-19T13:57:52.262901 #10] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 10) alive: 68769 sec (trial 1) Jun 19 14:10:36 jobhub app/web.3: W, [2013-06-19T14:10:36.849141 #14] WARN -- : #<Unicorn::HttpServer:0x00000002963690>: worker (pid: 14) exceeds memory limit (211341312 bytes > 211015086 bytes) ``` --------------------------------------- 一度決めてしまえばそんなに頻繁に変えない設定項目だと思いますが、ApacheBench や JMeter などで負荷テストをしながらおいしい値を探る場合、デプロイを繰り返すのは少々面倒です。 弊社では heroku で運用していることもあり、以下のように環境変数を使用するように変更しました。 ```rb:config.ru # Unicorn self-process killer require 'unicorn/worker_killer' max_request_min = ENV['UNICORN_MAX_REQUEST_MIN']&.to_i || 3072 max_request_max = ENV['UNICORN_MAX_REQUEST_MAX']&.to_i || 4096 # Max requests per worker use Unicorn::WorkerKiller::MaxRequests, max_request_min, max_request_max oom_min = ((ENV['UNICORN_OOM_MIN']&.to_i || 250) * (1024**2)) oom_max = ((ENV['UNICORN_OOM_MAX']&.to_i || 300) * (1024**2)) # Max memory size (RSS) per worker use Unicorn::WorkerKiller::Oom, oom_min, oom_max ``` まあ[こちら](http://mycatwantstolearnrails.blogspot.jp/2013/04/heroku-unicorn-request-backlog.html)をマネしたんですけどねw ちなみに、Gem の作者は 日本発のBigData取り扱いサービスである、 [TreasureData](http://www.treasure-data.com) の中の人のようですね。 作者が日本人って、なんとなく嬉しくなります。 |
|
| 899位 |
|
|||
|
22:17:38 |
(kayac.com 所属) |
|
テストフレームワークは、busterJSが一番慣れてたんですが、 部署御推薦の[mocha](http://mochajs.org/)をいっちょやってみるかー、と思っているこのごろです。 そしてついでに、 **「このへんは、node.jsの場合どうテストするのがかっこいいかしら!!」** と気になっていた部分について、いろいろ調べてみました。 mochaの細かい説明については、[公式](http://mochajs.org/)その他をみてください。 mochaっていうかほとんど[should](http://npmjs.org/package/should)ですよね。 ## 普通のテスト(libraryやcontrollerのテスト) テストしたいmoduleをrequireして、 shouldもrequireして、テストを書く。これが基本ですね。 ```javascript // テストしたいmoduleをrequire var hoge = require('../hoge'); // テスト用のライブラリをrequire (mochaの場合は、shouldがあればいいはず) var should = require('should'); // あとはテストを書くのみ describe('hoge', function () { it('足し算をする', function () { hoge.add(2, 3).should.equal(5); }); it('割り算をする', function () { hoge.divide(6, 3).should.equal(2); }); it('ゼロで割ったらぬるぽ', function () { // falseやnullを確認するときはこんな感じ should.not.exist(hoge.divide(5, 0)); }); }); ``` しかし、こうシンプルに書けない場合、 nodeのコードの中だけで完結しないようなコードは、どうテストするんだろう? と気になってたわけです。 ## express serverをテスト たとえば、express serverのルーティング。 テストコード内で実際にサーバーを起動させて、 実際にURLを叩いてテストする…? とか思ってたんですが、 ここでいい感じのサンプルを見つけました。 - [コードで一言: CoffeeScriptとExpressとMongoDbとMochaでNode.jsするメモ](http://codedehitokoto.blogspot.jp/2012/02/coffeescriptexpressmongodbmochanodejs.html) 簡単に書くと以下。 ```javascript // expressのroutesのみrequire var routes = require('../routes'); require('should'); describe('routes', function () { var req, res; beforeEach(function () { // 簡単に、フェイクのreqとresを用意 req = {}; res = { redirect: function () { }, render : function () { } }; }); describe('index', function () { it('should display index page with title', function (done) { // res.render、すなわち表示用に呼ばれる関数をフェイクで作成 // 事実上のcallback関数 res.render = function (view, vars) { // 'index'というviewを使うことを確認 view.should.equal('index'); // 変数'title'が'Express'になっていることを確認 vars.title.should.eql('Express'); // テスト終了 done(); }; // 実際に、routesのindexを実行 = アクセスしてみる routes.index(req, res); }); }); }); ``` ポイントは、 - expressの本体(app.js)ではなく、routesのみrequireすること - res.renderをその場で書くことで、callback関数のような役割になる ということ。 考えてみれば、expressは本体がかなり薄ーく作ってあるので、 routesだけテストすれば、ほぼサーバー自体のテストになるわけなんですね。 (urlとroutesの対応とかは、これでは見れてないけど) あと、ここまで書いた後に改めて調べたら、 ちゃんと[express-test](https://npmjs.org/package/express-test)というnpmもあったので、 そちらを使った方が素直なのかもしれませぬ。 ## cliをテスト nodeで書くものといえばサーバー、あるいはコマンドラインツールですよね。 コマンドラインツールも、実際に実行してテストした方がいい…? とかいろいろ悩んでました。 これに関しては、[npmで"cli"keywordがついてるやつ](https://npmjs.org/browse/keyword/cli)をコードリーディングしてみました。 が、**どうやらあんまりテスト書かれてない…?** [forever](https://npmjs.org/package/forever)とか[http-server](https://npmjs.org/package/http-server)とか[grunt-cli](https://npmjs.org/package/grunt-cli)とか、 "cli"の有名どころを読んでみたんですが、 本体のテストだけで、コマンドラインのインタフェース部分についてはテストされていない感じでした。 (gurnt-cliに関しては、全体通してもjshintしかしてない) そ、そういうもんなのかな…? とりあえず、 - cliの部分をできるだけ薄くして、オプション付け方とかまでどこかで書いてテストも書くとか、 - child_processで実際に実行してテストするとか、 ですかね。 また実際にやってみて考えます。 ## mongooseなモデルをテスト これはなんてこたぁない普通のテストだと思うんですが。 ただ、しばらくPerlばっかり書いてる時期があった名残で、 dbの初期化とかもかっこよく書きたい。 もっと言うと、node-configからtest用のconnect情報取って、つなぐとこまでは共通化して書きたい。 とか思ったので、test initスクリプトみたいなのを書いてみました。 ```javascript:lib/test.js process.env.NODE_ENV = 'test'; var mongoose = require('mongoose'), config = require('config'), async = require('async'); require('should'); var test = function (initModels) { before(function (done) { async.series([function (next) { mongoose.connect(config.db.url, next); }, function (next) { async.forEach(initModels || [], function (model, next) { mongoose.model(model).remove(next); }, next); }], done); }); }; module.exports = test; ``` これで、テストコード本体で以下のように呼び出すだけで、 ```javascript require('lib/test')(['User']); ``` - envを'test'に設定 - configをenvで出し分けしてあるので、test用のconfig情報が読まれる - beforeの中で、connectとdb初期化を実行 - ので、テストコード中ではそれらを意識しなくても勝手にやってくれる といかんじでかっこいいです。 しかしbefore便利ですね。 テストコード中の非同期ネストが確実に減らせてうれしい。 ## その他 - テストに使う固定のファイルは、```test/fixture```に置いているケースが多かった。 ## まとめ - mochaいい。 - Perlやっとくとテスト好きになってすごくいい。 - クライアントサイドのテストコードも読みたい。 |
|
| 900位 |
|
|||
|
19:40:26 |
(フリーランス 所属) |
|
環境は、rails 3.2.6、ruby 1.9.3、heroku-toolbelt/2.33.0 です。
*** #秘密鍵を作る (参考)https://devcenter.heroku.com/articles/csr ``` $ openssl genrsa -des3 -out server.orig.key 2048 ... Enter pass phrase for server.key: 適当なパスワードを。 Verifying - Enter pass phrase for server.key: もう一度同じパスワードを。 ``` WEBサーバーに自動でアップロードするために、パスワードを外しておきます。 ``` $ openssl rsa -in server.orig.key -out server.key ``` 署名リクエストを作ります ``` $ openssl req -new -key server.key -out server.csr You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:JP ※国名 State or Province Name (full name) [Some-State]:Hokkaido ※都道府県 Locality Name (eg, city) []:Hakodate ※市区町村 Organization Name (eg, company) [Internet Widgits Pty Ltd]:Helply LLC. ※会社名 Organizational Unit Name (eg, section) []: ※部署(入力せず) Common Name (eg, YOUR name) []:www.helply.net ※証明書に表示される名前 Email Address []:gen.tamura@helply.net ※メールアドレス Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []:※入力せず An optional company name []:※入力せず ``` 私は上記のように入力しました。お好みでいいと思います。 ※ちなみにこの一連の作業はRailsのappやconfigがある階層で保存してます。 *** #証明書を購入する (参考)https://devcenter.heroku.com/articles/ssl-certificate 1. 証明証を購入しましょう。私はHerokuおすすめのDNSimpleから購入しました。 https://dnsimple.com/ 2. アカウントをつくったら、**Domains**の**Add a Domain**から希望のドメインを追加しましょう。(私はhelply.netを追加。) 3. 追加したドメインの右端にある**Manage**をクリックします。 4. 画面中央の**SSL Certificates**にある**Buy an SSL certificate**をクリックします。 5. 購入にあたっての情報を入力します。 ※1点注意があって、サブドメインを指定する場合は$20/年で、*(ワイルドカード)だと$100/年となるのでご注意下さい。 6. 最後に「Please generate a private key and certificate signing request for me. Uncheck this box to provide the CSR yourself. If you generate your own CSR it must be valid.」とありますが、チェックはつけたままで登録しましょう。 (外すとcsrを自分で作ってね。とありますので、外すとハマる気がします) 7. 次のページに、「admin@yourdomain.com、webmaster@yourdomain.com、root@yourdomain.com」などの管理者用メールアドレスを選択する画面になります。これは後ほど、証明機関から承認URLを受信するために必要です。表記された該当のメールアドレスを持っていない場合は新たに該当のメールアドレスを取得する必要があるので、ご注意下さい。 8. 完了すると、DNSimpleから「8時間以内に証明機関から、証明書を承認するためのリンクが届きます。」という内容のメールが届きます。少し待ちましょう。(私は10分ぐらいで届きました。) 9. しばらくすると、GeoTrust(証明機関)から「Approver Emailが届くからよろしくね。」なるメールが届きます。そののち、管理者用メールアドレスに届く「RapidSSL Certificate Request Confirmation 」を確認し、「あなたのお名前 requests that you come to the URL below to review and approve this certificate request:」の文章の下のリンクをクリックします。 10. すると日本語で書かれた「RapidSSL® 注文の確認および承認」という書式が表示されますので、一番下の「承認します」をクリックします。 11. これで購入が完了しました。GeoTrustから完了メールが届きます。 #証明書をダウンロードする (参考)https://devcenter.heroku.com/articles/ssl-certificate の Download certificate files以下 1. DNSimpleを開き、**Domains**→**SSL Certificates**の**Details**をクリック。 2. Statusが「Certificate Issued」となっていれば、「Private Key:」と「Certificate:」が表示されているはずです。※購入完了してから、少し時間が必要かもしれません。 3. 「Private Key:」を```server.key```に追加します。 ※保存は最初の「-」から最後の改行までです。 ※エディタで開いて、一番下に追加します。 4. 「Certificate:」を```server.orig.crt```を新規に作成し、保存します。 ※保存は最初の「-」から最後の改行までです。 5. Herokuではサイト証明書と中間証明書を一つのファイルにまとめる必要があります。 そこで以下のコマンドを実行します。 ``` curl https://knowledge.rapidssl.com/library/VERISIGN/ALL_OTHER/RapidSSL%20Intermediate/RapidSSL_CA_bundle.pem > rapidssl_bundle.pem cat server.orig.crt rapidssl_bundle.pem > server.crt ``` *** #HerokuのSSL導入 \1. アドオンを追加する ``` $ heroku addons:add ssl Adding ssl on young-planet-6921... done, v1 ($20/mo) Next add your certificate with `heroku certs:add PEM KEY`. Use `heroku addons:docs ssl` to view documentation. ``` \2. ドメインを設定する(すでに独自ドメインを設定している場合は不要) ``` $ heroku domains:add www.mydomain.com Added www.mydomain.com to myapp... done ``` \3. プラグインを追加する ``` $ heroku plugins:install https://github.com/heroku/heroku-ssl-doctor.git ``` ※実は4でどうにも進まなくなり、Herokuのサポートへメールをしたら、このプラグインを教えてもらいました。 \4. 証明書を追加する ``` $ heroku certs:add server.crt server.key Adding SSL Endpoint to myapp... done myapp now served by tokyo-2121.herokussl.com. Certificate details: Expires At: 2012-10-31 21:53:18 GMT Issuer: C=US; ST=CA; L=SF; O=Heroku; CN=www.mydomain.com Starts At: 2011-11-01 21:53:18 GMT Subject: C=US; ST=CA; L=SF; O=Heroku; CN=www.mydomain.com SSL certificate is self signed. ``` *** #DNSを変更する \1. HerokuのIPアドレスを確認 さっきの4で表示されたEndpointを確認。 ここでは、tokyo-2121.herokussl.com. \2. ご利用のネームサーバーにレコードを追加する。 「www.mydomain.com」などの独自ドメインに対して、CNAMEで「host tokyo-2121.herokussl.com」を追加します。 #Railsの設定を変更する 最後にRailsの設定変更です。 デフォルトでは、コメントアウトになっていると思うので、 コメントインします。falseならtrueへ。 ```ruby:config/environments/production.rb config.force_ssl = true ``` 以上で、httpsでのアクセスが可能になります。 *** #※番外編 SSLを更新する場合 \1. SSL証明書が切れる2ヶ月ぐらい前に、DNSimpleから「Expiration Notification」のメールが届きます。 \2. 本文中の「Renew Now」をクリック。 \3. 「Generate a private key and certificate signing request for me」のチェックを入れたまま、「Renew SSL Certificate」のボタンをクリック。 \4. 「証明書を購入する」の7〜11と「証明書をダウンロードする」を実施する。 \5. 「HerokuのSSL導入」の4.のみを以下の通り実施する。 ``` $ heroku certs:update server.crt server.key ``` 以上で、更新が完了です。 |
|
| 901位 |
|
|||
|
21:33:29 |
(Dwango 所属) |
|
## よくやる variable = argument || default_value; パターン は割と罠
jQuery のソースコードなり、色んな実装で見るこのパターンのことです。 ``` javascript /** * @param {number} value * @param {number=} opt_value * @param {(function():*)=} opt_callback */ function doSomething(value, opt_value) { var option = opt_value || 10; // opt_value が渡されていなかったらデフォルト /** * 以下なんらかの処理 */ } ``` これは `undefined` を boolean キャストして `false` が返る仕様を利用したテクニックですが、実際は割と限定された用途で書けるものだと捉えた方が良いです。 ## まずいパターン このようなコンストラクタを作った場合 ``` javascript function Hoge(foo, opt_bar) { this.foo = foo || 'foo'; this.bar = opt_bar || 10; } ``` ### 目的を持って null を渡す場合に無効化される `null` も `undefined` 同様 `boolean` として扱うと `false` を返します。 ``` javascript var hoge = new Hoge('hey', null); hoge.bar; // 10 ``` ### opt_value が 0, [], {}, false, '' が渡って、それに意味がある場合 同様に `boolean` として扱うと `false` になるものもすべて無効化されます。 ``` javascript var hoge = new Hoge('hey', 0); hoge.bar; // 10 ``` ### opt_value をバリデーションしないと挙動が想定してない結果になる場合 利用側でそもそも渡すべきではない、という方針もありだけど (というか、そうであれば optional arguments を設計に入れない方が良い)、こういった失敗のパターン ``` javascript /** * @param {Array.<number>} scores * @param {number=} opt_index * @return {number} */ function average(scores, opt_index) { var l = opt_index || scores.length, // option 引数で配列を絞り込むつもり sum = 0; for (i = 0; i < l; i++) { sum += scores[i]; // l > scores.length であった場合 undefined が加算 } return sum / l; // l > scores.length であった場合 sum が NaN になってる } ``` ## 対策 ### opt_value === undefined ? default_value : opt_value; パターンにする 関数宣言内では `typeof undefined_value == 'undefined'` としなくても `RefferenceError` にはなりません。以下のように三項演算子で書くと、確実に引数が渡されていなかった、あるいは `undefined` を渡していた時のみデフォルト値を代入します。 ``` javascript function Hoge(foo, opt_bar) { this.foo = foo || 'foo'; this.bar = opt_bar === undefined ? 10 : opt_bar; } ``` ### supplememt() メソッドみたいなのを作る バリデーションも込みで効率化したい場合、(というか、↑ はたくさん書くとすごい読みづらいし糞コード化する) #### `supplememt(default_value, opt_arg, opt_callback)` ``` javascript /** * @description 関数宣言内で引数をバリデーションしたり * @param {*} default_value * @param {(*)=} opt_arg * @param {(function(*,*):*)=} opt_callback * @return {*} */ function supplement(default_value, opt_arg, opt_callback) { if (opt_arg === undefined) { return default_value; } if (opt_callback === undefined) { return opt_arg; } return opt_callback(default_value, opt_arg); } ``` #### こう使う これで 先のサンプルをこう修正します。 ``` javascript /** * @param {Array.<number>} scores * @param {number=} opt_index * @return {number} */ function average(scores, opt_index) { function validate(def, val) { return Math.max(Math.min(def, val), 0); } var l = supplement(score.length, opt_index, validate), sum = 0; for (i = 0; i < l; i++) { sum += scores[i]; } return sum / l; } ``` 以上です |
|
| 902位 |
|
|||
|
23:19:31 |
|
|
# はじめに
今日は12月10日。12月も中旬。2013年もあと21日。アドベントカレンダーも10日目です。 さて自分は, 「JavadでAndroidアプリを作っていて,今はC#でUnityゲーム作っている自分のC#のメモ(長め)」 と題して,C#の文法・機能などを紹介したいと思います。 Unityのアドベントカレンダーですので,Unityの機能・クラスやインスペクタービュー,Monobehaviorのサブクラスでよく書く処理も交えて紹介していきます。 自分は少し前までJava言語を用いてAndroidアプリを作成していました。 今はC#でUnityでゲームを開発しています。 そんな自分の独断と偏見で,ここはJavaと違うな,ここは便利だなと思ったC#の文法・機能を中心に紹介します。 夢中になって書いていて,気がついたら非常に長い投稿になってしまいました。次の節の項目リストの中で面白いと思ったところだけでも見ていただければと思います。 この文書が * 学校でJavaは習ったけれどもC#は習っていない学生の方 * 業務でJava経験があるので,Unityでよく使ったり便利なC#の文法・機能をさくっと眺めたい * Unityの参考書を買ったけど,ここのC#の書き方がわからない などなどの方に,もし少しでも,ほんの少しでも役にたつことがあれば嬉しいです。 # メモ ## 項目リスト * 命名・レイアウトなどのコーティング規則がJavaと違う * 入れ子の形でも書ける名前空間 * コードが読みやすくなる名前付き引数 * アノーテションに似ている属性(アトリビュート) * as演算子でキャストする * 内部的には整数の列挙型 * 構造体がある。Vector3やQuaternionは構造体 * インスタンスを作らないクラス,静的クラス * varキーワードで型推論 * 演算子のオーバーロードができる * 拡張メソッドで継承禁止クラス,列挙型,インタフェースにメソッドの実装追加 * 反復子とyieldキーワード。そしてStartCoroutineメソッド * イベント駆動のプログラミングが簡潔に。event・デリゲート * コレクション初期化子を使ってコレクションを見やすく初期化 * うっかりしているとはまりそう。virtual修飾子・override修飾子(仮想関数) * やっぱり便利!ラムダ式とLINQ * 匿名型がある(Javaの匿名クラスとは違う) ## 命名・レイアウトなどのコーティング規則Javaと違う メソッドの先頭が大文字だったり,定数がアッパーキャメルだったりということに自分は最初は違和感がありました。 C#の言語仕様としてはコーティング規則は定義されていないようですが,郷に入っては郷に従えという言葉がありますね。 ## 入れ子の形でも書ける名前空間 Javaと同じように,C#にも名前空間があります。 キーワードはnamespaceです。 ```csharp: namespace Mrstar.NameSpaceSample { public class SampleA { } } ``` SampleAの完全修飾名は,Mrstar.NameSpaceSample.SampleAです。 また以下のように,入れ子にもできます。 ```csharp: namespace Mrstar { namespace NameSpaceSample { public class SampleB { } } } ``` SampleBの完全修飾名は,Mrstar.NameSpaceSample.SampleBです。 利用側は, ```csharp: Mrstar.NameSpaceSample.SampleA sampleA = new Mrstar.NameSpaceSample.SampleA (); Mrstar.NameSpaceSample.SampleB sampleB = new Mrstar.NameSpaceSample.SampleB (); ``` とも書けますが, ```csharp: using Mrstar.NameSpaceSample; ``` というusingディレクティブを用いれば, ```csharp: SampleA sampleA = new SampleA (); SampleB sampleB = new SampleB (); ``` というように短く書けます。 Monobehaviorのサブクラスは,Unity 4.0より前バージョンでは名前空間以下に置けませんでしたが,4.0から置けるようになったようです。 [Unity 4.0のリリースノート](http://japan.unity3d.com/unity/whats-new/unity-4.0) ## コードが読みやすくなる名前付き引数 UnityのEditor拡張などで用いるEditorUtilityというクラスに,string型のインスタンスを4つ引数にとり,ファイル保存ダイアログを開く,SaveFilePanelというクラスメソッドがあります。 Unity公式の[EditorUtilityのSaveFilePanelメソッドのリファレンス](http://docsharp.unity3d.com/Documentation/ScriptReference/EditorUtility.SaveFilePanel.html)では,次のように呼び出しています。 ```csharp: EditorUtility.SaveFilePanel ("Save texture as PNG", "", texture.name + ".png", "png"); ``` パっと見ただけではそれぞれの引数の意味や,引数がどう使われるかが分かりません。 C#には名前付き引数があります。これを使って書いてみます。 ```csharp: EditorUtility.SaveFilePanel ( title: "Save texture as PNG", directory: "", defaultName: texture.name + ".png", extension: "png"); ``` クラス名,メソッド名,引数の名前からそれぞれの引数の使われ方が想像出来るのではないでしょうか。 次に,bool型の引数を1つとる例を考えます。 下記のようなsampleAnimationという名前のインスタンスが,Playというメソッドを呼び出しているとします。 ```csharp: sampleAnimation.Play (true); ``` このbool型の引数は繰り返しの有無の判定に使われるかもしれません。もしくは正方向のアニメーション実行か逆方向のアニメーション実行かを制御するためのものかもしれません。 ```csharp: sampleAnimation.Play (isOneShot: true); ``` 上のように名前付き引数を使って呼び出した場合,このPlayというメソッドのbool型の引数は1回のみ実行かループするのかの制御に使われているということが分かりますね。sampleAnimationインスタンスのクラスの中身を見なくてもいいですね。 名前付き引数はコードを書いた本人が書いているまさにその時には,あまり効果を発揮しないかもしれません。しかし他者がコードリビューをする際や,時間を空けてコードを読み返す時などは,とても効果的ではないでしょうか。 他にもC#のメソッドには省略可能な引数,ref修飾子のついた引数,out修飾子のついた引数などがあります。 また,Javaにもありましたが可変長引数もあります。 ## アノーテションに似ている属性(アトリビュート) C#にはJavaのアノテーションに似たアトリビュート(属性)があります。 Javaのアノテーションのように,クラスやメソッドなどに追加情報を与えることができます。 Unity独自の属性もいくつもあります。例えば,Monobehaviorのサブクラスで[ReauireComponent](http://docsharp.unity3d.com/Documentation/ScriptReference/RequireComponent.html)という属性を使うことで,自動的に必要なコンポーネントを付与することが可能になります。 またMonobehaviorクラスのサブクラスのインスペクタービューでの表示を制御するものもあります。次のようなAttributeSampleというサンプルクラスを使って紹介します。 ```csharp:AttributeSample.cs using UnityEngine; public class AttributeSample : MonoBehaviour { public int publicField; private int privateField; } ``` このクラスをGameObjectにコンポーネントとして付与した場合,インスペクターは次のようになります。  int型やfloat型,GameObject型やVector3型などのpublicなフィールドはインスペクターに表示され,そこから値・参照を設定可能です。privateなフィールドやstaticなフィールドは表示されません。 ここで,AttributeSampleを次のように書き換えます。 ```csharp:AttributeSample.cs using UnityEngine; public class AttributeSample : MonoBehaviour { [HideInInspector] public int publicField; [SerializeField] private int privateField; } ``` このように書き換えると,インスペクターは次のようになります。  publicなフィールドでも[HideInInspector](http://docsharp.unity3d.com/Documentation/ScriptReference/HideInInspector.html)属性がついたものはインスペクターには表示されなくなり, 逆にprivateなフィールドでも[SerializeField](http://docsharp.unity3d.com/Documentation/ScriptReference/SerializeField.html)属性がついたものはインスペクターに表示されます。 ## as演算子でキャストする [Instanitiate](http://docsharp.unity3d.com/Documentation/ScriptReference/Object.Instantiate.html)というメソッドがあります。これはプレファブをインスタンス化する際に用いるメソッドです。このメソッドはUnityにまだ少ししか触っていないという方でも,コインドーザを作るハンズオンや様々なチュートリアルで見たことがあるかもしれません。 さて,このInstanitiateメソッドの返り値の型はUnityEngine.Object型です。 インスタンス化したオブジェクトに何か処理をしたい場合GameObject型にしなくてはいけないことが多いのではないでしょうか。 その際, ```csharp: using UnityEngine; public class AsSample : MonoBehaviour { public GameObject prefab; void Start () { GameObject created = Instantiate (prefab) as GameObject; created.name = "creted object"; } } ``` という用に,as演算子を用いてキャストすることが可能です。 さて,Javaのように ```csharp: GameObject created = (GameObject) Instantiate (prefab); ``` というようにキャストすることも可能です。 キャストに失敗した場合,as演算子はnullになりますが、([型名])を用いてのキャストはInvalidCastExceptionが投げられるなど違いがあります。 またJavaのinstanceof演算子のように,型が一致しているかどうかをboolで返すis演算子というのもあります。型関連では更に,前述のRequireComponentという属性で用いるtypeofという演算子もあります。 ## 内部的には整数の列挙型 Javaの列挙型はクラスでした。メソッドを定義出来ましたし,フィールドやメソッド,クラス内に定数を定義したり,クラスメソッドも定義できました。 一方でC#の列挙型は内部的には整数型です。 ```csharp:Size.cs public enum Size { S, M, L, } ``` 列挙型の値は,次のように[型名].[メンバー名]でアクセスします。 ```csharp: Size size = Size.L; Debug.Log (Size.S); // S Debug.Log (Size.M); // M Debug.Log (Size.L); // L ``` 整数型と列挙型の間で型変換もできます。 ```csharp: int s = (int)Size.S; int m = (int)Size.M; int l = (int)Size.L; Debug.Log (s); // 0 Debug.Log (m); // 1 Debug.Log (l); // 2 Size sSize = (Size)0; Size mSize = (Size)1; Size lSize = (Size)2; Debug.Log (sSize); // S Debug.Log (mSize); // M Debug.Log (lSize); // L ``` 列挙型を定義する際に基になる整数型を指定することも可能です。(long, uintなど) また,Sは内部的に0、Mは内部的には1になっていますが,このメンバーの値も指定できます。 独自のメソッドは通常の方法では定義できませんが,後述する拡張メソッドを使えば列挙型でもメソッドを定義することが可能です。 またUnityにでは,列挙型のpublicフィールドはインスペクター上では次の画像のように,ドロップダウンボックスで表示され,設定しやすくなっています。  ## 構造体がある。Vector3やQuaternionは構造体 C#には構造体があります。 Unityではよく見る,位置や方向に用いる[Vector3]()型や回転・姿勢に用いる[Quaternion](http://docsharp.unity3d.com/Documentation/ScriptReference/Quaternion.html)型は構造体です。 構造体は,クラスと同じようにフィールドやメソッドを持てます。 しかしクラスと構造体は様々な違いがあります。 構造体の変数への代入はコピーになります。二つの変数の間でデータは共有されません。 ```csharp:IntWrappingClass.cs public class IntWrappingClass { public int IntValue; } ``` 上のクラスで,まずクラスの変数への代入を見てみます。 ```csharp: IntWrappingClass intWrappingClass = new IntWrappingClass (); intWrappingClass.IntValue = 1; Debug.Log (intWrappingClass.IntValue); // 1 IntWrappingClass otherVariable = intWrappingClass; otherVariable.IntValue = 2; Debug.Log (intWrappingClass.IntValue); // 2 Debug.Log (otherVariable.IntValue); // 2 ``` 二つの変数の参照先のオブジェクトは同じなので,どちらもIntValueの値は2になります。 次に,構造体の例を見てみます。 ```csharp:IntWrappingStruct.cs public struct IntWrappingStruct { public int IntValue; } ``` この構造体を使って,クラスと同じことをしてみます。 ```csharp: IntWrappingStruct intWrappingStruct = new IntWrappingStruct (); intWrappingStruct.IntValue = 1; Debug.Log (intWrappingStruct.IntValue); // 1 IntWrappingStruct otherVariable = intWrappingStruct; otherVariable.IntValue = 2; Debug.Log (intWrappingStruct.IntValue); // 1 Debug.Log (otherVariable.IntValue); // 2 ``` こちらはintWrappingStruct.IntValueは1のままです。 これは構造体は変数への代入時にコピーされるためです。 クラスと構造体には他にもたくさん違いがあります。 例えば,構造体の変数にはnullを代入できなかったり,継承できなかったり,抽象メソッドを持てなかったり,引数なしのコンストラクタが持てなかったり,配列などの扱いなどがあります。 ## インスタンスを作らないクラス,静的クラス Utility系のクラスなど,インスタンスを作らないで,定数やstaticなメソッドのみをもつクラスを作ることもあるのではないでしょうか。 そのような場合,Javaでは次のようにコンストラクタをprivateにして実装をすることもあると思います。 ```java: package com.mrstar.sample.utility_example; public class SampleUtility { public static final int SAMPLE_INT = 1; public static int calulate(int input) { return 0; } private SampleUtility() { } } ``` C#では,次のようにclassの前にstaticとつけるとインスタンスを作れない静的クラスとなります。 ```csharp: public static class SampleUtility { public static readonly int SampleInt = 1; public static int Calulate (int input) { return 0; } } ``` また,C#の静的クラスでは,staticでないメソッドやフィールドを定義するとコンパイルエラーになります。 ## varキーワードで型推論 ```csharp: Dictionary<int, List<string>> dict = new Dictionary<int, List<string>> (); ``` 長いですよね。無駄に長いですよね。 インスタンス側生成側で型を指定して、変数宣言側で型を指定して。 ここで,varを使います。 ```csharp: var dict = new Dictionary<int, List<string>> (); ``` C#ではこのように型推論によりすっきりと記述できます。 ```csharp: var creted = Instantiate(monster) as GameObject; ``` as演算子やキャストを使った場合、コードを読んでいて明らかにその型であることがわかるので,varを使ったほうが読みやすいかと思います。 ただ全てローカル変数をvarにするのはよくないと思います。コードを読む上で、変数の型が表す情報は多く,それがない場合何をしているかがわかりにくくなることがあると思います。 しかしインスタンス化、キャスト後や明らかにわかる場合はvarを使うのはいかがでしょうか。 ## 演算子のオーバーロードができる C#では演算子のオーバーロードができます。 オーバーロード可能な演算子としては,+演算子や,*演算子などの加減乗除の演算子やtrue演算子,false演算子,==演算子などがあります。 UnityでもVector3型やQuaternion型などでは,いくつかの演算子がオーバーロードされており直感的にそれぞれの型を扱うことができます。 ```csharp: Vector3 vectorAdded = new Vector3 (0, 1, 2) + new Vector3 (3, 4, 5); Vector3 vectorMultiplied = 1.5F * new Vector3 (1, 1, 0); ``` ## 拡張メソッドで継承禁止クラス,列挙型,インタフェースにメソッドの実装追加 2013年のUnity3Dのアドベントカレンダー[2日目の記事](http://qiita.com/MunenaoMiyata/items/24d0675861744c827668)を書かれた方も,この拡張メソッドを利用して,Transformを拡張する記事を書かれていました。 拡張メソッドを用いれば,Unityでよく使うGameObjectやTransformなどのクラスに対して,自分で定義したメソッドを追加・拡張して,継承などを用いずに[インスタンス名].[メソッド名()]という形でメソッドを呼び出すことができます。拡張メソッドを使うと,既存のクラスを変更しないで,呼び出し可能なメソッドを増やすことができます。 以下のようにExtensionTargetという何もメソッドの無いクラスに,拡張メソッドを追加してみます。 ```csharp:ExtensionTarget.cs public class ExtensionTarget { } ``` このクラスに拡張メソッドを追加するために,以下のようのクラスを定義します。 ```charp:ExtensionTargetExtentions.cs using UnityEngine; public static class ExtensionTargetExtensions { public static void SampleMethod (this ExtensionTarget extensionTarget) { Debug.Log ("SampleMethod"); } } ``` 利用側は次のようになります。 ```csharp: ExtensionTarget extensionTarget = new ExtensionTarget (); extensionTarget.SampleMethod (); // SampleMethod ``` ExtensionTarget型は,何もメソッドを持っていません。 ExtensionTargetExntensionsクラスでメソッドを定義します。 これにより,extensionTarget.SampleMethod ();と言う風に,拡張メソッドを呼び出すことができます。 拡張メソッドを定義する側は * 静的クラスでないと行けない * 拡張メソッドはstaticメソッドで定義する * 第一引数にthisをつける * 第一引数の型がメソッドを追加する型 を満たさなくてはいけません。 しかし継承と違って,拡張メソッドはprotectedなフィールドやメソッドにアクセスすることはできません。(もちろんprivateにも)また,既存のメソッドをオーバーライドしているわけでもなく,処理を書き換えているわけでもありません。 繰り返しになりますがUnityではGameObjectやTransformなどに,"こんなメソッドがあれば便利","よく書く複数行にわたる処理をどうにかしたい"などの状況で,拡張メソッドは有効だと思います。 これを用いることで、継承禁止のクラスや列挙型にもメソッドを追加することが可能です。 ちなみに, ```csharp:ISample.cs public interface ISample { } ``` ```csharp:ISampleExtensions.xs using UnityEngine; public static class ISampleExtensions { public static void SampleMethod (this ISample iSample) { Debug.Log ("SampleMethod"); } } ``` ```csharp:Sample.cs public class Sample : ISample { } ``` とすると,ミックスインのようなこともできるようです。 ## 反復子とyieldキーワード。そしてStartCoroutineメソッド 次のコードは,[StartCoroutine](http://docs.unity3d.com/Documentation/ScriptReference/MonoBehaviour.StartCoroutine.html)メソッドを使って1秒ごとにデバッグログを出力するコードです。 Unityを始めたばかりの方の中にも,コインドーザなど一定時間間隔でオブジェクトを出現させるオブジェクト(Spawner)などのコードで,StartCorouineメソッドを使ったサンプルを見た方もいるかと思います。 ```csharp:Sample.cs using UnityEngine; using System.Collections; public class DebugCoroutine Sample : MonoBehaviour { void Start () { StartCoroutine (DebugCoroutine ()); } private IEnumerator DebugCoroutine () { while (true) { Debug.Log ("Logging"); yield return new WaitForSeconds (1.0F); } } } ``` StartCoroutineメソッドは,IEnumerator型のオブジェクトを引数にとります。(string型とobject型をとるオーバーロードもあります。)このメソッドは,MonobehaviorクラスのメソッドでありUnity独自の物です。 一方で,IEnumerator型やyield return というキーワードはUnity独自の物ではなくて,もともとはC#のものです。 yield return文とyield breakという文を含むブロックは反復子ブロック(イテレーターブロック)と言います。反復子(イテレーター)を使うと,foreach文などで利用できる列挙可能なオブジェクトを簡潔に記述できます。 ```csharp:FrmTo.cs using System.Collections.Generic; public class FromTo { private int from; private int to; public FromTo (int from, int to) { this.from = from; this.to = to; } public IEnumerator<int> GetEnumerator () { for (int i = from; i <= to; i++) { yield return i; } } } ``` 利用側は次の通りです。 ```csharp: FromTo fromTo = new FromTo (0, 20); foreach (int i in fromTo) { Debug.Log (i); } ``` 自分の場合は,Coroutine関係でStartCoroutineメソッド,yield,IEnumeratorなどキーワードが一気に出過ぎて,混乱してしまいました。まずyieldなど反復子について勉強してから,別途StartCoroutineメソッドを理解するのが,理解への近道なのかもしれないと思います。 StartCoroutineメソッドで疑問に思ったら,まず「C# yield 反復子 イテレーター」などを調べてはいかがでしょう。 ## イベント駆動のプログラミングが簡潔に。event・デリゲート Javaを書いていて面倒なことの一つに,イベントリスナーを登録するため,イベントリスナーのインタフェースを実装した無名クラスをいちいちインスタンス化して記述することだと思います。 Androidにおける例をあげます。 ボタンをクリックした際に何かしらの処理をするコードです。 ```java:SampleActivity.java button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO implement here } }); ``` この中で大切な記述は, * buttonという変数の参照するオブジェクトに対して, * リスナーを登録する(setOnClickListener), * 今は「TODO implement here」となっている箇所の実際の処理 で,他の記述は定型文になっていて, * new View.OnClickListener() { * public void onClick(View v) { という記述は毎回書かなくてはいけませんが,冗長に思えます。 C#では似たようなことをする際に,次のように書けます。 ```csharp: button.Click += (sender, e) => { // TODO implement here }; ``` 非常に無駄が無く簡潔に記述できます。上記は,System.Windows.Controls.Buttonを用いた例です。 C#が持つevent、デリゲート,ラムダ式などの構文を用いることで簡潔にイベント駆動のコードが記述することが可能です。 ## getterやsetterの変わりにプロパティを使える ## オブジェクト初期化子での初期化 プロパティとオブジェクト初期化子を一緒に紹介します。 整数型のlevelと文字列型のnameというフィールドと,それぞれのsetter・getterをもつクラスPlayerがあります。 Javaでは,getterとsetterを用いてアクセスするのが定石です。(Effective Java 項目14 publicのクラスでは,publicのフィールドでなく,アクセッサーメソッドを使う) ```java:Player.java // これはJavaのコード public class Player { private int level; private String name; public Player(int level, String name) { this.level = level; this.name = name; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` さて,これをC#で書いてみます。 C#ではJavaのようにgetter・setterを定義することもできますが,プロパティーを利用するのが一般的です。(Effective C# 4.0 項目1 アクセス可能なデータメンバーの変わりに常にプロパティを使用すること) ```csharp:Player.cs public class Player { private int level; private string name; public Player (int level, string name) { this.level = level; this.name = name; } public int Level { get { return level; } set { level = value; } } public string Name { get { return name; } set { name = value; } } } ``` です。利用側は ```csharp: Player player = new Player (0, "Taro"); Debug.Log (player.Name); // Taro Debug.Log (player.Level); // 0 player.Level = 1; Debug.Log (player.Level); // 1 ``` 例えばこのようになります。まるで,フィールドにアクセスするように記述しています。 Javaのgetter・setterのように,値をget・setする時に同時に任意の処理をすることもできます。 またgetterにはpublic,setterにはprivateなどそれぞれに異なるアクセス修飾子をつけるなどJavaでもやりますが,プロパティでも同様なことができます。 このPlayer.csというクラスを更に短くしてみます。 自動実装プロパティ用いることで,内部的なlevelとnameというフィールドを消してみます。またコンストラクターを消し,初期化に関してはオブジェクト初期化でやります。 ```csharp:Player.cs public class Player { public int Level { get; set; } // 自動実装プロパティ public string Name { get; set; } } ``` 利用側は ```csharp: Player player = new Player { // オブジェクト初期化子による初期化 Name = "Taro", Level = 0, }; ``` です。 先に述べた名前付き引数と同じように,読みやすいのではないでしょうか。 ## コレクション初期化子を使ってコレクションを見やすく初期化 JavaにおいてArrayListを初期化をする際に、次のように書く必要があります。多少面倒です。 ```java:JavaにおけるListの初期化 // これはJavaコードです。 List<String> nameList = new ArrayList<String>(); nameList.add("Taro"); nameList.add("Jiro"); nameList.add("Saburo"); nameList.add("Shiro"); nameList.add("Goro"); ``` addメソッドの要素の数だけ呼び出す必要があります。 次のような、配列の初期化の方が記述が簡潔で読みやすいです。 ```java:Javaにおける配列の初期化 // これはJavaコードです。 String[] names = new String[]{"Taro", "Jiro", "Saburo", "Shiro", "Goro"}; ``` C#では,コレクションの初期化を配列のようにできるコレクション初期化子が存在します。 ```csharp:C#におけるコレクション初期化子を用いたコレクションの初期化 // これはC#コードです。 List<string> names = new List<string>() {"Taro", "Jiro", "Saburo", "Shiro", "Goro"}; ``` このように,配列のように初期化することが可能です。 C#のDictionary(JavaでいうMap)も次のように読みやすい形式で初期化することが可能です。 ```csharp:C#におけるDictionaryの初期化 Dictionary<int, string> dict = new Dictionary<string, int> { { 1, "taro" }, { 2, "jiro" }, { 3, "saburo" }, }; ``` コレクション初期化子は、実際はAddメソッドのシンタックスシュガーになっています。 さて,実はJavaも他のクラスや他のライブラリを用いて,C#のコレクション初期化子のように記述することも可能です。 Arraysクラスを経由する方法ではこのように, ```jaca: List<String> nameList = new ArrayList<String>(Arrays.asList("Taro", "Jiro", "Saburo", "Shiro", "Goro")); ``` [Guava](https://code.google.com/p/guava-libraries/)ライブラリのListsクラスのstaticメソッドのLists.newArrayListメソッドでは以下のようにかけます。 ```java: List<String> nameList = Lists.newArrayList("Taro", "Jiro", "Saburo", "Shiro", "Goro"); ``` ## うっかりしているとはまりそう。virtual修飾子・override修飾子(仮想関数) ```csharp:仮想関数の紹介(virtual・overrideを使わない) class Super { public void Hello () { Debug.Log ("Hello, this is Super class."); } } class Sub : Super { public void Hello () { Debug.Log ("Hello, this is Sub class."); } } ``` 上のようなクラス考えます。 ```csharp: Super super = new Super(); super.Hello(); // "Hello, this is Super class." Sub sub = new Sub(); sub.Hello(); /// Hello, this is Sub class. Super subInSuper = new Sub(); subInSuper.Hello(); // "Hello, this is Super class." <- 注目 ``` 利用側、最後のsubInSuper.Hello();の結果について注目です。 自分がC#を書き始めたころ,この結果はJavaと同じような挙動をして,"Hello, this is Sub class!"と表示されることを期待していました。しかし,実際は違いました。 次のように書き換えます。 ```csharp:仮想関数の紹介(virtual・overrideを使う) class Super { public virtual void Hello () // <- 注目 virtual修飾子が加わっている { Debug.Log ("Hello, this is Super class."); } } class Sub : Super { public override void Hello () // <- 注目 override修飾子が加わっている { Debug.Log ("Hello, this is Sub class."); } } ``` ```csharp: Super super = new Super(); super.Hello(); // "Hello, this is Super class." Sub sub = new Sub(); sub.Hello(); /// Hello, this is Sub class. Super subInSuper = new Sub(); subInSuper.Hello(); // "Hello, this is Sub class." <- 注目 ``` Javaと同じような挙動をしています。 仮想関数という概念があります。C#ではスーパークラスにvirtualメソッドをつけて、サブクラスにoverrideをつけると、挙動を変更できます。実はJavaではデフォルトでこのような仮想関数になっています。 ## やっぱり便利!ラムダ式とLINQ LINQです。ラムダ式です。 C#の本だったらそれぞれで1章もしくは章をまたいで紹介・説明されています。 LINQだけの本もいくつか出ています。 JavaでAndroidアプリを作っていた自分は、最初LINQの良さ・すごさがわかりませんでした。UnityでゲームをつくるためにC#を書き始めてしばらくして,こわごわLINQを使ってみて,少しずつ少しずつその良さ,すごさが分かり始めてきました。 前述のPlayerというクラスのリスト,playersがあります。 このリストの要素のインスタンスの中で,levelが80以上であるものの名前(name)のリストを作成するコードを例にLINQの良さを紹介します。 ```java:Javaを用いた例 int level = 80; List<String> highLevelPlayerNames = new ArrayList<String>(); for (Player player : players) { if (player.getLevel() >= level) { highLevelPlayerNames.add(player.getName()); } } ``` このようなコードになるかと思います。 すぐに「ああ,これは80以上のレベルのプレイヤーの名前のリストを作っているのだな」と分かるかと思います。 しかし,実際コードとしてはやっている処理は, 1. 出力結果のhighLevelPlayerNamesというリストを作成して, 2. playersリスト全体を反復しその中の要素のplayerの, 3. レベル(level)が80以上なPlayerは 4. 名前(name)を取得して, 5. それをhighLevelPlayerNamesというリストに追加する ということをしています。 このような記述に慣れているので,このようなコードを見たときに「ああ,これは80以上のレベルのプレイヤーの名前のリストを作っているのだな」と分かるかと思います。が,それは慣れているからであり,直感的・直接的に「80以上のレベルのプレイヤーの名前のリストを作る」ということをコードは述べていないと思います。どのように処理をするかは1〜5のようにはっきりコードは述べていますが,それにより何をしたいかは埋もれているのではないでしょうか。 次にC#でLINQを用いて同じように書いてみます。 ```cs:C#でLINQを用いた例 int level = 80; List<string> highLevelPlayerNames = players.Where (p => p.Level >= level) .Select (p => p.Name) .ToList (); ``` 少し説明します。 Whereはplayersの中身の要素をフィルタリングします。条件を指定してその条件を満たしたものだけにします。 ```cs: p => p.Level > level ``` は条件を表すラムダ式です。少し書き換えてみます。 ```cs: (Person person) => { bool isHighLavel = person.Level > level; return isHighLavel; } ``` 少しメソッドに近い形になりました。 「Person型のpersonを貰って,person.Levelがlevel(80)以上の物ならば,条件を満たすので真を返す。」という意味です。 Selectは要素の中身を加工・変換します。ここではPersonをそのプロパティの名前に変換します。 C#での例を説明すると,playersの要素の中で,レベルが80以上のもののみ(Whereの部分),Playerの名前に変換して(Selectの部分),(IEnumerable\<string\>を)ToListでList\<string\>にする。という記述になっています。 Javaでのコード例に比べると,C#でLINQを用いたコードはやりたいことを直接コードで記述しています。 余談ですが,ToListはいらない場合も多いようです。ToListの前はIEnumerable\<string\>型になっています。もしList<string>型での変数が必要ならばToListの呼び出しは必要ですが,状況によってはIEnumerable\<string\>型で済む場合もあるようです。 自分は最初,ラムダ式の記述方法やLINQでの処理に慣れませんでしたが,その簡潔で無駄な記述が少ない書き方や,やりたいことが直接読み取れるコードに今ではラムダ式・LINQがとても好きになりました。 ## 匿名型がある(Javaの匿名クラスとは違う)【追記】 下記はAndroidで,View.OnClickListenerインターフェースを実装した匿名クラスのインスタンスを用いて,ボタンにリスナーを設定するコードです。 ```java:Androidで,匿名クラスを用いてボタンにリスナーを設定 Button button = (Button)findViewById(R.id.button); button.findViewById(R.id.button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // ここにボタンが押されたときの処理 } }); ``` Javaでは,このようにコールバックやリスナーを匿名クラスを用いて実現することがあります。 C#には匿名型という機能が存在しますが,上記のようなJavaの匿名クラスとは用途・役割が異なります。 [こちらの投稿](http://qiita.com/RyotaMurohoshi/items/ecda8257178ba39867eb)にまとめたので,興味がある方は見てください。 # さいごに 以上です。 大変長くなってしまいました。 今回の内容は私の独断と偏見でピックアップし紹介させて頂きました。 なんでこれないの,これおかしくない,などご意見やご感想があればぜひお願いします。 また,間違い等ありましたらそちらも申し訳ありませんが,お願いします。 |
|
| 903位 |
|
|||
|
13:12:24 |
|
|
# Remote Push からの Background Fetch を試してみた
[前回](http://qiita.com/griffin_stewie/items/8371c09059b3ba7bb202 "Objective-C - Background Fetch を試してみた - Qiita [キータ]")は Background Fetch を試してみました。この方法ではアプリだけの実装でバックグラウンドで通信処理等を走らせる事ができますが、実行タイミングが OS 側の学習によるもので任意のタイミングでは発動させることができません。iOS 7 からは Background Fetch 以外にも Remote Push Notification をトリガーに Background Fetch を実行させる機能も追加されています。Remote Push Notification を送るためのサーバサイドの準備が必要ですが、任意のタイミングで実行できる(Rate Limit はあります)のは魅力的です。APNs 環境を作ったことがない僕が APNS 環境の構築から Remote Push Notification から Background Fetch を動かすまでのメモを残しておきます。 ## APNs 環境構築 今回、僕が使ったのは [Heroku](https://www.heroku.com/ "Heroku | Cloud Application Platform") + [Helios](http://helios.io/ "Helios") という環境を構築してみました。Heroku は個人開発者が動作検証に使う分には十分に無料の範囲でいろいろとためせて非常に助かります。Helios は AFNetworking の作者であり、Heroku 社員でもある Mattt が作った iOS デベロッパ向けの各種バックエンド機能を実装した Framework です。Heroku 社員でもあるだけに Heroku へのデプロイも簡単です。 まずは、Helios のインストール ``` gem install helios ``` 次に Helios のアプリケーションを作成 ``` helios new <任意のアプリケーション名> ``` これだけで必要なファイル群が作成され、git の管轄下になります。 今の Heroku は Ruby 2.0.0 で動いているので Gemfile に以下を追記。 ``` ruby '2.0.0' ``` APNs に必要な pem ファイルを作成します。このあたりはあらゆるところに情報があるので割愛します。 作成した pem ファイルをディレクトリに追加。 pem ファイルを読み込み、APNs が使えるようにするための設定を config.ru ファイルに追記 ``` service :push_notification, apn_certificate: './apple_push_notification.pem', apn_environment: 'development' ``` ここまでで下準備が大分整いました。あとは heroku 上にアプリケーションを作成します。 Heroku の基本的なセットアップは事前に終わっていることとします。 今回作成した Helios アプリの git リポジトリディレクトリ内で ``` heroku create ``` すると Heroku 上に環境を作ってくれます。このときに URL も出力されるので覚えておいた方があとあと便利です。ここまできたらあとは、 ``` git push heroku master ``` すれば Heroku 上へのデプロイも完了です。 # アプリへの組み込み ## Bundle ID の修正 普段の開発ではプロビジョニングプロファイルをワイルドカード OK な設定にしてサンプルアプリを作っているときにはあまり意識していないかも知れませんが APNs では Bundle ID が合致している必要があります。まずは Bundle ID をそろえておきます。プロビジョニングプロファイルもAPNs が受信出来るものをセットします。 ## APNs を使うためのコード 何はなくとも APNs を使うためには Apple のサーバに登録しないといけません。`application:didFinishLaunchingWithOptions:` 内あたりに ``` [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeNewsstandContentAvailability | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)]; ``` を書きます。個人的に試してみた感想では `UIRemoteNotificationTypeNewsstandContentAvailability` を宣言しておいた方が Remote Push Notification での Background Fetch が動きやすい気がしています。特にドキュメントに言及されていないように思えますので真偽のほどはわかりません。 Apple の APNs サーバーに対して登録を行ったあとは自身が用意する APNs サーバー側に取得したトークンを登録しておく必要があります。今回は Heroku 上にセットアップした Helios に対してこの作業を行います。今回は Helios を作った Mattt 作の ライブラリ Orbiter を使ってみます。Orbiter は AFNetworking ベースで作られた APNs 登録処理の補助ライブラリです。 ``` - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSURL *serverURL = [NSURL URLWithString:@"http://foobarbuzz.herokuapp.com/push_notification"]; Orbiter *orbiter = [[Orbiter alloc] initWithBaseURL:serverURL credential:nil]; [orbiter registerDeviceToken:deviceToken withAlias:nil success:^(id responseObject) { NSLog(@"Registration Success: %@", responseObject); } failure:^(NSError *error) { NSLog(@"Registration Error: %@", error); }]; } ``` 上記コードを書くだけでとりあえず動きます。個人的にハマったのは URL です。Orbiter のサンプルだと Heroku 上に作成したアプリの URL を書いていますが、これではダメでした。ググったりして調べてみると Helios 側のソースコードを除いてみたところ rack/push-notification に丸投げしてるようです。ということで `http://foobarbuzz.herokuapp.com` ではなく `http://foobarbuzz.herokuapp.com/push_notification` を指定します。Helios の github の README にも書いてありました。 これでアプリを起動して、orbiter の Registration Success まででれば OK。 意気揚々とビルドして ``` “aps-environment”エンタイトルメント文字列が見つかりません ``` とか言われたら正しいプロビジョニングプロファイルをセットしていない可能性があるので確認しましょう。僕は2回もやらかしました。 # 実際に Push を送る Helios の README には curl を使った例があるのですが、Remote Push Notification による Background Fetch を実行するために必要な "content-available" を送っても Helios 側が受け取れません。ソースコードを読んでみるとそもそも "content-available" には対応してなさそうです。ということでまたまた Mattt 謹製のライブラリ Houston を使って Push を送ります。 houston のインストールは ``` gem install houston ``` もしくは他の関連コマンドもまとめてインストールできる [nomad](http://nomad-cli.com/ "nomad :: world-class command line utilities for iOS development") を入れても良いかもしれません。 ``` gem install nomad-cli ``` インストールができたら普通の Push を送ってみます。 ``` apn push DEVICE_TOKE -c PATH_TO_PEM_FILE -m "Test Push" ``` DEVICE_TOKE は APNs に登録した際に取得したトークンです。Helios 側にも登録したはずなので、`http://foobarbuzz.herokuapp.com/admin/#push-notification` のように自身の Heroku アプリの URL にアクセスすることでもわかります。 PATH_TO_PEM_FILE は Heroku にデプロイした pem ファイルへのパスです。 -m オプションは Push に乗せるメッセージです。 これで実機で受信できれば OK です。 ## Remote Push から Background Fetch [前回](http://qiita.com/griffin_stewie/items/8371c09059b3ba7bb202 "Objective-C - Background Fetch を試してみた - Qiita [キータ]")と同様に Xcode 5 の Capabilities を開いて、`Remote notifications` にチェックをつけておきます。 次にメソッドの実装。これは[前回](http://qiita.com/griffin_stewie/items/8371c09059b3ba7bb202 "Objective-C - Background Fetch を試してみた - Qiita [キータ]")と似たようなメソッド `- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler` を実装します。 ``` - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { if(![userInfo[@"aps"][@"content-available"] intValue]) { completionHandler(UIBackgroundFetchResultNoData); return; } StepCountTweeter *tweeter = [[StepCountTweeter alloc] init]; [tweeter tweetStepCountWithUserName:nil tweetTextBlock:^NSString *(NSInteger numberOfSteps, NSDate *fromDate, NSDate *toDate, NSError *error) { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setLocale:[NSLocale systemLocale]]; [dateFormatter setDateFormat:@"MM/dd HH:mm:ss"]; NSString *text = [NSString stringWithFormat:@"%@ 〜 %@ の期間に iPhone 5s を持って %@ 歩 歩きました。 Tweet By RemoteNotification Fetch" , [dateFormatter stringFromDate:fromDate] , [dateFormatter stringFromDate:toDate] , [@(numberOfSteps) stringValue]]; UILocalNotification *localNotif = [[UILocalNotification alloc] init]; localNotif.timeZone = [NSTimeZone defaultTimeZone]; localNotif.alertBody = text; [[UIApplication sharedApplication] presentLocalNotificationNow:localNotif]; [self updateBadgeCountWithCurrentDate:toDate]; return text; } completionBlock:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) { if (error) { UILocalNotification *localNotif = [[UILocalNotification alloc] init]; localNotif.timeZone = [NSTimeZone defaultTimeZone]; localNotif.alertBody = [error description]; [[UIApplication sharedApplication] presentLocalNotificationNow:localNotif]; completionHandler(UIBackgroundFetchResultFailed); } else { completionHandler(UIBackgroundFetchResultNewData); } }]; } ``` "content-available" をチェックしているのは Fetch 処理が必要ない場合に呼ばれたときに余計な通信をしないためにしています。本当はもう少しいろんなシチュエーションに応じて分岐処理が必要かも知れません。 Push から発動させるには apn コマンドに -n オプションをつけておきます。 ``` apn push DEVICE_TOKE -c PATH_TO_PEM_FILE -n -m "Test Push" ``` ## Silent Push 次に Silent Push を送ってみます。Silent Push とは Payload にメッセージを含めず "content-available" のみを送るものです。この Silent Push の場合 Remote Push Notification による Background Fetch は発動しますが、通常の Push のように Push そのものは Notofication Center に表示されないことから Silent と呼ばれています。実装によっては、Silent Push を受け取り Background Fetch で動作が完了した際に Local Push を実行するということも出来ます。このような実装をした場合にはエンドユーザーに取っては Notification Center の通知が来たと同時にアプリを開けばその内容に応じたコンテンツがすでに取得済みということが出来ます。 Silent Push の送り方ですが、ここでも難点が。私の環境では ``` apn push DEVICE_TOKE -c PATH_TO_PEM_FILE -n ``` とするとエディタが開いてしまいます。 ``` apn push DEVICE_TOKE -c PATH_TO_PEM_FILE -P '{"aps": {"content-available":1}}' ``` とするか、Houston のサイトにあるようにスクリプトとして実装してしまう方法をとりました。 私の環境では Silent Push を基点に Background Fetch を動かすのは Rate Limit に引っかかっているせいなのか多少間をあけないと実行できませんでした。 # トラブルシューティング的ななにか * push を登録するときには `UIRemoteNotificationTypeNewsstandContentAvailability` を宣言しておいた方が Remote Push Notification での Background Fetch が動きやすい気がする * うまく Push が発動しない時には unregister してみる * いきなり込み入ったことをせずにシンプルにバッジの更新程度で試してみる # 感想 期待通りに動かない時があったりしたので、実際にタイムリーに発動させるためにはもう少し研究が必要な気がしますが期待度の高い機能なので取り組んでみる価値は高いと思いました。 |
|
| 904位 |
|
|||
|
00:36:53 |
(Nepula,Inc. 所属) |
|
(この記事は2013年に書かれたもので、内容については古くなっています。ご注意ください。)
いわゆる「逆ジオコーディング」と呼ばれる機能ですが、きっかけはこれら2つの記事です。 * [PHP - 総務省のデータを使って、緯度経度から市区町村の何丁目までを取り出す](http://qiita.com/hamichamp/items/ac9e80f1078febb9f1b9) * [PostgreSQL - 国土交通省のデータを使って、緯度経度から市区町村までを取り出す](http://qiita.com/masuidrive/items/21a282c7bf54fd6a4985) Solr や Elasticsearch でも同じことができるのでは、という事で Elasticsearch でやってみました。 ## Elasticsearch の導入 は、 * [Elasticsearch で位置情報を検索する手順 - Experiments Never Fail](http://amay077.github.io/blog/2013/09/09/using-spatialdata-with-elasticsearch/) をご覧ください。 ## 1. 総務省のデータをダウンロードする * [PHP - 総務省のデータを使って、緯度経度から市区町村の何丁目までを取り出す](http://qiita.com/hamichamp/items/ac9e80f1078febb9f1b9) とほぼ同じですが、「世界測地系緯度経度・G-XML形式」ではなく、 **「世界測地系緯度経度・shape形式」** を使います。 一応再掲すると、 1. [http://e-stat.go.jp/SG2/eStatGIS/page/download.html](http://e-stat.go.jp/SG2/eStatGIS/page/download.html) へ行く。 2. 左から「平成22年度国勢調査(小地域)」を選ぶ。 3. 「男女別人口総数及び世帯総数」(←なんでもいい)を選択して、「統計表各種データダウンロードへ」を押す。 4. 「世界測地系緯度経度・Shape形式」のデータ(下図参照)をダウンロードする。ダウンロードしたら zip ファイルのまま置いといて。  ## 2. Shapefile を GeoJSON 形式に変換する Elasticsearch へ投入できるデータ形式は JSON なので、ダウンロードした Shape形式のデータを JSON 形式の地理空間拡張である GeoJSON 形式に変換します。 スクリプトを書いてもできますが面倒なので、便利なオンラインツールに頼ることにします。 * [Free on-line GIS data format and coordinates converter](http://converter.mygeodata.eu/#convertVector) 手順は、 1. 上記サイトへ行き、「Run vector converter」を押す。 2. 「ファイルを選択」で、さっきダウンロードした ZIP ファイルを指定し、「Send ZIP File」を押す。 3. "Datasets description" というページになったら、その最下部にある「Chack available operations」を押す。(ここでは、不要なデータ項目を除外できるのだけど、面倒なので割愛) 4. ”Export to format:” で "GeoJSON" を選択。それ以外はそのままで「Proceed selected operation」を押す。 5. しばらく待つと、「Download the ZIP file」リンクが表れるので、押して変換結果をダウンロードする。 ダウンロードした ZIP ファイルを解凍すると、「xxx.json」ファイルが見つかります。それをテキストエディタで開くと、``features`` 以下に、住所エリアの情報が1行ずつ出力されています。 試しに1行取り出して、JSON を整形(見やすいよう適宜省略)してみると次のようになります。 ```javascript:town.json { "type": "Feature", "properties": { "KEN_NAME": "愛知県", "GST_NAME": "名古屋市", "CSS_NAME": "中区", "MOJI": "本丸", …省略… }, "geometry": { "type": "Polygon", "coordinates": [ [[136.895888, 35.187236], [136.897375, 35.187357], …中略… [136.895888, 35.187236]] ] } } ``` これは「愛知県名古屋市中区本丸」のデータですね。 ``properties`` 以下は、この住所エリアの属性情報を示しています。 ``geometry`` 以下が、この住所エリアの位置情報(ポリゴン)を示しています。 ## 3. Elasticsearch にスキーマを定義する Elasticseach はスキーマフリーですが、位置情報のところだけは明示的に宣言しないといけないらしいので、下のようなコマンドを実行して定義します。 あ、ここでは、 * Index名 : towns * Type名 : town としています。 「Index や Type って何?」という方は * [Elasticsearch用語の適当なまとめ](http://qiita.com/ise_daisuke/items/5e10e0b3ef9dffed08a9) をどうぞ。 ではコマンドです。 ```sh: curl -XPUT 'http://localhost:9200/towns/' curl -XPUT 'http://localhost:9200/towns/town/_mapping' -d ' { "town" : { "properties": { "geometry": { "type": "geo_shape", "tree": "quadtree", "precision": "1m" } } } }' ``` ``properties.geometry`` は、「geo_shape」 として扱う事を宣言しています。他の2つの設定は、インデックスの種類と精度を意味しますが、よく分かってません。 * [Geo Shape Type | Reference Guide | Elasticsearch](http://www.elasticsearch.org/guide/reference/mapping/geo-shape-type/) で勉強しましょう。 ## 4. データを Elasticsearch に投入する さて、いよいよこの「1行1JSON」のデータを、1行ずつ、Elasticsearch に投入します。 先の xxxx.json を置換なり何なりを駆使して、スクリプトにしちゃうのがてっとり早いでしょう。(json ファイルは Shift-jis なので、UTF-8 に変換しておきましょう。) 1行のデータを投入するコマンドは次のようになります。 ```sh curl -XPUT 'http://localhost:9200/towns/town/1' -d '{ "type": "Feature", "properties": { "KEN_NAME": "愛知県", "GST_NAME": "名古屋市", "CSS_NAME": "中区", "MOJI": "本丸", …省略… }, "geometry": { "type": "Polygon", "coordinates": [ [[136.895888, 35.187236], [136.897375, 35.187357], …中略… [136.895888, 35.187236]] ] } }' ``` ``towns/town/1`` の最後の「1」のところは、連番にする必要があります。(オートインクリメントとかないのかな?) 全件を PUT すつスクリプトファイルは、下の画像のような感じになると思います。(データのライセンスがどうか分からないのでスクリプトファイル自体を公開するのはやめておきます)  これをTerminal で実行すると、数分かからずに Elasticsearch にデータが投入完了します。 ``curl -XGET 'http://localhost:9200/towns/town/1'`` などを実行すれば、正しくデータが登録できたか確認できます。 ## 5. 検索してみる ついに来ました。 では、緯度経度を与えて住所が返ってくるところ、やってみましょう。 Elasticsearch では検索クエリも JSON で書きます。 例えば、[大須観音駅](http://yahoo.jp/zsXYL2) らへんの緯度経度(35.1613077,136.898282)の住所で検索する場合は、次のようにします。 ```sh curl -XPOST 'http://localhost:9200/towns/town/_search' -d '{ "query": { "filtered" : { "query" : { "match_all" : {} }, "filter" : { "geo_shape": { "town.geometry": { "shape": { "type" : "envelope", "coordinates" : [[136.898282, 35.1613077], [136.898282, 35.1613077]] } } } } } } }' ``` ``geo_shape`` フィルタを使い、条件に envelople(左上〜右下の領域) を指定します。今は「点」での検索をしたいので、左上、右下に同じ座標を指定します。 注意点は、 **「経度, 緯度」** の順であることです。 ※ [``geo_distance``](http://www.elasticsearch.org/guide/reference/query-dsl/geo-distance-filter/) というフィルタもありますが、こちらはデータが「点」専用なようで、今回のような「ポリゴン」には使えませんでした。 さて、上のコマンドを実行すると、下のような結果が得られます。(整形、省略済) ```js { "took":1, "timed_out":false, "_shards":{ "total":5, "successful":5, "failed":0 }, "hits":{ "total":1, "max_score":1.0, "hits":[{ "_index":"towns", "_type":"town", "_id":"29", "_score":1.0, "_source" : { "type": "Feature", "properties": { "KEN_NAME": "愛知県", "GST_NAME": "名古屋市", "CSS_NAME": "中区", "MOJI": "大須2丁目", …以下省略… ``` はい、「緯度経度(35.1613077,136.898282)」の住所は「愛知県名古屋市中区大須2丁目」であることが取得できました。 ## まとめ いかがでしょうか、Elasticsearch でも逆ジオコーディングの実装が、簡単にできることが分かりました。 PostGIS、MongoDB との対比では、 * セットアップが簡単 * REST API なので直接他のサービスとマッシュアップできる あたりがメリットでしょうか。 逆にデータの取り込みはひと工夫必要で、PostGIS の方が簡単です。 このデータ用の River plugin を作ればよいのでしょうが、方法がさっぱり…。 最後に、 [総務省のデータを使って、緯度経度から市区町村の何丁目までを取り出す](http://qiita.com/hamichamp/items/ac9e80f1078febb9f1b9) > 実は、データをダウンロードするのが、一番手間がかかります・・・。 でも言われていますが、まったくその通りです。 1市区町村ずつダウンロードとか正直やってられません。 総務省でも国土交通省でもどちらでも良いのですが、 **「全国の大字境界+属性データを一括入手する方法」** を用意して欲しいものです。 |
|
| 905位 |
|
|||
|
19:52:30 |
|
|
`git` の記事などを見ていると、よく `rev-parse` というコマンドが出てきます。
rev-parse単体でググっても使い道がよくわからないので、メモ変わりにrev-parseでできることをここに書いてみます。 【参考】 > git-rev-parse(1) Manual Page > https://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html ## 使用可能なリファレンス(ref)を渡すとハッシュを返す ### ハッシュ ``` $ git rev-parse 1838ad4786...02cc4e713a >> 02cc4e713a10dc68bcba40919f0f23eb62b45ec4 >> 1838ad478661d8cdb544c9adf921d08a97f7cc91 >> ^02cc4e713a10dc68bcba40919f0f23eb62b45ec4 ``` ### tag ``` $ git rev-parse v0.1.1 >> 4b7688eda62c07ec71f7a9576395861c252bd904 ``` ### 省略ハッシュ ``` $ git rev-parse 4b7688 >> 4b7688eda62c07ec71f7a9576395861c252bd904 ``` ## gitリポジトリのディレクトリを返す ``` $ git rev-parse --git-dir >> .git ``` ## 現在のディレクトリにリポジトリがあるかどうかを確認 ``` $ git rev-parse --is-inside-work-tree >> true ``` ## リポジトリが共有リポジトリか確認 ```shell $ git rev-parse --is-bare-repository >> false ``` ## トップレベルディレクトリを絶対パスで取得 ``` $ git rev-parse --show-toplevel >> /path/to/repository ``` ## サブディレクトリにいるときにトップレベルのディレクトリからの相対パスを出力 ``` $ git rev-parse --show-cdup >> path/ ``` ## 使用可能なすべてのリファレンスを出力 ``` $ git rev-parse --all >> d2c70a0299127fbfa3cb898cb0b1ffd1186a01b4 >> a8a66d65282a22ae93954a468121a66ffc6926a3 >> d8cf577b01c3b52b1d568cf32230cf3537c3a5f5 >> f55abf927a2300831bbffb877a6f6ae8bec701d6 >> 81732ec5d6040da5c1ad9b68022292c97d6d3198 >> 69249bdcb954bb851df9901ed6491119836c2c8d >> 63c8ebd4f31a1d580b8068f44b547d8418002972 >> b771459c556b79442ef0c5a1569c74a173c9492b >> 53d65668f327df193637c4150a45fab1eacd138f >> 4b7688eda62c07ec71f7a9576395861c252bd904 >> d490a6e5c0862bcd49450d79ba81d6d01470c42a ``` ### ブランチ ``` $ git rev-parse --branches >> d2c70a0299127fbfa3cb898cb0b1ffd1186a01b4 ``` ### タグ ``` $ git rev-parse --tags >> d8cf577b01c3b52b1d568cf32230cf3537c3a5f5 >> f55abf927a2300831bbffb877a6f6ae8bec701d6 ``` ### リモート ``` $ git rev-parse --remotes >> a8a66d65282a22ae93954a468121a66ffc6926a3 ``` 自分が知ってるのはこれくらいです。 もっとあればコメントお願いします! |
|
| 906位 |
|
|||
|
15:51:00 |
|
|
git 1.7からsparse checkout機能が利用できるらしい。
ざっくりとした使い方は以下の通り、 $ git clone リポジトリURL hoge $ cd hoge $ git config core.sparsecheckout true $ echo "hoge.txt" > .git/info/sparse-checkout $ git read-tree -m -u HEAD こうするとワーキングディレクトリ下はhoge.txtファイルだけチェックアウトされる様になるらしい。 これまで制作部スタッフにhtmlファイルを編集してもらうのに社内共有フォルダでファイルの受け渡しをしていたんだけど、この機能を使ったらもう少しスマートにならないかなと考え中。 gitスキルのない制作部スタッフにいきなり今日からgit運用にします。ってのは、ハードル高すぎるので、gitアクセスは当面、技術部スタッフが代行するとして共有フォルダにsparse checkoutで制作部スタッフが編集してよいhtmlファイルだけチェックアウトしておくのはどうだろう。 |
|
| 907位 |
|
|||
|
21:29:18 |
(Infiniteloop.co,ltd. 所属) |
|
## 参考文献
http://labs.gree.jp/blog/2010/07/456/ リアルタイム・ランキングを考える http://www.cyberagent.co.jp/technology/pdf/2011_10.pdf 元ネタについては、GREEさんや、サイバーエージェントさんが同じような方式でした。 これを真似てRailsで実装してみました ## 動作環境 * Ubuntu 12.04 * Ruby 1.9.3 * Rails 3.2.12 * MySQL 5.5 ## データ構造 こんな感じの構造です(minとmaxはコードに埋め込んだら実は無くても良い) ```ruby:schema.rb create_table "high_score_partitions", :force => true do |t| t.integer "min" t.integer "max" t.integer "user_count", :default => 0 t.datetime "created_at", :null => false t.datetime "updated_at", :null => false end add_index "high_score_partitions", ["user_count"], :name => "index_high_score_partitions_on_user_count" create_table "user_high_scores", :force => true do |t| t.integer "user_id" t.integer "high_score_partition_id" t.integer "score" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false end add_index "user_high_scores", ["high_score_partition_id"], :name => "index_user_high_scores_on_high_score_partition_id" add_index "user_high_scores", ["score"], :name => "index_user_high_scores_on_score" add_index "user_high_scores", ["user_id"], :name => "index_user_high_scores_on_user_id" ``` ## ダミーデータ類 12万~28万スコアくらいに集中すると想像してパーティションのコードを書いてみました。minとmaxはDBに持たなければ普通にコードを吐き出す感じで問題ないはず。 ```ruby:seeds.rb pt_max = 128 pt_more = [120000,280000] pt_last = 0 pt_last_size = 0 pt_max.times.each do |pt_id| pt = HighScorePartition.where(id: (pt_id + 1)).first_or_initialize if pt_more[0] <= pt_last and pt_last <= pt_more[1] size = 3000 else size = 8000 end pt.min = pt_last + pt_last_size pt.max = pt_last + pt_last_size + size - 1 pt_last = pt.min pt.user_count = 0 pt.save! pt_last_size = size p "#{pt.min} => {id: #{pt.id}, max: #{pt.max}}," end ``` ## 実装コード ```ruby:high_score_partition.rb class HighScorePartition < ActiveRecord::Base attr_accessible :id, :max, :min, :user_count has_many :user_high_scores def self.target score partition_map = HighScorePartitionMap::PARTITIONS.select {|k,v| k <= score and v[:max] >= score}.values.map {|v| v[:id]} self.where(id: partition_map) end def self.upper score partition_map = HighScorePartitionMap::PARTITIONS.select {|k,v| k <= score }.values.map {|v| v[:id]} self.where(id: partition_map) end end ``` ```ruby:high_score_permission_rank.rb class HighScorePartitionMap PARTITIONS = { 0 => {id: 1, max: 7999}, 8000 => {id: 2, max: 15999}, 16000 => {id: 3, max: 23999}, 24000 => {id: 4, max: 31999}, 32000 => {id: 5, max: 39999}, 40000 => {id: 6, max: 47999}, 48000 => {id: 7, max: 55999}, 56000 => {id: 8, max: 63999}, ~~中略~~ 740000 => {id: 126, max: 747999}, 748000 => {id: 127, max: 755999}, 756000 => {id: 128, max: 763999} } end ``` ```ruby:user_high_score.rb class UserHighScore < ActiveRecord::Base belongs_to :user belongs_to :high_score_partition attr_accessible :score before_save do |r| partition_map = HighScorePartitionMap::PARTITIONS.select {|k,v| k <= r.score and v[:max] >= r.score}.values.first # Not modify return true if r.high_score_partition_id == partition_map[:id] if r.high_score_partition_id where = [r.high_score_partition_id, partition_map[:id]] else where = partition_map[:id] end partition = HighScorePartition.where(id: where).lock(true).all.select{|v| v.id == partition_map[:id]}.first # Not found partition if cheat? raise "Score not found range partition" unless partition # Remove user for old pertition HighScorePartition.decrement_counter(:user_count, r.high_score_partition_id) if r.high_score_partition_id # Add user for new partition HighScorePartition.increment_counter(:user_count, partition) # New partition self.high_score_partition = partition end def current_rank total = HighScorePartition.upper(self.score).sum(:user_count) range = self.class.upper_byrange(self.score).count(:score) total + range + 1 end def rank_range user_count = 0 users = self.class.upper_byrange(self.score).limit(10).all return users if users.length >= 10 user_count = users.count if users.count if self.current_rank >= 10 partitions = HighScorePartition.order('min DESC').all else partitions = HighScorePartition.upper(self.score).order('min DESC').all end partitions.each do |partition| users << partition.user_high_scores.limit(10).all user_count += partition.user_count return users if user_count >= 10 end end def self.upper_byrange score target = HighScorePartition.target(score).first self.where(score: (score + 1)..target.max) end end ``` upper_byrangeで自分のスコアを入れてしまうと、自分も含め上位 という扱いになるようです。 ## ダミーテスト ```ruby:dummy.rb u1 = User.where(id: 1).first hs1 = UserHighScore.where(user_id: u1).first_or_initialize hs1.user = u hs1.score = 2235 hs1.save! u2 = User.where(id: 2).first hs2 = UserHighScore.where(user_id: u2).first_or_initialize hs2.user = u2 hs2.score = 2234 hs2.save! u3 = User.where(id: 3).first hs3 = UserHighScore.where(user_id: u3).first_or_initialize hs3.user = u3 hs3.score = 4334 hs3.save! hs1.current_rank hs2.current_rank hs3.current_rank ``` 結果は下記の通り ```ruby:irb irb(main):020:0* hs1.current_rank (0.7ms) SELECT SUM("high_score_partitions"."user_count") AS sum_id FROM "high_score_partitions" WHERE (min > 2235) HighScorePartition Load (0.2ms) SELECT "high_score_partitions".* FROM "high_score_partitions" WHERE ((min <= 2235 AND 2235 <= max)) LIMIT 1 (0.2ms) SELECT COUNT("user_high_scores"."score") FROM "user_high_scores" WHERE ("user_high_scores"."score" BETWEEN 2236 AND 2999) => 2 irb(main):021:0> hs2.current_rank (0.4ms) SELECT SUM("high_score_partitions"."user_count") AS sum_id FROM "high_score_partitions" WHERE (min > 2234) HighScorePartition Load (0.1ms) SELECT "high_score_partitions".* FROM "high_score_partitions" WHERE ((min <= 2234 AND 2234 <= max)) LIMIT 1 (0.1ms) SELECT COUNT("user_high_scores"."score") FROM "user_high_scores" WHERE ("user_high_scores"."score" BETWEEN 2235 AND 2999) => 3 irb(main):022:0> hs3.current_rank (0.5ms) SELECT SUM("high_score_partitions"."user_count") AS sum_id FROM "high_score_partitions" WHERE (min > 4334) HighScorePartition Load (0.1ms) SELECT "high_score_partitions".* FROM "high_score_partitions" WHERE ((min <= 4334 AND 4334 <= max)) LIMIT 1 (0.1ms) SELECT COUNT("user_high_scores"."score") FROM "user_high_scores" WHERE ("user_high_scores"."score" BETWEEN 4335 AND 4999) => 1 ``` ## ベンチマーク スコア数20万件で実施(legacyが普通にスコアから上位10位取得、newが今回の方式) user system total real 1time legacy: 0.000000 0.000000 0.000000 ( 0.001784) 1time new: 0.120000 0.030000 0.150000 ( 0.143830) 100time legacy: 0.630000 0.120000 0.750000 ( 2.116978) 100time new: 0.610000 0.250000 0.860000 ( 0.809772) 250time legacy: 1.470000 0.290000 1.760000 ( 4.827297) 250time new: 1.710000 0.520000 2.230000 ( 2.144867) 1000time legacy: 6.780000 1.200000 7.980000 ( 25.567251) 1000time new: 10.640000 2.230000 12.870000 ( 12.602131) ## 軽く感想 私が掛け持ちしている趣味方面のブラウザゲームで、リアルタイムランキング入れたいって話が上がっていたので、どうやったらいいかな〜?と思っていました。 先人の知恵が世の中には出まわっていたので、参考にしない手は無いと感じました。 ただし、2万件程度であればスロークエリには出るけど、ベンチ上は何も考えず普通にスコアから10位取得したほうが早くなりやすいことも分かってきました。 ## ツッコミとそのお返事 * パーティションのコード、ymlに書くなりしたら? * はい、ごもっともです(書いてた時は、力強くコードに書いていいかなーって思って書いてた * パーティションは別テーブルに切り出してあげたら? * 別テーブルにすることで動的にパーティションを最適値に変動する機能があるならそのほうがいいと思います。ただ、手でやるならエンジニアが触ることになるだろうし、どうせメンテにいれないと更新作業怖すぎなので、コードに書いたら一番はやいし、実は最適解なのかなーと思ってコードにしました。 * Web†DB PRESSの73号あたりにRedisの例があったけど? * RDBでやりたかったのと、先に今回のきっかけとなる資料を目にしたのでそっちを試したかったというのが理由です * ロックしないとダメなの? * ロックしないとやっぱ不整合出ました(パーティション上の対象データ件数の総和が何故か実際のデータ数より少ない数字になりました。ロックをちゃんとしたらそういうことは起こりません) * Rails側であれこれロックとかしてるけど、ストアドとかダメなの? * 個人的にはあまり好まれてない気がするので使ってません。 * partition = HighScorePartition.where(id: where).lock(true).all.select{|v| v.id == partition_map[:id]}.first ここ、なんでデータとってきてからselectメソッド叩いてるの? * ロックする時に、1〜2行ロックしているので、インクリ対象のほうをとってきてました。一度に対象の行をロックしないと、運悪くデッドロックになるパターンが密かに眠ってたりします。また再度クエリ投げるくらいなら2行とってきた後にプログラム的に抽出したほうが早いですし。 ## 勉強会発表 Ruby勉強会札幌 24回にて、ミニセッションという形で発表させて頂きました。 https://speakerdeck.com/miio/realtime-ranking |
|
| 908位 |
|
|||
|
13:35:00 |
(大阪大学 → 某社 所属) |
|
普段 Vim で Ruby を書いているのですが,そういえば Ruby を書くのを補助する Vim プラグイン少ないなぁと思って先月は Ruby プラグイン作成月間にしてみました.
本エントリでは僕が作成した3つのプラグインを紹介します. - [unite-ruby-require.vim](https://github.com/rhysd/unite-ruby-require.vim) `require` 対象の補完をする unite.vim ソース. - [neco-ruby-keyword-args](https://github.com/rhysd/neco-ruby-keyword-args) neocomplcache で Ruby 2.0 のキーワード引数を補完 - [vim-textobj-ruby](https://github.com/rhysd/vim-textobj-ruby) Ruby のブロックをテキストオブジェクト化 # unite-ruby-require.vim Ruby で外部のソースを読み込むのに使う `require` ですが,標準だけでも大量のライブラリがあり,なかなか全部覚えるのは大変です. そこで,unite.vim で `require` の候補をインクリメンタルに検索できるプラグイン [unite-ruby-require.vim](https://github.com/rhysd/unite-ruby-require.vim) を作りました. ``` :Unite ruby/require ``` で起動すると,Ruby の標準のライブラリやインストールした gem,bundler でプロジェクトローカルにインストールしているライブラリのパスを収集し,`require` の候補を一覧表示します. あとは unite.vim のインクリメンタルサーチで目的のライブラリを探すだけです. 例として,URI 関連のクラスを探したい場合は,`:Unite ruby/require` で開き,`uri` と入力するとこんな感じに抽出されます.  ここで `open-uri` を選択してエンターキーを押すと,カーソル行に `require 'open-uri'` が挿入されます. なお,rbenv などで Vim の Ruby インターフェースと異なる Ruby を使用している場合は, ```vim let g:unite_source_ruby_require_ruby_command = '$HOME/.rbenv/shims/ruby' ``` という風に,Ruby へのパスを定義しておいて下さい. # neco-ruby-keyword-args Ruby 2.0 では,引数を名前で指定するキーワード引数が追加されました. 今まではハッシュを引数にすることで擬似的に名前渡しを実現していましたが,もうそんなことをする必要はありません. ```ruby # ハッシュのような書き方で引数の名前とデフォルト値を指定 def test(tsura: 'poyo', yaba: 'poyo') puts tsura+yaba end # 指定しないとデフォルト値が使われる test #=> poyopoyo # 名前で指定するので順番は意識しなくて良い test(yaba: 'dameda', tsura: 'mou') #=> moudameda ``` これにより,引数の順序を守らないといけないなどの煩わしさから開放され,どの引数が何の値を示しているのか呼び出し側で明示できます. しかし,当然ですが代わりに引数に付けられた名前を覚えておかなければなりません. そこで,ソース内のキーワード引数名を収集し,自動で補完してくれるプラグイン[neco-ruby-keyword-args](https://github.com/rhysd/neco-ruby-keyword-args)を作りました. neocomplcache のソースとして実装しているので,neocomplcache を使っていることが前提です. 使用中のスクリーンショットはこんな感じです.  少し分かりにくいですが,一番下の行で `test` メソッドの `tsura` 引数を補完しようとしているところです. 補完候補の `[K]` というのがキーワード引数補完のマークで,その次にデフォルト値が `'poyo'` であることが表示されています. 候補収集の範囲については,相対パスで `require` した先のソースからも再帰的に収集します.なので, ```ruby:hoge.rb def hoge(foo: 'a', bar: 'b') end ``` ```ruby:huga.rb require './hoge.rb' ``` ```ruby:piyo.rb require './huga.rb' hoge #ここで補完できる ``` のように,再帰的に piyo.rb → huga.rb → hoge.rb と潜っていって候補を収集してくれます. # vim-textobj-ruby まず,テキストオブジェクトについて知らない方は `:help text-object` してみてください.Vim のとても便利な機能です. [vim-textobj-ruby](https://github.com/rhysd/vim-textobj-ruby) では,Ruby の `class` や `if`,`begin` のような `end` で終わるブロックをテキストオブジェクトにして一発で削除や選択やヤンクが行えるようになります. 似たようなプラグインに vim-textobj-rubyblock があるのですが,自分の環境でうまく動かなかったのと,シンタックスハイライトなどを使ってもっとうまく選択できると判断したため新しく作りました. なお,kana さんの [vim-textobj-user](https://github.com/kana/vim-textobj-user) が必要です. プラグインをインストールすると,モーション待ちモードに `r` マッピングが追加されます.(モーション待ちモードについては `:help mapmode-o` を参考にしてみて下さい) いくつかの使用例を示します. 以下の例では `#%` はカーソル位置です. ```ruby def hoge(yo) if yo puts "yo!" #% end puts "everyone!" end ``` `def` と `if` の2つのブロックがネストしています. `if` 文の内部にカーソルがある状態で `dar` を入力すると `if` のブロック全体が削除され, ```ruby def hoge(yo) #% puts "everyone!" end ``` となります.また, ```ruby def hoge(yo) if yo puts "yo!" end #% puts "everyone!" end ``` のように `if` の外で `def` の内部にカーソルがある場合に `vir` とすると `def` の内部(`if yo` から `puts ...` まで)がビジュアルモードで選択されます. つまり,カーソルがある位置のブロックが対象になってテキストオブジェクト化されます. #その他のプラグイン その他,僕が書いたものではないですが,気になっているプラグインを挙げておきます. - [neocomplcache-rsense](https://github.com/Shougo/neocomplcache-rsense) Ruby の開発援助ツール,RSense を vimproc で非同期に実行し補完を行うプラグインです. - [vim-ruby](https://github.com/vim-ruby/vim-ruby) Vim で Ruby を書くための色々便利な機能が詰まったプラグインです. オールインワンなプラグインがあまり好きでは無いため試していないですが,Vim の Ruby インターフェースを使って編集中のソースコードを部分的に実行しメソッドなどを取ってくる補完などがあるようです.ただし,rbenv などを使っている場合は Vim が使う Ruby と書いているソースを実際に実行する Ruby のバージョンがずれたりするため,注意が必要そうです. |
|
| 909位 |
|
|||
|
11:45:34 |
|
|
Last-Update:2012/04/23 メッセージはこちら↓ ```Bach:泣くほどみた無限ループメッセージ Rails is not currently installed on this system. To get the latest version, simply type: $ sudo gem install rails You can then rerun your "rails" command. ``` で、いくら`sudo gem install rails`を入力しても、railsコマンドを実行すると上記メッセージが表示されて変わらないと。 状況はといえば [非常に丁寧なこちらの解説サイト](http://www.oiax.jp/rails/zakkan/rails_3_1_installation_on_macosx.html)を参考に homebrewでruby-buildとrbenvをインストールして、ruby本体はrbenvにてインストールしたものが動いている状態でした。 で、railsがいくらやっても動かないと。 ここで、一から整理しながら考えてみて、rbenvやgemの仕組みを調べてみたところ + gemはシステム(Mac本体)ではなく、rubyが持つツールである。 + rbenvでは、rubyごとにgemを持つ。 ということ。 なので、いくらrbenvでrubyのモードを切り替えても、gem自体はシステムデフォルトのrubyのgemが使われていた様子。 さらに、rbenvでいれたrubyごとにgemを持つから、rbenvではrubyを入れる(切り替える)ごとにgemファイルが変わる模様(それを回避する方法もあるそうな。) つまり私のドハマリした状況は + ruby⇛rbenvのものが作動(1.9.3系) + gem⇛MacOSXのデフォルトのruby(1.8.7系)のgemが作動 + rails⇛上記のgemでinstallされたから、デフォルトruby内のgemに配置=/usr/bin/の中のrailsが使われている という入り組んだ状態になっていた。 調べてみると、railsは確かに/usr/bin/の中から呼ばれている。 正しく動作させるには + gem⇛rbenvで入れたruby1.9.3系のgemを起動させるようにする + rails⇛rbenv内のrubyの指定位置から呼ばれるようにする ということで、.bashrcなどにPATHを記述してPATHを貼り直す(システムの優先順位を決める:/usr/bin/よりもrbenvのものを優先させる) ここで注意点?がありまして いろんな「rbenvを使ったruby環境構築解説記事」では、PATHの指定が以下のようになっている ``` export PATH="$HOME/.rbenv/bin:$PATH" ``` が、いくら探しても~/.rbenv/のなかに、binというフォルダはない。私が特殊なのか、仕様が変わったのかはわかりません。 しかし、かわりに「shims」という似たようなフォルダを見つけた。 どうやら、この中にいろんなシステムが入っている様子。 ということで、.bashrcに以下を追記 ``` export PATH="$HOME/.rbenv/shims:$PATH" ``` その後、`gem install rails`としてrailsをインストールすると… 動いた!! まとめると *gemとはrubyそれぞれがもつツールである。しかし、rbenvをいれてもMacOSXはデフォルトでrubyとgemを持っているから、いくらgemコマンドを打ち込んでもデフォルトのrubyのgemが起動してしまう。gemコマンドが自分が使いたいrbenvのrubyで動くように、きちんとPATHを指定する必要がある。そうすれば、railsだって動く* ということです。 |
|
| 910位 |
|
|||
|
10:45:43 |
|
|
スーパーグローバル変数に直接アクセスするより、filter_input 経由したほうがいいとのこと。
```php: <?php // こう書くよりも $aaa = isset( $_GET["aaa"] ) ? $_GET["aaa"] : null; $bbb = isset( $_POST["bbb"] ) ? $_POST["bbb"] : null; // こう書くほうがよいとのこと $aaa = filter_input( INPUT_GET, "aaa" ); $bbb = filter_input( INPUT_POST, "bbb" ); ``` |
|
| 911位 |
|
|||
|
08:58:43 |
|
|
## rails gで不要なファイルが生成される・・・ Railsで開発する際に、rails gコマンドは欠かせないと思います。 しかし、特にscaffoldやcontrollerの生成時には不要なファイルが生成される場合があります。 例えば、apiを提供するアプリケーションにjsやcssのファイルは必要ありません。 ## config.generators application.rbのconfig.generatorsでrails gで生成されるファイルを設定できます! 例えば、 * apiだからjsとかcssとかいらないな * view_helperもいらないな * viewのテンプレートエンジンもひとまずなしで * viewとview_helperのテストはなくていい * fixtureはfactory_girlがいいな。 であれば、こんな感じで設定します ```ruby:application.rb config.generators do |g| g.stylesheets false g.javascripts false g.helper false g.template_engine false g.test_framework :rspec, view_specs: false, helper_specs: false, fixture: true g.fixture_replacement :factory_girl, dir: "spec/support/factories" end ``` 実行結果はこんな感じ。 ``` > rails g controller hoge/users index create app/controllers/hoge/users_controller.rb route get "users/index" invoke rspec create spec/controllers/hoge/users_controller_spec.rb invoke assets invoke js invoke css ``` もちろん、テンプレートエンジンにerbを使いたければ ```ruby:application.rb g.template_engine :erb ``` と書けば生成されます。 この設定を使っているプロジェクトはテンプレートエンジンとしてrablを使用しているのですが、rablはどうも認識されないもよう・・・ config.generatorsを設定してスマートなシステムを作りましょう! ## さらに詳しい説明 [ruby/rails/RailsGuidesをゆっくり和訳してみたよ/Creating and Customizing Rails Generators](http://wiki.usagee.co.jp/ruby/rails/RailsGuides%E3%82%92%E3%82%86%E3%81%A3%E3%81%8F%E3%82%8A%E5%92%8C%E8%A8%B3%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F%E3%82%88/Creating%20and%20Customizing%20Rails%20Generators) |
|
| 912位 |
|
|||
|
16:10:05 |
(株式会社ミラボ 所属) |
|
> 2013/12/27 追記:初回アクセス時に popState イベントが発生しリロードしてしまう問題への対策を記載しました。(この追記により、タイトルが 15行 → 16行 に変更になりました^^)
# pushState popState とは Ajax を使ったスムーズな画面遷移でも、URL を書き換え履歴に追加し、ブラウザバックに対応することができる仕組みです。SEO への影響を出すこと無く操作性を向上させることができます。 現在、pushState/popState を使ったクライアントサイドルーティングを実現するライブラリがいくつか作られていますが、今回は jQuery だけで行うシンプルな方法を紹介します。 参考: [Googleに続いてBingもAjaxにはpushStateを推奨](http://www.suzukikenichi.com/blog/bing-recommends-pushstate-with-ajax/) # 本題 jQuery だけで、15行程度でできる pushState popState を使った Ajax 画面遷移の方法です。 ```js if (window.history && window.history.pushState) { // 内容の読み込み処理 function loadContents(url) { $.get(url, { push_state: 'true' }) .then(function(content) { $('#main').html(content); }); } // pushState を使った遷移 $(document).on('click', '.push-state', function(e) { e.preventDefault(); var url = $(this).attr('href'); loadContents(url); history.pushState('', '', url); }); // popState イベントをハンドリング $(window).on('popstate', function(e){ if (!e.originalEvent.state) return; // 初回アクセス時に再読み込みしてしまう対策 loadContents(location.pathname); }); } ``` ## 内容の読み込み処理 ページの内容を Ajax で読み込んで反映する処理です。 サーバサイドに pushState による画面遷移であることを通知する必要があるなら、 `pushstate: 'true'` などのパラメタを渡します。または、サーバサイドで `HTTP_X_REQUESTED_WITH` パラメタを検出するという方法もあります。 読み込んだら、所定のコンテナ `例では <section id="main"></section> 等` に上書きします。 ## pushState を使った遷移 ハイパーリンクやボタンをクリックした際に、通常の画面遷移をキャンセルして内容の読み込みと pushState へのURL変更登録を行います。 上記の例では、 `class="push-state"` 属性がついた `a` タグに反応するようになります。 ## popState イベントをハンドリング 最後に「戻るボタン」「進むボタン」がクリックされた時の処理を定義します。 これは単純に、現在の URL を使って内容の読み込みを行うだけです。 ただ、popState イベントは **初回アクセス時にも発生してしまいます。** そのため `e.originalEvent.state` を使って、まだ pushState されていなければなにもしないという対策が必要となります。 |
|
| 913位 |
|
|||
|
09:47:47 |
|
|
最近[こんな記事](http://jules.boussekeyt.org/2013/backbone-functions.html)を見かけて、フーンと思いながら眺めていたら、便利そうな内容(Ease debugging of Backbone events)を見かけたので、自分なりにアレンジしてみました。
Backbone.jsで作成したオブジェクトで発火した全イベントをこんな感じでconsoleに出力してくれます。  ソースは以下(__コメントの指摘を受けて一行修正しました__)。 ```js:backbone.debug.js (function () { 'use strict'; // ログのスタイル var logStyles = { timestamp: { color: 'gray' }, label: { color: 'white', 'border-radius': '2px' }, event: { color: 'blue', 'font-weight': 'bold', 'font-size': '110%' } }; // ラベルの色 var labelColors= { Model: 'red', Collection: 'purple', View: 'green', Router: 'black' }; function debugEvents(parts) { return function (prefix) { this.__debugEvents = this.__debugEvents || {}; if (prefix === false) { return this.off('all', this.__debugEvents.log, this); } else if (prefix !== true) { this.__debugEvents.prefix = prefix || ''; } if ('log' in this.__debugEvents) { // イベント登録済チェック(二重登録防止) if ('_events' in this && 'all' in this._events) { var exists = _.some(this._events.all, function (item) { return item.callback === this.__debugEvents.log && item.context === this; }, this); if (exists) { return; } } } else { // イベント発火時の関数作成 var labelCss = _.extend(logStyles.label, { background: labelColors[parts] }); var css = { timestamp: _.map(logStyles.timestamp, function (val, key) { return key + ':' + val; }).join(';'), label: _.map(labelCss, function (val, key) { return key + ':' + val; }).join(';'), event: _.map(logStyles.event, function (val, key) { return key + ':' + val; }).join(';') }; this.__debugEvents.log = function(eventName) { var labelName = parts; if (this.__debugEvents.prefix) { labelName += ':' + this.__debugEvents.prefix; } console.debug( '%c%s %c%s%c %s', css.timestamp, new Date().toString().match(/(\d+:\d+:\d+)/)[1], css.label, ' ' + labelName + ' ', css.event, eventName, Array.prototype.slice.call(arguments, 1) ); // この行いらない(コメント参照) → if (eventName === 'remove') { this.off('all', this.__debugEvents.log, this); } }; } this.on('all', this.__debugEvents.log, this); }; } // Backboneの各クラスのプロトタイプにdebugEvents()メソッド付加 _.each(labelColors, function(val, key) { Backbone[key].prototype.debugEvents = debugEvents(key); }); })(); ``` アレンジってレベルじゃねーぞ!感がありますが、気にせずにいきましょう。 ## 使用方法 このスクリプトを「backbone.debug.js」みたいなファイル名で保存して、htmlで読み込みます。 ```html <script type="text/javascript" src="backbone.js"> <script type="text/javascript" src="backbone.debug.js"> ``` 「backbone.debug.js」を読み込んだ事により、Backboneの各オブジェクトで`debugEvents()`というメソッドが使用可能になります。 で、イベントログを出力したいオブジェクトの`debugEvents()`メソッドを実行します。 ```js var user = new Backbone.Model(); user.debugEvents('hoge'); ``` 引数の`'hoge'`はログ出力時にどのオブジェクトのログかを識別できるように表示する文字列です。なくても構いません。 これで、最初に紹介したスクリーンショットみたいなログをイベントの度にバンバン出力してくれます。 一時的にイベント表示をオフにしたい場合は、 ```js user.debugEvents(false); ``` というように引数に`false`を入れると、そのオブジェクトに関するログ出力はストップします。また、 ```js user.debugEvents(true); ``` で、ログ出力を再開できます。 ## ここが便利 イベントログは`console.debug()`で出力するので、Chromeの開発者ツールやFirefoxのFirebugのログフィルタでイベントログだけ見ることができます。Chromeではイベントログを除いたログを表示するフィルタもあるみたいです。 ## カスタマイズ 色が目に優しくない、ダサいという場合は、ソースの頭にある`logStyles`と`labelColors`を変更してみてください。CSS形式の定義です。 ## 念のため `debugEvents()`を実行したオブジェクトには`__debugEvents`というプロパティが作成されます。かぶる事はないとは思いますが一応。 ## デバッグ消し忘れの悲劇防止 開発時に`debugEvents()`を使って、本番前に消し忘れた場合、本番では「backbone.debug.js」は読み込まないと思うのでエラーが発生してしまいます。本番環境ではこんな感じの空関数を定義しておけば`debugEvents()`を消し忘れても安心。 ```js:本番用ダミーdebugEvents Backbone.Model.prototype.debugEvents = Backbone.Collection.prototype.debugEvents = Backbone.View.prototype.debugEvents = Backbone.Router.prototype.debugEvents = function () {}; ``` ## 大事な事を忘れていた consoleの機能が貧弱なIEでは動きません。`console.debug`の部分を`console.log`に変えればとりあえず動きますが、スタイル設定に対応していないので表示が残念になります。FirefoxもFirebugでない標準の開発者ツールだとスタイル対応していないみたいです。 ## まとめ ネタ元の<http://jules.boussekeyt.org/>では、他にも役立つ情報が色々とあったので興味があったらご覧になる事をオススメしますです。 ## 追記(2013/11/11) (@pchw)さんがbower component化してくれました。 <https://github.com/pchw/backbone-event-logger> AMD/RequireJS対応もしているようです。 |
|
| 914位 |
|
|||
|
04:21:18 |
|
|
セキュリティ対策などでWebサーバ側で指定した方が良いヘッダー。 ありそうでまとめられてなかったので投稿しておきます。 2016-02-05 update まだストックされる方がたまにいるようなので、簡単に更新しました。 ## XSS対策 * nginx ``` add_header X-XSS-Protection "1; mode=block"; ``` * Apache ``` Header set X-XSS-Protection "1; mode=block" ``` ## クリックジャッキング 例ではDENYにしているが、他にSAMEORIGIN、ALLOW-FROMなど * nginx ``` add_header X-Frame-Options DENY; ``` * Apache ``` Header set X-Frame-Options DENY ``` [X-Frame-Options レスポンスヘッダ - HTTP | MDN](https://developer.mozilla.org/ja/docs/HTTP/X-Frame-Options) ## IEで発生するコンテンツタイプSniffing対策 * nginx ``` add_header X-Content-Type-Options nosniff; ``` * Apache ``` Header set X-Content-Type-Options nosniff ``` ## Content-Security-Policy [CSP (Content Security Policy) - Security | MDN](https://developer.mozilla.org/ja/docs/Security/CSP) [弊社のホームページにContent Security Policy(CSP)を導入しました | HASHコンサルティングオフィシャルブログ](http://blog.hash-c.co.jp/2013/12/Content-Security-Policy-CSP.html) ## 以下https採用時 ### Public Key Pining SSL証明書などからbase64に変換後。オプションがいくつかあるので、サイトにあった形で。 * nginx ``` add_header Public-Key-Pins "pin-sha256='base64==xxxxxxxxxxxxxxxxxxxxxx='; max-age=5184000"; ``` [Public Key Pinning - Web security | MDN](https://developer.mozilla.org/ja/docs/Web/Security/Public_Key_Pinning) ### Strict-Transport-Security * nginx ``` add_header Strict-Transport-Security max-age=15768000; ``` [HTTP Strict Transport Security - Security | MDN](https://developer.mozilla.org/ja/docs/Security/HTTP_Strict_Transport_Security) |
|
| 915位 |
|
|||
|
10:25:59 |
(Wantedly, Inc. 所属) |
|
※コメントの指摘を修正済みです。 HTMLではForm内にテキスト一行入力系のフィールドが一つだった場合、そこでEnterキーを押すとSubmitされる仕様になっている。 詳しい仕様に関しては以下を参照: - HTML5の仕様: http://www.w3.org/TR/html5/forms.html#implicit-submission - 古くからの仕様: http://www.w3.org/MarkUp/html-spec/html-spec_8.html#SEC8.2 具体的には、inputのtypeが `text, search, url, tel, email, password, datetime, date, month, week, time, datetime-local, number` のどれかだと、この挙動になる。 これが厄介なのは、Form内にチェックボックスやテキストエリアなどの他のフィールドがあってもテキストフィールドが一つだったらサブミットしてしまう事。意外とそういう風になっていることに気づかないのでデフォルトでオフにしておくことをオススメする。 逆に、検索Boxなどsubmitして欲しい場合もあるので、`allow_submit`というクラスを明示的につけるとデフォルトの挙動に戻るように設計する。 ## デフォルトでこの挙動を解除する方法 常にロードされるJavascriptファイルないで以下のように記述する。 ```javascript $(function() { $(document).on("keypress", "input:not(.allow_submit)", function(event) { return event.which !== 13; }); }); ``` 自分のためにCoffeeScriptバージョンも書いておく。 ```coffee $ -> $(document).on "keypress", "input:not(.allow_submit)", (event) -> event.which != 13 ``` こうすることで、allow_submitを明示的にクラス名に付けない限り、Enterキーでサブミットされることを防ぐことが出来る用になる。 |
|
| 916位 |
|
|||
|
15:37:39 |
|
|
この投稿は自分のブログ記事をQiita用に整形したものです。追記事項があればブログの方に書いていきます。
__「Pythonの数値計算ライブラリ NumPy入門」__ http://rest-term.com/archives/2999/ # Pythonの数値計算ライブラリ NumPy入門  [Scientific Computing Tools For Python — Numpy](http://www.numpy.org/) > NumPy は Pythonプログラミング言語の拡張モジュールであり、大規模な多次元配列や行列のサポート、これらを操作するための大規模な高水準の数学関数ライブラリを提供する。(via Wikipedia) これまで知識があいまいだったNumPyについて、もう一度おさらいしたいと思います。NumPyはSciPyと併せて科学技術計算でよく利用されています。また、高速に行列演算ができるのでOpenCV(コンピュータビジョンライブラリ)でもNumPyを利用したPythonインタフェースが提供されるようになりました。 OpenCVのPythonバインディングについては2011年にブログでも取り上げていますので参考までに。 * [さくらVPSにOpenCVをインストールしてPythonから使う](http://rest-term.com/archives/2861/) ## 環境 CentOS 6.4 (x86_64) Python 2.7.5 (ELF 64-bit LSB executable) NumPy 1.7.1 * NumPy配列(numpy.ndarray)とは * numpy.ndarrayの属性(attributes) * データ型について * 配列の生成 * 配列形状の変更 * Indexing * Fancy Indexing * インデックスの検索 * 配列に対する操作/演算 * 配列の結合/分割、軸操作 * 配列のソート * 配列要素に対する演算 * Broadcasting * 配列の走査 * 統計関数 * ファイル入出力 ## NumPy配列(numpy.ndarray)とは > An ndarray is a (usually fixed-size) multidimensional container of items of the same type and size. numpy.ndarray は多次元配列を扱うクラスです。基本的に以下の制限があります。 * 配列内要素の型は全て同じ * 配列長は固定 (固定長配列) * 配列の各次元の要素数は同じ (※ ただし、制限付きで配列の形状変更は可能。説明は後述) C言語の配列とよく似ています。制限がある分、Pythonのリスト(list)型と比較して大規模な配列を扱う際の処理効率が良くなっています。また、これ以降 "配列" と表記したときは基本的に numpy.ndarray を指すこととします。 ```py:import ## import時に np というエイリアスを付けるのが慣例のようです。 import numpy as np ``` ### numpy.ndarrayの属性(attributes) numpy.ndarray の主な属性については以下の通り。効率良く処理するためにndarrayのデータはメモリの連続領域上に保持されていますが()、これらの属性を参照するとデータがメモリ上にどうレイアウトされているかを調べることができます。 > An instance of class ndarray consists of a contiguous one-dimensional segment of computer memory * [Internal memory layout of an ndarray](http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html#internal-memory-layout-of-an-ndarray) | | | | :----------------|:--------------------------------------------------------------------| | ndarray.flags | 配列データのメモリレイアウト情報 (numpy.flagsobj) | | ndarray.ndim | 配列の次元数 | | ndarray.size | 配列の要素数 | | ndarray.shape | 各次元の要素数 | | ndarray.itemsize | 1要素のバイト数 | | ndarray.strides | 各次元で次の要素に移動する際に必要なバイト数 | | ndarray.nbytes | 配列全体のバイト数 | | ndarray.dtype | 配列要素のデータ型 (numpy.dtype) | ```py:numpy.ndarray ## 二次元配列 (行列) >>> a = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]]) >>> a array([[ 1, 2, 3], [ 4, 5, 6], [ 7, 8, 9], [10, 11, 12]]) >>> a.flags C_CONTIGUOUS : True ## データがメモリ上に連続しているか(C配列型) F_CONTIGUOUS : False ## 同上(Fortran配列型) OWNDATA : True ## 自分のデータかどうか、ビュー(後述)の場合はFalse WRITEABLE : True ## データ変更可能か ALIGNED : True ## データ型がアラインされているか UPDATEIFCOPY : False ## Trueには変更できないので特に気にしなくて良い >>> a.ndim ## 次元数 2 >>> a.size ## 要素数 12 >>> a.shape ## 各次元の要素数 (行数, 列数) (4, 3) >>> a.itemsize ## 1要素のバイト数 8 >>> a.strides ## 24バイトで次の行、8バイトで次の列 (24, 8) >>> a.nbytes ## 配列全体のバイト数 (size*itemsize) 96 >>> a.dtype ## 要素のデータ型(後述) dtype('int64') ``` stridesは、メモリ上でa[0,0]とa[0,1]は8バイト離れており、a[0,0]とa[1,0]は24バイト離れていることを示します。データの並びがFortran型の場合は、strides は (8, 32) となります。一次元の場合はC型もFortran型も関係ありません。 ### データ型について 配列要素のデータ型はnumpy.dtypeというオブジェクトで扱われています。指定できる型については大きく分けて、論理型、符号付き整数型、符号無し整数型、浮動小数点型、複素数型の5つ。あとはデータ型のビット数別にそれぞれ指定できます。詳細は公式サイトを参照してください。 * [Data types — NumPy](http://docs.scipy.org/doc/numpy/user/basics.types.html) ```py:datatypes ## numpy.array() でdtypeオプションを指定しなければ自動的に設定される ## ここでは64ビット環境なので64ビットの型で生成される >>> a = np.array([1,2,3,4,5]) >>> a.dtype dtype('int64') ## 64ビット符号付き整数 >>> a.itemsize 8 >>> a = np.array([1.0,2.0,3.0,4.0,5.0]) >>> a.dtype dtype('float64') ## 64ビット浮動小数点数 >>> a.itemsize 8 ## numpy.array() でdtypeオプションを指定 ## 32ビット符号付き整数 >>> a = np.array([1,2,3], dtype=np.int32) >>> a.dtype dtype('int32') >>> a.itemsize 4 ## 8ビット符号無し整数 (画像処理ではよく使われる) >>> a = np.array([1,2,3], dtype=np.uint8) >>> a.dtype dtype('uint8') >>> a.itemsize 1 ``` データ型のキャストも可能です。ndarray.astype() を使います。 ```py:ndarray.astype() >>> a = np.array([1, 2, 3, 4, 5])) >>> a.dtype dtype('int64') ## int64型からint32型にキャスト >>> a.astype(np.int32) array([1, 2, 3, 4, 5], dtype=int32) >>> a = np.array([1.2, 2.4, 3.6, 4.8, 5.0]) >>> a.dtype dtype('float64') ## float64からint64型にキャスト、小数点以下は切り捨て >>> a.astype(np.int64) array([1, 2, 3, 4, 5]) ``` ダウンキャストでも例外は発生しません。また、ndarray.astype() は新しい配列を生成して返すので注意してください。 ## 配列の生成 まずは配列の生成方法から。生成方法はたくさんありますが、ここでは基本的でよく使われる方法を中心に紹介します。 ```py: >>> import numpy as np ## numpy.array() で生成、引数にはリスト(またはタプル)を渡す >>> np.array([1,4,3,2,5]) array([1, 4, 3, 2, 5]) ## 二次元配列もOK >>> np.array([[3,1,2], [6,4,5]]) array([[3, 1, 2], [6, 4, 5]]) ## numpy.zeros() で生成、全要素の値は0 >>> np.zeros(5) array([ 0., 0., 0., 0., 0.]) >>> np.zeros([2,3]) array([[ 0., 0., 0.], [ 0., 0., 0.]]) ## numpy.ones() で生成、全要素の値は1 >>> np.ones(5) array([ 1., 1., 1., 1., 1.]) >>> np.ones([2,3]) array([[ 1., 1., 1.], [ 1., 1., 1.]]) ## numpy.identity() で生成、単位行列 (正方行列なので引数は1つ) >>> np.identity(3) array([[ 1., 0., 0.], [ 0., 1., 0.], [ 0., 0., 1.]]) ## numpy.eye() で生成、identity() と似ているが列数指定ができる >>> np.eye(3, 2) array([[ 1., 0.], [ 0., 1.], [ 0., 0.]]) ## numpy.arange() で生成、組み込みの range() と同じ要領 >>> np.arange(10) array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) ## (始点,終点,増分)を指定 >>> np.arange(1.0, 2.0, 0.2) array([ 1. , 1.2, 1.4, 1.6, 1.8]) ## numpy.linspace() で生成、arange() と似ているが要素数を指定できる >>> np.linspace(1, 4, 6) array([ 1. , 1.6, 2.2, 2.8, 3.4, 4. ]) ## numpy.logspace() で生成、値は対数スケール(常用対数)で並べられる >>> np.logspace(2, 3, 4) array([ 100. , 215.443469 , 464.15888336, 1000. ]) ## 底(base)を2に指定 >>> np.logspace(2, 4 ,4, base=2) array([ 4. , 6.34960421, 10.0793684 , 16. ]) ## numpy.tile() で生成、要素を繰り返した配列を返す >>> np.tile([0,1,2,3,4], 2) array([0, 1, 2, 3, 4, 0, 1, 2, 3, 4]) ## numpy.meshgrid() で生成、縦横に等間隔な格子状配列 >>> a, b = np.meshgrid([1,2,3], [4,5,6,7]) >>> a array([[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]) >>> b array([[4, 4, 4], [5, 5, 5], [6, 6, 6], [7, 7, 7]]) ## numpy.tri() で生成、三角行列 >>> np.tri(3) array([[ 1., 0., 0.], [ 1., 1., 0.], [ 1., 1., 1.]]) ## numpy.diag() で生成、入力配列から対角要素を抜き出した配列を返す >>> a = np.array([[0,1,2], [3,4,5], [6,7,8]]) >>> a array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) >>> np.diag(a) array([0, 4, 8]) ## numpy.empty() で生成、領域の確保のみで初期化はされない >>> np.empty(5) array([ 1.06452759e-312, 1.06452759e-312, 1.00000000e+000, 1.00000000e+000, 2.37151510e-322]) ## ndarray.copy() で配列のディープコピー >>> a = np.array([1,2,3]) >>> b = a.copy() ## numpy.random モジュールの利用 ## numpy.random.randint() で整数乱数値を要素とした配列を生成 ## 生成する乱数の範囲(最小値、最大値)、要素数を指定 >>> np.random.randint(0,100,10) array([54, 68, 19, 57, 23, 27, 36, 99, 53, 70]) ``` ## 配列形状の変更 ndarrayオブジェクトは制限付きで配列形状の変更が可能です。 ※ ビュー(View:参照) NumPyの一部の操作で返されるオブジェクトはビュー(View)と呼ばれ、元データの参照となります。なので、ビュー内の値を変更すると元データも変更されるので注意してください。 ```py: ## 一次元配列 >>> a = np.arange(10) >> a array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) ## ndarray.reshape() で配列形状の変更 ## 二次元配列に変更、ビューを返す >>> b = a.reshape((2,5)) >>> b array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]) ## ビューの値を変更 >>> b[0] = 100 >>> b array([[100, 100, 100, 100, 100], [ 5, 6, 7, 8, 9]]) ## 元データも変更される >>> a array([100, 100, 100, 100, 100, 5, 6, 7, 8, 9]) ## 要素数が異なるとエラーとなる >>> a.reshape((2,3)) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: total size of new array must be unchanged ## ndarray.resize() で配列形状の変更(in-place) >>> a = np.arange(10) ## 要素数はそのままで二次元配列に変更、ビューは返さない >>> a.resize((2,5)) ## 元データの形状が変更される >>> a array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]) ## 生成後、一度でも変数を参照していると要素数(配列長)の変更は不可となる >>> a.resize(11) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: cannot resize an array references or is referenced by another array in this way. Use the resize function ## ただし refcheck オプションを False にすると参照チェックは行われない >>> a.resize(11, refcheck=False) >>> a array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]) ## ndarray.flatten() で一次元配列に変更、ビューではなくコピーを返す >>> a = np.arange(10).reshape((2,5)) >>> a array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]) >>> b = a.flatten() >>> b array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) ``` 返される配列がビューかコピーになるかはきちんと把握しておいた方がいいです。それと ndarray.reshape() はよく使うのでこれも覚えておくといいかと思います。 ## Indexing 配列要素の参照/代入、配列のスライスなどについて紹介します。基本的にPythonのリスト(list)とよく似た操作方法ですが、一部拡張構文があるので確認しておきます。 ```py: ## 二次元配列 (行列) >>> a = np.array([[0,1,2], [3,4,5], [6,7,8]]) >>> a array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) ## 要素の参照/代入 (リストと同じ) >>> a[2] array([7, 8, 9]) >>> a[1][2] 6 >>> a[1][2] = 1 >>> a[1][2] 1 ## 範囲外を指定するとIndexError >>> a[3] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: index out of bounds ## カンマ区切りで参照/代入 (拡張構文) ## a[行,列] >>> a[1,2] 6 ## インデックスに負数を指定すると末尾から数える >>> a[-1,-2] 8 >>> a[1,2] = 10 >>> a[1,2] 10 ## スライス (記法はリストと同じ、返り値はビュー) ## a[start:end:step] >>> a = np.arange(10) >>> a array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> a[1:8:2] array([1, 3, 5, 7]) >>> a[3:] ## a[3:10:1] array([3, 4, 5, 6, 7, 8, 9]) >>> a[:8] ## a[0:8:1] array([0, 1, 2, 3, 4, 5, 6, 7]) >>> a[::-2] ## a[-1:-10:-2] array([9, 7, 5, 3, 1]) ## 組み合わせてもOK >>> a = np.arange(25).reshape((5,5)) >>> a array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24]]) >>> a[:,3] = 10 ## a[0:5:1,3] = 10 (複数要素を一度に更新) >>> a array([[ 0, 1, 2, 10, 4], [ 5, 6, 7, 10, 9], [10, 11, 12, 10, 14], [15, 16, 17, 10, 19], [20, 21, 22, 10, 24]]) >>> a[:2,:3] = 100 ## a[0:2:1,0:3:1] = 100 >>> a array([[100, 100, 100, 10, 4], [100, 100, 100, 10, 9], [ 10, 11, 12, 10, 14], [ 15, 16, 17, 10, 19], [ 20, 21, 22, 10, 24]]) >>> a[2::2,::2] ## a[2:5:2,0:5:2] (省略すると逆に読みづらいケース) array([[10, 12, 14], [20, 22, 24]]) ``` ### Fancy Indexing 要素の位置や条件などでマスクした特殊なインデックス指定も可能です。 ```py: >>> a = np.arange(15,0,-1) >>> a array([15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]) ## 整数配列でインデックス指定、コピーを返す >>> a[[0,2,4,8]] array([15, 13, 11, 7]) ## bool型配列でインデックス指定、コピーを返す >>> n = a>10 >>> n array([ True, True, True, True, True, False, False, False, False, False, False, False, False, False, False], dtype=bool) >>> a[n] array([15, 14, 13, 12, 11]) ## インデックスに直接条件を指定してもOK >>> a[a>10] array([15, 14, 13, 12, 11]) ## 複数の条件がある場合はビット演算子を使う >>> a[(4<a) & (a<10)] array([9, 8, 7, 6, 5]) ## 値の代入もできる >>> a[a<8] = 0 >>> a array([15, 14, 13, 12, 11, 10, 9, 8, 0, 0, 0, 0, 0, 0, 0]) ``` ### インデックスの検索 ```py: ## numpy.argmax() で最大値要素の内で最も小さいインデックスを返す >>> a = np.tile(np.arange(3),3) >>> a array([0, 1, 2, 0, 1, 2, 0, 1, 2]) >>> np.argmax(a) 2 ## numpy.argmin() で最小値要素の内で最も小さいインデックスを返す >>> np.argmin(a) 0 ## numpy.nonzero() で非ゼロ要素のインデックス配列を返す >>> a = np.eye(3) >>> a array([[ 1., 0., 0.], [ 0., 1., 0.], [ 0., 0., 1.]]) >>> np.nonzero(a) ## 二次元配列なので二つの配列が返される (array([0, 1, 2]), array([0, 1, 2])) ## numpy.where() で条件に合致した要素のインデックス配列を返す >>> a = np.arange(15).reshape((3,5)) >>> a array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]]) >>> np.where(a%2==0) (array([0, 0, 0, 1, 1, 2, 2, 2]), array([0, 2, 4, 1, 3, 0, 2, 4])) ## numpy.select() で複数条件検索、インデックスに値をセット ## 第一引数: 条件の配列(bool配列) ## 第二引数: 条件に合致した要素のインデックスにセットする値の配列 >>> a = np.arange(10) >>> np.select([a<3, a>5], [a, a**2]) array([ 0, 1, 2, 0, 0, 0, 36, 49, 64, 81]) ``` ndarrayの内部構造はCの配列と似ていてもインタフェースはさすが高級言語ですね。 ただ、配列のスライス処理でNumPy固有の記法を使いすぎると読みづらくなるような気もするので、複数人で開発するときは節度ある使い方をした方が良さそうです。 ## 配列に対する操作/演算 前述の配列形状の変更以外にも様々な操作が可能です。ここでは紹介しきれないのでほんの一部だけ載せます。 ### 配列の結合/分割、軸操作 ```py: >>> a = np.arange(9).reshape((3,3)) >>> a array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) >>> b = np.arange(8,-1,-1).reshape((3,3)) >>> b array([[8, 7, 6], [5, 4, 3], [2, 1, 0]]) ## numpy.dstack() で二次元配列を結合して三次元配列にする >>> np.dstack((a,b)) array([[[0, 8], [1, 7], [2, 6]], [[3, 5], [4, 4], [5, 3]], [[6, 2], [7, 1], [8, 0]]]) ## numpy.hstack() で列方向に結合 >>> np.hstack((a,b)) array([[0, 1, 2, 8, 7, 6], [3, 4, 5, 5, 4, 3], [6, 7, 8, 2, 1, 0]]) ## numpy.vstack() で行方向に結合 >>> np.vstack((a,b)) array([[0, 1, 2], [3, 4, 5], [6, 7, 8], [8, 7, 6], [5, 4, 3], [2, 1, 0]]) ## numpy.dsplit() で三次元配列を分割 >>> a = np.arange(16).reshape(2,2,4) >>> a array([[[ 0, 1, 2, 3], [ 4, 5, 6, 7]], [[ 8, 9, 10, 11], [12, 13, 14, 15]]]) >>> np.dsplit(a,2) [array([[[ 0, 1], [ 4, 5]], [[ 8, 9], [12, 13]]]), array([[[ 2, 3], [ 6, 7]], [[10, 11], [14, 15]]])] ## numpy.hsplit() で列方向に分割 >>> a = np.arange(16).reshape(4,4) >>> a array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]]) >>> np.hsplit(a,2) [array([[ 0, 1], [ 4, 5], [ 8, 9], [12, 13]]), array([[ 2, 3], [ 6, 7], [10, 11], [14, 15]])] ## numpy.vsplit() で行方向に分割 >>> np.vsplit(a,2) [array([[0, 1, 2, 3], [4, 5, 6, 7]]), array([[ 8, 9, 10, 11], [12, 13, 14, 15]])] ## numpy.transpose() で配列を転置 >>> a = np.array([[1, 2], [3, 4]]) >>> a array([[1, 2], [3, 4]]) >>> np.transpose(a) array([[1, 3], [2, 4]]) ## ndarray.T でも良い >>> a.T array([[1, 3], [2, 4]]) ## numpy.swapaxes() で軸の交換 >>> a = np.array([[1,2,3]]) >>> np.swapaxes(a, 0, 1) array([[1], [2], [3]]) ``` ### 配列のソート ```py: ## numpy.sort() で配列をソートしてコピーを返す >>> a = np.random.randint(0,100,10) >>> a array([75, 24, 74, 49, 93, 18, 19, 85, 73, 90]) >>> np.sort(a) array([18, 19, 24, 49, 73, 74, 75, 85, 90, 93]) ## ndarray.sort() で配列を破壊的(in-place)にソートする >>> a.sort() >>> a array([18, 19, 24, 49, 73, 74, 75, 85, 90, 93]) ## 多次元配列の場合 >> a = np.array([[1,4,2],[9,6,8],[5,3,7]]) >>> a array([[1, 4, 2], [9, 6, 8], [5, 3, 7]]) >>> np.sort(a) array([[1, 2, 4], [6, 8, 9], [3, 5, 7]]) ## 軸を指定する (ここでは列でソート) >>> np.sort(a, axis=0) array([[1, 3, 2], [5, 4, 7], [9, 6, 8]]) ## 一次元配列にしてソート >>> np.sort(a, axis=None) array([1, 2, 3, 4, 5, 6, 7, 8, 9]) ``` ### 配列要素に対する演算 様々な配列要素に対する(element-wise)演算機能が提供されています。また、それらの機能の多くは各演算子を用いて利用することも可能です。 ```py: >>> a = np.array([1,2,3]) >>> b = np.array([4,5,6]) ## 加算 >>> a + b ## np.add(a,b) array([5, 7, 9]) ## 減算 >>> b - a ## np.subtract(b,a) array([3, 3, 3]) ## 乗算 >>> a * b ## np.multiply(a,b) array([ 4, 10, 18]) ## 除算 >>> b / a ## np.divide(b,a) array([4, 2, 2]) ## 剰余 >>> a % b ## np.mod(a,b) array([1, 2, 3]) ## 冪乗 >>> a ** b ## np.power(a,b) array([ 1, 32, 729]) ## 符号反転 >>> -a ## np.negative(a) array([-1, -2, -3]) ``` ## Broadcasting 前述の配列要素に対する各演算は同じサイズ/形状の配列同士で行っていましたが、NumPyではサイズ/形状の異なる配列同士の演算も可能で、これをブロードキャストを呼んでいます。 ```py: >>> a = np.array([1,2,3,4,5]) >>> b = np.array([10]) >>> a * b ## (1,5) * (1,1) = (1,5) array([10, 20, 30, 40, 50] >>> a = np.array([0,10,20,30]).reshape((4,1)) >>> a array([[ 0], [10], [20], [30]]) >>> b = np.array([0,1,2]) >>> a + b ## (4,1) + (1,3) = (4,3) array([[ 0, 1, 2], [10, 11, 12], [20, 21, 22], [30, 31, 32]]) ## ブロードキャストできない例 >>> b = np.array([[1,2],[3,4]]) >>> a + b Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: operands could not be broadcast together with shapes (4,1) (2,2) ``` 2つめの例は面白いですね。ブロードキャスト機能は上手く使いこなせばコード量を大きく減らすことができそうです。 ## 配列の走査 配列の走査はリスト(list)と同様に for in 構文を使って行うことができます。 ```py: >>> a = np.arange(9).reshape((3,3)) >>> a array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) ## リスト(list)と同様に for in で走査できる ## ここでは行毎に走査 >>> for row in a: ... print row ... [0 1 2] [3 4 5] [6 7 8] ## numpy.ndenumerate() で多次元配列の走査 ## enumerate() と同様にインデックスも取得できる >>> for i, value in np.ndenumerate(a): ... print i, value ... (0, 0) 0 (0, 1) 1 (0, 2) 2 (1, 0) 3 (1, 1) 4 (1, 2) 5 (2, 0) 6 (2, 1) 7 (2, 2) 8 ## ndarray.flat で一次元配列にして走査 >>> for value in a.flat: ... print value ... 0 1 2 3 4 5 6 7 8 ``` ## 統計関数 NumPyでは基礎的な統計処理用の関数群が提供されています。 ```py: >>> a = np.random.randint(0,500,20) >>> a array([260, 253, 185, 240, 252, 375, 63, 413, 293, 431, 207, 230, 288, 462, 270, 477, 451, 58, 408, 492]) >>> np.amax(a) ## 最大値 492 >>> np.amin(a) ## 最小値 58 >>> np.ptp(a) ## 値の範囲(最大値-最小値) 434 >>> np.mean(a) ## 算術平均 305.39999999999998 >>> np.median(a) ## 中央値 279.0 >>> np.std(a) ## 標準偏差 125.73519793597973 >>> np.var(a) ## 分散 15809.34 >>> b = np.random.randint(0,500,20) >>> b array([313, 117, 167, 353, 468, 289, 177, 196, 20, 70, 235, 280, 480, 125, 195, 271, 253, 55, 49, 354]) >>> np.corrcoef(a,b) ## 相関係数 array([[ 1. , 0.05950681], [ 0.05950681, 1. ]]) >>> c = np.random.randint(0,10,20) >>> c array([3, 8, 7, 9, 1, 8, 4, 0, 8, 3, 9, 4, 2, 1, 4, 3, 0, 4, 8, 4]) >>> np.histogram(c) ## ヒストグラム (array([2, 2, 1, 3, 5, 0, 0, 1, 4, 2]), array([ 0. , 0.9, 1.8, 2.7, 3.6, 4.5, 5.4, 6.3, 7.2, 8.1, 9. ])) ``` より高度な統計処理を行いたい場合は SciPy を利用することになると思います。 ## ファイル入出力 配列データのファイル入出力もできます。実験作業などではよくお世話になる機能です。 ```py: >>> a = np.array([[1,3,2], [4,6,5]]) >>> a array([[1, 3, 2], [4, 6, 5]]) ## numpy.savetxt() で配列データをASCII形式でファイルに書き出す >>> np.savetxt('data.txt', a) ## デリミタはオプションで自由に指定できる (TSV形式) >>> np.savetxt('data.txt', a, delimiter='\t') ## ASCII形式なのでファイルの中身は人間にも読める $ cat data.txt 1.000000000000000000e+00 3.000000000000000000e+00 2.000000000000000000e+00 4.000000000000000000e+00 6.000000000000000000e+00 5.000000000000000000e+00 ## numpy.loadtxt() でテキストファイルから配列データを読み込む >>> np.loadtxt('data.txt', delimiter='\t') array([[ 1., 3., 2.], [ 4., 6., 5.]]) ## numpy.save() で配列データをバイナリ形式でファイルに書き出す ## 出力ファイルの拡張子は .npy となる >>> np.save('data', a) ## numpy.load() でバイナリファイルから配列データを読み込む >>> np.load('data.npy') array([[1, 3, 2], [4, 6, 5]]) ## numpy.savez() で複数の配列データをバイナリ形式でファイルに書き出す >>> a, y = np.meshgrid([1,2,3], [4,5,6,7]) >>> a array([[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]) >>> y array([[4, 4, 4], [5, 5, 5], [6, 6, 6], [7, 7, 7]]) ## 出力ファイルの拡張子は .npz となる >>> np.savez('data', x=x, y=y) ## .npy, .npzファイル共に numpy.load() で読み込み可能 >>> npz = np.load('data.npz') >>> npz.files ['y', 'x'] >>> npz['x'] array([[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]) >>> npz['y'] array([[4, 4, 4], [5, 5, 5], [6, 6, 6], [7, 7, 7]]) ``` 巨大なデータを保存するときは、バイナリ形式で保存した方がファイルサイズが小さくなるので良いかと。 今回はNumPyの基礎について整理してみました。僕自身はNumPyを単体で利用することはほとんどないのですが、OpenCVのPythonバインディングを利用するときによくお世話になっています。NumPyは機能が豊富なため、必要に応じてその都度公式リファレンスを参照しながら使っていく必要がありそうです。 |
|
| 917位 |
|
|||
|
18:07:28 |
(University of Tokyo 所属) |
|
Step 0:
------- Macに[Homebrew](http://mxcl.github.com/homebrew/)が入っていない場合はインストールする Step 1: ------- highlightをインストールする ``` brew install highlight ``` (LuaとBoostもついでにインストールされます) Step 2: ------- ソースコードをハイライトする。 ``` highlight -O rtf myfile.php | pbcopy ``` このコマンドによってmyfile.phpの中身がRTF(リッチテキスト)形式でハイライトされ、結果がそのままクリップボードにコピーされます。 Step 3: ------- Keypointに貼り付ける。高さ・幅、フォントサイズなども自由にいじれます。めでたしめでたし。 (パワーポイントは強調・斜体などは反映されるが色は反映されない?) #元ネタ [Code highlighting for Keynote presentations](https://gist.github.com/jimbojsb/1630790) |
|
| 918位 |
|
|||
|
09:56:51 |
(XFLAG STUDIO, mixi, Inc. 所属) |
|
軽いメモ程度で。
※WireShark 2.0以降、GtkからQtへ変わり、XQuartzは不要になった模様。 ## Wireshark http://www.wireshark.org/download.html から OS X 10.6 and later Intel 64-bit .dmg のファイルをダウンロード、インストール もしくは、brewでインストール。 ``` brew cask install wireshark ``` ## Wiresharkのフィルタサンプル #### TCP portが80番 ``` tcp.port == 80 ``` #### TCP source portが80番 ``` tcp.srcport == 80 ``` #### TCP src addressが192.168.0.1 ``` ip.src == 192.168.0.1 ``` #### TCP dest addressが8.8.8.8 ``` ip.dst == 8.8.8.8 ``` #### TCP port 80かつsrc addressが192.168.0.1 ``` tcp.port == 80 and ip.src == 192.168.0.1 ``` #### TCP SYNのみ ``` tcp.flags.syn == 1 ``` #### TCP ACKのみ ``` tcp.flags.ack == 1 ``` #### HTTP host指定 ``` http.host == hogehoge.com ``` ### ICMPパケット ``` icmp ``` |
|
| 919位 |
|
|||
|
18:45:02 |
|
|
#はじめに
この記事は、MMD4Mecanim をお借りしてUnityにMMDモデルを持ってくるデモ的な記事です。 公式のチュートリアル(非常に丁寧で、この記事より圧倒的に読みやすい)や、 Unity仮面氏の動画([How to create Oculus VR Contents](https://www.youtube.com/watch?v=Kaum5g4jgns))等と 多くを重複する内容となっています。 この記事はQiitaに似た記事が無いようなので作成したものです。 MMD4Mecanimについての詳細は公式サイト([Stereoarts Homepage](http://stereoarts.jp/))をご参照下さい。 #デモ こうなります。  #MMD4Mecanimとは? Nora( [@Stereoarts](https://twitter.com/Stereoarts))氏が公開しているMMDモデルをUnityに持ち込むためのツールです。 データは氏のWebサイト、 [Stereoarts Homepage](http://stereoarts.jp/)からDLが可能となっています。 この記事からMMD4Mecanimを初めてご利用になられる方は、公式のチュートリアルに必ず目を通し、 規約等を遵守して頂きますよう宜しくお願いいたします。 #MMD4Mecanimを使ってMMDモデルがFBXに変換されるまで。 [Stereoarts Homepage](http://stereoarts.jp/)に用意されているチュートリアルを参考にするのが最も良いです。 チュートリアルはツールに同封されていますし、ウェブからも閲覧可能です。 以下の記事はMMD4Mecanimの紹介ということで参考程度にご利用下さい。 ##Unityの起動 画像はUnityPro試用版ですが通常版でも問題ありません。 レイアウトは2by3に変更しています。  ##MMD4MecanimのDL まず [Stereoarts Homepage](http://stereoarts.jp/)からMMD4MecanimをDLしてきます。  ##MMD4Mecanimのimport 配布形式がunitypackageになって便利になりました。 これをダブルクリックでimportします。  importされました。  ##モデル等のDL チュートリアルに沿って同一のモデルとモーションをお借りしてきます。 ①Tda式初音ミク・アペンド Ver1.00 作者の Tda さんのホームページ http://u16hp.blog134.fc2.com/ 配布ページ http://bowlroll.net/up/dl4576 ②WAVEFILE fullver. モーション 作製hino氏 http://www.nicovideo.jp/watch/sm18065127 配布ページ http://bowlroll.net/up/dl5983 ③曲はゴジマジPのWAVEFILEとなります。 http://www.nicovideo.jp/watch/sm14257396 ①②③をお借りして来たら、Projectにドラッグ&ドロップしますと次のようになります。  ##PMX2FBXを行う MMD4Mecanimをinportしていることによって、 Tda式初音ミク・アペンド_Ver1.00.MMD4Mecanimが生成されているはずです。 これをクリックしてInspectorを開きます。  モーションファイルをInspectorのVMDにドラッグ&ドロップします。  Processで変換を開始します。  変換が終わると、モデルのPrefabが出来ています。  #モデルをUnity上で動かすまで Prefabをドラッグ&ドロップします。  メニューバーから、GameObject > Create Other > Directional Light を選択し、 Directional Light を配置してシーンを明るくします。 モデルにモーションをつけるためにAnimator Controller を作製します。 Projectウィンドウから Create > Animator Controller を選択します。  モデルを選択してInspectorを表示し、 作製したAnimator Controller をControllerにドラッグ&ドロップします。  Animator Controller をダブルクリックすると シーンがAnimatorに切り替わります。  Prefabの中にあるモーションをAnimatorにドラッグ&ドロップします。  Main Cameraを選択して調整します。  再生を押すとモーションが行われます。  #終わりに ここまでで公式のチュー トリアル(基本編)http://stereoarts.jp/ を簡易的に終わらせた状態になります。 もしこの記事で初めてMMDモデルをUnityで動かすのに成功した方は、 是非、チュー トリアル(応用編)、モーフ拡張、オーディオ同期と進んでみて下さい。 またチュー トリアル(基本編)には、この記事より詳しく基本的な内容が書かれていますのでこちらもご覧下さい。 |
|
| 920位 |
|
|||
|
15:59:23 |
(Viibar 所属) |
|
2つのブランチをマージした際にコンフリクトしたとして、どちらかだけを全面的に適用したい場合はどうするか。
以下のように、`checkout --ours` と `checkout --theirs` を使い分ければ良い。 ``` # 2つのブランチ間でコンフリクトしているファイル fileA.txt と fileB.txt があるとする # fileA.txt を現在チェックアウトしているブランチ側の対応に合わせる場合 $ git checkout --ours fileA.txt $ git add fileA.txt # add を忘れずに # fileB.txt をマージさせたブランチ側に合わせる場合 $ git checkout --theirs fileB.txt $ git add fileB.txt $ git commit ``` |
|
| 921位 |
|
|||
|
20:47:29 |
(Akatsuki Inc. 所属) |
|
##概要 今更ながら本格的にRailsを触る事になったのでメモ。 Ruby on Rails Tutorialがとても良かったので、 そこで学んだエッセンスを自分なりに整理してみる。 Ruby on Rails Tutorial(chapter2) http://railstutorial.jp/chapters/a-demo-app?version=4.0#top ##リソースとは データモデルとwebインターフェイスが組み合わさったもの。 - データモデルとは 現実世界のあるモノを分類/抽象化して落とし込んだカタマリ (より具体的にいえば(今回の文脈では)「RDBMSに則って設計されたデータ群」と同義) ##### 「User」を表すデータモデルのサンプル |column|type| |---|---| |id|integer| |name|string| |email|string| - webインターフェイスとは データモデルをwebで取り扱えるようにしたもの - つまりリソースとは ####HTTPプロトコル経由で自由にCURD(作成/読み出し/更新/削除)できる(分類された)データ群 とみなすことができる。 なお、リソースはRESTという概念(後述)に基いている。 ##リソースを生成するコード ```sh $ rails generate scaffold User name:string email:string $ rake db:migrate #マイグレーションファイルを実行(dbの作成) ``` ##リソースに割り当てられるURL ユーザーのURLをUsersリソースで使用するコントローラアクションに 割り当てる(マッピングする)コード。 ```config/routes.rb DemoApp::Application.routes.draw do resources :users ~ end ``` このコードは、 URLとアクションの組み合わせ↓を効率的に作成する。 Usersリソースにおける、ページとURLの関係。 |URL| アクション| 用途| |-----------|------------|------------| |/users| index| すべてのユーザーを一覧するページ| |/users/1| show| id=1のユーザーを表示するページ| |/users/new| new| 新規ユーザーを作成するページ| |/users/1/edit| edit| id=1のユーザーを編集するページ| ##RailsのControllerの構成とそれを支えるREST Scaffoldで作成されるControllerに含まれるアクションは以下7つ。 |HTTP Request|アクション| |---|---| |GET|index、show、new、edit| |other| create、update、destroy| index、show、new、editアクションはいずれも「ページ」に対応するが、 それ以外にもcreate、update、destroyアクションがある。 通常、これらのアクションは、ページ出力せずにDB上の情報を操作する (ケースに応じてページを出力することもある)。 これらは全て「REST」に基づく。 ##RESTとは RESTとは、アプリケーションを構成するコンポーネント(Userなど)を、 ・RDBMSのCRUD(Create/Read/Update/Delete) ・HTTPRequestの各メソッド(GET/POST/PUT/DELETE) に対応させて、自由に作成/読み出し/更新/削除できるもの(リソース)として扱うアーキテクチャ。 ####Usersリソースに含まれるRESTfulなルーティング一覧 |HTTP Request| URL| アクション| 用途| |-----------|------------|------------|------------| |GET| /users| index| すべてのユーザーを表示するページ| |GET| /users/1| show| id=1のユーザーを表示するページ| |GET| /users/new| new| ユーザーを新規作成するページ| |POST| /users| create| ユーザーを作成するアクション| |GET| /users/1/edit |edit| id=1のユーザーを編集するページ| |PATCH| /users/1 |update| id=1のユーザーを更新するアクション| |DELETE| /users/1| destroy| id=1のユーザーを削除するアクション| URLには重複しているものが存在する。 たとえば、showアクションと updateアクションは、 どちらも/users/1というURLに対応している。 これらのアクション同士の違いは、それらのアクションに 対応するHTTP requestメソッドの違いでもある。 ##もう一つリソースを実装してみる ####「Microposts」を表すデータモデルsample |column|type| |---|---| |id|integer| |content|string| |user_id|integer| ``` $ rails generate scaffold Micropost content:string user_id:integer $ rake db:migrate #マイグレーションファイルを実行(dbの作成) ``` microposts用のルーティングルールが追加された。 ```config/routes.rb DemoApp::Application.routes.draw do resources :microposts resources :users ~ end ``` ##バリデーション(文字数制限)を入れてみる ```app/models/micropost.rb class Micropost < ActiveRecord::Base validates :content, length: { maximum: 140 } end ``` ##リレーションを付ける ```app/models/user.rb class User < ActiveRecord::Base has_many :microposts end ``` ```app/models/micropost.rb class Micropost < ActiveRecord::Base belongs_to :user validates :content, length: { maximum: 140 } end ``` ##Rails consoleを使って動作チェック ```sh $ rails console #Userモデルから任意のデータ(今回はid=2)を抽出、@testuserに代入 irb(main):006:0> @testuser = User.find(2) => #<User id: 2, name: "RailsBeginner", email: "rails@test.com", created_at: "2013-11-09 08:30:21", updated_at: "2013-11-09 08:30:21"> #@testuserが持っている(=リレーションのある)micropostsデータを閲覧 irb(main):007:0> @testuser.microposts => #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 2, content: "rails post test", user_id: 2, created_at: "2013-11-09 08:30:48", updated_at: "2013-11-09 08:30:48">]> irb(main):008:0> exit ``` 便利。 ##ついでにHerokuにデプロイ ``` $ heroku create $ git push heroku master $ heroku run rake db:migrate ``` http://afternoon-wildwood-5636.herokuapp.com/users http://afternoon-wildwood-5636.herokuapp.com/microposts ##以下に続く [Rails] RSpecによるBDD(振舞駆動開発)の基本 [SporkとGuardも] http://qiita.com/kidachi_/items/cb8910eb74e924456df9 |
|
| 922位 |
|
|||
|
00:24:40 |
|
|
joinsを使ってeager loadingをする方法です。
その前提の話として + N + 1 問題 + eager lodingとは + includesの欠点 + joins と長くなってしまいましたが、そんなん知ってるという方は最後の + joinsを使ってeager loadingをする方法 そこだけ見てください。 # N + 1 問題 ```ruby class Guild < ActiveRecord::Base has_many :users end class User < ActiveRecord::Base belongs_to :guild end ``` というモデルがあった時 ```ruby users = User.all users.each { |user| puts user.guild.name } ``` みたいなことをした時に(適当ですが) ```mysql SELECT `users`.* FROM `users` SELECT `guilds`.* FROM `guilds` WHERE `guilds`.`id` = 1 LIMIT 1 SELECT `guilds`.* FROM `guilds` WHERE `guilds`.`id` = 2 LIMIT 1 SELECT `guilds`.* FROM `guilds` WHERE `guilds`.`id` = 3 LIMIT 1 ``` `users.all`で一回。 `users.each { |user| puts user.guild.name }`で`users`の数の分だけSQLが発行されます。 これが N + 1 問題です。この問題を回避するのが eager lodingです # eager loadingとは ```ruby users = User.includes(:guild) ``` 上記は以下のSQLを発行されます。 ```mysql SELECT `users`.* FROM `users` SELECT `guilds`.* FROM `guilds` WHERE `guilds`.`id` IN (1, 2, 3) ``` `includes`を使用することで2つのSQLを発行し、`users`と`guilds`の両方を一気に読み込みます。 そのため、`users.each { |user| puts user.guild.name }`では一度もSQLを発行することなく処理ができます。 N + 1だったクエリ数が2回になります。この仕組みがeager loadingです。 # includesの欠点 eager loadingはパフォーマンス向上のために必要です。 しかし、`includes`には問題があります。 ```ruby User.includes(:guild).where("guilds.id IN (1, 2, 3)") ``` というように`where`句で`includes`で読み込んだテーブルを条件にしようとしたとき ```mysql SELECT `users`.`id` AS t0_r0, `users`.`guild_id` AS t0_r1, `users`.`name` AS t0_r2, `guilds`.`id` AS t1_r0, `guilds`.`name` AS t1_r1 FROM `users` LEFT OUTER JOIN `guilds` ON `guilds`.`id` = `users`.`guild_id` WHERE (guilds.id IN (1, 2, 3)) ``` というSQLになります。発行されるのは`LEFT JOIN`なので`guilds`が存在しない時でも`users`は全部ロードされます。 意図通りであれば問題なのですが、基本的にはこういう場合`joins`を使うことが推奨されています。 ### rails4 rails4では`references`を明示的に指定するようになってます。(らしい) http://qiita.com/joker1007/items/e951d0cf98309dba90db ```ruby User.includes(:guild).where("guilds.id IN (1, 2, 3)").references(:guilds) ``` # joins http://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-eager-loaded-associations 関連するテーブルを読み込み`where`句で関連テーブルを条件にするときは`includes`ではなく`joins`が推奨されています。 ```ruby User.joins(:guild).where("guilds.id IN (1, 2, 3)") ``` ```mysql SELECT `users`.* FROM `users` INNER JOIN `guilds` ON `guilds`.`id` = `users`.`guild_id` WHERE (guilds.id = 1,) ``` こちらは`INNER JOIN`なので先ほどの`includes`の問題がありせん。 しかし、eager loadingが効いていないのです。 `users.each { |user| puts user.guild.name }`を実行した時に ```mysql SELECT `guilds`.* FROM `guilds` WHERE `guilds`.`id` = 1 LIMIT 1 SELECT `guilds`.* FROM `guilds` WHERE `guilds`.`id` = 2 LIMIT 1 SELECT `guilds`.* FROM `guilds` WHERE `guilds`.`id` = 3 LIMIT 1 ``` というSQLが発行されてしまい、N + 1問題が起きます。 # joinsを使ってeager loadingをする方法 そこで、joinsを使ってeager loadingをする方法です。 `select`を使います。 ```ruby User.joins(:guild) .where("guilds.id IN (1, 2, 3)") .select("users.*, guilds.name AS hoge") ``` 次のSQLが発行されます。 ```mysql SELECT users.*, guilds.name AS hoge FROM `users` INNER JOIN `guilds` ON `guilds`.`id` = `users`.`guild_id` WHERE (guilds.id IN (1, 2, 3)) ``` もちろんですが、`SELECT users.*, guilds.name AS hoge`となり、`users.*`と`guilds.name`がロードされます。 ロードしておきたい`guilds`のカラムがたくさんあればその分`select`の中に書く必要があります。 ロードしたデータには`user.guild.hoge`や`user.guild.name`ではなく`user.hoge`という風にアクセスします。 ```ruby users.each { |user| puts user.hoge } ``` `user.guild.hoge`はもちろんエラーになります。 `user.guild.name`では`guild`のロードが実行されてしまってN + 1問題になってしまいます。 |
|
| 923位 |
|
|||
|
19:50:53 |
(10mado, LLC 所属) |
|
iPhoneからWebでHTMLのフォームから写真をアップロードしてもらったときなど、画像が回転して表示されてしまう場合があるようです。原因はExifのOrientationの値が「1」以外になっていること。
画像の向きの情報がExifに埋め込まれているけれど、ファイル自体は回転していない(=撮影した向きになっていない)ために表示がおかしくなるのです。 ブラウザーの仕様によりますが、ExifのOrientationの値を無視して、ファイルを表示しようとすると撮影した向きとは合わない表示になってしまうことになります。 解決するには、アップロードされたときにExifのOrientationを見て、サーバー側で画像を回転させてしまうのがオススメ。回転して、ExifのOrientationの値を正しい向きに補正します。 以下、PHPのimagick拡張を使った場合の例。 ```php: <?php $imagick = new \Imagick(); $imagick->readImage('test.jpg'); $format = strtolower($imagick->getImageFormat()); if ($format === 'jpeg') { $orientation = $imagick->getImageOrientation(); $isRotated = false; if ($orientation === \Imagick::ORIENTATION_RIGHTTOP) { $imagick->rotateImage('none', 90); $isRotated = true; } elseif ($orientation === \Imagick::ORIENTATION_BOTTOMRIGHT) { $imagick->rotateImage('none', 180); $isRotated = true; } elseif ($orientation === \Imagick::ORIENTATION_LEFTBOTTOM) { $imagick->rotateImage('none', 270); $isRotated = true; } if ($isRotated) { $imagick->setImageOrientation(\Imagick::ORIENTATION_TOPLEFT); } } ``` ちなみにExifのOrientationのテストをするには http://www.galloway.me.uk/2012/01/uiimageorientation-exif-orientation-sample-images/ にあるようなテスト画像を用意するのがいいかも。 ## 2016年9月9日追記 [imagick拡張のバージョン3.4.1](https://pecl.php.net/package-info.php?package=imagick&version=3.4.1)から、Imagickクラスに`autoOrient`メソッドが実装されました。これはImageMagickの`convert`コマンドに[`auto-orient`オプション](http://www.imagemagick.org/script/command-line-options.php#auto-orient)を付けて実行するのと同じ効果をもたらします。 つまり、上で記述したのと同じ内容がこう書けるようになりました。 ```php: <?php $imagick = new \Imagick(); $imagick->readImage('test.jpg'); $imagick->autoOrient(); ``` 簡単ですね。PHPも拡張も、できるだけ新しいバージョンを使うことをおすすめします。 |
|
| 924位 |
|
|||
|
22:57:54 |
|
|
 JSONを返すようなAPIを作っているとき、いつもは単に目で追うだけだったり、あるいはブラウザの拡張機能とかで誤魔化していましたが、Vimとjqで扱うようにしてからとても捗っています。 # jqで整形する jqという、コマンドラインで使えるJSONパーサーがあります。 - [jq](http://stedolan.github.io/jq/) Vimからjqを使うのに、次のようなコマンドをvimrcに定義しました。 ```vim command! -nargs=? Jq call s:Jq(<f-args>) function! s:Jq(...) if 0 == a:0 let l:arg = "." else let l:arg = a:1 endif execute "%! jq \"" . l:arg . "\"" endfunction ``` バッファ内の文字列全体に対してjqを実行し、変換後の文字列で置き換えます。 例えば、次のようなJSONがあったとして、 {"obj":{"list":[1,1,2,3,5,8,13]},"jstr":"\u3053\u3093\u306B\u3061\u306F\u4E16\u754C","str":"helloworld","num":1} 「:Jq」と実行 ```js { "num": 1, "str": "helloworld", "jstr": "こんにちは世界", "obj": { "list": [ 1, 1, 2, 3, 5, 8, 13 ] } } ``` 「:Jq .obj.list」と実行 ```js [ 1, 1, 2, 3, 5, 8, 13 ] ``` なんて風になります。 ばっちりUnicodeエスケープシーケンスの変換が行われて日本語が表示されていますし、色々加工も出来ちゃいます。 欠点はもちろんjqに依存してしまうことですが、普段から良くJSONに触れている方ならjqは入れておいて損はないと思いますよ! jqのフィルタ機能については、以下のブログがとても参考になります! - [jqコマンドが実は高性能すぎてビビッた話 - beatsync.net](http://beatsync.net/main/log20130428.html) # vim-jsonを入れてJSONファイルをより見やすくする JSONを表示するのに、filetypeをjavascriptにしてもいいのですが、どうせならJSON用のfiletypeプラグインを入れてしまうのをオススメします。 - [elzr/vim-json · GitHub](https://github.com/elzr/vim-json) ↑のgithubのページにスクリーンショットがありますが、filetypeをjavascriptにしたときよりも色分けされるようになりますし、文字列を囲う「"」も省略されて表示されるので、とても見易いです # jqの代わりにpythonで整形する - [Vim (with python) で json を整形 #Python #Vim #JSON - Qiita [キータ]](http://qiita.com/items/cc29b414a63e08cd4f89) 自分がVimでJSONを整形したいと思ったのも、この投稿がきっかけです。 もし、開発環境にjqを入れれない場合でも、Pythonならだいたい入っているのでこれで代用するのもありだと思います。 |
|
| 925位 |
|
|||
|
17:30:25 |
|
|
Jenkinsを使ってAndroidアプリのテスト環境を構築する。今更感がある内容だけど、ひとまとめになった情報がなかったので、メモも兼ねてココにまとめておく。構築した感想としては、Jenkinsにまともに触るのがはじめてな上に、Androidアプリ開発の事情も加わって、知識ゼロから構築するのは意外と大変だったということ。
# 前提条件 前提条件は次のとおり。 - Jenkinsサーバはローカルネットワークに用意 - ソースコードは git & [bitbucket.org](https://bitbucket.org)のプライベートリポジトリで管理 - EclipseでのAndroidアプリのプロジェクトは、アプリ本体とテストコードの2つを用意 - Jenkinsでのビルド&テストは、Androidのバージョンや解像度別に行う # 構築環境環境 主なソフトウェアの種類とバージョンは次の通り。 - Ubuntu 12.04(64bit) Desktop - Jenkins 1.514 - Android SDK r21 - Oracle JDK6 # Androidアプリのプロジェクトの下準備 アプリ開発は、通常ローカルPCで行っているはず。で、Jenkinsのサーバーを立ち上げる前に、まずローカルで設定しておかなきゃいけないことがあるので、まずはそれから。 今回は、以下のようなフォルダ構成で行う。Eclipseでプロジェクト&テストを生成した際の構成そのまま。つまり、同一ディレクトリに、アプリケーションのプロジェクトと、アプリテストのプロジェクトがある。 - foo/jenkins_smpapp1 - foo/jenkins_smpapp1test Jenkinsは、antを使ってビルドする。そのため、antのビルドで必要なbuild.xmlファイルが必要となるのだけど、そのファイルはandroid sdkのツールで生成可能。では、さっそく作ってみる。 $ android update project -p ./jenkins_smpapp1 $ android update test-project -m ../jenkins_smpapp1/ -p jenkins_smpapp1test らくちんですな。これでbuild.xmlファイルができたので、早速ターミナルからビルドする。 $ cd jenkins_smpapp1 $ ant clean debug install つづいてテストを実行してみる。実行する前には、実機の接続 or エミュレータ起動をお忘れ無く。 $ cd ../jenkins_smpapp1test $ ant clean debug install test これでテストが動作したらOK。動作が確認できればbitbucket.orgのプライベートリポジトリにgitを使って登録しておく。なお、このサンプルは、[jenkins_smpapp1](https://bitbucket.org/demuyan/jenkins_smpapp1)、[jenkins_smpapp1test](https://bitbucket.org/demuyan/jenkins_smpapp1test)で公開しているので、気軽に試したい方は、そちらをforkしてもらうとよいかと。 # jenkinsサーバーを構築する ここからJenkinsサーバーを構築していく。Ubuntu 12.04 Desktopのインストール直後の状態からはじめる。 ## Oracle JDK6をインストールする (OpenJDKではなく)Oracle JDK6をインストールする。インストールする方法はいくつかあるけど、一例として[Oracleが配布してるJDK6をUbuntuにインストールする方法](http://blog.cnu.jp/2012/03/18/install-jdk6-oracle/)を紹介しておく。 ## Jenkinsをインストール JenkinsはUbuntuの標準パッケージではなく、Jenkinsの運営団体(と呼べばいいのか?)から提供されているUbuntuのパッケージをインストールしてみた。Ubuntuで管理されているものよりは新しいものが来るはずなので。以下の手順は[Installing Jenkins on Ubuntu - Jenkins - Jenkins Wiki](https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins+on+Ubuntu)より。 $ wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add - $ sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list' $ sudo apt-get update $ sudo apt-get install jenkins jenkinsにアクセスは、http://localhost:8080/ でアクセス可能。 インストール後、ユーザー"jenkins"が追加される(ホームディレクトリは/var/lib/jenkins)。この後、SSHの設定やAndroid SDKのインストールするんだけど、そういったjenkinsが関係するソフトウェアや設定は、ユーザー"jenkins"に対して行うということは忘れないように。 ## Javaの切り替え Jenkinsをインストールすると、Jenkinsの他にOpenJDK 7やOpenJREがインストールされ、デフォルトで使用する設定となる。これだとAndroidアプリ開発ではトラブルの元となりがちなので、Oracle JDKがデフォルトとなるよう設定しておく。切り替えには、update-alternativesコマンドを利用する。 $ sudo update-alternatives --config java 選択肢 パス 優先度 状態 ------------------------------------------------------------ * 0 /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java 1061 自動モード 1 /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java 1061 手動モード 2 /usr/lib/jvm/java-6-oracle/bin/java 1 手動モード 現在の選択 [*] を保持するには Enter、さもなければ選択肢の番号のキーを押してください: <- ここで2をOracle JDKを選択する ## Android SDKのインストール Android SDKをインストールする。ユーザー"jenkins"がアクセスできるディレクトリなら、どこにインストールしてもよい。ここでは、Jenkinsのホーム以下にインストールした(つまり/var/lib/jenkins/android-sdkにAndroid SDKをインストールした) インストール方法の詳細については省略ということで。 ## 必要なパッケージをインストールする Ubuntuのパッケージからgitとantをインストールする。本来は環境変数ANT_HOMEの設定が必要だが、Jenkinsの設定でも指定できるので、ここでは設定しない。 $ sudo apt-get install git-core ant ## sshの設定 つぎにsshの設定を行う。これは、jenkinsがbitbucket.orgのプライベートリポジトリにアセクスため。ま、bitbucket.orgに限らず、リモートでgitのリポジトリを制御するには必須の設定となる。 ここでは、SSHの基本的な話は省略するので、詳しく知りたい人はSSHでググってくださいませ。これらSSHの設定や鍵は、ユーザー"jenkins"でログインした際に有効になっている必要がある。まず、ユーザー"jenkins"で利用する鍵を生成する。 $ sudo -u jenkins ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/var/lib/jenkins/.ssh/id_rsa): Created directory '/var/lib/jenkins/.ssh'. Enter passphrase (empty for no passphrase): <-パスフレーズを聞かれるが、Enterを押してスキップする Enter same passphrase again: Your identification has been saved in /var/lib/jenkins/.ssh/id_rsa. Your public key has been saved in /var/lib/jenkins/.ssh/id_rsa.pub. ちなみに、"sudo -u jenkins"ってのは、jenkinsの権限でコマンドを実行するための方法。こうすることで、jenkinsアカウントにログインせずに処理できるので、何かと便利。 鍵が生成された直後は、次のファイル構成になっているはず。 $ sudo -u jenkins ls -la /var/lib/jenkins/.ssh total 16 drwx------ 2 jenkins nogroup 4096 May 9 11:20 . drwxr-xr-x 9 jenkins adm 4096 May 9 11:20 .. -rw------- 1 jenkins nogroup 1679 May 9 11:20 id_rsa -rw-r--r-- 1 jenkins nogroup 396 May 9 11:20 id_rsa.pub この鍵を使って、bitbucket.orgのリポジトリにアクセスするためには、この公開鍵(id_rsa.pub)は、bitbucket.orgに登録しなきゃいけない。ということで、登録しておく。  公開鍵を設定したあとは、一度bitbucket.orgにアセクスして、サイトを認証(.ssh/known_hostファイルを生成)する必要がある。この時点ではファイルの取得までする必要はない(といいつつ、git cloneしちゃってるけど) $ sudo -u jenkins git clone git@bitbucket.org:demuyan/jenkins_smpapp1.git /var/lib/jenkins/tmp Cloning into '/var/lib/jenkins/tmp'... The authenticity of host 'bitbucket.org (207.223.240.181)' can't be established. RSA key fingerprint is 97:8c:1b:f2:6f:14:6b:5c:3b:ec:aa:46:46:74:7c:40. Are you sure you want to continue connecting (yes/no)? yes <- ここで yes と入れる Warning: Permanently added 'bitbucket.org,207.223.240.181' (RSA) to the list of known hosts. 続きは、[Jenkins設定編](http://qiita.com/items/366734f7e893ea6d0ab4)へ |
|
| 926位 |
|
|||
|
14:43:41 |
(heymeoto 所属) |
|
俺的ハマリポイントをつらつらと記録していきます。 ハマり次第追記していきます。 適宜ツッコミをよろしくお願いいたします! ------------------------------------ # invalid multibyte char (US-ASCII) (SyntaxError) これは、ソースファイルに日本語が含まれる場合のエラー。 該当ソースファイルの先頭に以下のおまじないを追加 ```ruby: # -*- coding: utf-8 -*- ``` [Ruby 1.9上のRailsでinvalid multibyte char (US-ASCII)の対処](http://d.hatena.ne.jp/kaorumori/20110623/1308848676) ------------------------------------ # odd number list for Hash (SyntaxError) このエラーがSyntaxエラーが原因じゃないときは、Rubyのバージョンを疑った方がいい。 [これからRubyを始めるためにやったことメモ3(miからRubyの最新版が呼べない)](http://d.hatena.ne.jp/honeniq/20120415/1334500216) ``` $ rvm use 1.9.2 --default ``` 1.8.xの時にエラーが出てた。なので新しいバージョンにセットしてやる。(なぜか戻っちゃうことがあるんだけどなぜ?) ------------------------------------ # NameError - uninitialized constant Object::Something これは、Rubyの規則があるらしくって、「モデルの名前は単数系にするべきだという、Railsの規約を守っていなかっただけ」で出るらしい。先頭大文字にしてもダメだった。Ruby奥深いよRuby。 [Railsメモ:多対多の関連を利用したときにNameError: uninitialized constant](http://stonhair.blogspot.jp/2011/10/railsnameerror-uninitialized-constant.html) ------------------------------------ # syntax error, unexpected tIDENTIFIER, expecting keyword_end (SyntaxError) コメントアウトを間違えて `//` にしたら出た。 `#` にしたら直った。 [他の言語に慣れた人がRubyを使ったときにつまずきがちな点](http://paulownia.hatenablog.com/entry/20120127/1327671167) ------------------------------------ # blockとかProcとかlambdaとかなによ [Rubyのblock、Proc、lambdaを理解する](http://d.hatena.ne.jp/shunsuk/20090101/1230816826) ------------------------------------ # データの永続化が出来ない [HerokuでRubyしてみてわかったこと](http://blog.livedoor.jp/maru_tak/archives/51251643.html) ------------------------------------ # no such file to load -- config (LoadError) ``` 13:18:40 web.1 | web.rb:3:in `require': no such file to load -- config (LoadError) ``` パスを明示化しないといけない。この場合だと、 ``` $ require 'config' ``` を ``` $ require './config' ``` ------------------------------------ # なんかよくわからんとこでLoadErrorとかでる rubyのバージョンが古い可能性なので新しくする。 ``` $ ruby --version ruby 1.8.7 (2011-02-18 patchlevel 334) [i686-darwin10] $ rvm use 1.9.2 --default ``` `--default`オプションをつけるとデフォルトでそのバージョンになる。 ------------------------------------ # heroku 参考集 - [HerokuでWebアプリ開発を始めるなら知っておきたいこと (6)ステージング環境](http://blog.ruedap.com/entry/20110504/ruby_heroku_web_app_development_tips_6) - [HerokuでWebアプリ開発を始めるなら知っておきたいこと (5)環境変数ENV](http://blog.ruedap.com/entry/20110503/ruby_heroku_web_app_development_tips_5) ------------------------------------ # bundle install 関連 ``` $ bundle install Resolving dependencies... Your Gemfile has no remote sources. If you need gems that are not already on your machine, add a line like this to your Gemfile: source 'https://rubygems.org' Could not find gem 'capistrano (>= 0) ruby' in the gems available on this machine. ``` `bundle install`で`Could not find gem `と怒られたら、 `Gemfile`の先頭に下記を追加。 ```Gemfile source 'https://rubygems.org' ``` |
|
| 927位 |
|
|||
|
16:16:31 |
(コロプラ 所属) |
|
画像描画関連のまとめ ##CGContextを使って画像描画 雰囲気としては、JavaScriptのCanvasを使ったことがある人であれば、あのイメージです。 画像を加工するフローとしては以下。 1. CGContextのサイズを決めて開く 2. CGContextを取得 3. (必要であれば)CGContextのtransformを設定 4. Draw/Render系メソッドを使ってCGContextに画像を描画 5. CGContextから描画済の画像を取得 6. CGContextを閉じる ###サンプルコード ```objc //コンテキストを開く CGSize size = [描画したい画像サイズ]; UIGraphicsBeginImageContext(size); //コンテキストを得る CGContextRef context = UIGraphicsGetCurrentContext(); //コンテキストの状態を保存 CGContextSaveGState(context); //コンテキストのtransformを操作(必要であれば) //例ではX軸で画像を反転 CGContextTranslateCTM(context, size.width, 0); CGContextScaleCTM(context, -1.0f, 1.0f); //UIImageの`drawInRect:`メソッドでコンテキストにレンダリング [aImage drawInRect:CGRectMake(0, 0, size.width, size.height)]; //コンテキストに描画済の画像を得る UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext(); //コンテキストを閉じる UIGraphicsEndImageContext(); // コンテキストを解放 CGContextRestoreGState(context); ``` `CGContextTranslateCTM`などのtransformを変更する関数の意味は、設定後にレンダリングされる画像の位置を変更する効果がある。(以下の画像参照) コンテキストに、`CGContextTranslateCTM(ctx, 100, 100)`を実行してから画像をレンダリングすると、画像の左上(原点)がコンテキストの`(100, 100)`の位置でレンダリングされる。  --------------------------- ##画像をリサイズする 画像をリサイズするにはまず、Core Graphicsを利用してリサイズ後の画像をレンダリングしなおして取得します。 簡単に手順を書くと 1. `UIGraphicsBeginImageContext([size])`関数を呼び出し、リサイズしたいサイズのコンテキストを開始 2. UIImageインスタンスの`drawInRect:`メソッドを使って現在のコンテキストに画像をレンダリング 3. `UIGraphicsGetImageFromCurrentImageContext()`関数を使って、現在のコンテキストからレンダリングされた画像を取得 という流れになります。 ###サンプルコード ```objc // リサイズ画像のサイズ CGSize resized_size = CGSizeMake(targetWidth, targetHeight); // リサイズしたいサイズを決める // コンテキストを開始 UIGraphicsBeginImageContext(resized_size); // 現在のコンテキストに画像をレンダリング [aImage drawInRect:CGRectMake(0, 0, resized_size.width, resized_size.height)]; // 現在のコンテキストから、レンダリングされた画像を取得 image = UIGraphicsGetImageFromCurrentContext(); // コンテキストを終了 UIGraphicsEndImageContext(); ``` --------------------------- ##Retinaディスプレイ対応デバイスで見ると画像がぼやける(荒くなる)ときの対処 上記の方法で画像を生成すると、`UIGraphicsBeginImageContext()`関数の引数に渡したサイズの画像になります。このサイズに、UIViewや画像のサイズをそのまま渡すと、Retinaで見た場合に荒くなる問題があります。 理由としては、プログラム上でやりとりしているサイズはRetinaディスプレイのサイズになっていないからです。 この問題に対処するためには、`UIGraphicsBeginImageContextWithOptions()`関数を利用します。 定義は以下の通り。 ```define.m void UIGraphicsBeginImageContextWithOptions( CGSize size, BOOL opaque, CGFloat scale ); ``` Referenceから引用すると、それぞれの引数の意味は以下になります。 > ##Parameters * **size** The size (measured in points) of the new bitmap context. This represents the size of the image returned by the UIGraphicsGetImageFromCurrentImageContext function. To get the size of the bitmap in pixels, you must multiply the width and height values by the value in the scale parameter. * **opaque** A Boolean flag indicating whether the bitmap is opaque. If you know the bitmap is fully opaque, specify YES to ignore the alpha channel and optimize the bitmap’s storage. Specifying NO means that the bitmap must include an alpha channel to handle any partially transparent pixels. * **scale** The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen. `size`は画像コンテキストのサイズ。生成したい画像のサイズですね。 注意点としては、Retina対応のためのサイズを指定「しない」ということです。 `opaque`は「不透明」のフラグ。完全に不透明な画像の場合は`YES`を指定するとパフォーマンスいいよ、ってことが書いてあります。 `scale`、これがRetina対応のために有効なオプションになります。 ここに`2`を指定すれば倍サイズとなり、めでたくRetina対応された画像が生成できます。 ただ、リファレンスを見ると`0.0`を指定するとmain screenの値を使用すると書いてあるので、基本的には`0.0`を指定しておくとよさそうです。 --------------------------- ##UIVIewをUIImageに変換する UIViewのlayerをCGContextに描画することで、UIViewの状態をUIImage化します。 ```objc // OpaqueueをNOにすると透過したキャプチャが取れる UIGraphicsBeginImageContextWithOptions(aView.frame.size, NO, 0.0); CGContextRef context = UIGraphicsGetCurrentContext(); [aView.layer renderInContext:context]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); ``` --------------------------- ##UIImageにCIFilterを使う まずはCIImageに変換し、そこからfilterを適用します。 ```objc // NSDataからUIImageを生成 UIImage *aImage = [[UIImage alloc] initWithData:imageData]; // UIImageをCIImageに変換 CIImage *filteredImage = [[CIImage alloc] initWithCGImage:aImage.CGImage]; // CIFilterを作成 CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"]; [filter setValue:filteredImage forKey:@"inputImage"]; // フィルタ後の画像を取得 filteredImage = filter.outputImage; // CIImageをUIImageに変換する CIContext *ciContext = [CIContext contextWithOptions:nil]; CGImageRef imageRef = [ciContext createCGImage:filteredImage fromRect:[filteredImage extent]]; UIImage *outputImage = [UIImage imageWithCGImage:imageRef scale:1.0f orientation:UIImageOrientationUp]; CGImageRelease(imageRef); UIImageView *aView = [[UIImageView alloc] initWithImage:outputImage]; [wself.view addSubview:aView]; ``` --------------------------- ##参考記事 * [自前で描画した内容がUIImageで ぼやける時の処置](http://d.akiroom.com/2012-03/cgcontext-uiimage-blur/) * [UIKit Function Reference](https://developer.apple.com/library/ios/documentation/uikit/reference/UIKitFunctionReference/Reference/reference.html) * [CGContext Reference](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGContext/Reference/reference.html) |
|
| 928位 |
|
|||
|
11:59:19 |
|
|
```
# 【ご注意ください】 この記事を書いた後にiBeacon周りの仕様は大きく変更しています。 そのため下記の情報は古い可能性がありますのでご注意ください ``` [@himara2](https://twitter.com/himara2) です。[iBeacon Advent Calendar](http://qiita.com/advent-calendar/2013/ibeacon) の4日目を担当させて頂きます。 ここまで概要や作品の紹介が多いようですので、私は開発時の注意点を書きます。 # 背景 以前「5分でわかるiBeacon」というスライドを公開させて頂きました。 * [5分でわかるiBeacon | SlideShare](http://www.slideshare.net/himaratsu/potatotips-ibeacon) <a href="http://b.hatena.ne.jp/entry/http://www.slideshare.net/himaratsu/potatotips-ibeacon"><img src="http://b.hatena.ne.jp/entry/image/http://www.slideshare.net/himaratsu/potatotips-ibeacon"></a> iOSデバイス2台で開発していたのですが、BLEに不慣れなこともありいくつかハマりました。 この記事ではこれからiBeacon開発を始める人向けに「開発時のハマリどころポイント」をまとめたいと思います。 ※なお、前提として上記のスライドで簡単にまとめた実装方法は理解している想定で進めます。 # iBeacon開発ハマリどころポイント ## 1. バックグラウンド処理の注意点 ### Background Mode にチェック入れる Xcodeのプロジェクトの設定を変更しないとバックグラウンド処理が行えないので注意。 CoreLocationのdelegate methodがバックグラウンドで呼ばれない時にチェック。  ### デバイス設定でバックグラウンド処理を許可する iOS 7からはデバイス設定でバックグラウンド処理が許可されていないとLocation Servicesを使えません。 以下の手順で設定します。  ### AppSwitcherでプロセスを落とした場合は呼ばれない iOS 7からAppSwitcher(ホームボタンダブルタップ画面で現れる画面)が導入されました。このAppSwitcherで上スワイプでプロセスを消すとバックグラウンド処理ができなくなります。  ## 2. 実装時の注意点 ### CoreBluetoothは使わなくてOK iBeaconの受信/発信ではCoreBluetoothはimportしなくてOKです。 CoreLocationフレームワークが必要に応じて適宜呼び出してくれているようです。 ### リージョン監視 → レージングの順で呼ぶ iBeaconでは、以下の2つを組み合わせて位置近接を測定します。 * エリアへの出入りを監視する「リージョン監視」 * エリア内でのビーコン情報を取得する「レージング」 それぞれの検知には順序があり、必ず 「リージョン監視」→「レージング」の順に呼ぶ必要があります。 同時に読んだりすると不具合が発生する可能性がありますので注意しましょう。 なお、それぞれの検知を開始/停止するメソッドは以下の通りです: * リージョン監視 * 開始: `- (void)startMonitoringForRegion:(CLRegion *)region;` * 停止: `- (void)stopMonitoringForRegion:(CLRegion *)region;` * レージングの開始 * 開始:`- (void)startRangingBeaconsInRegion:(CLBeaconRegion *)region;` * 停止:`- (void)stopRangingBeaconsInRegion:(CLBeaconRegion *)region;` ### 受信側アプリで登録できるUUIDはMAX20件まで 受信側のアプリではUUIDを指定して監視のターゲットに登録します。 登録の上限は20件のようで、それを超えた場合はエラーとなり以下のメソッドが呼ばれます。 ```objc - (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error { // error.codeには kCLErrorRegionMonitoringFailure が入っている } ``` ## 3. デバッグ時の注意点 ### [誤]アプリ起動直後からレージングエリアだと感知しない **12/4 13:12 編集** コメント欄で@chun_ryoさんよりご指摘頂きました。 レージングのエリア内でアプリを起動した場合でもビーコン感知は可能なようです。 詳細は下記のブログをご参考下さい: * [iBeacon Tips: 正しいビーコン監視方法](http://brightechno.com/blog/archives/220) `requestStateForRegion:`, `didDetermineState` を使って現在のステータスを取得できるようです。 ### 境界問題について 上述の通り「出入り」を感知するのですが、を感知できる境界付近をうろうろした時、出たり入ったりを毎回感知してはユーザーに情報を与えすぎてしまいます。 これを防ぐためにOS側が境界付近の管理を行っています。デバッグ時に領域への出入りを繰り返して反応がない場合は疑ってみましょう。 ### 正確な距離はとれない ビーコンからとれる情報は`CLBeacon`クラスに格納されますが、正確な距離を測ることはできません。 rssi(電波強度)はとれるので、これをベースにおおよその距離を算出することはできます。ただし周囲の環境によってrssiの値は上下します。 Appleのドキュメントにも正確な距離の測定用途ではないという話が書いてあるので、あくまで"おおよその位置"の検出に使うのが良さそうです。 ### ビーコン側はバックグラウンド処理できない 受信側のアプリはバックグラウンドで処理を受けることができますが、送信側アプリはできません。 # 今後もまとめていきます! 他にもハマりポイントがあれば随時追記していきますので、 コメント欄や[@himara2](https://twitter.com/himara2)まで教えてください。 この記事がこれから開発を始めるみなさんにとっての転ばぬ先の杖になることを願っています! |
|
| 929位 |
|
|||
|
10:59:20 |
(Akatsuki Inc. 所属) |
|
Ruby on Rails Tutorialのエッセンスを自分なりに整理してみる7 Railsにおけるリンクの記述方法とそのテスト http://qiita.com/kidachi_/items/d704e7eb63513c3831ae の続き。 Ruby on Rails Tutorial(chapter5) http://railstutorial.jp/chapters/filling-in-the-layout#sec-layout_exercises ##Rspecのリファクタリング 指定のページが指定の要素を持っている(もしくはいない)かをチェックするテストコード。 ```spec/requests/static_pages_spec.rb require 'spec_helper' describe "Static pages" do describe "Home page" do it "should have the h1 'Sample App'" do visit root_path page.should have_selector('h1', text: 'Sample App') end it "should have the base title" do visit root_path page.should have_selector('title', text: "Ruby on Rails Tutorial Sample App") end it "should not have a custom page title" do visit root_path page.should_not have_selector('title', text: '| Home') end end describe "About page" do it "should have the h1 'About'" do visit about_path page.should have_selector('h1', text: 'About Us') end it "should have the title 'About Us'" do visit about_path page.should have_selector('title', text: "Ruby on Rails Tutorial Sample App | About Us") end end ~ end ``` まず内容理解から。 ### describe テストの塊を表す。例えば ```rb describe "Static pages" do describe "Home page" do end end ``` ならば、「Static pages関連のテストの中のひとつである『Home page』のテスト」。 ### it "hoge" do ~ end 「hoge」はあくまでラベル。プログラム的な意味はない。 テスト実行時、失敗箇所を示すものとして表示される。 ブロック(do~end)の中身が実際の各テストのロジックとなる。 ```rb it "should have the h1 'Sample App'" do visit root_path page.should have_selector('h1', text: 'Sample App') end ``` 上の例では、「root_path(/)にアクセスした際、そのページが 'Sample App'という文字列のh1タグを持っていること」となる。 ## 洗練1 visit~、expect(page).to ~など、似たような構文が重複している。 →beforeとsubjectを用いて解決。 <a name="refactor1"></a> ### before {} 各テストが走る前に毎回実行されるメソッド。 →重複部を before に書くことでテストコードの重複を排除出来る。 ###subject {} ブロック({})内の評価結果が it 内のshouldのレシーバとなる。 本例テストのレシーバ(主語)はどちらもpageになるので、subjectとして括ることが出来る。 ### ※page レスポンスがHTMLである場合、その内容を本メソッドで取得できる。 これはCapybaraの機能のひとつ。 これらを用いてリファクタリングしたテスト。 ```spec/requests/static_pages_spec.rb require 'spec_helper' subject { page } describe "Home page" do before { visit root_path } it { should have_selector('h1', text: 'Sample App') } it { should have_selector 'title', text: "Ruby on Rails Tutorial Sample App" } it { should_not have_selector 'title', text: '| Home' } end describe "About page" do before { visit root_path } it { should have_selector('h1', text: 'About) } it { should have_selector 'title', text: "Ruby on Rails Tutorial Sample App | About Us" } end ``` ## 洗練2 text: "Ruby on Rails Tutorial Sample App"の部分をまとめるため、 helperを用いる ```app/helpers/application_helper.rb module ApplicationHelper # ページごとの完全なタイトルを返却 def full_title(page_title) base_title = "Ruby on Rails Tutorial Sample App" if page_title.empty? base_title else "#{base_title} | #{page_title}" end end end ``` helperメソッドfull_titleを用いて、もう一段簡潔になった。 ```spec/requests/static_pages_spec.rb require 'spec_helper' subject { page } describe "Home page" do before { visit root_path } it { should have_selector('h1', text: 'Sample App') } it { should have_selector('title', text: full_title('')) } it { should_not have_selector 'title', text: '| Home' } end describe "About page" do before { visit about_path } it { should have_selector('h1', text: 'About') } it { should have_selector('title', text: full_title('About Us')) } end ``` helperに対するテストも忘れずに。 ```spec/helpers/application_helper_spec.rb require 'spec_helper' describe ApplicationHelper do describe "full_title" do it "should include the page title" do expect(full_title("foo")).to match(/foo/) end it "should include the base title" do expect(full_title("foo")).to match(/^Ruby on Rails Tutorial Sample App/) end it "should not include a bar for the home page" do expect(full_title("")).not_to match(/\|/) end end end ``` ## 洗練3 it { should have_selector('h1', text: 'xxx') } が各describeで重複している。 →RSpecのShared Exampleとlet(変数生成)を使用してテストの冗長性を排除する。 ### Shared Exampleとは #### shared_examples_for "fuga" do ~ end で、各describeに共通するテストを記述できる。 その呼び出しは #### it_should_behave_like "fuga" で行う。 <a name="refactor3"></a> ### letとは テスト内にローカル変数を作成する。 シンボルとブロックを引数に取り、ブロックが返した値を保持するローカル変数となる。 変数の名前はシンボルの値。 ```rb let(:variable) { 'hoge' } # 値’hoge’を持つ変数variableが使えるようになる。 ``` 生成された変数はテスト中すべてのbeforeまたはitブロックで利用できる。 ### 共通箇所の定義 Shared Exampleで共通箇所を定義する。 またローカル変数variable1,2を用いる(実際の定義は各describeで行う)。 ```rb describe "Static pages" do subject { page } shared_examples_for "fuga" do it { should have_content(variable1) } it { should have_title(full_title(variable2)) } end ~ ``` その上で各describeで呼び出し。 - ローカル変数variableへの値代入(let(:variable) { value }) - Shared Exampleの呼び出しはit_should_behave_like "fuga" ```rb describe "Home page" do let(:variable1) { 'Sample App' } let(:variable2) { '' } it_should_behave_like "fuga" end ``` →describe "Home page" 内で、 - it { should have_content('Sample App') } - it { should have_title(full_title("")) } が実行される。 ### だいぶ簡潔になった ```spec/requests/static_pages_spec.rb require 'spec_helper' describe "Static pages" do subject { page } shared_examples_for "all static pages" do it { should have_content(heading) } it { should have_title(full_title(page_title)) } end describe "Home page" do before { visit root_path } let(:heading) { 'Sample App' } let(:page_title) { '' } it_should_behave_like "all static pages" it { should_not have_title('| Home') } end describe "About page" do before { visit about_path } let(:heading) { 'About' } let(:page_title) { 'About' } it_should_behave_like "all static pages" end ``` ## 以下に続く [Rails] 基本的なユーザ管理用Modelをテスト駆動で実装してみる http://qiita.com/kidachi_/items/99f2c90788bd931ea3ee |
|
| 930位 |
|
|||
|
03:51:47 |
(FOURIER.Inc 所属) |
|
## RailsでGoogleMapを表示させたい! と思って調べてみたら、早速出てきました。 その名も[Google Maps for Rails](https://github.com/apneadiving/Google-Maps-for-Rails)。 ## 早速使ってみる Githubに載っているチュートリアルに従ってコードを書いて行きます。 YouTubeにも[動画が上がっています](http://www.youtube.com/watch?v=R0l-7en3dUw&feature=youtu.be)。 #### とりあえずプロジェクトを用意します。 ```shell rails new gmap cd gmap ``` #### 用意するGemを記述 ```ruby:Gemfile gem "gmaps4rails" gem "geocoder" ``` ```shell bundle install ``` #### 緯度経度のデータを格納するModelを用意します。 ```shell rails g scaffold user title:string description:string address:string latitude:float longitude:float rake db:migrate ``` Userができたら以下のコードを記述します。 ```ruby:user.rb class User < ActiveRecord::Base geocoded_by :address after_validation :geocode end ``` #### JSのURLを指定します ```erb:views/layouts/application.html.erb <script src="//maps.google.com/maps/api/js?v=3.13&sensor=false&libraries=geometry" type="text/javascript"></script> <script src='//google-maps-utility-library-v3.googlecode.com/svn/tags/markerclustererplus/2.0.14/src/markerclusterer_packed.js' type='text/javascript'></script> ``` underscore.jsは[こちら](http://underscorejs.org/underscore-min.js)よりコピーしてきます。 ```javascript:vendor/assts/javascripts/underscore.js (省略) ``` ```javascript:assets/javascripts/application.js //= require underscore //= require gmaps/google ``` #### 地図ページを用意します ```shell rails g controller map index ``` ```erb:views/map/index.html.erb <div style='width: 800px;'> <div id="map" style='width: 800px; height: 400px;'></div> </div> <script type="text/javascript"> handler = Gmaps.build('Google'); handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){ markers = handler.addMarkers(<%=raw @hash.to_json %>); handler.bounds.extendWith(markers); handler.fitMapToBounds(); }); </script> ``` #### Controllerを編集します ```ruby:controllers/map_controller.rb class MapController < ApplicationController def index @users = User.all @hash = Gmaps4rails.build_markers(@users) do |user, marker| marker.lat user.latitude marker.lng user.longitude marker.infowindow user.description marker.json({title: user.title}) end end end ``` #### Userデータを登録 この時点で先にUserにデータを入れておきます。 Userの作成ページにて、Addressに適当な地名を入力します。 更新時にgmap4railsのほうで緯度経度を取得してLatitide/Longitudeに格納してくれます。 どの程度までかわかりませんが、日本語で登録しても問題ないようです。 こんな感じで。 | title | address | latitude | longitude | |:-----------:|:------------:|:------------:|:-----:| | 名古屋の場所 | 名古屋 | 35.1814464 | 136.906398 | | 浜松の場所 | 浜松 | 34.7108344 | 137.7261258 | | 静岡の場所 | 静岡 |34.975562 |138.3827956 | #### 動作確認! さて、いよいよ起動です。 rails s して[localhost:3000/map/index](localhost:3000/map/index)にアクセスしてみます。 デフォルトのままだと味気ないのでTwitterBootstrapRailsを見た目を多少整えています。  ### というわけで 簡単でしたね。 ここまででYouTubeの動画の半分くらいです。後半は色々なオプションの説明となっていました。 それ以外の機能については[Wiki](https://github.com/apneadiving/Google-Maps-for-Rails/wiki)のほうに色々書いてあるみたいですよ。 |
|
| 931位 |
|
|||
|
15:16:24 |
|
|
容量が足りなくなったときに、新しいディスクを追加して既存のVG・LV・ファイルシステムを拡張する手順。 ## 新しいディスク上にPVを作成 新しいディスク上にPVを作成する。fdiskパーティションを作成しておく必要はない。 以下は /dev/sdg 上にPVを作成する場合の実行例。 ``` # pvcreate /dev/sdg Physical volume "/dev/sdg" successfully created ``` ## 拡張したいLVが含まれるVGに新しいPVを追加 以下は既存のVG VolGroup00 にPV /dev/sdg を追加する場合の実行例。 ``` # vgextend VolGroup00 /dev/sdg Volume group "VolGroup00" successfully extended ``` vgdisplay を実行し実行結果の" --- Physical volumes ---" の部分を見ることで、新しいPVが追加されたことを確認できる。 ``` # vgdisplay -v VolGroup00 Using volume group(s) on command line Finding volume group "VolGroup00" ・ ・ (中略) ・ ・ --- Physical volumes --- PV Name /dev/sdf PV UUID MJ1JPJ-2kCu-Q3i9-ep6T-T2sn-oDEy-WA6Jw0 PV Status allocatable Total PE / Free PE 25599 / 0 PV Name /dev/sdg PV UUID fTHPYv-A4GM-JP9X-Ru0P-qJwn-W6wq-XoZRt6 PV Status allocatable Total PE / Free PE 25599 / 25599 ``` ## LVの拡張 lvextendを実行することで、拡張されたVGの未使用領域をLVに追加できる。以下はVG上の未使用領域のすべてをLVに追加する場合の実行例。 ``` # lvextend -l +100%FREE /dev/VolGroup00/LogVol00 Extending logical volume LogVol00 to 199.99 GiB Logical volume user successfully resized ``` ## ファイルシステムの拡張 LVを拡張してもファイルシステムは以前の大きさのままなので、最後にファイルシステムの拡張も行う必要がある。 ext3/ext4の場合、resize2fsを実行する。この処理は数分かかる。 ``` # resize2fs /dev/VolGroup00/LogVol00 resize2fs 1.41.12 (17-May-2010) Filesystem at /dev/VolGroup00/LogVol00 is mounted on /var/.....; on-line resizing required old desc_blocks = 7, new_desc_blocks = 13 Performing an on-line resize of /dev/VolGroup00/LogVol00 to 52426752 (4k) blocks. The filesystem on /dev/VolGroup00/LogVol00 is now 52426752 blocks long. ``` 処理完了後、dfコマンドでファイルシステムが拡張されたことを確認する。 ``` # df -h /var/..... Filesystem Size Used Avail Use% マウント位置 /dev/VolGroup00/LogVol00 197G 88G 100G 47% /var/..... ``` |
|
| 932位 |
|
|||
|
18:56:31 |
(DeNA Co., Ltd. 所属) |
|
デフォルトの設定だと別のマシンでビルドしたapkはインストールできない。 なので、複数人とかで開発していると既存アプリをアンインストールしてからインストールする場面が結構起こる。 いい加減面倒になったのでちょっと調べてみた。 # apkを端末にインストールするには署名が必要 debugビルドだろうがreleaseビルドだろうがkeystoreによる署名は必要らしい。 debugビルドの場合、自動的にkeystoreが作られている。 なので、 - 複数台のマシンで開発している - Jenkinsで自動ビルドもさせている とかいう状況だとそれぞれで別のkeystoreを使っているから別のマシンからインストールできない。 # debug用keystoreの保存場所 ## MacやLinux `~/.android/debug.keystore` ## Windows XP `C:\Documents and Settings\<user>\.android\` ## Windows Vista and Windows 7 `C:\Users\<user>\.android\` ## このdebug.keystoreを… 共有のdebug用keystoreを作って上記の場所におけばいい。 debug用keystoreの条件は http://developer.android.com/intl/ja/tools/publishing/app-signing.html#debugmode に書いてある。 | key | value | |:--------:|:-------:| | Keystore name | debug.keystore | | Keystore password | android | | Key alias | androiddebugkey | | Key password | android | | CN | CN=Android Debug,O=Android,C=US | ターミナルで ```console: keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -keyalg RSA -validity 10000 -dname "CN=Android Debug,O=Android,C=US" ``` とやれば生成できる。 |
|
| 933位 |
|
|||
|
16:19:40 |
|
|
**_随時更新中です。ご了承ください。_**
**_書いた人に重大な欠陥があるため、言語仕様と合わないなど多いです。絶賛フィックス中……_** ####現在までの問題点一覧 **\_\_(アンダーバー2つ)** C++ではすべて予約語。 Cでもリンク時に異常が出る可能性あり? *** コーディングスタイル、それは全プログラマを宗教戦争へと貶める魔の言葉のひとつである。(他にはスクリプト言語宗教戦争、エディタ宗教戦争などがある) ##基本方針 * 伝説を信じない。 * 真実を受け入れる。 * 言語特有の伝統的な作法がある場合、そちらを優先する。 * その作法が伝説に由来するものならば、訂正する。 * 業務でコードを書くプログラマ、つまりある程度の範囲でコードを他者(あるいは他社)と共有する必要があるプログラマとしてまとめる。 * そもそも個人のコーディングスタイルなど、**誰も興味が無い。**自由に書けばいい。本当に個人のものならば。 * そのため、ある程度強制を感じさせる口調で書く。少なくとも、私が関わって、私が管理することになるプロジェクトでは、自分にも他者にもこのコーディングスタイルを強制する。 * いくつか多様性を許す記述があるが、少なくとも**プロジェクト内では統一するべきである。**そうでなければ、コーディングスタイルや規約はまったく意味を成さない。 ##注意点 C/C++/Objective-Cの3言語を中心に述べる。 他言語に応用できる点はじゅうぶんにあるが、取り上げない。 **社内規約、あるいは自分への制約が主な意図であるので、これを読むあなたに対して有無を言わさず強制するものではない。** **参考にして頂けるのなら光栄だが、向き不向きは人によるとしか言いようがない。このスタイルに従ったからといって生産性や品質が向上することはまったく保証できない。** ##インデント ###インデント * インデントは**半角スペース4文字**を1ユニットとするべきである。 * 全角でもなく、タブでもない。 * 単純に扱いやすいから。 * 水平タブはエディタや環境によって何文字分送るか統一されていない。これは伝説ではなく、紛れも無い事実である。 * 最上層のブロックはインデントなしの新しい行で始める。ヒエラルキー構造の外観を素早く把握できる。つまり以下のようになる。 ```sample.c Hoge* Hogehoge(void) { /* hogehoge */ } ``` ```sample.cpp class Hoge { private: public: }; //あるいは namespace Foo { class Hoge { private: public: }; } ``` ```objc:sample.m @interface Hoge : NSObject { Foo* _iVal; } @end @implementation Hoge - (instancetype)init { self = [super init]; if (self) { /* hogehoge */ } return self; } ``` ##定数・マクロ * 環境に依存しない定数はすべて**必要なスコープでconst修飾子を付けた変数**、もしくは**列挙体**として定義する。 * defineマクロによる定数は環境に依存する(可能性のある)ものに限定する。 * デバッガで変数名、値共に確認しやすい。 * パフォーマンス上の問題が発生しない限り、引数付きのマクロではなく**インライン関数**を使う。 * インライン関数は型チェックが行われるのでよりデバッグが行い易い。 * 引数付きマクロと異なり、実装ファイルに書くことができるので、隠蔽ができる。 ##制御文 * ifやwhileの条件は、必ず**何がどうなっているか**の順序で書く。つまり以下のようになる。 ``` if (hoge == 1) { /* hogehoge */ } ``` * if、whileの条件式は、**常に真か偽かを問われている。** * ifの場合、真ならば実行、偽ならばスルー、あるいはelseにジャンプである。 * whileの場合、真ならば実行、偽ならば終了である * 確かに、ヨーダ記法(1 == hogeのように記述する)は、発見しづらいミスを防ぐのに有用かもしれないが、明らかに英語として不自然である。(if 1 equals hogeよりもif hoge equals 1のほうが直感的) * そもそも条件式はプログラマが第一に習得し、使いこなすべきものであるので、hoge = 1のようなミスはもはやケアレスミスではない。 * 論理型(ここでは_Bool,bool,BOOLの総称)を条件とする場合は、**いちいちhoge == trueやhoge == falseなどと書かない。** * 少なくともC99/C11/C++11/Objective-Cでは0が偽、それ以外が真であることが仕様かつ、まず間違いなくfalse = 0,true = !0(あるいは1)と定義されているので、これらの記述は冗長である。 * もし万が一にもfalse = 1, true = 0などと定義されている環境を使用しているなら、さっさと窓から放り投げたほうが賢明である。 * これは、上記の**何がどうなっているか**の順で書くという原則に矛盾しない。if (hoge) {}という文は英語でif (hoge) is true, {}と等価である。 * hoge == 1を(hoge == 1) == trueと書くC/C++/Objective-Cプログラマはまずいない。もちろん、そういうプログラマがいても間違ってはいないが、冗長であるのことは誰もが認める事実だろう。 * 条件を否定したい場合は論理否定演算子!を使う。 * NULL,nilについても同様である。NULL,nilはどちらも仕様で**偽**であるとされている。この2つが偽でない処理系はゴミである。 ##コメント * コメントは重要である。この事実に変わりはない。 * しかしやたらと書けばいいというものではない。コミュニケーションは口頭、あるいはメール、メッセンジャを使えばいい。 * 必要なこと以外は書かない。 ###コメントに書くべきこと * コードの役割 ####以上 * 突き詰めて言えば、この一点のみである。くだらない愚痴やTODOなどは個人で管理するべきものであり、公開するべきものではない。 ``` //TODO: 関数を動くようにする //なぜか動かない //2012 10/x 追加 ``` * 以上のようなコメントはなんの役にもたたない。 * TODOリストは専用のアプリケーションや使い慣れたエディタで管理したほうが見渡しが効くだろう。 * どの部分を追加したとか削除したなどもバージョン管理システムを導入するだけで解決できる。 * **//なぜか動かない**に至っては論外としか言いようがない。動かないのならばなぜ動かないのかを調査し、動くようにするのがプログラマの義務である。 ####試験的なコメントに関する規約 **_以下の内容はやや試験的、左翼的な規約となる。議論の余地はじゅうぶんにあることを先に述べる。_** *** * コメントはコードの役割のみを書くことが許されること、またC/C++/Objective-Cの現代的な作法ではカプセル化や隠蔽が重要なテクニックとなることを踏まえると、**コメントを書くのはヘッダファイルのみ**となる。 * ヘッダファイルを公開されるインターフェイス、実装ファイルを公開される必要のないカプセルと捉えた場合、使用する側はインターフェイスだけを見ればよいことになり、使い方はそこに書かれていればよい。 * ただし、実装ファイルは正常に動作し、メンテナンス性を確保されていなければいけない。今のコードを編集するのはあなただけかもしれないが、半年後そのコードを保守するのは新人プログラマかもしれないからだ。 * 端的に言って、**見ただけで何をしているのか理解できないコードはそれ自体がバグである。**アクロバティックなコード(何をしているのか、5秒以上考えなければいけない)、教科書に載っているようなコード(1行ごとに解説が入っている親切なもの)は、はっきり言って読みづらい。 * プロジェクトに関わるプログラマは、お互いにお互いのスキルレベルを認知し合い、高め合っていく義務がある。それから初めてコード規約に意味が生じる。 *** ##命名規則 * 変数、関数、クラスなど、名前をつける必要があるものは最大限努力して**英語でわかりやすい名前**をつけるべきである。 * 誰にとってわかりやすいのか。当然、プロジェクトに参加する全員である。 * 英語の理解度は、この規則において壁になりやすい。私には意味がわかる英単語でも、隣の席のプログラマにとっては辞書をひかなくてはいけないかもしれない。フランス語由来の英単語なんて出てきた場合にはもっと複雑である。 * 「無理をして英語を使わず、母語を使ったらいいではないか」そう言う人もいる。しかし、C/C++/Objective-Cは**英語に似た論理文法で記述する高級プログラミング言語**なので、英語を使ったほうが自然である。 * 英語記述の段階でも、いわゆるアメリカ英語とイギリス英語の綴り違い問題(e.g. color vs colour, gray vs grey etc.)がある。一般的にアメリカ英語の綴りのほうが短く省力的である。 * 以上のことを鑑みて、プログラマは**空気をよく読み**、**積極的に意見交換**をしなければいけない。この規則に関しては、プロジェクト単位での流動性が激しいためだ。 * インターフェイスとして公開しない関数、メソッドは**\_\_(アンダーバー2つ)で囲う。**つまり、以下のようになる。 ``` Hoge* __Hogehoge__(void) { /* hogehoge */ } - (Hoge*)__hogehoge__ { /* hogehoge */ } ``` * 囲うことによって、公開されないという意味が際立つ。 * C/C++の標準ライブラリや、Cocoaの非公開メソッド、あるいはコンパイラによってアンダーバー1つ(\_hoge)、頭にだけアンダーバー2つ(\_\_hoge)が予約されている可能性があるため、後ろにもつける。 * 公開するものと識別ができるのならばアンダーバーでなくともいい(例えば、sec接頭辞にするなど)が、公開されない特別なものであるという見た目の情報量は低下するかもしれない。 * C++のメンバ変数、Objective-Cのインスタンス変数は、**\_(アンダーバー1つ)で始める。**つまり、以下のようになる。 ``` class Hoge { public: int _val; } @interface Hoge : NSObject { @public int _val; } @end ``` * 標準ライブラリや親クラスのメンバ変数・インスタンス変数と衝突する可能性はじゅうぶんにある。その場合は法則を乱さず、**変数名を変える。** * パフォーマンス上の問題がない限り、メンバ変数・インスタンス変数には**直接アクセスしない。**必ず、アクセサメソッドを経由する。 * アクセサは使用を徹底して初めて保守性によい影響を及ぼす。そのため、たとえパフォーマンスに問題があったとしても、最初に評価すべきなのはロジックや使用しているアルゴリズムである。アクセサは最後の砦にし、場合によっては心中(実行環境を改善するなど)する覚悟で臨むべきである。 * C++では積極的に名前空間を用いるべきである。 #####以下、気が向いたら書き足す。 |
|
| 934位 |
|
|||
|
18:37:34 |
|
|
https://html.spec.whatwg.org/multipage/syntax.html#optional-tags の和訳 1. `<html>` はその最初の内容がコメントでなければ省略できる 2. `</html>` は直後にコメントが続かなければ省略できる 3. `<head>` は内容が空か、最初の内容が要素なら省略できる 4. `</head>` は直後に空白文字かコメントがなければ省略できる 5. `<body>` は内容が空か、最初の内容が空白文字かコメントでなければ省略できるが、最初の要素が meta, link, script, style, template なら省略できない 6. `</body>` は直後にコメントが続かなければ省略できる 7. `</li>` は直後に li 要素が続くか、親要素にそれ以上内容がなければ省略できる 8. `</dt>` は直後に dt, dd 要素が続けば省略できる 9. `</dd>` は直後に dt, dd 要素が続くか、親要素にそれ以上内容がなければ省略できる 10. `</p>` は直後に address, article, aside, blockquote, details, div, dl, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup, hr, main, menu, nav, ol, p, pre, section, table, ul 要素が続くか、親要素が a, audio, del, ins, map, noscript, video 以外で親要素にそれ以上内容がなければ省略できる 11. `</rt>` は直後に rt, rp 要素が続くか、親要素にそれ以上内容がなければ省略できる 12. `</rp>` は直後に rt, rp 要素が続くか、親要素にそれ以上内容がなければ省略できる 13. `</optgroup>` は直後に別の optgroup 要素が続くか、親要素にそれ以上内容がなければ省略できる 14. `</option>` は直後に option, optgroup 要素が続くか、親要素にそれ以上内容がなければ省略できる 15. `<colgroup>` はその最初の内容が col 要素で、直前に終了タグを省略した colgroup 要素がなければ省略できる (ただし空要素の場合省略できない) 16. `</colgroup>` は直後に空白文字かコメントがなければ省略できる 17. `</caption>` は直後に空白文字かコメントがなければ省略できる 18. `</thead>` は直後に tbody, tfoot 要素が続けば省略できる 19. `<tbody>` はその最初の内容が tr で、直前に終了タグを省略した tbody, thead, tfoot 要素がなければ省略できる 20. `</tbody>` は直後に tbody, tfoot 要素が続くか、親要素にそれ以上内容がなければ省略できる 21. `</tfoot>` は親要素にそれ以上内容がなければ省略できる 22. `</tr>` は直後に tr 要素が続くか、親要素にそれ以上内容がなければ省略できる 23. `</td>` は直後に td, th 要素が続くか、親要素にそれ以上内容がなければ省略できる 24. `</th>` は直後に td, th 要素が続くか、親要素にそれ以上内容がなければ省略できる いずれの場合にも属性を持つ場合、開始タグは省略できない。 下記で valid かチェックできる http://validator.w3.org/ valid な例 ```html <!DOCTYPE html> <meta charset="utf-8"> <title>タイトル</title> <p>段落 <ul> <li>A <li>B </ul> <dl> <dt>HTML <dd>Hyper Text Markup Language </dl> <ruby> Ruby <rp>(<rt>ルビー<rp>) </ruby> <select> <optgroup label="one2two"> <option>1 <option>2 <optgroup label="three"> <option>3 </select> <table> <colgroup span="2"> <tr> <td>セル1 <td>セル2 </table> ``` |
|
| 935位 |
|
|||
|
09:59:43 |
|
|
Androidだとそれ用の書き方があるからいいんだけど、iOSだとJSでやるしかなさげ。
### Andoroidの場合 ```html "intent://hoge.com#Intent;scheme=get-hoge;package=com.hoge.activity;end" ``` ### iOSの場合 ```html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>iOSで開いた時に、URLスキームが存在すればアプリを開く、存在しなければAppStoreを開く</title> <script type="text/javascript"> $(function(){ var userAgent = navigator.userAgent.toLowerCase(); if (userAgent.search(/iphone|ipad|ipod/) > -1) { // Launch myapp via URL scheme launch_frame.location.href= "myapp://"; setTimeout(function(){ // Open App DL page in iTunes Store location.href= "itmss://itunes.apple.com/us/app/myapp/id99999999?ls=1&mt=8"; } , 500); } }); </script> </head> <body> <div style="width:0; height:0; overflow:hidden;"> <iframe id="launch_frame" name="launch_frame"> </iframe> </div> </body> </html> ``` |
|
| 936位 |
|
|||
|
13:11:06 |
|
|
new/create時に関連テーブルも更新する記述について、忘れていたのでまとめてみました。(意外と参考書とかにも載っておらず・・・) 今回利用しているバージョンは下記。 Ruby -> 2.1.0 rails -> 4.0.2 ###### 20140307 記述を一部修正しました ##元記事 下記エントリの転載になります。 [【Rails】has_many throughな関係で、複数レコードを new/create する時の書き方 - rokuro Fire](http://www.rokurofire.info/2014/02/26/rails_tablerelationship/) ##やりたいこと * postを1件更新するときに関連テーブルを複数件数一気に更新する ##Model + 各モデルの関係を定義。ここはいつものhas_many through。 + posts <-> author_posts <-> authors + authorsは "name" カラムを持つ + formでネストさせるので、accepts_nested_attributes_for も設定します。 ```app/models/post.rb class Post < ActiveRecord::Base has_many :author_posts has_many :authors, :through => :author_posts accepts_nested_attributes_for :authors end ``` ```app/models/author.rb class Author < ActiveRecord::Base has_many :author_posts has_many :posts, :through => :author_posts end ``` ```app/models/author_post.rb class AuthorPost < ActiveRecord::Base belongs_to :post belongs_to :author end ``` ##まずはirbで動作を確認 ```cmd # postのインスタンスを作成 irb > post = Post.new => #<Post id: nil, title: nil, created_at: nil, updated_at: nil> # 空のインスタンスを確認 irb > post => #<Post id: nil, title: nil, created_at: nil, updated_at: nil> # post.author_postsの確認 # 空のArray irb > post.author_posts => #<ActiveRecord::Associations::CollectionProxy []> # post.authorsの確認 # 空のArray irb > post.authors => #<ActiveRecord::Associations::CollectionProxy []> # buildで、postの関連テーブルauthorのインスタンスを作成 irb > post.authors.build => #<Author id: nil, name: nil, created_at: nil, updated_at: nil> # post.authorsのArrayに、作成したインスタンスが格納されている irb > post.authors => #<ActiveRecord::Associations::CollectionProxy [#<Author id: nil, name: nil, created_at: nil, updated_at: nil>]> # 試しにもう一回buildしてみる irb > post.authors.build => #<Author id: nil, name: nil, created_at: nil, updated_at: nil> # post.authorsのArrayに、2個目のインスタンスがpushされている irb > post.author => #<ActiveRecord::Associations::CollectionProxy [#<Author id: nil, name: nil, created_at: nil, updated_at: nil>, #<Author id: nil, name: nil, created_at: nil, updated_at: nil>]> # post.author_posts のArrayはこの時点では空 irb > post.author_posts => #<ActiveRecord::Associations::CollectionProxy []> # saveしてみる # 関連テーブルにもINSERTされている irb > post.save (0.5ms) BEGIN SQL (4.4ms) INSERT INTO `posts` (`created_at`, `updated_at`) VALUES ('2014-02-23 08:20:58', '2014-02-23 08:20:58') SQL (0.2ms) INSERT INTO `authors` (`created_at`, `updated_at`) VALUES ('2014-02-23 08:20:58', '2014-02-23 08:20:58') SQL (0.3ms) INSERT INTO `author_posts` (`author_id`, `created_at`, `post_id`, `updated_at`) VALUES (3, '2014-02-23 08:20:58', 10, '2014-02-23 08:20:58') SQL (0.2ms) INSERT INTO `authors` (`created_at`, `updated_at`) VALUES ('2014-02-23 08:20:58', '2014-02-23 08:20:58') SQL (0.2ms) INSERT INTO `author_posts` (`author_id`, `created_at`, `post_id`, `updated_at`) VALUES (4, '2014-02-23 08:20:58', 10, '2014-02-23 08:20:58') (2.2ms) COMMIT => true # save後に post.author_posts を再確認 # Arrayに値が入っている irb > post.author_posts => #<ActiveRecord::Associations::CollectionProxy [#<Author id: 1, name: nil, created_at: "2014-02-23 08:41:41", updated_at: "2014-02-23 08:41:41">, #<Author id: 2, name: nil, created_at: "2014-02-23 08:41:41", updated_at: "2014-02-23 08:41:41">]> ``` 関連オブジェクトの値を更新する際は、post.authors.build のようにする必要があります 。複数個の場合は複数回buildしてあげればよさそうです。 上記を元に、View、Controllerを作っていきます。 ##View(フォーム) ```app/views/posts/_form.html.erb <%= form_for(@post) do |f| %> <% @post.authors.each do |author| %> <%= f.fields_for :authors, author do |author_field| %> <%= author_field.text_field :name %> <% end %> <% end %> <% end %> ``` <del> "authors_attributes[]"の部分は、普通は:authorsと書くようですが・・・ 複数個更新するときには上記のように書くと、同名のパラメータを配列に格納してくれます。 また、authorsの"name"を更新したいので、text_fieldには":name"と記述します。 </del> 追記):authors でも動作しました。 ###authors_attributesのパラメータ params[:post][:authors_attributes]の中身は下記になります。 ``` "authors_attributes"=>[{"name"=>"著者1"}, {"name"=>"著者2"}, {"name"=>"著者3"}]} ``` ### (実験)f.fields_forの引数を :authors にした場合 ``` "authors_attributes"=>{"0"=>{"name"=>"あ"}, "1"=>{"name"=>"い"}, "2"=>{"name"=>"う"}}} ``` <del> keyに個数を表す数値が入ってきてしまいました。 これが気持ち悪くて配列で取得するようにしています。 </del> 追記)こちらで正しいようです。 ##Controller ```app/controllers/posts_controller.rb def new @post = Post.new # 今回は分かりやすく、authorは固定で3枠作成 # 1postで最大3author追加出来る 3.times { # 関連オブジェクトをbuild @post.authors.build } end def create @post = Post.new(post_params); @post.save end # strong parameters private def post_params params.require(:post).permit(:title, authors_attributes: [:name]) end ``` <del> 本当はcreateに何も追記したくなかったのですが・・・(これ以外の方法がわかりませんでした。) また、strong parametersのホワイトリストに ":authors_attributes => []" を追記しています。 </del> 追記)上記のようにstrong parametersに記述をすることで、余計な処理を書かずに更新出来ました。 ただし、データを取り出して何らかの加工をする場合には、Arrayで取得しても良さそうです。 ##参考 * [YOMiTOKU: Railsでの似たような言葉での違いメモ](http://banker0507.blogspot.jp/2013/02/rails.html) |
|
| 937位 |
|
|||
|
13:11:10 |
(KidsStar Inc. 所属) |
|
お仕事の絡みで、タイトルの要件が必要になりそうだったので備忘録も兼ねて。 # 前提 * EC2 の Distribution は Amazon Linux * インスタンスは AutoScale によって増えたり減ったりする # やること 1. S3 Bucket の準備 1. IAM の準備 * IAM User で対応する場合 * IAM Role で対応する場合 1. s3fs のインストール 1. s3fs の設定・起動 # やりかた ## S3 Bucket の準備 まぁ、普通に Management Console から追加するだけ。 ログとかはご随意に。 ## IAM の準備 二通りのやり方があるので、お好みで。 ### IAM User で対応する場合 このケースは、EC2 起動時のオプションとして IAM Role を **割り当てたくない(られない)** 場合に使うと良いか? 1. IAM Management Console で **Create a New Group of Users** をクリック  1. グループ名を設定して **Continue**  1. `Select Policy Template` の中にある `Amazon S3 Full Access` を **Select**<br />※割と下の方にあるので、スクロールしないと出てこない。  1. テンプレートから生成された Policy 構文が表示されるので、そのまま **Continue**  1. ユーザ名決めて **Continue**  1. 内容に問題が無ければ **Continue**  1. 作成に成功し、一番重要な `Access Key ID` と `Secret Access Key` が表示されるので、控える。<br />**Download Credencials** しとくと良い。  ### IAM Role で対応する場合 鍵情報などを EC2 のインスタンス内に残したくない場合はコッチかな? EC2 インスタンスの起動時に下記で作成した IAM Role を設定する必要がある。 1. IAM Management Console の左ペインにある Roles から **Create New Role** をクリック  1. ロール名を決めて **Continue**  1. AWS Service Roles の Amazon EC2 を **Select**  1. Select Policy Template の中にある `Amazon S3 Full Access` を **Select**<br />※割と下の方にあるので、スクロールしないと出てこない。  1. テンプレートから生成された Policy 構文が表示されるので、そのまま **Continue**  1. 内容に問題が無ければ **Create Role**  ## s3fs のインストール 2014年01月24日現在、最新版は **1.76** の様子。 [Google Code](https://code.google.com/p/s3fs/) にあるのが最新かと思いきや、 [GitHub](https://github.com/s3fs-fuse/s3fs-fuse) に移動しているようなので注意w なお、RPM などは用意されていないので、自分で `make` する必要がある。 `rpmbuild` でもしとくと幸せになれるかも?w ### 先ずは、必要となるパッケージのインストール Amazon のリポジトリにあるパッケージで賄える。 ```bash:yum sudo yum -y install gcc-c++ fuse fuse-devel libcurl-devel libxml2-devel openssl-devel ``` ### 続いて、s3fs のソース取得 ```bash:git git clone https://github.com/s3fs-fuse/s3fs-fuse.git ``` ### configure, make, make install */usr/bin/s3fs* に置きたかったので `--prefix=/usr` を `./configure` のオプションに食わせてます。 ```bash:make cd s3fs-fuse/ ./autogen.sh ./configure --prefix=/usr make sudo make install ``` ## s3fs の設定・起動 作成した IAM の種類に応じて、若干設定内容が変わる。 ### マウント前の設定 マウントポイントとなるディレクトリを作る。 私は */mnt/s3fs* にしてる。 ```bash:mkdir sudo mkdir <Mount Point> ``` #### IAM を User で作成した場合 s3fs を用いてマウントする際の認証用に、作成時に控えた `Access Key ID` と `Secret Access Key` を設定する。 色々置き場があるっぽいが、ココでは */etc/passwd-s3fs* に配置する。 ※置き場については、マニュアル参照のこと。 ```bash:passwd-s3fs設定 echo "<Access Key ID>:<Secret Access Key>" | sudo tee -a /etc/passwd-s3fs sudo chmod 640 /etc/passwd-s3fs ``` #### IAM を Role で作成した場合 EC2 インスタンス起動時に IAM Role を正しく選択していれば、特に設定するコトはない。はず。 ### マウント お待ちかねのマウント。 なお、デフォルトだと root:root でマウントされてしまって、読み書きが出来ない状態になってしまうので、 オプションに uid, gid を渡して、マウント時の Owner を変更する必要がある。 ※uid, gid は `id <User Name>` コマンドで確認出来る。 下記の例では、`default_acl=public-read` としているが、これは外部に直接公開するケースが有り得たので、こうしている。 外部公開する必要が無いなら private にしておくのがオヌヌメ。 CloudFront 経由で配信したい場合でも Distribution を正しく設定すれば private で問題無かったはず。 #### IAM を User で作成した場合 ```bash:s3fs sudo /usr/bin/s3fs <Bucket Name> <Mount Point> -o rw,allow_other,uid=<UID>,gid=<GID>,default_acl=public-read ``` #### IAM を Role で作成した場合 ```bash:s3fs sudo /usr/bin/s3fs <Bucket Name> <Mount Point> -o rw,allow_other,uid=<UID>,gid=<GID>,default_acl=public-read,iam_role=<IAM Role Name> ``` ### 確認 マウントポイントを `ls` するなり `df` するなりすれば良いんじゃないかな? ちなみに、`df` した結果は以下。 <pre> $ df -kh Filesystem Size Used Avail Use% マウント位置 /dev/xvda1 30G 16G 13G 55% / tmpfs 298M 0 298M 0% /dev/shm s3fs 256T 0 256T 0% /mnt/s3fs </pre> 256TB のファイルシステムがマウントされてますね! ### 起動時設定 このままだと、起動時に自動的にはマウントしてくれない。 ので、今回作ったインスタンスをベースにした AMI からの AutoScaling とか、普通にインスタンス再起動した時とかに、「あれ?S3 マウントしたディレクトリが見られない…!?」みたいなコトになる。 ので、起動時に自動的にマウントしてくれるように設定してしまう。 んで、このとき、一般的には */etc/fstab* に記載して終わりにするんだろうけど、IAM を Role にした場合、fstab の設定が読まれる時点ではまだ Amazon の API を叩けないっぽいので、マウントに失敗する。 ので、以下のように IAM の種類によってやり方を変える #### IAM を User で作成した場合 User の場合は API 叩く必要が無いので、素直に fstab に書く。 ```bash:/etc/fstab echo "/usr/bin/s3fs#<Bucket Name> <Mount Point> fuse rw,allow_other,uid=<UID>,gid=<GID>,default_acl=public-read 0 0" | sudo tee -a /etc/fstab ``` #### IAM を Role で作成した場合 Role の場合は、API が叩けるようになる **起動時スクリプトが走るタイミング** でマウントする。 */etc/rc.d/rc.local* とかでやると良いんじゃないかな? ```bash:/etc/rc.d/rc.local echo "/usr/bin/s3fs <Bucket Name> <Mount Point> -o rw,allow_other,uid=<UID>,gid=<GID>,default_acl=public-read,iam_role=<IAM Role>" | sudo tee -a /etc/rc.d/rc.local ``` 上記の例では、 */etc/rc.d/rc.local* の最下部に追記してるけど、他にも何か処理してるなら、順番は気を付けた方が良いかも。 # おわりに と、偉そうなことを散々書いておきながら、まだ商用環境での稼働はしておりませんっ!w ※遠からず対応するとは思うが、まだ実験中だったりする :-P 導入して、運用安定したらレポしようかしら…。 |
|
| 938位 |
|
|||
|
00:04:10 |
(Wantedly 所属) |
|
[Wantedly](https://www.wantedly.com/)で実際に開発しているstoryboardの一部公開してみました。
うまくいったなということをまだ公開してないアプリの実例を見せながら紹介します。 初めて作ったアプリでは、Containerの存在を知らず、TableViewをScrollViewControllerに埋め込んで、ページャーなどを実装していました。そのため、どうしてもコピペの多いアプリを作ってしまってました。 今回は、Viewの中にContainerをたくさん設置して、一覧を表示させる必要があるパーツはすべてUITableViewControllerを継承して作成しました。  その結果、 一覧画面を表示したり、  ローディングを出したり、  通信失敗後の更新ボタンを設置したり、  することが、ある共通のUITableViewControllerを継承したクラスを使うことで実装できました。 ※ スクリーンショットはまだ開発中なのでそのまま公開されるかは分かりません。 メリット ・ContainerとStoryboardとの組み合わせが非常によく実装がすごく早くなった ・TableViewでの分岐も減り、ViewControllerのコードが減って読みやすくなった ・どのViewControllerがどのViewControllerが繋がっているかすぐ分かるようになった デメリット ・TableViewを多用するため、そのままだとスクロールが遅くなる ・Storyboardがノートパソコンの画面だと見にくい ・Storyboardを多用するため、頻繁にトラックパッドを使う必要がある ・そのためノートパソコンでの開発が増えて、好きなキーボードのタイピングが遅くなった。。 最後は気持ちになってしまいましたが、デメリット多く書いてますが、それ以上に開発効率がよかったので、Storyboard上でStoryboardを設置して、UITableViewController使うのはオススメです。 Wantedlyのアプリも近日出しますのでお楽しみに!! https://www.wantedly.com/ |
|
| 939位 |
|
|||
|
16:50:36 |
|
|
**この投稿は[現実逃避アドベントカレンダー2013](http://katryo.hatenablog.com/entry/2013/12/19/001823)の4日目の記事です。** [2日目の記事](http://qiita.com/katryo/items/62291ba328de9d12bd30)でBing APIを使ってフェッチしたhtmlを使うので、[2日目](http://qiita.com/katryo/items/62291ba328de9d12bd30)を先に読んでおくと理解しやすいです。 ## 本稿を3行でまとめる - [scikit-learn](http://scikit-learn.org/stable/index.html)というPythonのライブラリを調べた - [2日目](http://qiita.com/katryo/items/62291ba328de9d12bd30)で保存したhtml内の語のtf-idfを計算した - 語とtfidfのマッピングを確認した ## 参考 [scikit-learn公式、テキストの素性抽出ドキュメント](http://scikit-learn.org/stable/modules/feature_extraction.html#text-feature-extraction) [scikit-learnを使ってTweet中の単語のtfidf計算](http://blog.parosky.net/archives/2212) ## 完成品 https://github.com/katryo/tfidf_with_sklearn Fork me! ## 理論 ### tfidfの定義 - tf-idfは **tf * idf** の値。あるドキュメント(文書)集合において、あるドキュメントの、ある単語につけられる。tf-idfが高い語は重要と考えることができる。情報検索において、語への重みづけに使える。 - tf (Term Frequency)は **その単語 (Term) の、そのドキュメントでの出現回数 / そのドキュメントで出現したすべての単語の総数** 。単語がその文書で何度も使われていると大きくなる。 - idf (Inverse Document Frequency)はdfの逆数。ただし実際には計算しやすくするようにlogで対数を取る。なので **log(1/df)** となる。logの底は普通2だが、別にeでも10でもよい。はず。 - df (Document Frequency)は **その単語が出現したドキュメントの数 / 全ドキュメント数** 。単語が広いトピックで用いられていると大きくなる。「は」や「を」、英語だと “is” や “that” などで非常に大きくなる。あるドキュメント集合の、ある単語につけられる値。 ## scikit-learnでの、テキストからの素性抽出 [scikit-learn公式、テキストの素性抽出ドキュメント](http://scikit-learn.org/stable/modules/feature_extraction.html#text-feature-extraction)の内容を一部訳しながら、自分なりの理解を加えて説明する。 scikit-learnでテキストから素性を抽出するとき、3つの処理が必要になる。 - tokenizing: テキストをbag-of-wordsに変換する。英語の場合はホワイトスペースで分割してから記号などノイズを除去するだけでOKだが、日本語でやる場合、MeCabやKyTeaのような形態素解析器を使う。scikit-learnには日本語の形態素解析器は入っていないので、この処理が別に必要になる。 - counting: 個々のドキュメントごとに、それぞれの語の出現頻度を数える。 - normalizing and weighting: 正規化と重みづけ。語の出現頻度とドキュメント内の語の数とドキュメント数でtf-idfを計算して、さらにそれを使いやすい値に変換する。 scikit-learnでは以上3つの手順をまとめて **vectorization** つまり「ベクトル化」と呼んでいる。後で登場するTfidfVectorizerは3つの手順すべてを行える。すでに途中まで手順を終えているなら、途中からの計算もできるし、途中までの計算もできる。 ちなみにscikit-learnはbag-of-wordsだけでなく、2つ以上の語の連続に着目したn-gramでのtfidf計算もできるが今回はやらない。 ### CountVectorizer sklearn.feature_extraction.textにいるCountVectorizerは、tokenizingとcountingができる。Countingの結果はベクトルで表現されているのでVectorizer。 [公式ドキュメントではこの箇所で説明されている。](http://scikit-learn.org/stable/modules/feature_extraction.html#common-vectorizer-usage) ### TfidfTransformer 同じくsklearn.feature_extraction.textにあるTfidfTransformerはnormalizingを受け持つ。fit_transformメソッドで、ただの「ドキュメントごとの語の出現頻度」をもとにtfidfを計算して、さらに正規化までしてくれる。[公式ドキュメントではここ。](http://scikit-learn.org/stable/modules/feature_extraction.html#tfidf-term-weighting) ### TfidfVectorizer CountVectorizerとTfidfTransformerの機能を併せ持つ存在。まさに三位一体、トリニティフォーム。生テキストから素性抽出するときはこれを使うと便利。 ## 実際に使ってみた ### tfidfの高い語を見てみる ここからが本題。8つのクエリで取得した400のWebページ内の語、36934種類。これらのなかから、「出現したドキュメントでtfidfが0.1より大きな語」をprintする。 まずtfidfの計算はかなり高コストなので、tfidfを計算したあと、結果をpickle化しておこう。 ```python:set_tfidf_with_sklearn_to_fetched_pages.py import utils import constants import pickle import os from sklearn.feature_extraction.text import TfidfVectorizer def is_bigger_than_min_tfidf(term, terms, tfidfs): ''' [term for term in terms if is_bigger_than_min_tfidf(term, terms, tfidfs)]で使う list化した、語たちのtfidfの値のなかから、順番に当てる関数。 tfidfの値がMIN_TFIDFよりも大きければTrueを返す ''' if tfidfs[terms.index(term)] > constants.MIN_TFIDF: return True return False def tfidf(pages): # analyzerは文字列を入れると文字列のlistが返る関数 vectorizer = TfidfVectorizer(analyzer=utils.stems, min_df=1, max_df=50) corpus = [page.text for page in pages] x = vectorizer.fit_transform(corpus) # ここから下は返す値と関係ない。tfidfの高い語がどんなものか見てみたかっただけ terms = vectorizer.get_feature_names() tfidfs = x.toarray()[constants.DOC_NUM] print([term for term in terms if is_bigger_than_min_tfidf(term, terms, tfidfs)]) print('合計%i種類の単語が%iページから見つかりました。' % (len(terms), len(pages))) return x, vectorizer # xはtfidf_resultとしてmainで受け取る if __name__ == '__main__': utils.go_to_fetched_pages_dir() pages = utils.load_all_html_files() # pagesはhtmlをフェッチしてtextにセットずみ tfidf_result, vectorizer = tfidf(pages) # tfidf_resultはtfidf関数のx pkl_tfidf_result_path = os.path.join('..', constants.TFIDF_RESULT_PKL_FILENAME) pkl_tfidf_vectorizer_path = os.path.join('..', constants.TFIDF_VECTORIZER_PKL_FILENAME) with open(pkl_tfidf_result_path, 'wb') as f: pickle.dump(tfidf_result, f) with open(pkl_tfidf_vectorizer_path, 'wb') as f: pickle.dump(vectorizer, f) ``` tfidf関数のなかで ``` vectorizer = TfidfVectorizer(analyzer=utils.stems, min_df=1, max_df=50) ``` としている。analyzerは文字列を入れると文字列のlistを返す関数を入れる。デフォルトではホワイトスペースで分割して1文字の記号を除去するだけだが、日本語で行うときは形態素解析器を利用した関数を自分で作って設定してやる必要がある。utils.stems関数はMeCabで形態素解析をして語幹に変換してlistにして返す関数で、後述するutils.py内に書いた。 tfidf関数の中でprintしているのは、「胃もたれ」で検索した結果ページの1つの中から発見できる単語のなかで、tfidfの値が0.1以上のものである。これの結果は後述する。 コード中に出てくるutilsは以下のようなもので、多様な場面で使う便利な関数を集めている。 ```python:utils.py import MeCab import constants import os import pdb from web_page import WebPage def _split_to_words(text, to_stem=False): """ 入力: 'すべて自分のほうへ' 出力: tuple(['すべて', '自分', 'の', 'ほう', 'へ']) """ tagger = MeCab.Tagger('mecabrc') # 別のTaggerを使ってもいい mecab_result = tagger.parse(text) info_of_words = mecab_result.split('\n') words = [] for info in info_of_words: # macabで分けると、文の最後に’’が、その手前に'EOS'が来る if info == 'EOS' or info == '': break # info => 'な\t助詞,終助詞,*,*,*,*,な,ナ,ナ' info_elems = info.split(',') # 6番目に、無活用系の単語が入る。もし6番目が'*'だったら0番目を入れる if info_elems[6] == '*': # info_elems[0] => 'ヴァンロッサム\t名詞' words.append(info_elems[0][:-3]) continue if to_stem: # 語幹に変換 words.append(info_elems[6]) continue # 語をそのまま words.append(info_elems[0][:-3]) return words def words(text): words = _split_to_words(text=text, to_stem=False) return words def stems(text): stems = _split_to_words(text=text, to_stem=True) return stems def load_all_html_files(): pages = [] for query in constants.QUERIES: pages.extend(load_html_files_with_query(query)) return pages def load_html_files_with_query(query): pages = [] for i in range(constants.NUM_OF_FETCHED_PAGES): with open('%s_%s.html' % (query, str(i)), 'r') as f: page = WebPage() page.html_body = f.read() page.remove_html_tags() pages.append(page) return pages def load_html_files(): """ HTMLファイルがあるディレクトリにいる前提で使う """ pages = load_html_files_with_query(constants.QUERY) return pages def go_to_fetched_pages_dir(): if not os.path.exists(constants.FETCHED_PAGES_DIR_NAME): os.mkdir(constants.FETCHED_PAGES_DIR_NAME) os.chdir(constants.FETCHED_PAGES_DIR_NAME) ``` そしてconstantsは以下の通り。 ```python:constants.py FETCHED_PAGES_DIR_NAME = 'fetched_pages' QUERIES = '胃もたれ 虫歯 花粉症対策 鬱 機械 骨折 肩こり 書類'.split(' ') NUM_OF_FETCHED_PAGES = 50 NB_PKL_FILENAME = 'naive_bayes_classifier.pkl' DOC_NUM = 0 MIN_TFIDF = 0.1 TFIDF_RESULT_PKL_FILENAME = 'tfidf_result.pkl' TFIDF_VECTORIZER_PKL_FILENAME = 'tfidf_vectorizer.pkl' ``` QUERIESの順番を見てもらえば、「胃もたれ」カテゴリーが最初に来ることがわかるだろう。DOC_NUM定数は今回の実験のために作ったもので、「胃もたれ」カテゴリーの0番目のファイル、つまり「胃もたれ_0.html」という名前のファイルを指定するために使った。 さて。このコードを実行しよう。 ``` $ python set_tfidf_with_sklearn_to_fetched_pages.py ``` scikit-learnを使っていてもtfidfの計算には時間がかかる。僕の環境では25.81秒かかった。結果。 ``` ['gaJsHost', 'https', 'たれる', 'やけ', '空気嚥下症', '胃酸過多症', '胸', '調理', '食材', '食道裂孔ヘルニア'] 合計36934種類の単語が400ページから見つかりました。 ``` 胃もたれっぽい語だ。胃もたれ_0.html内の語のなかでtfidfが0.1を超えているのは以上10種類の語ということがわかった。 gaJsHostとhttpsはJavaScriptのコードの一部と思われる。うぬー。こういうノイズは全部削除したいのだが、うまい方法が思いつかない。いっそのことアルファベットだけの語は排除したほうがいいかもしれない。 ちなみに「食道裂孔ヘルニア」のような語はMeCabのIPADIC(IPADICの由来については[この記事](http://parame.mwj.jp/blog/0209)が詳しい)には入っていないので、Wikipediaやはてなキーワードの語を辞書に入れるなどして強化する必要がある。やりかたはググってください。 ### 語とtfidfの値のマッピングを確認 公式ページを読んだのだが、tfidfを計算した結果はscipyのcsr_matrixという型で出力される。これはスパースな(0が多い)行列で、個々のドキュメント内の語のtf-idfを0から1までの小数で表現している。 ``` (Pdb) type(x) <class 'scipy.sparse.csr.csr_matrix'> ``` そのtfidfの値の集合と、語とのマッピングがどうなっているかわからなかった(あとでわかった)ので、pdb.set_trace()を使って簡単な実験を行ってみた。 使うメソッドはTfidfVectorizerが持つ - get_feature_names - inverse_transform そしてscipy.sparse.csr_matrixが持つ - toarray である。 まず、文書番号0のWebPageを調べたところ、[胃もたれ.com](http://atdesk.jp/)というページだった。このページに出現する語がどのように表現されているか、調べてみる。 tfidfの計算結果をpickle化したあと、以下のコードを実行した。 ```python:play_with_tfidf.py # -*- coding: utf-8 -*- import pickle import constants import pdb def is_bigger_than_min_tfidf(term, terms, tfidfs): ''' [term for term in terms if is_bigger_than_min_tfidf(term, terms, tfidfs)]で使う list化した、語たちのtfidfの値のなかから、順番に当てる関数。 tfidfの値がMIN_TFIDFよりも大きければTrueを返す ''' if tfidfs[terms.index(term)] > constants.MIN_TFIDF: return True return False if __name__ == '__main__': with open(constants.TFIDF_VECTORIZER_PKL_FILENAME, 'rb') as f: vectorizer = pickle.load(f) with open(constants.TFIDF_RESULT_PKL_FILENAME, 'rb') as f: x = pickle.load(f) pdb.set_trace() terms = vectorizer.get_feature_names() for i in range(3): tfidfs = x.toarray()[i] print([term for term in terms if is_bigger_than_min_tfidf(term, terms, tfidfs)]) ``` pdb.set_traceでブレイクポイントとなり、そこからは対話環境で値を出力できるので、色々な確認作業ができる。 ``` (Pdb) vectorizer.inverse_transform(x)[0] > array(['食道裂孔ヘルニア', '食材', '食事療法', '運営', '逆流性食道炎', '調理', '胸', '胃酸過多症', '胃痛', '胃潰瘍', '胃下垂', '胃がん', '空気嚥下症', '漢方薬', '構造', '慢性胃炎', '十二指腸潰瘍', '医療保険', '免責', '企業情報', 'ポリープ', 'ツボ', 'ケア', 'アメリカンファミリー生命保険会社', 'アフラック', 'ゆく', 'やけ', 'に関して', 'たれる', 'unescape', 'try', 'ssl', 'protocol', 'javascript', 'inquiry', 'https', 'gaJsHost', 'ga', 'err', 'comCopyright', 'analytics', 'Inc', 'Cscript', 'CROSSFINITY', '=\'"', "='", ':"', '.")', '."', ')\u3000', '(("', '("%', "'%", '"))'], dtype='<U26') ``` 「食道裂孔ヘルニア」という語は珍しく、他のページにはめったに出現しないと思われるので、この語をマーカーとして使うことに決めた。 ``` (Pdb) vectorizer.get_feature_names().index('食道裂孔ヘルニア') 36097 ``` で36097番目の語だとわかった。では、0番目のドキュメント(つまり胃もたれ.com)における36097番目の語のtfidfの値は? ``` (Pdb) x.toarray()[0][36097] 0.10163697033184078 ``` かなり高い。文書番号0において単語番号36097の語はtfidfが0.10163697033184078であることがわかった。これだけ高い(まずなにより0でない)tfidfの値が、偶然単語番号36097で出現するとは思えない。x.toarray()はとても疎な行列であり、ほとんどの要素が0のはずだ。よって、vectorizer.get_feature_names()で取れる語リストの順番とx.toarray()で取れるtfidfがセットされた語の順番は同じだと考えていい。 こうして、単語一覧は同じ順序が保たれていることが確認できた。「単語の順番は保たれる」と、公式ドキュメントのどこかに書いてあると思う。 このあと、pdb.set_trace()を削除して、もういちどplay_with_tfidf.pyを実行した。 ``` ['gaJsHost', 'https', 'たれる', 'やけ', '空気嚥下症', '胃酸過多症', '胸', '調理', '食材', '食道裂孔ヘルニア'] ['たれる', 'むかつき', 'やけ', '胃痛', '胸', '過ぎる'] ['TVCM', 'ぐする', 'たれる', 'のむ', 'もたれる', 'やけ', 'やける', 'り', 'アクション', 'サイエンス', 'サクロン', 'セルベール', 'トリプル', 'ベール', '二日酔い', '弱る', '整える', '粘液', '胃痛', '胃薬', '胸', '膨満'] ``` これらの語はtfidfが高く(いかにも高そう)、文書と胃もたれカテゴリーとの類似度を計算する際の素性として有用だと考えられる。 ## まとめ scikit-learn便利。 コードはGithubに上げました。 https://github.com/katryo/tfidf_with_sklearn ## 次回予告 tfidfの計算機能を実装して、scikit-learnとの比較をしたい。 |
|
| 940位 |
|
|||
|
00:50:07 |
|
|
Androidアプリを開発している時に何かと必要になってくる画像読み込み
URLから画像を表示したい時とか画像をキャッシュしておきたいとか色々とやりたいけど、実装が大変だし面倒。 そんな中見つけたライブラリが[Universal Image Loader for Android](https://github.com/nostra13/Android-Universal-Image-Loader)だったのですが、これが中々便利でした。 検索してみると情報が少なかったり古かったりしていたので、ちょっと細かく紹介していこうと思います。 # 特徴 - Android 2.0以降から使える - 導入めっちゃ簡単 - 非同期で画像を読み込み可能 - ディスクキャッシュやメモリキャッシュも簡単実装 - キャッシュする時はディレクトリの容量やファイル数で制限できる - 読み込み時のリスナーをセットできるので、失敗時の画像を表示したりも可能 - 表示時の画像の拡大・縮小もやってくれる - 割りと細かいカスタマイズできそう(Bitmap変換とかそこらへん) # 導入 jarで取り込む方法と[Maven](http://maven.apache.org/)から入れる方法の2つがあるみたいです。 ## jarで入れる方法 1. GitHubのReadmeにjarのダウンロードリンクが貼られてるのでそこからuniversal-image-loader-1.8.4.jarあたりをダウンロードする。 https://github.com/nostra13/Android-Universal-Image-Loader#downloads 2. 自分のプロジェクト内のlibsフォルダにダウンロードしたjarを置く これだけでライブラリが使えます、便利ですね。 ## Mavenで入れる方法 1. これをpom.xmlに書く ```pom.xml <dependency> <groupId>com.nostra13.universalimageloader</groupId> <artifactId>universal-image-loader</artifactId> <version>1.8.4</version> </dependency> ``` これだけでライブラリが使えます、便利ですね。 # 使い方 ## 大まかな流れ 1. ImageLoaderConfiguration.BuilderからImageLoaderConfigurationを生成する 2. DisplayImageOptions.BuilderからDisplayImageOptionsを生成する 3. ImageLoader.displayImageまたはImageLoader.loadImageで画像の読み込みをする ## 細かい使い方 AndroidManifestにパーミッションを追加する ```AndroidManifest.xml <uses-permission android:name="android.permission.INTERNET" /> <!-- SDカードにキャッシュしたい場合 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ``` ImageLoaderの初期化をApplicationで行いたいのでAndroidManifestに宣言する。 ```AndroidManifest.xml <application android:name="com.hoge.MyApplication"> ... </application> ``` AndroidManifestに宣言したMyApplicationを作る ```MyApplication.java public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); // グローバル設定の生成と初期化を行う ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()) .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) .memoryCacheSize(2 * 1024 * 1024) ... // ImageLoaderConfigurationの設定をメソッドチェインで繋いでいく .build(); ImageLoader.getInstance().init(config); } } ``` ちなみにこの初期化を行わないとエラーで落ちるので注意 http://stackoverflow.com/questions/15627031/how-to-use-android-universal-image-loader ### ImageLoaderConfiguration.Builderの設定項目 - **memoryCacheExtraOptions** - メモリキャッシュのサイズを制限できる - **discCacheExtraOptions** - ディスクキャッシュの設定ができる - 引数で渡せるCompressFormatはこれ - http://developer.android.com/reference/android/graphics/Bitmap.CompressFormat.html - **taskExecutor** - 処理を行うためのExecutor - 用途に応じてAsyncTask.SERIAL_EXECUTORもしくはAsyncTask.THREAD_POOL_EXECUTORあたりを渡すと良さそう - **taskExecutorForCachedImages** - キャッシュしている画像を処理する時のExecutor - **threadPoolSize** - 非同期で処理する数 - **threadPriority** - 非同期処理の優先順位 - **tasksProcessingOrder** - これよくわかってない。キューに積まれたタスクをどう処理するかみたいのだろうか - **denyCacheImageMultipleSizesInMemory** - メモリキャッシュ時に、サイズの違う同じ画像を格納するのを許容しないようにする - **memoryCache** - メモリキャッシュの設定 - FIFOLimitedMemoryCache - キャッシュの制限サイズを超えた時に、最近読み込んだ画像を削除する - LRULimitedMemoryCache - キャッシュの制限サイズを超えた時に、一番古い画像を削除する - LargestLimitedMemoryCache - キャッシュの制限サイズを超えた時に、一番サイズが大きい画像を削除する - UsingFreqLimitedMemoryCache - キャッシュの制限サイズを超えた時に、使用頻度が低い画像を削除する - LimitedAgeMemoryCache - オブジェクトの有効期限が過ぎたら更新される? - LruMemoryCache - 制限サイズ以下になるまで画像を削除する - **memoryCacheSize** - メモリキャッシュの制限サイズ - **discCache** - ディスクキャッシュの設定 - FileCountLimitedDiscCache - ファイル数で制限する。制限を超えた場合は古いファイルから削除される - LimitedAgeDiscCache - 指定された期間を過ぎたファイルが削除される - TotalSizeLimitedDiscCache - 最大サイズで制限する。制限を超えた場合は使用頻度の低いファイルから削除される - UnlimitedDiscCache - 制限なし - **discCacheSize** - ディスクキャッシュの制限サイズ - **discCacheFileCount** - ディスクキャッシュのファイル数 - **discCacheFileNameGenerator** - キャッシュファイルの名前を生成するクラスを設定 - FileNameGenerator - URIからファイル名を生成 - HashCodeFileNameGenerator - ハッシュからファイル名を生成 - Md5FileNameGenerator - MD5からファイル名を生成 - **imageDownloader** - ダウンローダーをセットする - BaseImageDownloader - 基本 - NetworkDeniedImageDownloader - ネットワークを遮断する(Exceptionが飛ぶ) - SlowNetworkImageDownloader - **imageDecoder** - Bitmapに変換する処理のクラスをセットする - BaseImageDecoder - 基本 - **defaultDisplayImageOptions** - 後で説明するDisplayImageOptionsをセットする - **enableLogging** - ログを出力する - **build** - ImageLoaderConfigurationを生成する ### DisplayImageOptions.Builderの設定項目 - **showStubImage** - 読込中の画像を設定する - **showImageForEmptyUri** - URIがnullだった時の画像を設定する - **showImageOnFail** - 読み込み失敗時の画像を設定する - **resetViewBeforeLoading** - 読み込み前にViewをリセットする - **delayBeforeLoading** - 読み込みのタスクを開始するまでの遅延時間を設定する - **cacheInMemory** - メモリキャッシュさせる - **cacheOnDisc** - ディスクキャッシュさせる - **preProcessor** - あんまりよくわかってない - **postProcessor** - あんまりよくわかってない - **extraForDownloader** - あんまりよくわかってない - **imageScaleType** - 画像の拡大縮小を設定する - NONE - 何もしない - IN_SAMPLE_POWER_OF_2 - 小さい画像が生成されるまでのステップを二倍軽減? - IN_SAMPLE_INT - inSampleSizeで拡大縮小する http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize - EXACTLY - ターゲットサイズに対して正確に縮小させる。画像がターゲットサイズよりも小さかった場合は拡大しない - EXACTLY_STRETCHED - ターゲットサイズに対して正確に拡大縮小させる - **bitmapConfig** - Bitmap.Configをセットできる http://developer.android.com/reference/android/graphics/Bitmap.Config.html - **decodingOptions** - あんまりよくわかってない - **displayer** - 表示方法を設定する - SimpleBitmapDisplayer - 基本 - FadeInBitmapDisplayer - フェードインのアニメーションをかける - FakeBitmapDisplayer - 何も表示しない - RoundedBitmapDisplayer - 角丸 - **handler** - Handlerをセットできる - **build** - DisplayImageOptionsを生成する 実際に画像を読み込みたい時はImageLoaderのインスタンスを取得して、loadImageまたはdisplayImageを呼べば良い ```HogeActivity.java @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.hoge); ImageView imageView = (ImageView) findViewById(R.id.fuga); String imageUrl = "http://hoge.com" ImageLoader loader = ImageLoader.getInstance(); // loadImageを使う場合 loader.loadImage(imageUrl, new SimpleImageLoadingListener() { @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { imageView.setImageBitmap(loadedImage); } }); // displayImageを使う場合 loader.displayImage(imageUrl, imageView); ... } ``` これだけで画像読み込み周りの処理が大体できちゃうのは便利。 この他の使い方などはこちらに色々載ってた https://github.com/nostra13/Android-Universal-Image-Loader#usage |
|
| 941位 |
|
|||
|
22:52:04 |
|
|
対象となるgemのインストール Gemfileに以下を追記する ```Gemfile gem "therubyracer" gem "less-rails" #Sprockets (what Rails 3.1 uses for its asset pipeline) supports LESS gem "twitter-bootstrap-rails" ``` ・twitter-bootstrap-railsはLESSを使用するため、therubyracerが必須 ・LESSのスタイルシートを使うにはless-railsというgemが必要 ・JavaScriptのエンジンであるv8*3をRubyから使えるようにするgem (謎) ということで上記3つのgemを入れます。 追記し終わったらgemの更新 >$ bundle install それが終わったらbootstrapジェネレータを使ってassetsへincludeファイルをインストール >$ rails generate bootstrap:install less 続いてlayoutファイルの生成 >rails generate bootstrap:layout application fixed レスポンシブデザインに対応させたいときはこっちのコマンド >rails generate bootstrap:layout application fluid #####注意 layoutにtwitter-bootstrapを反映させることでfavicon_link_tagが挿入されるのでこのままdeployするとfavicon_link_tagがないことでassets_precompileの段階でエラーが起きます。 deployするときはlayoutからfavicon関連を削除してください。 ##デザイン bootstrap公式ページ(http://bootswatch.com/) から 気に入ったのを選び、variables.lessをbootstrap_and_overrides.css.lessへ追記すればデザインが変更できます。 ##参考 ・Twitter Bootstrap for Rails 3.1 Asset Pipeline https://github.com/seyhunak/twitter-bootstrap-rails ・twitter-bootstrap-rails ことはじめ http://qiita.com/items/7b7e5934004a49591ed8 |
|
| 942位 |
|
|||
|
00:52:08 |
(sikmi Inc. 所属) |
|
gitでの話ですが、bitbucketにpushして自動でサーバにデプロイするまでの一連の流れをメモ ネットで探すと見つかるのですが、ちょっとわかりにくかったので 開発初心者なのでいろいろよろしくないやり方も多いと思います もしご指摘あれば頂けるとうれぴーです こんなんでも誰かの役に立てばいいなと思いました 全体の流れとしては、 1. bitbucketにpush 2. (自動的に)bitbucketからサーバにPOST 3. PHPでPOST受けて、(自動的に)git pullする って感じ ## 環境とか 下記の環境やら想定でやってます * bitbucketでgitでコード管理している * サーバ:さくらVPS 1Gプランのやつ * サーバでgit,PHPが動く ## bitbucketの設定 1. ログインしたら、右上の方にある歯車アイコンをクリック 2. 左メニューの`Services`をクリック 3. 下のような画面になるので、プルダウンメニューから`POST`を選択し、`Add servise`ボタンをクリック  4. すると画像のようにPOSTという項目が追加される 5. URLに`デプロイ先サーバのURL+deploy.php`を入力し`Save` 例:http://yourdomain.com/deploy.php URLについては後で解説 都合の良いものに変えちゃって下さい この設定をすると、bitbucketにpushする度に指定したURLにPOSTしてくれます ちなみに、今回は使っておりませんがPOSTの際に[こんな感じ](https://confluence.atlassian.com/display/BITBUCKET/POST+hook+management)のデータもやりとりしているので、もっといろいろやれそう ## サーバ側の準備 これが結構めんどかった 開発初心者なので、いい勉強になりました ちょいちょい sudo -u apache というのを使っておりますが、 これは権限の管理をapacheユーザやるためですねー >ここからはサーバコマンドライン >sudo -u apache ssh-keygen 作成先は /var/www/.ssh/ pass phraseはなし 作成したid_rsa.pubをbitbucketの`Deployment keys`に追加 さっきの歯車アイコンのとこにあります >サーバコマンドライン >sudo -u apache git clone [自動デプロイしたいbitbucketのリポジトリ] ## サーバ側のプログラム サーバ側にはPOSTを受け取り処理するPHPファイルを置きます ```php:deploy.php <?php if($_SERVER["REQUEST_METHOD"] != "POST"){ // GETによるアクセス }else{ // POSTによるアクセス $cmd = '. /var/www/html/update.sh'; $retstr = system($cmd, $res); $fp = fopen("deploy.log", "a"); fwrite($fp, date('c')."\n"); if ($retstr === FALSE || 0 !== $res) { fwrite($fp, "cmd false\n"); } else { fwrite($fp, $res."\n"); } fclose($fp); } ?> ``` このPHPファイルでは下記の`/var/www/html/update.sh`を読み込んでます git pullしてるだけです 現状だとどんなbranchがpushされてもgit pullします 特に触らなければbranchはmasterのままなので、masterが更新されたときだけWebには反映されます ```Bash:update.sh #!/bin/sh cd /var/www/html/[rep-name] /usr/bin/git --git-dir=/var/www/html/[rep-name]/.git pull ``` ## まとめ とりあえずこれで、bitbucketにpushして自動的にサーバに更新を反映できます 超重要な注意点ですが、このまま使うと誰でもこのPHPにPOSTアクセスすると無条件にgit pullしちゃうという超危険な状態です POST元がbitbucketのときだけgit pullするみたいな書き方しないとですね (初心者なのでよくわからない) デプロイって書いちゃいましたが、これはgitの更新だけですね^^; アプリケーションだったら、app restart的な処理をupdate.shに書き加える必要があります (現在nodeアプリ開発中ですが、うまくできない) 参考:[gitにpushしたwebサイトを自動的に同期させる](http://transrain.net/blog/2011/04/05/211200) |
|
| 943位 |
|
|||
|
23:47:56 |
|
|
「今まで Rails でテストを疎かにしてきたけど、これからはちゃんとテストを書きたいな」と思っている人のためのチュートリアルです。 (自分用のメモでもあります。。) ## 環境 * Mac OS X 10.6.8 * Ruby 1.9.2 * Rails 3.2.6 * RSpec 2.10.0 * Spork 0.9.2 * FactoryGirl 3.4.0 * DatabaseCleaner 0.8.0 * MySQL 5.5.15 ## プロジェクトを生成 チュートリアル用の Rails 3.2 アプリケーションを生成します。 Rails 標準の Test::Unit ではなく、RSpec を使います。 ``` $ rvm 1.9.2-p290@rails32 $ mkdir ~/projects && cd ~/projects $ rails new blog -T -d mysql --skip-bundle && cd blog create create README.rdoc create Rakefile create config.ru create .gitignore create Gemfile create app create app/assets/images/rails.png create app/assets/javascripts/application.js create app/assets/stylesheets/application.css create app/controllers/application_controller.rb create app/helpers/application_helper.rb create app/mailers create app/models create app/views/layouts/application.html.erb create app/mailers/.gitkeep create app/models/.gitkeep create config create config/routes.rb create config/application.rb create config/environment.rb create config/environments create config/environments/development.rb create config/environments/production.rb create config/environments/test.rb create config/initializers create config/initializers/backtrace_silencers.rb create config/initializers/inflections.rb create config/initializers/mime_types.rb create config/initializers/secret_token.rb create config/initializers/session_store.rb create config/initializers/wrap_parameters.rb create config/locales create config/locales/en.yml create config/boot.rb create config/database.yml create db create db/seeds.rb create doc create doc/README_FOR_APP create lib create lib/tasks create lib/tasks/.gitkeep create lib/assets create lib/assets/.gitkeep create log create log/.gitkeep create public create public/404.html create public/422.html create public/500.html create public/favicon.ico create public/index.html create public/robots.txt create script create script/rails create tmp/cache create tmp/cache/assets create vendor/assets/javascripts create vendor/assets/javascripts/.gitkeep create vendor/assets/stylesheets create vendor/assets/stylesheets/.gitkeep create vendor/plugins create vendor/plugins/.gitkeep ``` ## RSpecを使う RSpec を使うため、Gemfile に追記します。 ``` $ vim Gemfile ``` ```ruby:Gemfile group :development, :test do gem 'rspec-rails', '2.10.1' end ``` gem をインストールします。 ``` $ bundle --path vendor/bundle Fetching source index for https://rubygems.org/ Installing rake (0.9.2.2) Installing i18n (0.6.0) Installing multi_json (1.3.6) Installing activesupport (3.2.6) Installing builder (3.0.0) Installing activemodel (3.2.6) Installing erubis (2.7.0) Installing journey (1.0.4) Installing rack (1.4.1) Installing rack-cache (1.2) Installing rack-test (0.6.1) Installing hike (1.2.1) Installing tilt (1.3.3) Installing sprockets (2.1.3) Installing actionpack (3.2.6) Installing mime-types (1.18) Installing polyglot (0.3.3) Installing treetop (1.4.10) Installing mail (2.4.4) Installing actionmailer (3.2.6) Installing arel (3.0.2) Installing tzinfo (0.3.33) Installing activerecord (3.2.6) Installing activeresource (3.2.6) Using bundler (1.0.21) Installing coffee-script-source (1.3.3) Installing execjs (1.4.0) Installing coffee-script (2.2.0) Installing rack-ssl (1.3.2) Installing json (1.7.3) with native extensions Installing rdoc (3.12) Installing thor (0.15.2) Installing railties (3.2.6) Installing coffee-rails (3.2.2) Installing diff-lcs (1.1.3) Installing jquery-rails (2.0.2) Installing mysql2 (0.3.11) with native extensions Installing rails (3.2.6) Installing rspec-core (2.10.1) Installing rspec-expectations (2.10.0) Installing rspec-mocks (2.10.1) Installing rspec (2.10.0) Installing rspec-rails (2.10.1) Installing sass (3.1.19) Installing sass-rails (3.2.5) Installing uglifier (1.2.4) Your bundle is complete! It was installed into ./vendor/bundle ``` `bundle install` は `bundle` と省略できます。 また、`--path` オプションにより gem のインストール先を指定すると、.bundle/config ファイルが生成されます。 ```.bundle/config --- BUNDLE_PATH: vendor/bundle BUNDLE_DISABLE_SHARED_GEMS: '1' ``` `BUNDLE_PATH` が設定されるため、次回以降 `bundle install` する際に `--path` は必要ありません。 次に、RSpec の初期設定を行います。 ``` $ bundle exec rails g rspec:install create .rspec create spec create spec/spec_helper.rb ``` これで、Rails で RSpec が使えるようになりました。 ## アプリケーションの雛形を生成 例として、Article モデルを扱うコードを Scaffold で生成します。 ``` $ bundle exec rails g scaffold article title:string content:text invoke active_record create db/migrate/20120616114653_create_articles.rb create app/models/article.rb invoke rspec create spec/models/article_spec.rb invoke resource_route route resources :articles invoke scaffold_controller create app/controllers/articles_controller.rb invoke erb create app/views/articles create app/views/articles/index.html.erb create app/views/articles/edit.html.erb create app/views/articles/show.html.erb create app/views/articles/new.html.erb create app/views/articles/_form.html.erb invoke rspec create spec/controllers/articles_controller_spec.rb create spec/views/articles/edit.html.erb_spec.rb create spec/views/articles/index.html.erb_spec.rb create spec/views/articles/new.html.erb_spec.rb create spec/views/articles/show.html.erb_spec.rb invoke helper create spec/helpers/articles_helper_spec.rb create spec/routing/articles_routing_spec.rb invoke rspec create spec/requests/articles_spec.rb invoke helper create app/helpers/articles_helper.rb invoke rspec invoke assets invoke coffee create app/assets/javascripts/articles.js.coffee invoke scss create app/assets/stylesheets/articles.css.scss invoke scss create app/assets/stylesheets/scaffolds.css.scss ``` DB の設定を行います。 ``` $ vim config/database.yml ``` ```config/database.yml common: &common adapter: mysql2 encoding: utf8 reconnect: false pool: 5 username: root password: yourpassword host: localhost development: <<: *common database: blog_dev test: <<: *common database: blog_test production: <<: *common database: blog ``` DB およびテーブルを作成します。 ``` $ bundle exec rake db:create $ bundle exec rake db:migrate == CreateArticles: migrating ================================================= -- create_table(:articles) -> 0.0896s == CreateArticles: migrated (0.0897s) ======================================== ``` これで、アプリケーションが動作するようになりました。 ## テストを実行 ひとまず、テストを実行してみます。 ``` $ bundle exec rake ``` 下記のコマンドでも実行できます。 ``` $ bundle exec rspec spec ``` 2つのテストが Pending となっていることがわかります。 * spec/helpers/articles_helper_spec.rb * spec/models/article_spec.rb Pending のテストコードを削除します。 ``` $ vim spec/helpers/articles_helper_spec.rb ``` ```ruby:spec/helpers/articles_helper_spec.rb require 'spec_helper' # Specs in this file have access to a helper object that includes # the ArticlesHelper. For example: # # describe ArticlesHelper do # describe "string concat" do # it "concats two strings with spaces" do # helper.concat_strings("this","that").should == "this that" # end # end # end describe ArticlesHelper do # pending "add some examples to (or delete) #{__FILE__}" を削除 end ``` ``` $ vim spec/models/article_spec.rb ``` ```ruby:spec/models/article_spec.rb require 'spec_helper' describe Article do # pending "add some examples to (or delete) #{__FILE__}" を削除 end ``` テストを実行し、すべてのテストが「成功」となっていることを確認します。 ``` $ bundle exec rspec spec ............................ Finished in 0.80978 seconds 28 examples, 0 failures ``` ## テストコードを実装 正常にモデルが new されることを検証するテストを書いてみます。 ``` $ vim spec/models/article_spec.rb ``` ```ruby:spec/models/article_spec.rb # coding: utf-8 require 'spec_helper' describe Article do describe '.new' do context 'given valid attributes' do subject { Article.new(:title => 'a', :content => 'a') } it { should be_valid } end end end ``` テストを実行して「成功」となっていることを確認します。 ``` $ bundle exec rspec spec ............................. Finished in 0.69964 seconds 29 examples, 0 failures ``` ひとつのファイルのみ実行したい場合は、下記のように実行します。 ``` $ bundle exec rspec spec/models/article_spec.rb . Finished in 0.09452 seconds 1 example, 0 failures ``` 続いて、Article モデルの title は必須となることを想定してテストを書きます。 ``` $ vim spec/models/article_spec.rb ``` ```ruby:spec/models/article_spec.rb # coding: utf-8 require 'spec_helper' describe Article do describe '.new' do context 'given valid attributes' do subject { Article.new(:title => 'a', :content => 'a') } it { should be_valid } end # 追記ここから context 'given null title' do subject { Article.new(:content => 'a') } it { should have(1).errors_on(:title) } end # 追記ここまで end end ``` ``` $ vim spec/controllers/articles_controller_spec.rb ``` ```ruby:spec/controllers/articles_controller_spec.rb def valid_attributes {:title => 'a', :content => 'a'} end ``` プロダクトコードを実装していませんが、試しにテストを実行してみます。 ``` $ bundle exec rspec spec .................F............ Failures: 1) Article.new given null title Failure/Error: it { should have(1).errors_on(:title) } expected 1 errors on :title, got 0 # ./spec/models/article_spec.rb:14:in `block (4 levels) in <top (required)>' Finished in 0.74384 seconds 30 examples, 1 failure Failed examples: rspec ./spec/models/article_spec.rb:14 # Article.new given null title ``` 結果は当然「失敗」となります。 それでは、Article モデルにバリデーションを実装します。 ``` $ vim app/models/article.rb ``` ```ruby:app/models/article.rb # coding: utf-8 class Article < ActiveRecord::Base attr_accessible :content, :title validates(:title, :presence => true) end ``` テストを実行します。 ``` $ bundle exec rspec spec .............................. Finished in 0.87777 seconds 30 examples, 0 failures ``` バリデーションを実装したため、結果は「成功」となります。 その2へ続きます。 |
|
| 944位 |
|
|||
|
00:04:14 |
(Pivotal 所属) |
|
1. `gem list` でリストを表示し、
2. `cut -d" " -f1` で名前を抽出し、 3. それを `gem uninstall` に投げる。 を下記で実行できる。 ``` gem list | cut -d" " -f1 | xargs gem uninstall -aIx ``` |
|
| 945位 |
|
|||
|
16:22:50 |
(DTS corporation 所属) |
|
# はじめに
デザインに手をかけず、とりあえずアプリを動かすためにBootstrapを使っています。 デフォルトのまま使えるかと思ったら、やっぱりちょこちょこ調整したくなったのでメモ。 # 2つのやり方 場合によって次のやり方を使い分けています。使い分けは後述します。 ## CSSを上書きする カスタマイズ用のcssファイルを作ってbootstrapのCSSより後から読み込みます。 ```html <!-- Bootstrap --> <link href="/xxx/public/bootstrap-3.0.3-dist/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- カスタマイズ用CSS --> <link rel="stylesheet" media="screen" href="/xxx/public/stylesheets/bootstrap-custom.css"> <link rel="stylesheet" media="screen" href="/xxx/public/stylesheets/my.css"> ``` カスタマイズ用CSSは2つに分けています。 bootstrap-custom.cssは、全体に適用するものを書きます。 my.cssは一部分だけ(このdivの中身だけ変えたい)というものを書きます。 Bootstrapのデフォルトと異なるスタイルを適用しているのが全体なのか一部なのか意識するためです。 ## 公式サイトの[Customize and download](http://getbootstrap.com/customize/)を使う ここでlessの変数を変更すると、それを反映したcssがダウンロードできます。 変数がたくさんあってくじけそうになりますが、必要なとこだけ変更すればいいので大丈夫です! >  # 全体的にフォントサイズを変えたい [Customize and download](http://getbootstrap.com/customize/)で@font-size-baseを変更します。 Less variablesの配下のTypographyの中にあります。 Bootstrapでは@font-size-baseを元にしてH1~H6などのサイズを相対的に決めていますので、1か所変えるだけで全てのフォントサイズが変更できます。 >  # 全体的にフォントの種類を変えたい これもフォントサイズと同様に、[Customize and download](http://getbootstrap.com/customize/)を使います。 @font-family-baseでフォントの種類が決まるのですが、@font-family-baseがさらに@font-family-sans-serifを参照しているのでこちらを変更します。 次の例はMeiryo UIを追加しているところです。 >  # 全体的にxxxを変えたいんだけど[Customize and download](http://getbootstrap.com/customize/)で設定できないよ? lessの変数になっていない項目は、CSSを上書きします。 たとえばTooltipの色やフォントサイズを変える等。 ```bootstrap-custom.css /* ツールチップ */ .tooltip > .tooltip-inner { background-color: #d9534f; font-size: 16px; font-weight: normal; } .tooltip.top > .tooltip-arrow { border-top-color: #d9534f; } ``` # 部分的にxxxを変えたい たとえば、サイト内にあるテーブルのうち、ある1つのテーブルのスタイルだけを変えたいといった場合、cssを上書きします。 部分的な適用であることを意識するために、my.cssに書いておくといいでしょう。 ```my.css /* テーブル */ .my-table .table-hover tbody .tr-pointer:hover td { background-color: #AFEEEE; cursor: pointer; } ``` |
|
| 946位 |
|
|||
|
14:22:25 |
(COOKPAD Inc. 所属) |
|
JavaScript のデバッガ、便利ですよね。つかって、ますよね!
Chrome, Firefox 両方の Developer Tool (Web Inspector) には JavaScript デバッガが付属しています。両者には Event Listener Breakpoints 機能が存在し、特定イベントのリスナ全部にブレークポイントを仕掛ける事が可能です。 しかし、jQuery の `$.on`, `$.bind`, その他 `$.click(fn)` 等でイベントハンドラを定義すると jQuery の中で `addEventListeners` が呼ばれ内部で処理をした後に実際に jQuery に渡した関数が呼ばれます。 そのため、JavaScript デバッガは jQuery 内部でブレークし本来飛んでほしいはずの関数に一発で飛んでくれません。 そこで Firefox には [Library black boxing](https://developer.mozilla.org/en-US/docs/Tools/Debugger#Black_boxing), Chrome には Developer Tools experimentals の一つに Skip stepping through sources with particular names という機能が存在します。 Firefox は UI がもう整っていてスキップしたいファイルの横にあるボタンを押す事で black box とする事ができて、black box されているソースはステップ実行でスルーされる仕様のようです。こちらは既に公式のドキュメントが存在するためリンクに留め、詳細な手段は割愛します: https://developer.mozilla.org/en-US/docs/Tools/Debugger#Black_boxing Chrome はまだ Experimental 扱い (chrome://flags からの有効化が必要) でドキュメントもなさそうなので以下に。 1. chrome://flags で "Enable Developer Tools experiments." を有効化する 2. Chrome を再起動 3. Developer Tool を開いて、右上歯車→Experiments→Enable frameworks debugging support をオン 4. Developer Tool を一度 __閉じてもういちど開く__ 5. Developer Tool 右上歯車→General→Sources→Skip stepping through sources with particular names のチェックを入れる 6. Pattern にスキップしたいファイルの一部を入れる (`jquery` 等) これでとりあえず jQuery 等をステップ実行時にスルーする事ができました。Firefox みたいに UI ととのって non experimental になるのが待ち遠しい。 ## 確認 - Chrome Version 34.0.1797.2 dev ## 参考 - http://www.divshot.com/blog/tips-and-tricks/ignoring-library-code-while-debugging-in-chrome/ - https://src.chromium.org/viewvc/blink?revision=155244&view=revision |
|
| 947位 |
|
|||
|
03:48:33 |
(Wondershake, Inc. 所属) |
|
GNU Screen 開発版から移ってきた新人です。 移行して10日間くらい使ってみて、最低限ここ覚えておけば/いじっておけば業務に支障ないかな、って思うまとめです。 (ただし、vimmer向け) よろしくおねがいします。 ## 最低限覚えたぽいコマンド ### セッションの一覧 ``` tmux ls ``` ### セッションの再開 ``` tmux attach tmux a ``` ### セッション殺す ``` tmux kill-session -t session_name ``` ### セッションの中断 ``` <C-t> d ``` ### 縦分割 デフォルト ``` <C-t> % ``` デフォルトから変えた。 ```.tmux.conf unbind % bind | split-window -h ``` ``` <C-t> | ``` ### 横分割 デフォルト ``` <C-t> " ``` デフォルトから変えた。 ```.tmux.conf unbind '"' bind '-' split-window -v ``` ``` <C-t> - ``` ### Paneの移動 下記でvimっぽくするので、覚えない。 ### Paneをバラす ``` <C-t> ! ``` ### Windowの作成 ``` <C-t> c ``` ### Windowの移動 次のWindow ``` <C-t> n ``` 前のWindow ``` <C-t> p ``` ### kill Window ``` <C-t> & ``` ### Windowの名前変更 作業中にWindow増やしたい!と思ったときに、 適した名前変えると分かりやすくて良いです。 ``` <C-t> , ``` ### 困ったら ``` <C-t> ? ``` ## 少しだけカスタマイズ ### prefixの変更 Screen時代からずっと変えてたので、慣れてる方に変えました。 ```.tmux.conf unbind-key C-b set-option -g prefix C-z bind-key C-z send-prefix ``` ### 設定の再読み込み 最初は設定いじりまくるので、とりあえず。 ```.tmux.conf unbind r bind C-r source-file ~/.tmux.conf ; display-message "reloaded" ``` http://www.mindfuzz.net/?p=178 ### vimmer向け emacs向けもあるようです。 ``` set-window-option -g mode-keys vi ``` ### 履歴を長く Screenでもやってたので、とりまこれくらいは。 ``` set-option -g history-limit 10000 ``` ### Paneの移動 最初、Windowの移動をviライクにしていたのだけど、Screenでもwindowはn/pで移動なので、Paneの移動をviライクにした。 そのほうがviと画面切り替えた時もスムーズ。 ```.tmux.conf unbind l bind -r k select-pane -U bind -r j select-pane -D bind -r h select-pane -L bind -r l select-pane -R ``` ### Windowの移動 これやめた。 ### tmuxで選択した文字列をMacのクリップボードへ ここに書くと長くなるので割愛ですが、やりました。 あんまりスマートじゃないけど、無いよりは全然良い。 https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard/ ```.tmux.conf # pbcopy for mac patch # SEE: https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard/ set-option -g default-command "reattach-to-user-namespace -l zsh" bind ^y run-shell 'tmux-pbcopy' ``` ## 感想 Screenからうつってきた人はwindowとpaneの概念を理解するまでしっくりこないかも。 でも、カスタマイズが全体的に楽な気がします。hackしやすいのは良いこと。 あと、tmuxinator使うようになったら、paneを編集するコマンドわりと使わなくなった。 リモートでも動かすようになればまた変わるかな。 |
|
| 948位 |
|
|||
|
21:33:38 |
|
|
MySQLには文字列の照合順序(collation)というのがあって、MySQL側でのcharset utf8のときのデフォルトの照合順序はutf8_general_ciです。 ActiveRecord::Migrationでは明示的に照合順序を指定しない場合、charset utf8で照合順序utf8_unicode_ciのデータベースを作成しますが、これは少なくとも日本語圏では多くの人が期待する挙動ではないと思われるので注意が必要です。 たとえば、以下のようなファミリーテーブルをrake db:migrateすると ```ruby:db/migrate/20121112110702_create_families.rb # coding: utf-8 class CreateFamilies < ActiveRecord::Migration def change create_table :families do |t| t.string :name t.string :relationship end # add_index :families, :relationship, unique: true Family.create(name: 'ユイ', relationship: '本人') Family.create(name: 'キリト', relationship: 'パパ') Family.create(name: 'アスナ', relationship: 'ハハ') end end ``` utf8_unicode_ciなテーブルが作成されます。 ```sql CREATE TABLE `families` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `relationship` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ``` ```sql SELECT * FROM families; +----+-----------+--------------+ | id | name | relationship | +----+-----------+--------------+ | 1 | ユイ | 本人 | | 2 | キリト | パパ | | 3 | アスナ | ハハ | +----+-----------+--------------+ ``` ここで'パパ'を検索すると、utf8_unicode_ciでは'ハ'と'パ'は等価と扱われるので'ハハ'も検索に引っかかります。この挙動を期待するケースはあまり多くないのではないでしょうか。 ```sql SELECT * FROM families WHERE relationship = 'パパ'; +----+-----------+--------------+ | id | name | relationship | +----+-----------+--------------+ | 2 | キリト | パパ | | 3 | アスナ | ハハ | +----+-----------+--------------+ ``` また、relationshipカラムにUNIQUEインデックスを張ってあると'パパ'と'ハハ'がUNIQUE制約を満たせないので以下のようにエラーになります。 ``` Mysql2::Error: Duplicate entry 'ハハ' for key 'relationship': INSERT INTO `families` (`name`, `relationship`) VALUES ('アスナ', 'ハハ') ``` ということで、照合順序utf8_general_ciなデータベースを作成したいわけですが、これはコンフィグで`charset`と`collation`を指定するだけで簡単にいけます。 ```yaml:config/database.yml development: adapter: mysql2 encoding: utf8 charset: utf8 collation: utf8_general_ci reconnect: false database: sao_development ``` これでrake db:migrateすると ```sql CREATE TABLE `families` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `relationship` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `relationship` (`relationship`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ``` MySQL側のデフォルトの照合順序(utf8_general_ci)で作成されて ```sql SELECT * FROM families WHERE relationship = 'パパ'; +----+-----------+--------------+ | id | name | relationship | +----+-----------+--------------+ | 2 | キリト | パパ | +----+-----------+--------------+ ``` ちゃんと'パパ'だけが検索に引っかかりました! これで家族におばあちゃんが増えても安心ですね!! |
|
| 949位 |
|
|||
|
22:46:56 |
|
|

新しく普通に使える **http proxyサーバ** を書いた。 # https をサポートする http proxy サーバ 前回、Qiitaに投稿した記事「[Node.jsによる必要最小限のhttpサーバとhttpsサーバとhttp proxyサーバ](https://qiita.com/LightSpeedC/items/3cae342fd7e79a21869f)」では、 **http proxyサーバ** と言っても、本当に http だけしかサービスしてくれないので、 proxy 経由では、Google さえアクセスできず、あまり役に立ちませんでした。 そこで、今度は HTTP/1.1 の CONNECT メソッドをサポートしました。 これで Google も Facebook もアクセスできます。 またまた、必要最小限の機能しか実装していません。 なので、 **注意点** がひとつあります。 この **http proxyサーバ** を動作させる前に、以下のリンクの情報をお読みください。 [脆弱なホストを狙った不正中継を見抜く - @IT](http://www.atmarkit.co.jp/ait/articles/0711/07/news125.html) HTTP/1.1 CONNECT をサポートするという事は、つまり この **http proxyサーバ** にアクセスできる人は、ここから到達できる全ての IP と port 全てに、 つまり踏み台にして、どこにでも、どの様なプロトコルでもアクセスできる様になるという事です。 **http proxyサーバ** は、こういう不正アクセスにも気をつけていなければ、 どんな事でも許可してしまう事になります。 内向きから外向きはいいけど、外部からのアクセスは許可しない、など。 ファイアウォールの無い環境では実行しない方がいいかも。 このプログラムは、セキュリティ的に なんにもしていませんので、ご注意ください。 # http proxy サーバのコード **httpsをサポートするhttp proxyサーバ** を無理やり80桁以内×80行に収めました。(笑 http service port 番号と同じ行数で実装できた。わ~い。 サービスする時のport番号のデフォルトは、やはり 8080番でいいかな。 ```js:http-proxy-server-in-80-lines.js 'use strict'; const http = require('http'), url = require('url'), net = require('net'); const HTTP_PORT = process.argv[2] || 8080; // internal proxy server port const PROXY_URL = process.argv[3] || null; // external proxy server URL const PROXY_HOST = PROXY_URL ? url.parse(PROXY_URL).hostname : null; const PROXY_PORT = PROXY_URL ? (url.parse(PROXY_URL).port || 80) : null; const server = http.createServer(function onCliReq(cliReq, cliRes) { let svrSoc; const cliSoc = cliReq.socket, x = url.parse(cliReq.url); const svrReq = http.request({host: PROXY_HOST || x.hostname, port: PROXY_PORT || x.port || 80, path: PROXY_URL ? cliReq.url : x.path, method: cliReq.method, headers: cliReq.headers, agent: cliSoc.$agent}, function onSvrRes(svrRes) { svrSoc = svrRes.socket; cliRes.writeHead(svrRes.statusCode, svrRes.headers); svrRes.pipe(cliRes); }); cliReq.pipe(svrReq); svrReq.on('error', function onSvrReqErr(err) { cliRes.writeHead(400, err.message, {'content-type': 'text/html'}); cliRes.end('<h1>' + err.message + '<br/>' + cliReq.url + '</h1>'); onErr(err, 'svrReq', x.hostname + ':' + (x.port || 80), svrSoc); }); }) .on('clientError', (err, soc) => onErr(err, 'cliErr', '', soc)) .on('connect', function onCliConn(cliReq, cliSoc, cliHead) { const x = url.parse('https://' + cliReq.url); let svrSoc; if (PROXY_URL) { const svrReq = http.request({host: PROXY_HOST, port: PROXY_PORT, path: cliReq.url, method: cliReq.method, headers: cliReq.headers, agent: cliSoc.$agent}); svrReq.end(); svrReq.on('connect', function onSvrConn(svrRes, svrSoc2, svrHead) { svrSoc = svrSoc2; cliSoc.write('HTTP/1.0 200 Connection established\r\n\r\n'); if (cliHead && cliHead.length) svrSoc.write(cliHead); if (svrHead && svrHead.length) cliSoc.write(svrHead); svrSoc.pipe(cliSoc); cliSoc.pipe(svrSoc); svrSoc.on('error', err => onErr(err, 'svrSoc', cliReq.url, cliSoc)); }); svrReq.on('error', err => onErr(err, 'svrRq2', cliReq.url, cliSoc)); } else { svrSoc = net.connect(x.port || 443, x.hostname, function onSvrConn() { cliSoc.write('HTTP/1.0 200 Connection established\r\n\r\n'); if (cliHead && cliHead.length) svrSoc.write(cliHead); cliSoc.pipe(svrSoc); }); svrSoc.pipe(cliSoc); svrSoc.on('error', err => onErr(err, 'svrSoc', cliReq.url, cliSoc)); } cliSoc.on('error', err => onErr(err, 'cliSoc', cliReq.url, svrSoc)); }) .on('connection', function onConn(cliSoc) { cliSoc.$agent = new http.Agent({keepAlive: true}); cliSoc.$agent.on('error', err => console.log('agent:', err)); }) .listen(HTTP_PORT, () => console.log('http proxy server started on port ' + HTTP_PORT + (PROXY_URL ? ' -> ' + PROXY_HOST + ':' + PROXY_PORT : ''))); function onErr(err, msg, url, soc) { if (soc) soc.end(); console.log('%s %s: %s', new Date().toLocaleTimeString(), msg, url, err + ''); } ``` # 起動方法 `http-proxy-server-in-80-lines.js` として保存してください。 以下の様なコマンドで起動します。 そのまま外部に接続できる環境の時: `node http-proxy-server-in-80-lines 8080` 会社内など外部に接続する時に、proxy サーバが必要な場合: `node http-proxy-server-in-80-lines 8888 http://127.0.0.1:8080` 上記の両方を起動すると、多段串という事になりますね。 # 特徴と特記事項 ## https サポート https をサポートしました。 HTTP/1.1 CONNECTメソッドを実装する必要がありました。 ## 上位の proxy サーバが指定可能 自身も proxy サーバですが、リクエストを更に上位の proxy サーバに投げられる様になっています。 ## Node.js と http プロトコル勉強用 できるだけ、正しく実装したつもりです。 どこで何を実行すべきなのか、考えてコードを配置しました。 特に関数定義などは配置する場所で内側で使用できる変数が変わります。 変数が参照できるからといって、タイミングによって中身が正しいとも限りませんしね。 ## 必要十分なエラー処理 まぁ、落ちない程度に、必要と思われる場所だけにエラー処理を記述しました。 無名関数(匿名関数)に名前を付けました。じゃ、無名(匿名)じゃないな。 エラーで落ちた時、どこで落ちたかわかりにくいからです。 ## Node.js らしく実装 Node.js らしいプログラムとして実装したつもりです。 通常 Node では、readable, end のイベントを使用するプログラムが多いですが、 それらを使用せず stream を pipe で繋ぐ事にすると、結構、効率が良い様に感じます。 ※dataとendイベントよりreadableとendイベントを使用しましょう。可能ならpipeを。 ## 短い 必要最小限の機能だけを実装する事に専念しました。 更に行数を減らすためにエラー処理関数は closure 使ったりしています。 妙なコードに見えると思いますが、80行に収めるためにちょっと苦労しました。 そもそも closure を理解しないと、このプログラム全体の動きは正しく理解できないと思います。 ## 依存しているファイルが無い npm などを使って外部のモジュールを利用する事は大事だと思いますが、 お勉強用なので、単独で動作できる事を目標にし、1つのファイルで実装しました。 ## それなりに動作検証した この記事はここにある **http proxyサーバ** を2つ起動し多段串として動作させたまま、書いています。 まぁ、この1週間くらいは、ずっとこの状態です。 ## 性能 1つのプロセスでJavaScriptは1スレッドで動きます。 それ以外に4スレッド~6スレッド程度、I/Oのためのスレッドが作られたりしている様です。 メモリも30MB~70MBくらいだし、今時すっげ~少ないんじゃね。 CPUも突発的には使用している様だけど、2段のproxy越しに、キーインしながら この記事を書けているんだから、まぁいい方でしょう。 リソースもそんなに食わないし、性能は悪くは無いと思います。 みなさん、是非、試してみてください。 # 次はこのプログラムをどう料理すべきか ## セキュリティ対策 アクセスしてくる IP の範囲などを決めないと怖いですね。 例えば以下の様なコードを追加すれば、自分以外は誰も使用できません。 ```js:http-proxy-server-plus-connection-security.js const whiteAddressList = {}; whiteAddressList['::1'] = true; whiteAddressList['::ffff:127.0.0.1'] = true; whiteAddressList['::ffff:192.168.251.1'] = true; server.on('connection', function onConn(cliSoc) { if (cliSoc.remoteAddress in whiteAddressList) return; console.log(new Date().toLocaleTimeString() + ' reject: from: ' + cliSoc.remoteAddress); cliSoc.destroy(); }); ``` ## 広告/AD BLOCK, コマーシャル/CMカット ホスト名やURLの一部を利用して、無理やり通信を終わらせたりできます。 ちょっと頑張れば違うコンテンツに差し替える事も可能です。 ただし、http の場合は URL の hostname や path やヘッダなども使って細かく制限できますが、 https の場合ヘッダや内容は暗号化されているので、 IP (hostname) と port 番号だけで制御しなければいけません。 Ad Block くらいなら ChromeやFirefoxなどの拡張プラグインの方が便利かな。 ## 接続数や接続時間を表示させてみる http Serverの connection イベントで接続数をカウントアップ、 その引数の socket の close イベントで接続数をカウントダウンすれば 接続数の遷移がわかります。 最近は常時接続状態のサイトも多い様ですね。 サイトでは Google, Facebook, ブラウザでは Chrome など。 以下のコードを server 変数が存在する所に追加してください。 一番最後で結構です。 ```js:http-proxy-server-plus-connection-count-and-time.js var connCount = 0; server.on('connection', function onConn(cliSoc) { cliSoc.connTime = new Date(); console.log('++conn: ' + (++connCount) + ' from: ' + cliSoc.remoteAddress); cliSoc.on('close', function onDisconn() { console.log('--conn: ' + (--connCount) + ' time: ' + (new Date() - cliSoc.connTime) / 1000.0 + ' sec'); }); }); ``` 多段串にしてみると、proxyに常時いくつ接続しているか、httpsで常時いくつ接続しているか、わかります。 ## 簡易ロードバランサ 複数のAPサーバに向き先を変えて接続させるとか、できそうですね。 keep-alive connection は振り分けられないかな。 ## あまり良くないがステルスモード ヘッダ上の proxy を示す様な痕跡を消すなどすれば、ステルスモードになります。 [診断くん](http://taruo.net/e/) にも proxy を疑われる事はないと思います。 (生でアクセスした時と同じヘッダであれば区別つかなくて当然ですが) 注意: 本来 proxy サーバは connection ヘッダを転送してはいけない事になっているみたい。 [HTTP Connections](http://www.studyinghttp.net/connections#ConnectionHeader) ```js:http-proxy-plus-stealth-mode.js if ('proxy-connection' in cliReq.headers) { cliReq.headers['connection'] = cliReq.headers['proxy-connection']; delete cliReq.headers['proxy-connection']; delete cliReq.headers['cache-control']; } ``` ## その他 後はログ機能などでしょうか。 # 参考文献 node.jsでHTTPプロキシ経由でhttpsアクセスするには https://qiita.com/kjunichi/items/02f6d340c1b903a976ee node.jsでhttpsも扱えるProxyを作った https://qiita.com/kjunichi/items/dfdd928945c9e6e202a5 node.jsでhttpsも使えるHttp proxyを書いている https://kjunichi.cocolog-nifty.com/misc/2012/04/nodejshttpshttp.html 同じ人の記事でした。上記のコードを試してみました。 一部は動作するものの net で直接 TCP/IP を扱っているのにHTTP プロトコルの解析を サボっているので、うまく動作しないことがありました。 Content-Length だけでなく Transfer-Encoding: chunked やら いろいろ考えると HTTP を生で扱うなら、かなり頑張らないといけないと思います。 # コメントがあればどうぞ 可能な限り対応・改良いたします。 2014/03/21 追記: 不具合があり http.Agent に対応した。88行になった。 2014/03/28 追記: v0.11.12 で http.Agent から request メソッドが無くなったので本来の形式に変更した。 2016/03/31 追記: node v4/v5 対応。80行になった。 2017/02/07 追記: node v6/v7 動作確認。メソッドチェインに変更。70行になった。 |
|
| 950位 |
|
|||
|
18:38:10 |
|
|

(スクショ、ぼんやり気味ですんません...) 最近の VCS はもっぱら git だが、現場では Subversion がまだまだ現役。 その一方、Mac で使いやすいクライアントが少ないということで、自分が昔から使っている svnX を取り上げてみる。 ## svnX のいいとこ * 無料 以上! 機能的にはまあまあなんだけど、使い勝手はいまひとつというか、ある操作に対して何が起こるのか予測しにくいというか、いろんな機能がそこかしこに隠れている感じ。 それを発見できたときは面白いんだけど、はじめて触る人からしたら面倒なだけなんで、ここで簡単にまとめておきます。 ちなみにお金に余裕のある方は [Versions](http://versionsapp.com) がいいそうな。 ## 配布元 本家は [こっち](http://www.lachoseinteractive.net/en/community/subversion/svnx/) なんだけど、古いバイナリしか置いてないので [こちら](http://code.google.com/p/svnx/) からダウンロード。 現時点の最新版は 1.3.4 です。 こいつはガワだけなので、Subversion の本体も別途入れておくこと。 ## 使い方 最初の起動では、もしかしたらウィンドウが何も出てこないかもしれません。 とりあえず Window メニューを展開しましょう。 * Repositories : 登録リポジトリ一覧パネルを表示。 * Working Copies : チェックアウトした作業コピー一覧パネルを表示。 * Activity : 実施した操作の履歴パネルを表示。 ### チェックアウト(svn checkout) * Repositories パネルを開く。 * リスト右下の [+] ボタンをクリックし、以下を入力。 * Name: 管理用の名称。(自由) * Path: リポジトリの URL。 * User: リポジトリへログインユーザ名。 * Password: ログインパスワード。 * リストに追加された項目をダブルクリックするとウィンドウが表示され、リポジトリに接続する。 * ウィンドウ最下部のツリーを下り、チェックアウトしたいディレクトリを選択。 * ツールバーの Checkout をクリックし、チェックアウト先を選択して実行。 * チェックアウト対象ディレクトリは作成されないので、必要であれば自分で作成してそこをチェックアウト先にすること。 * Working Copies パネルにチェックアウト先ディレクトリが追加される。 ### 更新(svn update) * Working Copies パネルを開く。 * リストから更新対象の作業コピーをダブルクリックするとウィンドウが表示される。 * ツールバーの Update をクリック。 * 詳細は Output をクリックすると表示されるドロワーに出力される。 ### コミット(svn commit) * Working Copies パネルを開く。 * リストからコミット対象の作業コピーをダブルクリックするとウィンドウが表示される。 * コミット対象ファイルを選択し、Commit ボタンをクリック。 * コミットメッセージを入力して実行。 ## 応用 ### リポジトリウィンドウの詳細 以下の 4 つのペインで構成される。 > コミット内容ペインは履歴ペイン上にある「歯車アイコン」をクリックすると表示できる。 * 履歴ペイン : コミットの履歴。 * コミットメッセージペイン : 履歴ペインで選択されている行(リビジョン)のメッセージを表示。 * コミット内容ペイン : 履歴ペインで選択されている行(リビジョン)で実施された操作と対象ファイルを表示。 * ツリーペイン : 履歴ペインでチェックされているリビジョンのファイルツリーを表示。 #### 特定ディレクトリまたはファイルの履歴を見るには * ツリーペインで対象ディレクトリまたはファイルをダブルクリック。 * 履歴ペインに、対象の変更が行われたリビジョンだけが表示される。 * 元に戻りたい場合は履歴ペイン上部の URL(太字の部分)をクリック。 #### 変更の差分(diff)を見るには * 次のいずれかを選択し、ツールバーの Diff を実行する。 * 履歴ペインの行 : 選択行のリビジョンと、その前のリビジョンの差分。 * コミット内容ペインの行 : 選択行のファイルについて、そのリビジョンと前のリビジョンの差分。 * ツリーペインの項目 : 選択ディレクトリ以下またはファイルと、その前のリビジョンの差分。 #### タグやブランチを作成するには * ツリーペインから対象ディレクトリを選択。 * ツールバーの Copy をクリックして実行。 * Target で生成対象ディレクトリ(tags または branches)を選択。 * Name にディレクトリ名を入力。 * Commit Message に入力して実行。 ### 作業コピーウィンドウの詳細 作業コピーウィンドウには次の表示モードがある。 モードの切り替えはツールバーの View から行える。 * 階層表示 : 左ペインにディレクトリ階層、右ペインに選択中の階層下にあるファイルとディレクトリを表示。 * フラット表示 : 作業コピー下にある全てのファイルを表示。 * フラット表示(スマート) : 作業コピー下で追加・変更および管理外のファイル・ディレクトリのみを表示。 #### リポジトリとの差分(diff)を見るには * 変更した対象ファイルを選択。 * ツールバーの Diff をクリックして実行。 #### リポジトリの内容に戻す(revert)するには * 変更した対象ファイルを選択。 * Revert をクリックして実行。 * Recursive オプションを ON にすれば、指定ディレクトリ以下をまとめて戻せる。 ## FileMerge のエンコーディングエラー対応 Diff を行ったときのデフォルトアプリケーションである FileMerge に UTF-8 で書いたテキストを渡すと警告が表示される場合があるので、それを止める方法。 http://builder.japan.zdnet.com/os-admin/sp_snow-leopard-09/20404333/ 拡張子にワイルドカードが使えないのでいちいち指定しないといけないのが面倒。 |
|
| 951位 |
|
|||
|
11:50:21 |
(HiganWorks LLC. 所属) |
|
> 更新:v1.2.2.devに対応。 [Chef Inc. (旧Opscode)のtest-kitchen](https://github.com/opscode/test-kitchen)について、テストのライフサイクルとサブコマンドの使い方を説明する。 1.2系について。 ## Test-Kitchenヘルプ まずはkitchenコマンドを叩くとヘルプが表示される。 ```bash:kitchen $ kitchen Commands: kitchen console # Kitchen Console! kitchen converge [INSTANCE|REGEXP|all] # Change instance state to converge. Use a provisioner to configure one or more instances kitchen create [INSTANCE|REGEXP|all] # Change instance state to create. Start one or more instances kitchen destroy [INSTANCE|REGEXP|all] # Change instance state to destroy. Delete all information for one or more instances kitchen diagnose [INSTANCE|REGEXP|all] # Show computed diagnostic configuration kitchen driver # Driver subcommands kitchen driver create [NAME] # Create a new Kitchen Driver gem project kitchen driver discover # Discover Test Kitchen drivers published on RubyGems kitchen driver help [COMMAND] # Describe subcommands or one specific subcommand kitchen exec INSTANCE|REGEXP -c REMOTE_COMMAND # Execute command on one or more instance kitchen help [COMMAND] # Describe available commands or one specific command kitchen init # Adds some configuration to your cookbook so Kitchen can rock kitchen list [INSTANCE|REGEXP|all] # Lists one or more instances kitchen login INSTANCE|REGEXP # Log in to one instance kitchen setup [INSTANCE|REGEXP|all] # Change instance state to setup. Prepare to run automated tests. Install busser and related gems on one or more instances kitchen test [INSTANCE|REGEXP|all] # Test (destroy, create, converge, setup, verify and destroy) one or more instances kitchen verify [INSTANCE|REGEXP|all] # Change instance state to verify. Run automated tests on one or more instances kitchen version # Print Kitchen's version information ``` `kitchen help {subcomand}`で詳細ヘルプが表示される。 ```bash:kitchen_help_init $ kitchen help init Usage: kitchen init Options: -D, [--driver=one two three] # One or more Kitchen Driver gems to be installed or added to a Gemfile # Default: kitchen-vagrant -P, [--provisioner=PROVISIONER] # The default Kitchen Provisioner to use # Default: chef_solo [--create-gemfile], [--no-create-gemfile] # Whether or not to create a Gemfile if one does not exist. Default: false Description: Init will add Test Kitchen support to an existing project for convergence integration testing. A default .kitchen.yml file (which is intended to be customized) is created in the project's root directory and one or more gems will be added to the project's Gemfile. ``` 流れがわかっていれば後はこのヘルプを引けば良いので、都度参照する。 ## 用語:Instance テストに使うVMは`Instance`と呼ばれ、命名法則と数は下記の式で決まる。 `Instance` = `${suite}-${platform}`の組み合わせ全部。 `platform`に`[ubuntu,centos]`とあって、`suite`に`[web, db]`とあった場合は - web-ubuntu - web-centos - db-ubuntu - db-centos と4つのインスタンス定義ができる。 ## Test-kitchenのライフサイクル ### 初期化 |コマンド| 効能 | |---|---| |kitchen init| .kitchen.yml を作成する、まずはこれから。デフォルトのドライバはVagrant。 | |kitchen driver discover | Rubygemsからtest-kitchen用に公開されているドライバ一覧を表示する。使いたいものをGemでインストールする。<br /> 依存にtest-kitchenがあり、`kitchen-` ではじまるものを抽出しているようだ。| ### テスト ここが大事。 |コマンド| 効能 | |---|---| |kitchen create| インスタンスを作成する | |kitchen setup| chefとbusserをインストールしてChef-repoをアップロード、初回のconvergenceを行う。 | |kitchen converge | chef-reposが再度アップロードされ、Chef-Solo(またはローカルモード)を実行する。<br /> 繰り返し実行可能。 | |kitchen verify| testディレクトリがアップロードされ、busser経由でテストスイートをインストールし、テストを実行する。<br /> 繰り返し実行可能。 | |kitchen destroy| インスタンスを破棄する。 | |kitchen test| 上記テストのライフサイクルを一括で行う。 <br />実行時にインスタンスがすでに存在する場合は初手でも**Destroy**が実行される。 | test一発でもよいが、個別に何をやっているか知っていると捗る。 ## 単発のタスク |コマンド| 効能 | |---|---| |kitchen exec|起動中のインスタンスに対して任意のコマンドを実行する。createの直後にごにょごにょとか、verifyの前にちょっと実行しておきたい事があったらこれで。 | ## 構成確認、デバッグ |コマンド| 効能 | |---|---| |kitchen list|`.kitchen.yml`にある`Instance`リストとライフサイクル上の状態を表示する。<br /> `Instance`の状態は逐一`.kitchen/`以下にyamlで吐かれているのでそれをみてもよい。 | |kitchen login|`Instance`にSSHでログインする。execで物足りない時に。| |kitchen console| .kitchen.yml と設定を読み込んだRubyのプロンプトが立ち上がる。<br /> `@instances`や`@suites`で各種設定が妥当か確認できる。 | |kitchen diagnose|`.kitchen.yml`ほかによって最終的に有効になっている各種設定をダンプする。consoleの簡易版。 | あれば便利という程度。 ## ドライバ開発 |コマンド| 効能 | |---|---| |kitchen driver| ドライバ作成用のGemプロジェクト雛形を作成する。 <br /> 最低限必要なメソッドが用意されるので、穴埋めをするだけでドライバが作成できる。 | 細かい作り方は既存のプラグインを参考にするしかなさそう。 ## おすすめの使い方 ### ローカルで create, setupと一つづつ実行しても良いが、`kitchen test`には `--destroy=never`というオプションをつけて実行するとインスタンスが破棄されずに保持される。 まず`kitchen test --destroy=never`で一気に`verify`まで行った後、適当にレシピを修正しながら`converge, verify`を繰り返すといった使い方が良いだろう。 シメにはもう一度`kitchen test`(`--destroy`オプションなし)で、インスタンスのBootから一気通貫テストをする。 ### CIと CIから継続的にテストを行うならば、`--destroy=always`オプションが良い。 通常は`converge, verify`に失敗したらインスタンスは保持されて、デバッグのチャンスが与えられるところだが、CIから自動でテストする場合はインスタンスに残られても困る。 結果に関係なく、インスタンスを破棄しておくため`always`を使う。 |
|
| 952位 |
|
|||
|
16:12:12 |
(株式会社キッズスター 所属) |
|
Grapeは、RubyでRESTライクなAPIを生成する為のマイクロフレームワークです。  photo by [Anders Ljungberg](http://www.flickr.com/photos/a_t_ljungberg/251602869/) ## リソース * [GitHub](https://github.com/intridea/grape) * [RubyGems.org](http://rubygems.org/gems/grape) * [The Ruby Toolbox](https://www.ruby-toolbox.com/projects/grape) ## インストール Gemfileに、grapeを追記する。 ```ruby:Gemfile gem 'grape' ``` インストールする。 ```bash $ bundle install ``` ## 初期設定 libディレクトリ配下に、API実装ファイルを新規作成します。 ```bash $ touch lib/api.rb ``` libディレクトリ配下を自動的に読み込むように設定します。 ```ruby:config/application.rb # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths += %W(#{config.root}/lib) ``` ルーティングに、API実装ファイルのマウントを追記します。 ```ruby:config/routes.rb mount API => "/" ``` ## 利用方法 基本的な、Grapeの利用方法は以下のようになります。 `params`というブロックにて、パラメータのバリデータチェックも可能です。 ```ruby:lib/api.rb class API < Grape::API # APIアクセスに接頭辞を付加 # ex) http://localhost:3000/api prefix "api" # APIアクセスにバージョン情報を付加 # ex) http://localhost:3000/api/v1 version 'v1', :using => :path resource "users" do # ex) http://localhost:3000/api/v1/users desc "returns all users" get do User.all end # ex) OK: http://localhost:3000/api/v1/users/1 # ex) NG: http://localhost:3000/api/v1/users/a desc "return a user" params do requires :id, type: Integer optional :name, type: String end get ':id' do User.find(params[:id]) end end end ``` ## 合わせて読みたい * [APIの作成に特化したRuby製フレームワーク grape を試してみた | FIRN.JP](http://firn.jp/2011/05/27/grape) |
|
| 953位 |
|
|||
|
14:38:25 |
(アライドアーキテクツ株式会社 所属) |
|
「はじめに」の「はじめに」
------- 2016年版としてマイグレーションしました。 特にこだわりが無い場合は、こちらを参照してください。 http://qiita.com/t_ishida/items/694b2356ed23a8d89295 はじめに --------- こんな感じで資料を作ろうとしていた草稿です。 文中のソースコードの正誤とかは見きれていません。 ツッコミとか有れば、よろしくお願いしますm( _ _ )m PHPUnitを使ったからといって、どんなソースコードもテストできる訳ではありません。 テストをし易いようにクラスを設計している必要があります。また、そのように設計していてもUnitテストに入れることの出来ない箇所は出てきます。Unitテストに入れることの出来ない箇所は出来ないと割り切らなければなりません。むしろ、どれだけのコードをUnitテストに入れることが出来るか? というのが設計者の腕の見せどころになるでしょう。 極論を言うと 「どんなクラスでも疎結合に実装していなければならない」 ということです。 クラス設計的には密結合であるべき箇所も疎結合に実装します。これが鉄則です。クラス設計的に疎結合であることが好ましくなかろうと「テストできないより、テストできるクラスの方が良いに決まってる」というマインドで臨んでください。 単体テストというもの -------------- 「クラス単体をテストする」ということです。 * 依存クラス * 設定値 * 外部システム(DB, API, ファイル, コマンド) そういったものの"依存"を切り離して「そのクラスを設計し」、「そのクラスを実装し」、「そのクラスのテストを作る」ということになります。"依存"とは極論を言えば * インスタンスを利用するメソッドが、直接インスタンスを new する * ロジックの中で 設定ファイルを直接参照する * ロジックの中で 直接ファイルを書き出す * ロジックの中で 直接DBを覗く * ロジックの中で 直接APIを覗く * ロジックの中で 直接コマンドを叩く ようなことです。 これらは全部ラッパーを作って依存関係を切り離しておき実際に単体テストとして記述する時にはテスト用のモックで代用するということになります。 ```php <?php class Hoge { protected $_Settings = null; protected $_FileManager = null; protected $_DBManager = null; protected $_APIManager = null; protected $_CommandManager = null; public function __construct ( $settings , $file_manager, $db_manager, $api_manager, $command_manager) { $this->_Settings = $settings; $this->_FileManager = $file_manager; $this->_DBManager = $db_manager; $this->_APIManager = $api_manager; $this->_CommandManager = $command_manager; } } ``` new に関する問題 -------------- 前述の中で「ロジック内で 別のインスタンスをnew する」という問題について説明しませんでした。これは実に難しい問題であるためです。ひとつの答えは「new する」という責務を別のクラスに切り出して「Factoryのインスタンスを渡す」ということです。「newをするだけの責務を持ったクラス」をコンストラクタ、setter、もしくは、メソッドの引数として渡してあげるということですね。 ```php <?php class Parent extends Hoge { private $_Something = null; private $_Something2 = null; private $_Something3 = null; public getSomething ( ) { return $this->_Something; } public getSomething2 ( ) { return $this->_Something2; } public getSomething3 ( ) { return $this->_Something3; } public function save ( $factory ) { $this->_DBManager->begin (); try { $id = $this->_DBManager->save ( $this ); $child = $factory->create ( array ( 'parent_id' => $id, 'name' => 'child of ' . $id, )); $this->id = $id; $this->_Something = $child->getSomething(); $this->_Something2 = $child->getSomething2(); $this->_Something3 = $child->getSomething3(); $this->_DBManager->save ( $child ); $this->_DBManager->commit (); } catch (DBException $e ) { $this->_DBManager->rollback (); throw $e; } return $this->id; } } class Child { private $_ParentID = null; private $_Name = null; public function __construct ( $props ) { $this->_ParentID = $props['parent_id']; $this->_Name = $props['name']; } public function getSomething () { return $this->_ParentID * 3; } public function getSomething2 () { return $this->_ParentID % 2 ; } public function getSomething3 () { return $this->_ParentID / 4; } } // factory は省きますね m(_ _)m ``` これはこれで酷い設計ではありますが、とりあえず * factory の クラスは「引数に応じてインスタンスを生成する」をテスト出来ていれば良い * Child は Child の テストを書ける * Parent は 全部がモックで動くことを確認出来れば良い と、それぞれがテストの書ける状況になりました。 例に挙げているコードはそうなってはいませんが、 僕個人の解としてはクラス設計の際 * 単数形のクラス * 複数形のクラス[ファクトリでありDBManagerである] と分けて考えることにしています。 * 単数形のクラスはコンストラクタで hasMany の関係の複数形のクラスを受け取る * 単数形の保存は自身の複数形のクラスの責務 * DBManagerはDBのラッパーを受け取る とすることで多くのケースにおいてUnitテストに入れることが 出来る設計になると思います。 PHPUnitのインストール --------------------- さて前置きが長くなりましたが、PEARでインストールします。 他にも方法はありますが、include_path を 通したりと面倒なことがあるので、 そいういうのは慣れてからにしましょう。 「とりあえずインストールして試してみる」 ということを目的とするのならばPEARでインストールすることをお勧めします。 ``` pear channel-discover pear.phpunit.de pear install phpunit/PHPUnit ``` PHPUnitの使い方 --------------- コード中にコメントの形で書きますね。 雰囲気を掴んでもらうことを目的としているので、 これだけ読んでも実際には使えないかも知れません。 詳細は下記を参照してください。 http://www.phpunit.de/manual/3.7/ja/index.html ```php <?php class ParentTest extends PHPUnit_Framework_TestCase { public function testSave () { /// <<< ここから モック作成 // コンフィグ, File, API, Command のモック作ります // ※実際には今回使わないのでnull渡しでも良いけど、説明のためにこうします $config = $this->getMockBuilder( 'Config' ) ->disableOriginalConstructor() ->getMock(); $file = $this->getMockBuilder( 'FileManager' ) ->disableOriginalConstructor() ->getMock(); $api = $this->getMockBuilder( 'APIManger' ) ->disableOriginalConstructor() ->getMock(); $cmd = $this->getMockBuilder( 'CommandManger' ) ->disableOriginalConstructor() ->getMock(); // 実際に動作するモックを作ります // DBの定義 $db = $this->getMockBuilder( 'DBManager' ) // コンストラクタを呼びません ->disableOriginalConstructor() // save, begin, commit を使います ->setMethods ( array ( 'save', 'begin', 'commit', ))->getMock(); // Factory の 定義 $factory = $this->getMockBuilder( 'ChildFactory' ) // コンストラクタは呼びません ->disableOriginalConstructor() // create を 使います ->setMethods ( array ( 'create', ))->getMock(); // Child は オリジナルそのまま使います $child = new Child ( ); // // DB->beginの振舞いを定義します // $db // 1回しか呼びません(複数回呼ぶとテスト失敗です) ->expects ( $this->once() ) // beginです ->method ( 'begin' ) // 1を返します ->will ( $this->returnValue ( 1 ) ); // // DB->saveの振舞いを定義します // $db // 何回呼んでもOKです ->expects ( $this->any() ) // このメソッドはsaveです ->method ( 'save' ) // 1回目は1, 2回目は2を返します ->will ( $this->onConsecutiveCalls ( 1, 2 ) ); // // DB->commitの振舞いを定義します // $db // 1回しか呼びません(複数回呼ぶとテスト失敗です) ->expects ( $this->once() ) // このメソッドはcommitです ->method ( 'commit' ) // 1を返します ->will ( $this->returnValue ( 1 ) ); $factory // 1回しか呼びません(複数回呼ぶとテスト失敗です) ->expcets ( $this->once() ) // このメソッドは create です ->method ( 'create' ) ->with ( array ( 'parent_id' => 1, 'name' => 'child of ' . 1, )) // Childを実際にインスタンス化して返してみます ->will ( $this->returnValue ( new Child ( array ( 'parent_id' => 1, 'name' => 'child of 1', )))); // ここまでモック作成 >> // 実テスト $obj = new Parent( $conf, $file, $db, $api, $command ); // save の 戻り値は 1 のはず $this->assertEquals ( 1,$obj->save ( $factory )); // doSomething の 戻り値は 3 のはず $this->assertEquals ( 3, $obj->getSomething() ); // doSomething2 の 戻り値は 1 のはず $this->assertEquals ( 1, $obj->getSomething2()); // doSomething3 の 戻り値は 0.25 のはず $this->assertEquals ( 0.25, $obj->getSomething3()); } } ``` FAQ _______ * これ、めっちゃ大変だと思うんですけど? => 大変です。でも、毎度全部テストする方が大変です。 * 時間かかるんじゃないの? => かかります。でも、毎度全部テストする方が時間かかります。 * これだけじゃバグ発見しきれないのでは? => 勿論しきれません。でも、やらないより余程減らせるはずです * 既存のシステムに組み入れられない(TT) => 既存のシステムは手を入れないと、導入できないシステムの方が多いでしょう。レガシーコード改善ガイド読むとヒントが掴めるかも |
|
| 954位 |
|
|||
|
18:33:37 |
(フリーランス 所属) |
|
Objective-Cでインスタンス変数が宣言可能なのは次の3カ所です。
Case 1:ヘッダー内 ```objc:SampleClass.h @interface SampleClass : NSObject{ int i; } ``` Case 2:クラス拡張(Class extension)内 ```objc:SampleClass.m @interface ViewController (){ int i; } ``` Case 3:implementationの直後 ```objc:SampleClass.m @implementation ViewController{ int i; } ``` では、どこで宣言するのがベストなのでしょうか? こちらのAppleの公式ドキュメントを参考にすると、 https://developer.apple.com/jp/devcenter/ios/library/documentation/ObjC.pdf 38ページの先頭に以下の記述があります。 ``` インスタンス変数は実装詳細であり、通常、クラス自身の外からアクセスされることはあり ません。さらに、実装ブロック内に宣言すること、あるいは宣言済みプロパティから自動生 成させることも可能です。したがって通常は、インスタンス変数宣言をパブリックインター フェイスで行うべきではないので、波括弧も省略してください。 ``` このように、Apple側はCase1のようにヘッダーファイル内でメンバ変数の宣言することを推奨していないようです。 また、このドキュメントでは、Case3のようにimplementationの直後でインスタンス変数の宣言をしていますね。 ところが、Case3の場合ちょっと問題があります。 Storyboardにおいて、Outletの接続を行う場合です。 例えば、以下のように書いてStoryboardでOutletの接続を行おうとすると、 ```objc @implementation ViewController{ IBOutlet UILabel *label; } ``` Storyboardの当該箇所にこのインスタンス名がすぐには表示されません。 クリーン後、しばらく時間が経つと表示される場合が多いです(Xcode 5.0.2で確認)。 これはXcodeの不具合(?)かと思います。 以下の箇所に書けば当該箇所にインスタンス名が即反映されます。 ```objc:SampleClass.h @interface SampleClass : NSObject{ IBOutlet UILabel *label; } ``` ```objc:SampleClass.m @interface ViewController (){ IBOutlet UILabel *label; } ``` 上記の理由で、StoryboardとOutletの接続を行う場合は、Case3のimplementationの直後以外で宣言した方がいいかと思います。しかし、Storyboardを使わないのであればここに宣言するのもありかと思います。 Case1のヘッダーファイル内で宣言した場合ですが、前述のAppleの公式ドキュメントの43ページにあるように、通常はインスタンス変数の宣言前に@protectedのディレクティブがついていることになっています。 従って、意図的に@publicをつけない限りサブクラス以外のクラス外からアクセスしようとするとコンパイルエラーが発生してしまうので、アクセスはサブクラスに限られるようです。 サブクラスからも隠蔽したい場合は@protectedをつければ大丈夫です。 従って、他のクラスからの意図しないアクセスが問題になることはそれほど無いように思えます。 ただし、.演算子や->演算子で他のクラスからアクセスしようとすると自動補完候補に挙がってしまうようです。 Case2のクラス拡張内に宣言する場合、今のところ特にデメリットは無いように思えます。 それにしても、このクラス拡張というものは他に用途はあるのでしょうか。 前述のAppleのドキュメントを読んでも、使いどころが思い浮かびません(^^; Case1〜3のどれを選択するかは好みの問題も多いとは思いますが、個人的な好みでは実装ファイルが肥大化するのが嫌なのと、ファイルの先頭までスクロールするのが嫌なのでヘッダーファイル内に宣言することが多いです。 Appleの中の人ごめんなさい(^^; |
|
| 955位 |
|
|||
|
18:26:46 |
(IMJ Corporation 所属) |
|
いつも同じサイト見てる気がするのでまとめてみる。
# HTML5 対応状況 ## PC向けブラウザの状況 **Can I use...** ( HTML5,JavaScript ) [http://caniuse.com/](http://caniuse.com/ "http://caniuse.com/") ### ブラウザ別 **IE Platform Status** IE,Edge [http://status.modern.ie/](http://status.modern.ie/ "http://status.modern.ie/") [http://dev.modern.ie/platform/status/](http://dev.modern.ie/platform/status/ "http://dev.modern.ie/platform/status/") **Chromium Dashboard** Chromium [http://www.chromestatus.com/features](http://www.chromestatus.com/features "http://www.chromestatus.com/features") **Mozilla Developer Network** Firefox [https://developer.mozilla.org/ja/docs/Web](https://developer.mozilla.org/ja/docs/Web "https://developer.mozilla.org/ja/docs/Web") **Safari Developer Library** Safari [https://developer.apple.com/library/safari/navigation/](https://developer.apple.com/library/safari/navigation/ "https://developer.apple.com/library/safari/navigation/") ## モバイル端末向けブラウザの対応状況 **MOBILE HTML5** ( HTML5,JavaScript ) [http://mobilehtml5.org/](http://mobilehtml5.org/ "http://mobilehtml5.org/") ## その他 **Browserscope** ( HTML5,CSS3,JavaScript ) 現在見ているブラウザの実装具合などが見れる (久しぶりにさわるブラウザとか、興味ないブラウザ触るとき。) [http://www.browserscope.org/](http://www.browserscope.org/ "http://www.browserscope.org/") **HTML5TEST** ( HTML5) 現在見ているブラウザの実装具合などが見れる [http://html5test.com/](http://html5test.com/ "http://html5test.com/") **CSS5TEST** ( CSS3 ) 現在見ているブラウザの実装具合などが見れる [http://css3test.com/](http://css3test.com/ "http://css3test.com/") **HTML5 PLEASE** ( HTML5,CSS3,JavaScipt ) 各項目に実装方法などの注意点が書いてある [http://html5please.com/](http://html5please.com/ "http://html5please.com/") **findmebyIP** ( HTML5,CSS3,JavaScipt ) 閲覧している環境の実装状況がわかる [http://fmbip.com/](http://fmbip.com/ "http://fmbip.com/") **CSS3 Click Chart** ( CSS3 ) コードのスニペットが書いてある [http://css3clickchart.com/](http://css3clickchart.com/ "http://css3clickchart.com/") **html5 snippets** ( HTML5,CSS3 ) コードのスニペットが書いてある [http://html5snippets.com/](http://html5snippets.com/ "http://html5snippets.com/") **ねこだまのCSSリファレンス&デモのトップ > CSSセレクタ・サポート対応表** 忘れた頃のIE対応の時にひっかかるサイト [http://webdev-nekodama.xii.jp/css/sSupport.php](http://webdev-nekodama.xii.jp/css/sSupport.php "http://webdev-nekodama.xii.jp/css/sSupport.php") ## ちょっと影響範囲調べるとき **NETMARKETSHARE** ( HTML5,CSS3,JavaScipt ) ブラウザシェアとか、解像度のシェアとか。 [http://www.netmarketshare.com/](http://www.netmarketshare.com/ "http://www.netmarketshare.com/") |
|
| 956位 |
|
|||
|
02:40:41 |
|
|
``ls``コマンドで出力されるファイルやディレクトリに色をつけたい場合、``LS_COLORS``という環境変数にカラー設定を記述することはよく知られていることと思います。
しかし、これをいちいち手動で設定したり追加したりするのは面倒です。 そこで今回は``dircolors``というコマンドを用いて、カラー設定を読み込む方法を紹介します。 今回読み込むカラー設定は[dircolors-solarized](https://github.com/seebi/dircolors-solarized)です。 名前の通り、みんな大好きsolarizedのカラー設定です。 今でもちょくちょく更新されていて、色々な拡張子のファイルを良い感じに色付けしてくれます。 ##カラー設定の読み込み では本題のカラー設定の読み込み方法についてです。 ``dircolors-solarized``はcloneなりDLなりして持ってきてもらうとして、その中にある``dircolors.ansi-universal``というカラー設定を読み込んでみましょう。 ```sh eval $(dircolors /path/to/dircolors-solarized/dircolors.ansi-universal) ``` これだけで``LS_COLORS``が設定されます。 (``/path/to``の部分は適当に読み替えて下さい) ##Mac(BSD系)で使用する方法 続いてMacでも``LS_COLORS``を使用する方法です。 ``LS_COLORS``はGNU版の``ls``にカラーを設定するための環境変数です。 しかし、MacにはBSD版の``ls``しか入っていないため、GNU版の``ls``を入れる必要があります。 (※ BSD版``ls``のカラー設定は``LSCOLORS``に記述します。ただし記述方法が異なります) また``dircolors``コマンドについてもMacには入っていません。 そこでGNU系のコマンドを集めた``coreutils``をインストールします。 既にインストールしている方も多いと思いますが。。。 homebrewを使用している場合は、以下のコマンドでインストール可能です。 ``` $ brew install coreutils ``` これで、GNU版lsの``gls``と``gdircolors``が使用可能になります。 ``gls``を普段から使用したい場合は、``ls``にaliasを設定しておくと良いでしょう。 あとは同様にして``LS_COLORS``を設定します。 ```sh eval $(gdircolors /path/to/dircolors-solarized/dircolors.ansi-universal) ``` ##zshの補完にも同じ色を設定 zshを使用している場合、補完候補として表示されるファイルやディレクトリにも``ls``と同じカラーを設定したいと思うでしょう。 そこで以下のように設定することで、簡単にカラー設定を一致させることができます。 ```sh if [ -n "$LS_COLORS" ]; then zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS} fi ``` ##個人的なおすすめとまとめ 最後は個人的なオススメです。 ``dircolors``で読み込むカラー設定ファイルは、シンボリックリンクを貼ってそちらを読み込むようにすると良いと思います。 カラー設定を変更したい場合は、リンクを張り替えるだけですみます。 ```sh $ ln -s /path/to/dircolors-solarized/dircolors.ansi-universal ~/.dircolors ``` というわけで、最終的には以下の様な設定となります。 ```sh # デフォルト設定(別になくても良い) LS_COLORS="デフォルトの色設定(ご自由に)" export LS_COLORS if [ -f ~/.dircolors ]; then if type dircolors > /dev/null 2>&1; then eval $(dircolors ~/.dircolors) elif type gdircolors > /dev/null 2>&1; then eval $(gdircolors ~/.dircolors) fi fi ``` これを``.bashrc``や``.zshrc``などに書いておけばよいでしょう。 zshを使用している場合には、先の補完候補の色設定も追加しておきましよう。 |
|
| 957位 |
|
|||
|
02:16:08 |
|
|
Jenkinsマスタが壊れた時のリカバリ(再構築)や、同じ設定のJenkinsを複数立てたい時などのために、Jenkinsの設定情報やプラグインのバックアップ・リストアについてメモ。
## 設定情報、静的コンテンツ #### バックアップ 該当するのは以下のファイル。 - ``${JENKINS_HOME}``直下の``*.xml`` - ``${JENKINS_HOME}/jobs/${JOB_NAME}/config.xml`` - ``${JENKINS_HOME}/userContent``以下の全ファイル バックアップの方法としては、以下のものがある。 - [くりにっきさん](http://sue445.hatenablog.com/entry/2013/12/08/005317)作 [jenkins-backup-script](https://github.com/sue445/jenkins-backup-script) - 設定情報だけでなくプラグインもバックアップ対象。バックアップだけならこれ一つでOK。 - あくまでスクリプトなので、実行のためのジョブは自分で定義する必要がある - [thinBackup Plugin](https://wiki.jenkins-ci.org/display/JENKINS/thinBackup) - バックアップ、リストアがJenkinsの画面から簡単にできる(主観) - プラグインはバックアップの対象外 リポジトリでバージョン管理したい場合は、自分でスクリプトを作りこむなどして対応する。Subversionの例は公式に記述がある。かなり古い(2010年2月)記事だけど、ポイントは今も変わってないはず。 https://jenkins-ci.org/content/keeping-your-configuration-and-data-subversion #### リストア ファイルを同じ場所に戻して、「設定の再読み込み」やJenkinsの再起動をする。 ただしownerやgroupが間違っていると、読み込みされないので注意。 ## プラグイン #### バックアップ 該当するのは以下のファイル - ``${JENKINS_HOME}/plugins``直下の``*.jpi`` #### リストア jpiファイルを``${JENKINS_HOME}/plugins``に置いてJenkinsを再起動すればOK。 プラグインマネージャで確認しても、きちんと「インストール済みプラグイン」として認識されているはず。 ### プラグインの管理機構 仕事で使っているCI環境では、jpiファイルもすべて(約30プラグインで20MB超)リポジトリにチェックインしている。これはバッドプラクティスの気もするが、20MBくらいなら気にするサイズでもないし、頻繁に更新するものでもないので、当面はこれでいいかなぁと思っている。 とは言え、Jenkinsのプラグインにおける、bundlerやMavenのような依存管理機構も少し考えてみる。 今インストールされているプラグインの一覧を取得するには、JenkinsのRemote Access APIを使えば可能。 http://xx.xx.xx.xx:8080/pluginManager/api/xml?depth=1 http://xx.xx.xx.xx:8080/pluginManager/api/json?depth=1 ## バックアップ・リストアの対象とはしないもの - Jenkins本体 - ビルド履歴 - 成果物 この辺は大事に保管しておくものでもないし、バックアップしようとするとディスクを圧迫するので、対象外。 ## 参考記事 - 「[Jenkins のジョブの設定を版管理するいい方法はない?](http://qiita.com/magicant/items/50dbab790af5317b0752)」 by magicantさん |
|
| 958位 |
|
|||
|
00:02:20 |
(株式会社ソニックガーデン 所属) |
|
### ※注意
この記事で紹介している nested form gem は2016/02/25の時点で3年間メンテナンスがされていないため、今でもメンテナンスされている類似gemの cocoon gem を利用した方が良さそうです。 参考記事:[cocoon の nested_form との比較と導入方法 - Qiita](http://qiita.com/Matsushin/items/4829e12da2834d6e386e) ### 以下記事本文 多対多の関係にある時にフォームを作るとき、フォームでどんどん関連を追加していけるようにしたい場合には Nested Form Gemが便利というお話。 例えば、UserモデルとEventモデルが互いに多対多の関連で情報を持っているとする。  ユーザーは参加申請フォームで一気に複数のEventに登録出来るようにしたいです。 まずは、gemから ```erb #Gemfile gem "nested_form" ``` を追加。 忘れずに app/assets/javascripts/application.jsに ```js //= require jquery_nested_form ``` を追加。 ひとまず、多対多の関係を作りたいので、中間モデル(Entry)を作る ```erb #app/model/entry.rb class Entry < ActiveRecord::Base belogns_to :event belogns_to :user end #app/model/event.rb class Event < ActiveRecord::Base has_many :entries has_many :users, through: :entries end #app/model/user.rb class User < ActiveRecord::Base has_many :entries has_many :events, through: :entries accepts_nested_attributes_for :entries, allow_destroy: true end ``` Rails4系では attr_accessibleを書かずにコントローラーでStrong parameterで指定します。 ```erb #users_controller.rb private def user_params params.require(:user).permit(entries_attributes: [:id, :event_id, :_destroy]) ``` このように、中間テーブルのところをホワイトリストに追加してあげれば動くようになります。 そして、viewの方は ```erb #app/views/users/_form.html.erb <%= nested_form_for @user do |f| %> <div class="field"> <%= f.fields_for :entries do |entry| %> <%= render "entry_fields", f: entry %> <% end %> <p> <%= f.link_to_add "Add Event", :entries %> </p> <div class="actions"> <%= f.submit %> </div> <% end %> #app/views/users/_entry_fields.html.erb <li class="fields"> <%= f.hidden_field :id %> <%= f.select :musician_id, options_for_select([['—', nil]] + Event.all.map{|m| [m.name, m.id]}, f.object.event_id) %> <%= f.link_to_remove 'Remove Event' %> </li> ``` ってやると、  みたいな感じで、すべてのEventを取得し表示して選べるようにしてくれます。そして、Add Eventボタンを押せば複数のEventを指定できるようになります。 |
|
| 959位 |
|
|||
|
07:04:44 |
(IMJ (http://www.imjp.co.jp/) 所属) |
|
LinuxOSがまるっと動くという小型の基板 [Raspberry Pi](http://www.raspberrypi.org/) に興味をひかれ、いじくってみることにしました。これまでに [Arduino](http://arduino.cc/) をさわったことはありましたが、Arduino は Processing ベースの独自言語でプログラムを書きますので、ツッコんだことをやろうとするとややハードルが高いかも知れません。Linux が動くということは、既にあるさまざまなプログラムやリソースが使える、ということだと思うので、期待高し、です。 初心者なので、書籍『<a href="http://www.amazon.co.jp/gp/product/4899773528/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4899773528&linkCode=as2&tag=pxt25503-22">Raspberry Piで遊ぼう!</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=pxt25503-22&l=as2&o=9&a=4899773528" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />』を参考に進めていきたいと思います。 # 用意するもの とりあえず今回は、起動して終了するところまでをやってみます。そこまでの操作に必要な最低限の機材は次の通りです。 - Raspberry Pi 本体<br /> まずは Raspberry Pi の本体が必要です。<a href="http://www.amazon.co.jp/gp/product/B00CYA6UKI/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=B00CYA6UKI&linkCode=as2&tag=pxt25503-22">Type A</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=pxt25503-22&l=as2&o=9&a=B00CYA6UKI" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> と <a href="http://www.amazon.co.jp/gp/product/B00CBWMXVE/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=B00CBWMXVE&linkCode=as2&tag=pxt25503-22">Type B</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=pxt25503-22&l=as2&o=9&a=B00CBWMXVE" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> があります。Type A は廉価版で、メモリが少なくて、有線LANが使えません。私はちょっとリッチに<a href="http://www.amazon.co.jp/gp/product/B00DGKUHFE/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=B00DGKUHFE&linkCode=as2&tag=pxt25503-22">クリアケースがセットになってる Type B</a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=pxt25503-22&l=as2&o=9&a=B00DGKUHFE" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />、を買いましたが、ケースは特に必要ありません。 - 電源アダプタと接続用のマイクロUSBケーブル<br /> 1ポートで1000mA以上出力できる電源アダプタが必要です。PCのUSBポートは普通500mAなので、ちょっと足りません。iPhoneについてくるアダプタなどが使えます。<br />  - SDカード<br /> Raspberry Pi にはディスク領域がついていません。そのため、SDカードを挿して使う必要があります。このカードは、Raspberry Pi の起動ディスクになります。今回は、ありあわせで「ELECOM SDHC 4GB Class10」というSDカードを使用します。<br />  - キーボード<br /> 起動ができると、とりあえずコマンドラインでいろいろ操作することになるので、キーボードは必要です。 - ディスプレイとディスプレイケーブル (HDMIケーブル、またはコンポジットケーブル)<br /> 一旦、ディスプレイに繋がないと、動いたかどうかわからないし、操作もできないので、用意しましょう。Raspberry Pi にはHDMIとコンポジット出力がありますが、どっちでもよさそうです。ディスプレイはテレビでもOKです。今回は、ありあわせで、コンポジットでご家庭のテレビにつなぐ、という構成でやってみます。 ということで、下記は機材集合写真。これに加えて、ディスプレイとしてお部屋のテレビを使いました。  その他、デスクトップGUI環境を動かすならマウスがあるとよいとか、LANにつなぐならLANケーブルがあるとよいとか、スピーカーをつなぐと音も聞こえるとかありますが、なくても起動はできるので今回は用意しませんでした。 # Raspbian "wheezy" OS の起動ディスク(SDカード)を作る Raspbian は、Raspberry Pi 向けに最適化した Debianベース のOS、とのことです。他にも Raspberry Pi 向けのOSがたくさんあるようですが、今回はRaspbianを使ってみることにします。 書籍によると、4GB以上のSDカードが必要とのこと。ウェブを検索すると 2GB のカードでもできている人もたくさんいますが、4GBのカードを用意しました。ちなみに、選んだSDカードと Raspberry Pi との相性があるそうです。[RPi SD cards](http://elinux.org/RPi_SD_cards) のページに、SDカード製品の相性表が掲載されているので、ここで「ok」表示のあるカードを選んでおくとベターでしょう。今回は、「ELECOM SDHC 4GB Class10」というSDカードを使用します。 OSのイメージファイルは [ダウンロードページ](http://www.raspberrypi.org/downloads) から入手できます。 - [2013-12-20-wheezy-raspbian.zip](http://downloads.raspberrypi.org/raspbian_latest) (784MB) 書籍を読むと、Windows用には 「Win32 Disk Imager」や「DDforWindows」、Mac用には Raspberry Piの起動ディスクを作るためのツール「RPi-sd card builder」をお勧めしていますが、ここでは <code>dd</code> コマンドを使うことにします。(ちなみに Mac OS X Mountain Lion でやっています) OSだけに、784MBとなかなか大きいファイルです。ダウンロードに時間もかかりますが、気長に待ちます。ダウンロードされてきたZIPを解凍すると、 2013-12-20-wheezy-raspbian.img というディスクイメージファイルが得られます。このイメージをSDカードに復元して、起動ディスクを作成します。 Mac では、SDカードは挿入すると、<code>/Volumes/{$カード名}</code> という場所に認識されます。カード名は、SDカードそれぞれに固有のボリュームラベルがつけられるので、決まった名前になりません。SDカードをMacに挿入すると名前がわかるので、必ず調べてからコマンドを実行してください。 ここでは、わかりやすいように "RASPBIAN" というボリュームラベルをつけました。 <strong>注意:出力先のボリューム名は絶対に間違わないように注意してください。このコマンドを間違ったデバイス(例えば "Macintosh HD" とか!)に実行すると、ディスクの中身がぜんぶ一瞬で消去されてしまいます!</strong> まず、<code>df</code>コマンドで、RASPBIAN がどのデバイスで認識されているか調べます。 ```Bash:command $ df ``` すると、次のような表示が得られます。 ``` Filesystem 512-blocks Used Available Capacity iused ifree %iused Mounted on /dev/disk1 234643592 194054152 40077440 83% 24320767 5009680 83% / devfs 379 379 0 100% 656 0 100% /dev map -hosts 0 0 0 100% 0 0 100% /net map auto_home 0 0 0 100% 0 0 100% /home /dev/disk2s1 7798784 6016 7792768 1% 0 0 100% /Volumes/RASPBIAN ``` <code>/dev/disk2s1</code> が RASPBIAN であるいることがわかります。このディスクにイメージを展開するのですが、その前に <code>/dev/disk2s1</code> をアンマウント(<code>umount</code>コマンド)します。 ```Bash:command $ sudo umount /Volumes/RASPBIAN ``` 成功したら、<code>dd</code>コマンドでイメージをSDカードに展開します。 <code>dd</code>コマンドのifオプションで入力ファイル(この場合 2013-12-20-wheezy-raspbian.img)を、ofオプションで出力先(この場合 SDカード)を指定します。 <q>unmount は/dev/disk2s1 でいいんだけど、ddで書き込むときはdisk2s1の先頭にrをつけて、s1を削る</q>([出典](http://qiita.com/hfm/items/96d20d9cc29fb46fd8a3)) らしいので、コマンドは次のようになります。 ```Bash:command $ cd (ZIP解凍先のフォルダ) $ sudo dd bs=1m if="./2013-12-20-wheezy-raspbian.img" of="/dev/rdisk2" ``` 数分待つと、次の様に出て、完了します。 ``` 2825+0 records in 2825+0 records out 2962227200 bytes transferred in 154.902121 secs (19123219 bytes/sec) ``` これで Raspberry Pi の起動ディスクはできているはず。SDカードを抜き取る前に、<code>/dev/disk2s1</code>をアンマウントします。 ```Bash:command $ diskutil unmount /dev/disk2s1 ``` ``` Volume boot on disk2s1 unmounted ``` # Raspberry Pi を起動して初期設定 起動ディスクの書き込みができたら、SDカードを抜いて、いよいよ Raspberry Pi に挿入して起動してみます。 機器は次の写真のように接続します。  コンポジットケーブルは黄色い端子(映像用)だけ使います。片側を Raspberry Pi の黄色い端子に、もう一方をテレビのビデオ入力につなぎます。  写真では、Raspberry Pi がクリアケースに入っていますが、普通はハダカです。気にしないでください。  USBポートは2つついています。マウスとキーボードをつなぐということで2つ付いているんだと思いますが、今回はキーボードだけつなぎます。 Raspberry Pi は、電源が供給されると自動的に起動します。電源を供給する前に起動ディスクを挿入してください。 起動が成功すると、次の動画のような画面が表示されるはずです。 - http://youtu.be/ZhiDy5mukjs はじめに虹色の正方形(?)が一瞬表示されます。コマンドがだーーーーーっと流れていくような起動中の画面が表示されたあと、Raspberry Pi Software Configuration Tool(raspi-config) の画面が表示されます。これは、Raspberry Pi の初期設定を行うメニューで、初回起動時にのみ表示されます。  よくわからないメニューがたくさんありますが、とりあえず<a href="http://www.amazon.co.jp/gp/product/4899773528/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4899773528&linkCode=as2&tag=pxt25503-22">本</a>を見ながらやってみた操作が下記です。 - 1 Expand Filesystem<br /> OSをセットアップした直後は、空き容量が数百MBくらいで固定されていて、容量の大きいSDカードを使っていても認識されていない状態。このメニューを実行すると、使える容量を増やすことができます。 - 4 Internationalisation Options > I3 Change Keyboard Layout<br /> キーボードの配列を選択します。WindowsPCでよく使う普通の日本語キーボードを使っている場合、次のように選択します。 - Keyboard models で「Generic 105-key (Intl) PC」を選択 - Keyboard layout に日本語配列の選択肢がないので、「other」 を選択 - Country of origin for the keyboard で「Japanese」を選択 - 再び Keyboard layout に戻ります。今度は日本語キーボードが選択肢にあるので、「Japanese - Japanese (OADG 109A)」を選択 - Key to function as AltGr で「The default for the keyboard layout」を選択 - Compose key で「No compose key」を選択 - 最後に、Use Control + Alt + Backspace to terminate the X server? と問われるので、<code>\<Yes></code> としておきました。 - 4 Internationalisation Options > I2 Change Timezone<br /> タイムゾーンの設定を行います。Asia、Tokyo を順に選択。 - 8 Advanced Options > A4 SSH<br /> SSH接続の有効・無効を切り替えます。<code>Enable</code> を選択。 ここまでやって、<code>\<Finish></code> を選択して次に進みます。 <q>Would you like to reboot now?</q> に <code>\<Yes></code> と答えると、Raspberry Pi は再起動されます。 # Raspberry Pi セットアップ完了! 次に Raspberry Pi を起動して、 ``` raspberrypi login: _ ``` が表示されたら、Raspberry Pi のセットアップは完了です。  Raspbian "wheezy" OS には、あらかじめ pi というユーザーが作られているので、<code>pi</code> と入力してEnterキーを押します。すると、パスワードを求められるので、パスワードを入力します。ユーザー pi のデフォルトのパスワードは、"raspberry" です。 ログインが成功すると、Linux の標準のコマンドが普通に使えます。<code>cd</code> とか <code>ls</code> とかで内部を徘徊してみると面白いでしょう。(そうでもないかw) # Raspberry Pi をシャットダウンする 起動できたので、最後にシャットダウンをしてみます。 いきなり電源を抜くとデータが破損する可能性があるので、ちゃんとシャットダウンの処理をしてから電源を抜くようにします。 ```Bash:command $ sudo shutdown -h now ``` まず、このコマンドを入力します。しばらくまっていると、終了処理が画面に流れて、最後に画面が真っ暗になります。 電源供給を示す PWR という赤いランプの横にある ACT というランプが点滅している間は、SDカードにアクセスしています。シャットダウンコマンドを発行してもしばらくは ACT ランプが点滅しています。これが消えるまで待って、電源ケーブルを抜きます。 # まとめ - SDカードに Raspbian "wheezy" OS をインストールし、起動ディスクを作りました。 - 起動ディスクを Raspberry Pi に挿し、起動しました。 - Raspberry Pi を初期設定しました。 - Raspberry Pi をシャットダウンしました。 ここまでで入門完了とします。 参考にした<a href="http://www.amazon.co.jp/gp/product/4899773528/ref=as_li_ss_tl?ie=UTF8&camp=247&creative=7399&creativeASIN=4899773528&linkCode=as2&tag=pxt25503-22">書籍</a>には、無線LANに接続する方法、ウェブカメラを接続する方法、センサーやモーターなどのデバイスを制御する方法も載っているので、引き続きこれらにもチャレンジしていきたいと思います。 ※この記事はこちらにアーカイブしました。→ http://www.pxt.jp/ja/diary/article/287/ |
|
| 960位 |
|
|||
|
22:25:42 |
(コロプラ 所属) |
|
最近Unityを初めて、あまりのできのよさに感動を覚えつつ、物理演算とかがあまりにも手軽に行えるので、さすがに仕組みをまったく知らずに使うのは問題だろうと、物理シミュレーションの勉強をしました。 ゴールとしていた、実際に動くものが作れたのでそのまとめです。 ただ、あくまで勉強が目的なので軽量化などはしていません。そのため、結構冗長な書き方をしていて実際に使うにはだいぶ重いです。 --- **実際に作ったサンプル**  [実際の動作サンプル](http://jsdo.it/edo_m18/rFMy4) サンプルでは三角形と四角形、そして円との衝突判定を行い、衝突時に応答する部分まで作っています。 ここでは、この実装をしていくにあたって、躓いた点やメモなど自分が学んだことをつらつらと書いていきす。 --- ##シミュレーションパイプライン さて、物理エンジンは与えられた剛体同士の衝突など、物理的な挙動を計算により導き出すエンジンです。 そのエンジンの仕組みに「パイプライン」があります。 一言で言うと、決まった工程(パイプ)を抜けると結果が導き出される、というものです。 詳細については、[0から学習しているときにまとめた記事](http://qiita.com/edo_m18/items/6051d2d8e422a41d0c13)があるので、そちらを参照ください。 ##今回やったこと 今回の簡易2D物理エンジンの実装で、色々と理論を個別に実装しつつ、それらを組み合わせる形で実装しました。 そのため、その過程で必要になった理論やメモなどを順番に書いていきます。 ##慣性モーメントを求める まず最初にやったのが、この **慣性モーメント** を求める方法でした。 慣性モーメント(3次元だと慣性テンソル)から手を出したのは、2次元ではスカラー(float型などのいわゆる実数)で表せるのに対し、3次元だと行列になり、さらに参考にしていた書籍が2次元についてはあまり詳しく解説していなかったためです。 ###慣性テンソル? 慣性テンソルはベクトルを変換する行列?というような定義です。 (ちなみに慣性モーメントは **回転のしにくさ** を表す量です。質量が **動かしづらさ** を表すので、質量の回転版、と覚えておくと分かりやすいです) なぜ2次元ではスカラー値なのに対し、3次元では行列となるのか。 それは、2次元ではZ軸を基準にしてしか回転しませんが、3次元の場合は任意の軸で回転することができます。 実際のものを想像してもらえば分かると思いますが、 **どこを中心にして回すか** によって **回りづらさ** が変わります。 つまり中心にできる軸が無限にあるわけです。 それをいい感じに導き出してくれるのが慣性テンソル(行列)というわけです。 ###動作サンプル  [実際に作って動くサンプルはこちら](http://jsdo.it/edo_m18/vAIT) 画面をドラッグしてラインを引くと、中心からの距離とラインの長さ(力)に応じて、回転するスピードが変わります。 さらに、質量を設定するとそれだけ回転しづらくなるのが確認できるようになっています。 ###三角形の慣性モーメントの求め方 さて、問題の三角形の慣性モーメントの求め方ですが、理論的なところはまだあまり分かっていません( ;´Д`) 慣性テンソルは$I(Inertia・イナーシャ)$の記号を使い、以下の式が成り立ちます。 ```math I_0 = mr^2 ``` これは、物体のとある質点を取り出したとき、原点からの距離$r$の二乗と、質量$m$との積になる、という意味です。 そしてとある質点ではなく、「重心」の慣性テンソルの求め方が、(2次元の場合)以下になります。 三角形の質量を$m$、原点を始点とし三角形の各頂点を終点としたベクトルをそれぞれ$r_1, r_2, r_3$とします。 この三角形の慣性モーメント$I$は、以下の式で求められます。 ```math I = \frac{1}{18}(|r_1|^2 + |r_2|^2 + |r_3|^2 - r_2 \cdot r_3 - r_3 \cdot r_1 - r_1 \cdot r_2) ``` 特に重心が原点にある場合は次のようになります。 ```math I = \frac{1}{12}m(|r_1|^2 + |r_2|^2 + |r_3|^2) ``` 上記の式は[こちらの記事を参考](http://d.hatena.ne.jp/merom686/20091202/1259759869)にさせてもらいました。 この式を利用すれば、質量と頂点が分かれば慣性モーメント(慣性テンソル)を求めることができます。 これをプログラムにしたのが以下です。(実際につくったものからの抜粋です) ```javascript var v1 = this.vertices[0]; var v2 = this.vertices[1]; var v3 = this.vertices[2]; var v1len = vec2.lengthSqr(v1); var v2len = vec2.lengthSqr(v2); var v3len = vec2.lengthSqr(v3); var v2v3 = vec2.dot(v2, v3); var v3v1 = vec2.dot(v3, v1); var v1v2 = vec2.dot(v1, v2); var I = (1 / 18) * this.mass * (v1len + v2len + v3len - v2v3 - v3v1 - v1v2); this.inertia = I; ``` ###円の慣性モーメントの求め方 [円(円板)の慣性モーメントの求め方](http://www14.plala.or.jp/phys/mechanics/34.html)を参考にしました。 質量$m$、半径$a$の中心軸まわりの慣性モーメントの式は以下。 ```math I = \int_0^a r^2 (\rho 2 \pi rdr) \\ 一様な円板とすると \\ \rho = \frac{m}{\pi a^2} \\ となるので、 \\ I = \frac{1}{2}ma^2 ``` ##衝突検出 次に、衝突検出の仕組みについて説明します。物理シミュレーションではこの衝突検出が一番重要な意味を持ちます。 なぜなら、物理シミュレーションはとても細かい数字で構成されるため、少しの誤差の積み重ねがとても大きなものになってしまうからです。 衝突検出には **ミンコフスキー差** と呼ばれる理論と、 **GJKアルゴリズム** 、 **EPA(Expanding Polythope Algorithm)法** というアルゴリズムが使われます。 ミンコフスキー差については、[以前の記事](http://qiita.com/edo_m18/items/6051d2d8e422a41d0c13)に書いているのでそちらを見てください。 理論自体はとてもシンプルなものです。 さて、ミンコフスキー差の理論が分かっても、それをなんとかプログラムにしないとなりません。 ###線分と点との最短距離を求める こちらも[サンプル](http://jsdo.it/edo_m18/2ESW)を作っています。  線分と点との最短距離は、その線分に垂線の足に当たる点か、あるいは線分の端になります。 これは実際に考えてみると分かると思います。 そしてそれをプログラムにするために参考にしたのが以下です。 ####線分と点との最短距離の求め方 線分ABの端点をそれぞれ$(x_0, y_0), (x_1, y_1)$とし、直線との最短距離を求めたい点を$(px, py)$とする。 まず点Pから線分ABに垂線を下ろし、その長さを計る。 まずは線分AB上の点を表すのに、パラメトリックに$(x_0 + dx * t, y_0 + dy * t)$で表すことにする。 ちなみに$dx, dy$はそれぞれ、$(x_1 - x_0), (y_1 - y_0)$と線分の長さに該当する値。 そして前述の媒介変数tは0〜1の間にある限り、線分AB上に点があることを示している。 (つまり、`t == 1` のとき、点の位置は $x_1, y_1$ の位置になる) そして垂線の足(*)$(tx, ty)$が線分上にない場合は、線分の端点$(x_0, y_0), (x_1, y_1)$のうち、垂線の足に近いほうの端点が最短距離となる。 * … 垂線の足は、直線に対して垂直に降ろした線との交差する点のこと。 ##### 解き方 垂線の足にあたる点は $(x_0 + dx * t - px, y_0 + dy * t - py)$ と書ける。 (点Pと上記で書いたパラメトリックに表した点との差分を計算することで垂線ベクトルを求めている) そして線分と垂線のベクトルの内積は以下のように書ける。(ちなみに垂直なベクトルなので結果は0になる) ```math A \cdot B = (dx, dy) \cdot (x_0 + dx * t - px, y_0 + dy * t - py) \\ = (dx^2 + dy^2)t + dx(x_0 - px) + dy(y_0 - py) ``` このとき、 ```math a = (dx^2 + dy^2) \\ b = dx(x_0 - px) + dy(y_0 - py) ``` と置くと、a == 0の場合はベクトルABが同じ点になることを示すため、線分ではなく点になり、点Pとは点と点の距離を求めればいいことになる。 そしてそうでなければ、上記式より$at + b = 0$となり、$t = -\frac{b}{a}$となる。 上記解き方は、[こちらの記事](http://toycode.com/hiroshi/etc/line_point.html)を参考にさせて頂きました。 こちらのRubyプログラムをJavaScriptにすると・・。 ```javascript /** * 線分と点との最短距離を求める */ function distLine(x0, y0, x1, y1, px, py) { var dx = x1 - x0; var dy = y1 - y0; var a = dx * dx + dy * dy; if (a === 0) { return Math.sqrt((x0 - px) * (x0 - px) + (y0 - py) * (y0 - py)); } var b = dx * (x0 - px) + dy * (y0 - py); var t = -(b / a); if (t < 0.0) { t = 0.0; } if (t > 1.0) { t = 1.0; } var x = t * dx + x0; var y = t * dy + y0; return Math.sqrt((x - px) * (x - px) + (y - py) * (y - py)); } ``` ------- ####サポート写像 上記の最短距離を求めるのは、ミンコフスキー差の理論で使うサポート写像を求める際に用います。 サポート写像は、任意の方向を向いているベクトルを決め、そのベクトル方向に一番遠い点を見つけ出す **関数** です。  そしてこのミンコフスキー差のサポート写像、ミンコフスキー差の図形を計算で求めなくても、ある簡単な方法で求めることができます。 ミンコフスキー差はふたつの物体を使って作る図形でした。 なので、それぞれの図形のサポート写像を求め、単純にそれを足すことで(正確には「ミンコフスキー差」なので引くことで)その写像を求める、というものです。 そして、この写像を求めるのに使われるのが内積です。 内積にはいくつかのとても便利な性質があり、それを利用します。 具体的には[こちらに少しだけまとめている](http://qiita.com/edo_m18/items/ae0c8ea57a0bd98f1cee)ので参照ください。 この内積の性質を利用すると、上記のサポート写像を求めることができます。 これの[サンプル](http://jsdo.it/edo_m18/sPPN)も上げているので見てみてください。 画面をドラッグすると、内積を使ってその図形の頂点の位置を、ドラッグで生成したベクトルに投影した点を表示します。 そしてこの理論をプログラムにする上で最後に必要になるのは、三角形の内側に点が存在するかどうかのチェックです。 #####三角形の内点かの判定 これ自体は、実は実装はとても簡単です。 理屈としては、三角形の各辺に対して点がどちら側にあるか、を判定すればいいのです。 辺に対してすべて内側の方向に点が存在すれば、それはつまり三角形の内側に点があることになります。 逆に、どれかひとつの辺でも反対側に点があればそれは三角形の中に点は含まれていないことを表しています。 これを実現しているのが[こちらのサンプル](http://jsdo.it/edo_m18/g48N)です。 処理のコア部分を抜き出したのが以下です。 ```javascript var originPos = this._originPos; var v0 = this._foundPoints[0]; var v1 = this._foundPoints[1]; var v2 = this._foundPoints[2]; //三角形の各辺のベクトルを得る var edge0 = vec2.sub(v1, v0); var edge1 = vec2.sub(v2, v1); var edge2 = vec2.sub(v0, v2); //v0から見た辺の向きベクトルを得る var ce0 = vec2.sub(v1, v0); var ce1 = vec2.sub(v2, v0); var CCW = 1; //それぞれの辺の位置関係を外積によって確認し、 //時計回りか反時計回りかを判定 //時計回りの場合は、以後の判定のプラスマイナスを逆転させる if (vec2.cross(ce0, ce1) < 0) { CCW = -1; } //原点が三角形の辺の内側にあるかを判定 //3つの辺すべてに置いて内側という判定の場合は //原点は三角形の内側に存在している var cp0 = vec2.sub(originPos, v0); if (vec2.cross(edge0, cp0) * CCW <= 0) { return false; } var cp1 = vec2.sub(originPos, v1); if (vec2.cross(edge1, cp1) * CCW <= 0) { return false; } var cp2 = vec2.sub(originPos, v2); if (vec2.cross(edge2, cp2) * CCW <= 0) { return false; } return true; ``` さて、これで衝突判定に必要なプログラムが揃いました。 これを用いて衝突検出を行い、もし衝突が検出されたら、次は衝突点から **撃力(Impluse・インパルス)** を求め、それを元に、衝突応答を計算してやります。 --- ###GJKアルゴリズム 衝突検出する方法はいくつかあるみたいですが、今回参考にしたのは「GJKアルゴリズム」と呼ばれるものです。 このアルゴリズムは前述した「ミンコフスキ差」を利用した衝突検出です。 ここからは、ミンコフスキ差を理解している前提で話を進めます。 まず、前提としてミンコフスキ差が原点を含んでいる場合はふたつの物体は衝突しています。 ここではこのミンコフスキ差が実際に原点を含んでいるかどうかを検出する方法を解説します。 それが「GJKアルゴリズム」です。 GJKアルゴリズムは前述の「サポート写像」を利用して衝突検出を行います。 [こちらの記事](http://angra.blog31.fc2.com/blog-entry-115.html)がとても分かりやすく書かれているので、こちらを見てもらうと分かるかと思います。 ###EPA(Expanding Polythope Algorithm)法 GJKアルゴリズムと一緒に用いられ、衝突時の位置と距離(貫通深度)を調べる方法です。 基本的にはGJKアルゴリズムで使われる方法を用いて検出を行います。 これの詳細もやはり前述の記事がとても参考になるのでそちらを参照してください。 ####サンプル [三角形同士の衝突のサンプル](http://jsdo.it/edo_m18/jKC4)を作りました。 ###衝突点の座標を調べる さて、前述までで貫通深度(どのくらいめり込んでいるか)と、どの方向に物体を動かしたらいいかの **衝突法線** (衝突が解消されるのに一番距離が短い方向)が分かりました。 ただ、これは方向と深度(ベクトルの長さ)が分かっただけで、実際に衝突している点はまだ分かっていません。 最初、前述までで求められるのかと思っていたので若干ハマりました。 画像にすると以下の感じです。  衝突点の座標は、意外とコロンブスの卵的な感じで求めます。 まずざっくり手順を書くと、 1. 物体を衝突法線方向に、貫通深度より若干長い距離移動させる 2. その後、その移動後の物体同士の最接近点を求める  という手順です。 要は、一回衝突をなかったことにして、その上で一番近い点を求める、というのをやります。 実際は三角形同士の一番近い点を求めることになります。 こちらも手順をざっくり書くと、 1. 三角形Aの全頂点と、三角形Bの各辺との最短点を求める 2. 今度は逆に三角形Bの全頂点と三角形Aの各辺との最短点を求める という手順です。 要は総当りで調べる、ってことですw (本当なら、計算上除外できる点がいくつかあるので、そこは除外して速度アップなどをしますが、今回はそこまでの対応はしていません) そして点と線分の最短点の求め方は前述の通りです。 この方法を使って、総当りで各点と各辺の最短距離を求め、その中で一番短い距離にある点が衝突点だ、というわけです。 言葉にするとわりとシンプルですが、これをプログラムにすると意外とめんどくさいです。 が、実装自体は分かりやすいでしょう。 ちなみに求めた衝突点は、それぞれの剛体のローカル座標に変換して保持しておきます。 理由は、衝突応答の計算時にローカル座標のほうが計算がしやすいためです。 さぁ、これで次の衝突応答(拘束の解消)ステージで必要な **貫通深度** 、 **衝突法線** 、 **衝突点の座標** が求まりました。 この情報を元に、衝突の応答を計算します。 ##衝突応答 検出応答のステージでは、上記の情報を元に、衝突の結果がどうなるのかを計算します。 まず、なぜ「拘束の解消」なのかと言うと、衝突した瞬間は次の時点(タイムステップ)では反発方向に移動することになります。 つまり、 **反発方向に拘束されている** 、と言い換えることができます。 今回、作っていく過程では書籍の中で説明されている公式をそのまま使いました。 いちおう、一般的に解いてからそれをプログラムに落としこむのですが、この一般式は読んで理解しているものの、説明できる理解度ではないので割愛します。 ここでは、出てきた公式とちょっとした説明にとどめます。 ```math \begin{equation} J = \frac{-v_r(e + 1)}{1 / m_1 + 1 / m_2 + n \cdot[(r_1 \times n) / I_1] \times r_1 + n \cdot [(r_2 \times n) / I_2] \times r_2} \end{equation} ``` 記号の意味は以下の通り。 | 記号 | 意味 | |:----:|:----:| | $m$ | 質量 | | $e$ | 反発係数 | | $v_r$ | 物体Aと物体Bの相対速度 | | $n$ | 衝突法線 | | $I$ | 慣性モーメント(慣性テンソル) | また、これを実際のプログラムにしたのが以下です。 ```javascript var solverBodies = []; var bias = ns.contactBias; var slop = ns.slop; var iteration = ns.iteration; var rigidbodies = this._rigidbodies; var pairs = this._pairs; var timeStep = this._timeStep; //ソルバー用プロキシを作成 //剛体の情報のうち、拘束の解消に使うものをコピー for (var i = 0, l = rigidbodies.length; i < l; i++) { var body = rigidbodies[i]; var solverBody = new ns.SolverBody(body); solverBodies.push(solverBody); } //拘束のセットアップ for (var i = 0, l = pairs.length; i < l; i++) { var pair = pairs[i]; if (pair.contacts.length === 0) { continue; } var bodyA = rigidbodies[pair.objA.id]; var solverBodyA = solverBodies[pair.objA.id]; var bodyB = rigidbodies[pair.objB.id]; var solverBodyB = solverBodies[pair.objB.id]; pair.friction = sqrt(bodyA.friction * bodyB.friction); for (var j = 0, k = pair.contacts.length; j < k; j++) { var contact = pair.contacts[j]; var cp = contact.contactPoint; var dp = contact.depthPoint; var r1 = vec3(vec2.sub(cp, solverBodyA.centerVert), 0); var r2 = vec3(vec2.sub(cp, solverBodyB.centerVert), 0); //いったん衝突法線を3次元ベクトルにする var n = vec2.normalize(dp); var normal = vec3(n, 0); var velocityA = vec3.add(vec3(bodyA.velocity, 0), vec3.cross(vec3(0, 0, bodyA.angularVelocity * DEG_TO_RAD), r1)); var velocityB = vec3.add(vec3(bodyB.velocity, 0), vec3.cross(vec3(0, 0, bodyB.angularVelocity * DEG_TO_RAD), r2)); //2つの物体の相対速度を求める(V1 - V2) var relativeVelocity = vec3.sub(velocityA, velocityB); //接線ベクトル用変数 var tangent1 = vec3(0.0); //接線ベクトルを求める this._calcTangentVector(normal, tangent1); var restitution = pair.typeNew ? 0.5 * (bodyA.restitution + bodyB.restitution) : 0.0; // 衝突法線方向の計算 { var axis = normal; //rhs = Right Hand Side = 右辺 contact.constraints[0].jacDiagInv = 1.0 / ( (solverBodyA.massInv + solverBodyB.massInv) + vec3.dot(axis, vec3.cross(vec3.multiplyScalar(vec3.cross(r1, axis), solverBodyA.inertiaInv), r1)) + vec3.dot(axis, vec3.cross(vec3.multiplyScalar(vec3.cross(r2, axis), solverBodyB.inertiaInv), r2)) ); contact.constraints[0].rhs = -((1 + restitution) * vec3.dot(relativeVelocity, axis)); contact.constraints[0].rhs -= (bias * max(0.0, contact.distance + slop)) / timeStep; // position error contact.constraints[0].rhs *= contact.constraints[0].jacDiagInv; contact.constraints[0].lowerLimit = -Number.MAX_VALUE;; contact.constraints[0].upperLimit = 0.0; contact.constraints[0].axis = axis; } //Tangent1 { var axis = tangent1; contact.constraints[1].jacDiagInv = 1.0 / ( (solverBodyA.massInv + solverBodyB.massInv) + vec3.dot(axis, vec3.cross(vec3.multiplyScalar(vec3.cross(r1, axis), solverBodyA.inertiaInv), r1)) + vec3.dot(axis, vec3.cross(vec3.multiplyScalar(vec3.cross(r2, axis), solverBodyB.inertiaInv), r2)) ); contact.constraints[1].rhs = -vec3.dot(relativeVelocity, axis); contact.constraints[1].rhs *= contact.constraints[1].jacDiagInv; contact.constraints[1].lowerLimit = 0.0; contact.constraints[1].upperLimit = 0.0; contact.constraints[1].axis = axis; } //Warm starting { //あとで } } } //拘束の演算 for (var itr = 0; itr < iteration; itr++) { for (var i = 0, l = pairs.length; i < l; i++) { var pair = pairs[i]; var solverBodyA = solverBodies[pair.objA.id]; var solverBodyB = solverBodies[pair.objB.id]; //検出された衝突情報を元に撃力を計算 for (var j = 0, k = pair.contacts.length; j < k; j++) { var contact = pair.contacts[j]; var cp = contact.contactPoint; var r1 = vec3(vec2.sub(cp, solverBodyA.centerVert), 0); var r2 = vec3(vec2.sub(cp, solverBodyB.centerVert), 0); //Normal { var constraint = contact.constraints[0]; var deltaImpulse = constraint.rhs; var deltaVelocityA = vec3.add(vec3(solverBodyA.deltaLinearVelocity, 0), vec3.cross(vec3(0.0, 0.0, solverBodyA.deltaAngularVelocity), r1)); var deltaVelocityB = vec3.add(vec3(solverBodyB.deltaLinearVelocity, 0), vec3.cross(vec3(0.0, 0.0, solverBodyB.deltaAngularVelocity), r2)); deltaImpulse -= constraint.jacDiagInv * vec3.dot(constraint.axis, vec3.sub(deltaVelocityA, deltaVelocityB)); var oldImpulse = constraint.accumImpulse; constraint.accumImpulse = ns.clamp(constraint.lowerLimit, constraint.upperLimit, oldImpulse + deltaImpulse); deltaImpulse = constraint.accumImpulse - oldImpulse; solverBodyA.deltaLinearVelocity = vec2.add(solverBodyA.deltaLinearVelocity, vec2.multiplyScalar(constraint.axis, deltaImpulse * solverBodyA.massInv)); solverBodyA.deltaAngularVelocity += deltaImpulse * solverBodyA.inertiaInv * vec2.cross(r1, constraint.axis); solverBodyB.deltaLinearVelocity = vec2.sub(solverBodyB.deltaLinearVelocity, vec2.multiplyScalar(constraint.axis, deltaImpulse * solverBodyB.massInv)); solverBodyB.deltaAngularVelocity -= deltaImpulse * solverBodyB.inertiaInv * vec2.cross(r2, constraint.axis); } var maxFriction = pair.friction * abs(contact.constraints[0].accumImpulse); contact.constraints[1].lowerLimit = -maxFriction; contact.constraints[1].upperLimit = maxFriction; // Tangent { var constraint = contact.constraints[1]; var deltaImpulse = constraint.rhs; var deltaVelocityA = vec3.add(vec3(solverBodyA.deltaLinearVelocity, 0), vec3.cross(vec3(0.0, 0.0, solverBodyA.deltaAngularVelocity), r1)); var deltaVelocityB = vec3.add(vec3(solverBodyB.deltaLinearVelocity, 0), vec3.cross(vec3(0.0, 0.0, solverBodyB.deltaAngularVelocity), r2)); deltaImpulse -= constraint.jacDiagInv * vec3.dot(constraint.axis, vec3.sub(deltaVelocityA, deltaVelocityB)); var oldImpulse = constraint.accumImpulse; constraint.accumImpulse = ns.clamp(constraint.lowerLimit, constraint.upperLimit, oldImpulse + deltaImpulse); deltaImpulse = constraint.accumImpulse - oldImpulse; solverBodyA.deltaLinearVelocity = vec2.add(solverBodyA.deltaLinearVelocity, vec2.multiplyScalar(constraint.axis, deltaImpulse * solverBodyA.massInv)); solverBodyA.deltaAngularVelocity += deltaImpulse * solverBodyA.inertiaInv * vec2.cross(r1, constraint.axis); solverBodyB.deltaLinearVelocity = vec2.sub(solverBodyB.deltaLinearVelocity, vec2.multiplyScalar(constraint.axis, deltaImpulse * solverBodyB.massInv)); solverBodyB.deltaAngularVelocity -= deltaImpulse * solverBodyB.inertiaInv * vec2.cross(r2, constraint.axis); } } } } //計算結果を速度、回転に追加 for (var i = 0, l = rigidbodies.length; i < l; i++) { var body = rigidbodies[i]; var solverBody = solverBodies[i]; body.velocity = vec2.add(body.velocity, solverBody.deltaLinearVelocity); body.angularVelocity += solverBody.deltaAngularVelocity * RAD_TO_DEG; } ``` これを元に、[実際に動かしたサンプルはこちら](http://jsdo.it/edo_m18/rFMy4) また、プログラム中で使っているベクトル計算(vec2とかvec3とか)は、自身のライブラリを使用しています。 ([Githubで公開](https://github.com/edom18/MathJS)しています) ##剛体の更新 さて、以上で各種必要な計算が終わりました。 あとは次のタイムステップでの剛体の位置や速度を更新してやり、ループ処理の中で順次更新されていけばめでたく物理シミュレーションに則した動きになる、というわけです。 ##作っていく上でハマったこととかメモ ###とあるベクトルに垂直なベクトルを求める 垂直は、内積が0になるベクトル($A \cdot B = 0$)となるので、 $A = (a, b)$というベクトルがあるとすると、$B = (-b, a)$か$B = (b, -a)$という二通りのベクトルが考えられる。 例) ```math A = (2, 3) \\ A \cdot B = Ax * Bx + Ay * By = 0 より、 \\ 2Bx + 3By = 0 \\ このとき、0になるのはB = (-3, 2) or (3, -2)となる。 \\ \\ 2 * -3 + 3 * 2 = 0 \\ 2 * 3 + 3 + -2 = 0 ``` ###2次元での角速度 書籍やネットでの記事を参考にしていると、どうしても2次元の話と3次元の話が混在して、いざ実装しようとしたときに外積を求める式なのに、対応する変数がスカラーだったりと、式をどう解釈していいか混乱した。 角速度から、角運動量を求める公式は ```math L = r \times \omega ``` と、外積を使います。 が、2次元の場合は角速度($\omega$)はスカラーで表せるのと、計算を簡単にするために常にスカラーで保持していたため、外積?!となりました。(外積はベクトル同士の掛け算) んで、2次元での角速度は、要は3次元でZ軸だけの回転と見なせることに気づき、以下のようにベクトルを生成して対応。 *※スカラーは大きさのみを持つ量。言ってみれば実数。プログラムにしたらfloatとか。* ```javascript //r … 物体の原点から接触点までのベクトル // objA … 計算対象の物体A // t … タイムステップ var angularVelocity = vec3.cross(r, vec3(0, 0, objA.angularVelocity * t)); ``` というふうにして、無理やり3次元ベクトル化して計算しました。 その他、外積が必要だけど3次元じゃねーよ!っていうのも、上記の理屈からZ軸の値を考えて追加して計算することでなんとかそれらしく実装。(ただ、もっといい実装は方法必ずあるけど、とりあえず理解が目的なのでこのへんで終了) --- ##参考にした記事 * [三角形の慣性モーメント](http://d.hatena.ne.jp/merom686/20091202/1259759869) * [GJK アルゴリズム 説明](http://angra.blog31.fc2.com/blog-entry-115.html) * [一様な円盤の慣性モーメント](http://www14.plala.or.jp/phys/mechanics/34.html) |
|
| 961位 |
|
|||
|
10:55:40 |
(Syntax Sugar Inc. 所属) |
|
### github
https://github.com/pawurb/termit コマンドラインからGoogle Translationを使用して翻訳することができます。コミットメッセージを考えるときなんかに便利! `gem install termit` ### English to Japanese ```bash $ termit en ja "Convention over configuration (also known as coding by convention) is a software design paradigm which seeks to decrease the number of decisions that developers need to make, gaining simplicity, but not necessarily losing flexibility." => 設定(これも慣例によりコーディングとして知られている)上の条約は、シンプルさを獲得し、開発者が行う必要があることを意思決定の数を減少させるが、必ずしも柔軟性を失っていないことを目的ソフトウェア設計パラダイムである。 ``` ### Japanese to English ```bash $ termit ja en "Ruby on Rails(ルビーオンレイルズ)は、オープンソースのWebアプリケーションフレームワークである。" => Ruby on Rails ( Ruby on Rails ) is a Web application framework of open source . ``` ## 同意語機能 `-s` (synonyms) フラグを渡すと同意語が存在する場合はそれを表示します。 ```bash $ termit en ja -s 'commit' => コミット => Synonyms: コミット, 犯す, 委託, 働く, 演じる, 預ける, 作る ``` ## 音声読み上げ機能 `-t` (talk) フラグを渡すと翻訳した文字列を読み上げてくれます。 ```bash $ termit ja en 'こんにちは' -t => Hello # Hello と読み上げられる ``` この機能を使うにはmpg123が必要です。 Ubuntu: ```bash $ sudo apt-get install mpg123 ``` MacOSX: ```bash $ brew install mpg123 ``` |
|
| 962位 |
|
|||
|
19:28:30 |
|
|
### 背景
railsでは`rails g scaffold`などでModelを作成すれば、 自動的にidが付与されます。 しかもprimary_keyでauto_incrementでかつindexも張られるので、 普段はidを気にする必要はありません。 railsを使い、自分でデータ構造を決める場合はrailsの流儀に則った方が楽で、問題も起こりません。 しかし、古いデータを活用した場合、流儀にそぐわない事もあり得ます。 今回は規約に沿わない場合の対応について大きく分けて2つの場合について説明します。 なお環境は以下の物で検証しています。 * ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.4.0] * Rails 4.0.0 * composite_primary_keys (6.0.0) ### 主キーがidではない 主キーがidではない場合として、 具体的に商品(Item)テーブルの主キーが文字列である場合を考えます。 主キーがidで無い場合のポイントは `create_tabel`の`id: false`です。 これによってidが自動生成されることはなくなります。 他のオプションで`primary_key`と言う物もありますが、 これはinteger型で名前をidから変更するだけの物です。 下記の例では`null: false`とする事でnot nullな制約を追加し、 `add_index :items, :code, unique: true`でユニーク制約を定義しています。 #### Migrationファイル ```ruby class CreateItems < ActiveRecord::Migration def change create_table :items, id: false do |t| # 商品コード t.string :code, limit: 8, null: false # 商品名 t.string :name end add_index :items, :code, unique: true end end ``` モデル側には`self.primary_key`で主キーを設定します。 これによって`find`メソッドで主キーによる取得が可能になります。 #### Item Model ```ruby class Item < ActiveRecord::Base # 主キー設定 self.primary_key = :code end ``` ### 複合主キーを扱いたい 次は複合主キーを扱う場合についてです。 具体的に先ほど使った商品(Item)について、 各商品に任意のタグ(Tag)がついているとします。 タグは商品コード(item_code)とタグコード(code)の複合主キーを持っています。 railsで複合主キーを扱う場合、 [composite_primary_keys](https://github.com/composite-primary-keys/composite_primary_keys)のgemを使用します。 composite_primary_keysの準備として`Gemfile`に`gem 'composite_primary_keys'`を追加し`bundle install`します。 Migrationファイルのポイントは、 `:item_code`と`:code`の組み合わせがユニークである制約の定義です。 複合主キーがユニークである制約は`add_index :tags, [:item_code, :code], unique: true`だけでも十分です。 複合主キーが2つ3つと長くなる場合、インデックス名が長すぎてエラーが発生する事もあるので、 その場合は`name`でインデックス名を指定してあげましょう。 #### Migrationファイル ```ruby class CreateTags < ActiveRecord::Migration def change create_table :tags, id: false do |t| # 商品コード t.string :item_code, limit: 8 # タグコード t.string :code, limit: 8 # タグ名称 t.string :name end add_index :tags, [:item_code, :code], unique: true, name: 'composite_index' end end ``` モデルに関しては、 先ほどは`self.primary_key`を使いましたが、 複合主キーの場合は`self.primary_keys`を使います。 これによって`Tag.find 'item_code', 'code'`と、 findメソッドで複合主キーを扱うことが可能になります。 また、`foreign_key`によって、どのカラムが外部キーなのかを指定することで、 関連を定義することが可能です。 #### Tag Model ```ruby class Tag < ActiveRecord::Base # 主キー設定 self.primary_keys = :item_code, :code # 商品との関連 belongs_to :item, foreign_key: :item_code end ``` #### Item Model ```ruby class Item < ActiveRecord::Base # 主キー設定 self.primary_key = :code # タグとの関連 has_many :tags, foreign_key: :item_code end ``` ### 問題 composite_primary_keysによって複合主キーを取り扱うことが出来るようになりました。 しかし、少し問題が発生する場合もあります。 それはModelに対して`to_json`を呼び出した場合などで、以下のエラーが発生します。 ``` TypeError: ["item_code", "code"] is not a symbol ``` 根本では`serializable_hash`を呼び出すところでのエラーなので、 モデルでserializable_hashをオーバーライドする事で解決します。 ```ruby def serializable_hash(options={}) options = { :only => [:item_code, :code, :name_code] }.update(options) super(options) end ``` |
|
| 963位 |
|
|||
|
16:38:45 |
(Increments inc. 所属) |
|
稀に
> masterでは直ってるけど、Rubygemsにはまだ出てないから、Gemfileに > > ```ruby > gem 'spring', github: "jonleighton/spring" > ``` > > って書いて `bundle update spring` を実行してね。 > > 参考: https://github.com/jonleighton/spring/issues/143#issuecomment-17984728 みたいなことがある。だいたいの場合はこれでいいんだけど、ここで引用しているspringの場合、これで直るのは `bundle exec spring` だけで、 `bundle exec` 無しだと実行できない。(僕の勘違いかも知れないので、正しいやり方を知っている人は是非コメントで教えてください。) 我慢して古いバージョンをRubygemsから引っ張ってきてインストールしてもいいんだけど、やっぱりbundlerでインストールしてあるのと同じバージョンにしたい。 で、gemコマンドにはgitリポジトリを指定して直接インストールする機能が無いので、こういう場面ではspecific_installというgemを使う。今回の場合は以下のようになる。 ```zsh: gem install specific_install gem specific_install -l 'git://github.com/jonleighton/spring.git' ``` |
|
| 964位 |
|
|||
|
20:39:10 |
|
|
特に注意しなければならないことはないので、普通に rbenv をインストールすればおわり。
# 1.必要なパッケージをインストール ``` $ su - # apt-get install build-essential bison libreadline6-dev curl git-core zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev autoconf libncurses5-dev # exit ``` # 2.git で rbenv, ruby-build をダウンロード (clone) (9/25 追記)実は Debian 7.0 なら rbenv を apt-get で入れられるらしいです。 ``` $ cd $ git clone git://github.com/sstephenson/rbenv.git .rbenv $ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build ``` # 3.もろもろの設定 rbenvを使うために必要な設定を ~/.bashrc に書きます。 ここでは vi を使っていますが、テキストエディタならなんでもよいです。 ``` $ vi ~/.bashrc ``` 以下を書いて保存する。 ``` export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)" ``` 保存したら反映。 ``` $ source ~/.bashrc ``` # 4.Rubyのインストール 以下のようにすると、インストール可能なバージョンの一覧が表示されます。 ``` $ rbenv install --list ``` たくさん表示されますが、好きなものをダウンロードしましょう。 よくわからない人は、1.9.3-pXXX のXXX部分の数字が一番大きいのがおすすめです(執筆時点では 1.9.3-p392)。 ``` $ rbenv install 1.9.3-p392 ``` 数分〜数十分はかかります。気長に待ちましょう。 終わったら rehash で rbenv を更新します。 ``` $ rbenv rehash ``` 最後に、今インストールしたバージョンを global に指定して、Ruby のバージョンを確認すれば完了です。 ``` $ rbenv global 1.9.3-p392 $ ruby --version ruby 1.9.3p392 (2013-02-22 revision 39386) [i686-linux] ``` |
|
| 965位 |
|
|||
|
11:43:19 |
|
|
gitで外部リポジトリを取り込んで利用するには、`submodule`(サブモジュール) と`subtree merging`(サブツリーマージ)の2手法があります。 私の観測範囲内では、サブモジュールはよく利用されていますが、サブツリーマージは目にしません。 そこで、サブツリーマージを試してみての使い分けや思ったことをつらつらと。 ### 大雑把な要点を最初に書いておく * 外部リポジトリを外のものとして取り込むならサブモジュール * 外部リポジトリを取り込みつつ、中のものとして手を加えていくならサブツリーマージ * git初心者にはサブモジュール(異論ありそう) ## サブツリーマージ使ってみた [github.com/marutanm/dotfiles](https://github.com/marutanm/dotfiles) 中身としては、よくある環境設定ファイル一覧です。 前提として、 * tmux.conf([gistにおいていた](https://gist.github.com/896088)) * vim設定([githubにおいていた](https://github.com/marutanm/.vim)) * zsh設定([githubにおいていた](https://github.com/marutanm/.zsh)) というファイル群があり、これをサブツリーマージして`dotfiles`というひとつのリポジトリにまとめました。 さらに、git設定を別ブランチで作成し、サブツリーマージでmasterにとりこみました。このgit設定はあくまでもブランチであり、独立したリポジトリはありません。 ## 手順 省略。主として以下を参考にしました。 * http://git-scm.com/book/ch6-7.html * [上記の日本語版](http://git-scm.com/book/ja/Git-%E3%81%AE%E3%81%95%E3%81%BE%E3%81%96%E3%81%BE%E3%81%AA%E3%83%84%E3%83%BC%E3%83%AB-%E3%82%B5%E3%83%96%E3%83%84%E3%83%AA%E3%83%BC%E3%83%9E%E3%83%BC%E3%82%B8) * http://www.kernel.org/pub/software/scm/git/docs/howto/using-merge-subtree.html ## サブモジュールとサブツリーマージの違い サブモジュールだと、外部リポジトリの特定のリビジョンに対しての参照をもっています。あくまでも参照なので外部リポジトリのファイルはコミットされておらず、`git clone`しただけではファイルおちてきません。 サブツリーマージでは、外部リポジトリは通常リモートリポジトリとして追加されます。 つまりは別ブランチです。取り込むときには`read-tree`を使い、さらには`subtree`ストラテジを指定してマージすることも可能です。(前者はあくまでファイルをとりこむだけ、後者によりコミットツリーがひとつになる、と私は認識しています。) [前述のリポジトリのネットワークグラフ](https://github.com/marutanm/dotfiles/network)を見ればわかりますが、複数ブランチがそれぞれのコミット履歴をもっています。これがそれぞれ外部リポジトリと対応しており、マージコミットでmasterブランチに統合されています。 サブモジュールを使うと、外部リポジトリのコミット履歴はここには表示されません。 要は、サブモジュールは外部リポジトリを含めたひとつのブランチとして扱う、サブツリーマージは外部リポジトリそれぞれをブランチとして複数ブランチを扱うということです。 # サブモジュールの使いどころ **外部リポジトリを取り込んで使いたいが、外のものとして使いたい場合。**もう少し踏み込んでいうと、 * (外部リポジトリに)手を加えることはない * (外部リポジトリの)コミット履歴は気にしない ような場合は、サブモジュールがよさそうです。サブモジュールのコミット履歴を見るときや手をくわえるときは、そのリポジトリに対して別途gitを操作する必要があります。サブモジュールはあくまでも別リポジトリなのです。 # サブツリーマージの使いどころ サブツリーマージにおける外部リポジトリは、単にリモートリポジトリ。つまるところ別ブランチなわけです。 ネットワークグラフも併せて一覧できますし、`diff-tree`でdiffも見られる。ブランチ間の操作も`subtree`ストラテジを指定することにより、`merge`だろうが`pull`だろうが`cherry-pick`だろうが`rebase`だろうが可能です。サブディレクトリに移動せずとも、外部リポジトリに対する任意のgit操作ができます。もちろんコミットしたものを`push`してもとの外部リポジトリに反映することも容易です。 つまり、**外部リポジトリを取り込みつつ、中のものとして手を加えたい場合**にはサブツリーマージを選択するのが良さそうです。 # サブモジュールやめてサブツリーマージ使おうかな? ケースバイケース。 試してみたところ、サブツリーマージしようとする外部リポジトリが、さらにサブモジュールをもっていると破滅を導きます。 ### `git clone && git submodule update --init`で済むのは存外に容易かもしれない サブモジュールを使うと`git clone`にくわえて`git submodule update --init`をする必要があります。これを煩雑と捉えるかどうか。それさえしてしまえば作業コピーの状態は同じになります。 サブツリーマージを使うと、`git clone`のみでファイルが落ちてくるので作業コピーはおおよそ同じになりますが、サブツリーのリモートリポジトリ等の実際に開発をはじめられるまでの作業環境の復元にはもう一手間かかります。 また、サブツリーマージでは外部リポジトリをブランチとして扱います。つまり**必然的に複数ブランチを育てていくことになります。**コミットがどのブランチに属するべきかを考え、それぞれのコミット履歴を整然と保つためにコミットを分割し、統合し、移動する必要があります。 そういった意味で、冒頭の**git初心者にはサブモジュール**につながります。サブツリーマージと比較してかんたんに作業可能な状態になりますし、その後の運用もかんたんな気がします。(git初心者の定義にもよりますが、たとえばGUIツールではサブツリーマージできないのでは?) 特にチームでgitリポジトリとして運用していくとき、全メンバがgitに十分に習熟していない限りは、サブモジュールを選択した方が無難でしょう。 ### じゃあサブツリーマージのメリットは? サブツリーマージがもたらしてくれるのは、**より柔軟なプロジェクト運用**だと思います。たとえば、ひとつのプロジェクトで複数のモジュールを開発していくときなど。それぞれのモジュールをメインリポジトリ内の独立したブランチあるいは別のリポジトリとしておいて、メインリポジトリのメインブランチでサブツリーマージでとりこみます。 ``` # メインブランチのイメージ % tree /main-repository/ |-- api-server # リモートリポジトリserverに対応 `-- client-app # リモートリポジトリclientに対応 % git remote server client % git checkout server # メインブランチでいうapi-server以下が展開される % git checkout client # メインブランチでいうclient-app以下が展開される ``` メインブランチはなくても良さそうですが、モジュール間の同期をとる場合に役立ちます。具体的には、APIサーバのモジュールとクライアントアプリのモジュールを同時開発していく場合など、基本的にはそれぞれ独立して開発していけますが、API仕様変更などで同期をとらざるを得ないタイミングが少なからず訪れるでしょう。 通常は各モジュールのブランチに対してコミットしていき、それを任意のタイミングでサブツリーマージでメインブランチに取り込みます。また、モジュール間で同期する必要がある場合はメインブランチにコミットして各モジュールに対してサブツリーマージで反映するという手も考えられます。 モジュールをリポジトリorブランチで独立させておくことによるメリットは、**デプロイ時などに各モジュールのみを取得可能**となるところです。gitではリポジトリのサブディレクトリのみを取得することができません。そこでリポジトリorブランチを独立させておけば、それぞれのモジュールのみを取得できます。APIサーバのコード群のみを取得してデプロイ、あるいはクライアントアプリのコード群のみを取得してビルドといったことが可能となります。 柔軟な運用が可能になる反面、複数ブランチとその関係性を管理していく煩雑さは無視できません。ブランチ間のサブツリーマージはgit-hookである程度自動的に処理できるよう構築するとしても、サブモジュールと比較して導入の敷居は高いように感じます。 プロジェクトの要件と、それに対してどういったリポジトリ運用が理想的か。サブモジュールだけではなく、サブツリーマージもひとつの案として検討してみる価値はあるかもしれません。 |
|
| 966位 |
|
|||
|
16:08:17 |
|
|
ほとんどGitHubで完結するのですが、要件を整理するところだけは何か欲しいなとと思いPivotal Trackerを使い始めてみました。Pivotal Trackerの使い方自体はまた別の機会でまとめるとして、ここでは、PivotalTrackerのストーリーと関連するGitHubのコミットを紐づける方法をご紹介します。
## PivotalTrackerの設定 まず、PivotalTrackerに接続できるように、APIトークンを発行します。 [PROFILE]ニューから選択して、[API Token]のセクションに[Create New Token]というリンクをクリックします。そうすると、[API Token]が生成され表示されるのでコピーしておきます。 ## GitHubの設定 次に、GitHub側でリポジトリの[Admin]をクリックして管理画面を表示します。[Service Hooks]タブを選択します。連携できるサービスの一覧が表示されるので、一覧からPivotalTrackerを探して選択します。[Token]欄に先ほどコピーした、[API Token]をペースとします。[Active]にチェックをして、[Update Settings]をクリックして設定を保存します。 以上で連携の設定は完了です。 ## コミットメッセージ記述の仕方 実際のコミットメッセージの表記の仕方は、Pivotal Trackerの<a href="https://www.pivotaltracker.com/help/api?version=v5#GitHub">ヘルプページ</a>を和訳したものを記載します。 和訳: SCMのコミットと特定のTrackerストーリーを関連付けるには、コミットメッセージにストーリーID(複数可)とストーリーの状態変更を示した専用の記述を含めます。ハッシュマーク(#)の後にストーリーIDを記載して大括弧([])で囲んだコミットメッセージを記載します。もし、ストーリーが開始していなければ(状態が"Not Yet Started"のもの)、コミットメッセージは自動で開始状態にします。例えば、ScottyがSCMのリビジョン54321で下記のメッセージをコミットしたとします。 ``` [#12345677 #12345678] Diverting power from warp drive to torpedoes. ``` Trackerのストーリー12345677と12345678に下記のコメントが追加されます。もし、ストーリーが開始されていなければ、状態を"Started"にします。 ``` Commit: Scotty 54321 [#12345677 #12345678] Diverting power from warp drive to torpedoes. ``` コミットメッセージを使ってストーリーを自動で終了するには、大括弧内でストーリーIDに加えて、"fixed", "completed", "finished"を含めます。また、ストーリーIDの前や後に"Fix"や"FIXES"を記載したり、別の動詞の格や形も使うこともできます。注記: "features"の場合は、状態を"finished"にします。"chores"の場合は、状態を"accepted"にします。 例: ``` [Fixes #12345678] Torpedoes now sufficiently powered. ``` 環境によっては、コードがコミットされたタイミングで自動でデプロイする事でしょう。そのような環境では、"delivers"を含めれると、そのストーリーの状態を"delivered"にします。 ``` [Delivers #12345679] Scotty can beam up Captain Kirk. ``` ## おわりに これで、コミットメッセージに所定の形式で記載すれば、Pivotal Tracker上で、ストーリーから関連するコミットがリンクされるようになりました。 Pivotal Trackerは非常にシンプルで使い勝手がよくて気に入りました。また、もう少し運用してみて、慣れてきたらPivotal Tracker自体に関しても書いていきたいと思います。 いままではRedmineを使っていたのですが、これからはPivotal Tracker + GitHubがいいですね。 |
|
| 967位 |
|
|||
|
16:38:15 |
|
|
Jenkins の使い方をメモする。
#環境 ##OS Windows7 64bit ##Jenkins 1.544 ##APサーバ Tomcat 7.0.42 #インストール ##war ファイルのダウンロード [Welcome to Jenkins CI! | Jenkins CI](http://jenkins-ci.org/) にアクセスして、 war ファイルをダウンロードする。 ##JENKINS_HOME の設定 環境変数 `JENKINS_HOME` を設定する。 この `JENKINS_HOME` には、バージョン管理システムからチェックアウトしてきたファイルなどが保存される。 デフォルトでは、実行ユーザのホームフォルダ以下に `.jenkins` というフォルダが作成され、そこが利用される。 ##デプロイ Tomcat の `webapps` フォルダにダウンロードした war ファイルを配置する。 ##動作確認 Tomcat を起動して `http://localhost:8080/jenkins/` にアクセスする(ホストとポートは適宜読み替え)。  #簡単なプロジェクトを作って Jenkins で CI してみる 次のような構成の簡単なプロジェクトを作って、 Jenkins で CI してみる。 - Java の CUI プログラム - ビルドは Ant で実施 - ユニットテストは JUnit で作成 - バージョン管理に SVN を使用 ##プロジェクトの構成 ```test:フォルダ構成 ├─build │ build.xml │ ├─lib │ ├─runtime │ │ commons-lang3-3.1.jar │ │ │ └─test │ hamcrest-core-1.3.jar │ junit-4.11.jar │ ├─scripts │ execute.bat │ ├─src │ └─sample │ └─jenkins │ Main.java │ └─test └─sample └─jenkins MainTest.java ``` ```java:src/sample/jenkins/Main.java package sample.jenkins; import org.apache.commons.lang3.StringUtils; public class Main { public static void main(String[] args) { String str = args[0]; int repeat = Integer.parseInt(args[1]); System.out.println(repeat(str, repeat)); } public static String repeat(String str, int repeat) { return StringUtils.repeat(str, repeat); } } ``` ```java:test/sample/jenkins/MainTest.java package sample.jenkins; import org.junit.Test; import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; public class MainTest { @Test public void repeatメソッドにhogeと5を渡したら_hogehogehogehogehoge_が返ってくること() { // exercise String actual = Main.repeat("hoge", 5); // verify assertThat(actual, is("hogehogehogehogehoge")); } } ``` ```xml:build/build.xml <?xml version="1.0" encoding="Shift_JIS"?> <project basedir="."> <property name="work.dir" location="work" /> <property name="classes.dir" location="${work.dir}/classes" /> <property name="assemble.dir" location="${work.dir}/assemble" /> <property name="src.dir" location="../src" /> <property name="test.src.dir" location="../test" /> <property name="lib.dir" location="../lib" /> <property name="scripts.dir" location="../scripts" /> <property name="start.script" location="${scripts.dir}/execute.bat" /> <property name="test.report.dir" location="${work.dir}/test-report" /> <property name="src.encoding" value="Shift_JIS" /> <property name="main.class" value="sample.jenkins.Main" /> <property name="test.class" value="sample.jenkins.MainTest" /> <property name="jar.name" value="jenkins-sample.jar" /> <property name="zip.name" value="jenkins-sample.zip" /> <path id="runtime.classpath"> <fileset dir="${lib.dir}"> <include name="runtime/*.jar" /> </fileset> </path> <path id="test.classpath"> <fileset dir="${lib.dir}"> <include name="test/*.jar" /> </fileset> </path> <target name="clean" description="Ant タスクで生成された全てのファイルを削除します"> <delete dir="${work.dir}" /> <delete> <fileset dir="." includes="TEST*" /> </delete> </target> <target name="compile" description="ソースコードをコンパイルして class ファイルを生成します"> <mkdir dir="${classes.dir}" /> <javac srcdir="${src.dir}" destdir="${classes.dir}" encoding="${src.encoding}" classpathref="runtime.classpath" /> </target> <target name="compile-test" depends="compile" description="テストコードをコンパイルして class ファイルを生成します"> <mkdir dir="${classes.dir}" /> <javac srcdir="${test.src.dir}" destdir="${classes.dir}" encoding="${src.encoding}" classpathref="test.classpath" /> </target> <target name="exec" depends="compile" description="コンパイルしたアプリケーションを実行します"> <java classname="${main.class}" classpath="${classes.dir}" classpathref="runtime.classpath"> <arg value="hoge" /> <arg value="5" /> </java> </target> <target name="test" depends="compile-test" description="JUnit のテストを実行します"> <mkdir dir="${test.report.dir}" /> <junit> <classpath location="${classes.dir}" /> <classpath refid="runtime.classpath" /> <classpath refid="test.classpath" /> <test name="${test.class}" todir="${test.report.dir}" /> <formatter type="plain" extension=".txt" /> </junit> </target> <target name="jar" depends="test" description="ソースコードをコンパイルして jar ファイルに固めます"> <jar destfile="${work.dir}/${jar.name}" basedir="${classes.dir}" excludes="**/*Test.class" /> </target> <target name="build" depends="jar" description="プロジェクトをビルドして配布可能な zip ファイルを生成します"> <mkdir dir="${assemble.dir}/lib" /> <mkdir dir="${assemble.dir}/bin" /> <copy file="${start.script}" todir="${assemble.dir}/bin" /> <copy todir="${assemble.dir}/lib"> <fileset dir="${lib.dir}/runtime" includes="**/*.jar" /> <fileset file="${work.dir}/${jar.name}" /> </copy> <zip destfile="${work.dir}/${zip.name}" basedir="${assemble.dir}" /> </target> </project> ``` ```bat:scripts/execute.bat @echo off set BASE_DIR=%~dp0 java -cp "%BASE_DIR%../lib/*" sample.jenkins.Main %* ``` build.xml ファイル書いてて、心底早く Gradle に移るべきだと感じた。。。 ##新規ジョブを作成する Jenkins で上記プロジェクトを CI できるようにする。 - メニューの [新規ジョブ作成] を選択。 - [ジョブ名] を入力し、 [フリースタイル・プロジェクトのビルド] を選択して [OK]。 - [説明] や [オプション] を必要に応じて設定。 - [ソースコード管理] で [subversion] を選択。 - [リポジトリ URL] を入力(不要なファイルをチェックアウトしているとビルドが遅くなるので、最小限になるようにパスを指定する)。 - [ローカルモジュールディレクトリ(オプション)] を入力(デフォルトは `%JENKINS_HOME%/workspace/<ジョブ名>`)。 - [Repository depth] と [Ignore externals] はデフォルト(`infinity` と未選択)。 - [チェックアウト方式] と [リポジトリ・ブラウザ]はデフォルト(`'svn update' を実行` と `(自動)`)。 - [ビルド・トリガ] は、今はとりあえず「チェックなし」で置いておく。 - [ビルド] で [Ant の呼び出し] を選択する。 - [ターゲット] に実行する Ant タスク名を記述する。今回は `build` タスクを呼び出すので、 `build` とだけ入力。 - [高度な設定] をクリックして [ビルドファイル] に `./build/build.xml` と入力(デフォルトはルートフォルダの `build.xml` が使用される)。 - [ビルド後の処理] は今は設定しない。 - [保存] をクリック。 ジョブができ上がったら、早速メニューにある [ビルド実行] をクリックする。 ビルドが完了すると、 [ビルド履歴] にビルド結果が表示される。 青丸ならビルドは成功。赤丸ならビルドは失敗。  ##定期的にビルドを実行する ビルドを定期的に実行するように設定する。 設定は、先ほどスルーした [ビルド・トリガ] で行う。 - メニューの [設定] をクリックして、ジョブの設定画面に移動する。 - [ビルド・トリガ] で [定期的に実行] にチェックを入れる。 - [スケジュール] に `H/5 * * * *` と入力(5 分に 1 回実行)。 ※入力域横の?をクリックすれば、詳細な入力方法が見られる。 - [保存] をクリック。 あとは 5 分毎にジョブが実行されるか wktk しながら待つ。 ##JUnit のテスト結果を集計する JUnit のテスト結果を確認する場合、デフォルトではジョブ結果の [コンソール出力] を確認するしかない。 分かりにくい上に、テストで失敗していてもビルドプロセス自体がエラー無しで完了していると、ビルドは成功(青丸)になってしまう。 JUnit のテスト結果を集計するようにすることで、テスト結果が見やすくなり、かつテストで失敗があるとビルド結果が黄色になるようにできる。 ###XML 形式でテスト結果が出力されるように `build.xml` を修正する Jenkins にテスト結果を集計させるためには、テスト結果を xml 形式で出力しなければならない。 なので、 `build.xml` のテスト結果出力形式を以下のように変更する。 ```xml:変更前 <target name="test" depends="compile-test" description="JUnit のテストを実行します"> <mkdir dir="${test.report.dir}" /> <junit> <classpath location="${classes.dir}" /> <classpath refid="runtime.classpath" /> <classpath refid="test.classpath" /> <test name="${test.class}" todir="${test.report.dir}" /> <formatter type="plain" extension=".txt" /> </junit> </target> ``` ```xml:変更後 <target name="test" depends="compile-test" description="JUnit のテストを実行します"> <mkdir dir="${test.report.dir}" /> <junit> <classpath location="${classes.dir}" /> <classpath refid="runtime.classpath" /> <classpath refid="test.classpath" /> <test name="${test.class}" todir="${test.report.dir}" /> <formatter type="xml" extension=".xml" /> <!-- type を xml に変更 --> </junit> </target> ``` ###Jenkins 上でテスト結果を出力する 設定を始める前に、テスト結果の xml ファイルを Jenkins のワークスペース上に出力しておく必要がある。 テスト結果を xml で出力するように `build.xml` を編集したら、再度 Jenkins 上でビルドを実行してテスト結果ファイルを出力しておく。 ###Jenkins の設定を変更する - [設定] をクリック。 - [ビルド後の処理] で [ビルド後の処理の追加] から [JUnit テスト結果の集計] を選択。 - [テスト結果 XML] にテスト結果の xml ファイルが出力されるファイルへのパスを入力する([ワークスペースルート] のリンクを開いて、そこからパスを調べて入力すれば間違えないと思う)。 - [保存] をクリック。 ###テスト結果を確認する ビルドを実行してテストが失敗すると、以下のようにテスト結果が黄色になり、テストが失敗したことがわかる。  ##ビルド結果をメールで通知する ビルド結果をメールで通知できるように設定する。 ###Jenkins の URL を設定する 通知メールには失敗したジョブの URL が貼り付けられる。 デフォルトだと、その URL のホストが `localhost` になっている。 このままだと、メールを受け取ってもリンクを開くことができないので、正しいホスト名になるよう設定を変更する必要がある。 - トップページに移って、 [Jenkins の管理] を選択。 - [Jenkins の位置] の [Jenkins の URL] が `http://localhost:8080/jenkins` となっているので、外部からでも見れるように URL を変更する。 ###SMTP サーバの設定をする メールを送信するのに使用する SMTP サーバの設定をする。 とりあえず Gmail のアカウントを使って送信してみる。 - トップページに移って、 [Jenkins の管理] を選択。 - [システムの設定] を選択。 - [E-mail 通知] を設定する。 - [SMTP サーバー] に `smtp.googlemail.com` と入力。 - [E-mail のサフィックス] に `@gmail.com` と入力。 - [SMTP 認証] にチェックを入れる。 - [ユーザ名] と [パスワード] に Gmail のメールアドレスとパスワードを入力。 - [SSL] にチェックを入れる。 - [SMTP ポート] に `465` を入力。 - [メールを送信して設定を確認] にチェックを入れる。 - [テストメールの宛先] にメールの送信先アドレスを入力して [設定を確認] をクリック。 - [テストメールの宛先] に指定したアドレスにメールが届いたら [保存] をクリック。 ###ビルドが完了したらメールで通知するように設定する - メール通知したいジョブを開き、 [設定] を選択。 - [ビルド後の処理] で [ビルド後の処理の追加] から [E-mail 通知] を選択する。 - [宛先] にメールの送信先アドレスを入力する。 - [保存] をクリック。 ###動作確認 わざとビルドが失敗するようにファイルをコミットしてから、ビルドを実行する。 指定したメールアドレスに届いていたら OK。  #Gradle を使ったプロジェクトも CI してみる 本命の Gradle を使ったプロジェクトを Jenkins で CI してみる。 ##プロジェクトの構成 ```text:フォルダ構成 │ build.gradle └─src ├─main │ └─java │ └─sample │ └─jenkins │ Main.java └─test └─java └─sample └─jenkins MainTest.java ``` ```groovy:build.gradle apply plugin: 'application' repositories { mavenCentral() } dependencies { compile 'org.apache.commons:commons-lang3:3.1' testCompile 'junit:junit:4.11' testCompile 'org.hamcrest:hamcrest-library:1.3' } mainClassName = 'sample.gradle.Main' distZip { archiveName = 'jenkins-sample.zip' } ``` ※Java の実装は Ant と同じ。 ##Jenkins の Gradle プラグインをインストールする Jenkins から Gradle のビルドファイルを実行できるようにするため、プラグインをインストールする。 - トップページに移って、 [Jenkins の管理] を選択。 - [プラグインの管理] を選択。 - [利用可能] タブを開き、 [フィルター] に `gradle` と入力。 - [Gradle Plugin] の [インストール] にチェックを入れて [再起動せずにインストール] をクリック。 - インストールが完了したら、再び [Jenkins の管理] を選び、次は [システムの設定] を選択する。 - [Gradle] の項目が追加されているので、 [Gradle 追加] をクリックする。 - [name] に任意の名前を入力。 - 既に Gradle はインストール済みなので、 [自動インストール] のチェックを外す。 - [GRADLE_HOME] に、 Gradle のインストールフォルダのパスを入力。 - [保存] をクリック。 ##ジョブを追加する Gradle プロジェクトを Jenkins にジョブとして登録する。 基本的な手順は Ant のときと同じ。違うのは、 [ビルド] の [ビルド手順の追加] で `Invoke Gradle script` を選択する点。 - 先ほど Jenkins に登録した Gradle を使うなら、 [Invoke Gradle] を選択する。 Gradle Wrapper を使用する場合は、そちらを選択する。今回は、 [Invoke Gradle] を選択。 - [Tasks] に `test distZip` と入力。 - それ以外はデフォルト(空)のまま [保存] をクリック。 ※`build.gradle` が `%JENKINS_HOME%/workspace/<ジョブ名>` の直下にない場合は、 [Build File] を設定すること。 ##Gradle の 起動ファイルを修正する このままビルドを実行すると、次のようなエラーが発生する。 ```text: Could not load Logmanager "org.apache.juli.ClassLoaderLogManager" java.lang.ClassNotFoundException: org.apache.juli.ClassLoaderLogManager at java.net.URLClassLoader$1.run(URLClassLoader.java:366) at java.net.URLClassLoader$1.run(URLClassLoader.java:355) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:354) (以下略) ``` どうやら、 Tomcat の catalina.bat で `JAVA_OPTS` という環境変数に `-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager` という設定をしているのだが、この設定が Gradle の起動ファイルにまで伝わってしまっているのが原因らしい。 Gradle のクラスパス上には `org.apache.juli.ClassLoaderLogManager` が無いので、 `ClassNotFoundException` が発生してしまうという残念な状態。 ```bat:catalina.bat (前略) if not "%LOGGING_MANAGER%" == "" goto noJuliManager set LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager :noJuliManager set JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER% (後略) ``` ```bat:gradle.bat (前略) @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.launcher.GradleMain %CMD_LINE_ARGS% (後略) ``` 対策として、 gradle.bat を直接書き換えて、実行前に `JAVA_OPTS` に空を設定する方法があるらしい。 > Anyway, I modified the gradle.bat file to unset JAVA_OPTS (set JAVA_OPTS=) and that takes care of this issue - at least as a workaround. [[GRADLE-1245] Hudson and org.apache.juli.ClassLoaderLogManager - Gradle Issues](http://issues.gradle.org/browse/GRADLE-1245) ```bat:修正後のgradle.bat REM Tomcat にデプロイした Jenkins から呼ばれたときに Tomcat の JAVA_OPTS が伝播するのを防ぐ set JAVA_OPTS= @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.launcher.GradleMain %CMD_LINE_ARGS% ``` Tomcat と Gradle と Jenkins のそれぞれで同様のバグ報告がされてて若干カオスな感じです。 - [Tomcat のバグ報告](https://issues.apache.org/bugzilla/show_bug.cgi?id=54601) - [Gradle のバグ報告](http://issues.gradle.org/browse/GRADLE-1245) - [Jenkins のバグ報告](https://issues.jenkins-ci.org/browse/JENKINS-7702) ##動作確認 ようやくビルドが実行できる。 - ジョブのメニューから [ビルド実行] を選択して、ビルドが実行できることを確かめる。 #参考 - [Jenkins実践入門](http://www.amazon.co.jp/Jenkins%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-%EF%BD%9E%E3%83%93%E3%83%AB%E3%83%89%E3%83%BB%E3%83%86%E3%82%B9%E3%83%88%E3%83%BB%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%82%92%E8%87%AA%E5%8B%95%E5%8C%96%E3%81%99%E3%82%8B%E6%8A%80%E8%A1%93-WEB-DB-PRESS-plus/dp/4774148911) - [JenkinsでGradleを使おう - しおしおの雑記帳](http://siosio.hatenablog.com/entry/2012/05/26/220858) |
|
| 968位 |
|
|||
|
18:39:01 |
(Akatsuki Inc. 所属) |
|
##Chefとは サーバ設定や更新を自動化するツール。 レポジトリ>クックブック>レシピ という階層で管理する。 一つのシステムにつきレポジトリ一つ、という粒度。 利用形態には以下の二つがある。 ####Chef-Server + Chef-Client →Chef ClientがChef Serverからhttpで必要な情報をGETし、実行する。ChefServerの管理はちょっと大変。 ####Chef-Solo →サーバ管理を必要としないスタンドアロン版。 ##Chefのインストール ```sh $ curl -L http://www.opscode.com/chef/install.sh | sudo bash ``` もしくは ```sh $ gem install chef ``` ## レポジトリ作成 OpsCodeがgithubに公開しているレポジトリのひな形 (後述のknife-soloを使えばそちらで肩代わりできる。) ```sh $ git clone git://github.com/opscode/chef-repo.git ``` ## クックブック作成 knifeを用いる ##knifeとは chefレポジトリを操作するためのツール。 クックブックの作成はこれで行う。 chef solo環境で利用するのは主に以下の二つ ・knife cookbook(クックブックの作成) ・knife solo ##knifeの初期設定 ``` $ knife configure ``` 諸々の質問事項は全てデフォルトでok。 初期化が完了すると ~/.chef/knife.rbに設定ファイルが保存される。 ##クックブックの作成 ``` $ cd chef-repo $ knife cookbook reate hello -o cookbooks ``` cookbooksディレクトリ内にhelloというクックブックを作成。 ##レシピの作成 クックブック作成時点でレシピのひな形は作成済となる。 ``` $ vi cookbooks/hello/recipes/default.rb ``` "Hello, Chef!"を出力するだけのレシピ。 ``` log "Hello, Chef!" ``` ##Chef Soloの実行 Chef Solo実行時に実行するレシピを記述するJSONファイルを用意する。 chef-repoディレクトリ直下に配置する。 ```localhost.json { "run_list":[ "recipe[hello]" ] } ``` Chefが利用するテンポラリディレクトリやクックブックのパスを指定する設定ファイルも必要。同じくchef-repoディレクトリ直下に配置する。 ```solo.rb file_cache_path "/tmp/chef-solo" cookbook_path ["/home/ec2-user/chef-repo/cookbooks"] ``` ##実行 ``` $ sudo chef-solo -c solo.rb -j ./localhost.json ``` ##chef-soloの流れまとめ ・レシピを作る ・JSONファイル内で、実行レシピを指定する ・chef-soloコマンドで実行する ##レシピサンプル ###特定パッケージのインストール ```default.rb package "zsh" do action :install end ``` ###複数パッケージをまとめてインストール ```default.rb %w{zsh gcc make readline-devel}.each do |pkg| package pkg do actoin :install end end ``` ##iptablesをオフに ```default.rb service 'iptables' do action [:disable, :stop] end ``` ※chefは何度実行しても、エラー等は発生しない。 →冪等性(何度操作を行っても、結果が同じであること)がある。 ##Resourceとは Chefが提供するDSL(Ruby組み込みの文法ではない)。 レシピ内で用いる、「サーバの状態に何らかの影響を与える命令」のこと。 log、packageなど ##ノード(node)とは 管理対象の各サーバのこと。 ※その文脈で言うと、JSONファイルに書くのは特定のノードの「状態」となる。 JSONファイル内のデータ構造をNode Objectと呼び、そのプロパティとして、以下の2つを列挙する。 ・そのノードに適用するべきレシピ ・そのノードが持っている変数(Attribute) Chef Serverを用いる場合にはChef Serverに格納しておくが、 Chef Soloの場合はその格納場所がないので、ローカルにJSONファイルとして書いておく。 ## knife-soloを用いた実用例はこちら knife-soloによるChefの実行 http://qiita.com/kidachi_/items/b222fb2892e6108c46d5 |
|
| 969位 |
|
|||
|
17:35:11 |
|
|
#テストの必要性
以前のシステム開発と言えば、ウォーターフォールのような要件から設計、実装、テストのような徐々に作業を細分化し最後にテストを行う手法が主流、というかSierとかでは基本今でもこの手法が多いはず。 しかし、現在はアジャイル開発の普及にともない、TDD、BDDに代表されるようなテスト重視の開発スタイルが増加している。 その中で実際テストコードを書いてみようとすると、Javaではテストフレームワークやツール、ライブラリ等が多数あり、どれを選択すべきか悩みどころになってくる。 ここではそれらを整理するため、Javaのテストコードを書くときに必要な情報をまとめておきます。 #どれを使うか・・・ 個人的には、どれを使いうかと考えた時に以下に注意している。 * 簡単、シンプル テストコードをするために、時間がかかり過ぎたり、可読性が低下したり、ロジックがあまり複雑になるようなものは避ける。 * 情報量 実際コードを書くと、疑問や問題にぶつかったりするので、その際はある程度情報量が多いものを選ぶことが多い。 テストを重視して素晴らしいテストコードは出来ても、実際のコードはまともに動かない。 では話にならない。情報量の多い=問題が起きた時の作業時間短縮。なので、そのあたりのバランスは重要だと思う。 * 再利用性 独自の技術や、記述すぎて開発がストップしたときに、他の技術に以降できない。また、一から調べ直さないと使えないってものは極力避けたい。 エンジニアとしてはそれはそれで勉強になるけど。。。 #Javaのテストフレームワーク、ツール、ライブラリなど [Wikipedia ユニットテスト・フレームワーク一覧#Java](http://ja.wikipedia.org/wiki/%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88%E3%83%86%E3%82%B9%E3%83%88%E3%83%BB%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E4%B8%80%E8%A6%A7#Java) [Web Site Test Tools and Site Management Tools - Java](http://www.softwareqatest.com/qatweb1.html#JAVA) ざっとみただけでも多数あり、中には既に開発がストップしているものもある。 #人気のあるのは 実際に使われるることの多いものものをまとめ。 ####ユニットテストフレームワーク * JUnit Javaのテストと言えばまずこれ。機能も必要十分で情報も充実してる。主に単体テストフェーズの自動化を行う。 * TestNG JUnitより後発のため、記述の簡潔化、機能追加などある。使い方はJUnitとほぼ同等で、JUnitからも簡単に以降できる。 現状両者開発も続いているのでTestNGの方が後発のため、記述が簡単だったり、機能的に充実している部分もあるが、JUnitもアップデートで機能充実が図られている。 その気になれば大半のテストはどちらでも書けるので、どちらが圧倒的に優位ってことはない。 現状JUnitを使っている現場の方が圧倒的に多い気がする。 参考 [技術/TDD/JUni 4.10 と TestNG 6.x系 機能比較(by JUnit実践入門)](http://www.glamenv-septzen.net/view/1141) ####モックライブラリ * Mockito * JMockit そのほかにも、jMock、EasyMock、PowerMockなどMockライブラリは多数ある。 その中で機能の多さではJMockitが多機能。記述的にはMockitoの方が簡単に記述しやすい。 参考 [MockingToolkitComparisonMatrix](https://code.google.com/p/jmockit/wiki/MockingToolkitComparisonMatrix) [JMockitは理想的なモックフレームワーク](http://d.hatena.ne.jp/j5ik2o/20110205/1296922274) ####データベース * DBUnit Junitの拡張で、テストデータをDBに投入、後片付け、メソッド実行後のDBの状態を確認などを行う機能がある。 ####ブラウザ * Selenium ブラウザテストの自動化を行う。 テスト対象は画面ベースのため、プログラミング言語に関係なくテストを行える。 #結論 今自分が使うなら、JUnit + Jmockit + DBUnit + Selenium。 後は、使うアプリケーションフレームワークがもとから上記の機能を有しているテスト機能。 アプリケーションフレームワーク独自の機能をより完結にテストできる場合が多いので、あればそちらを優先したほうが良い場合もある。 #おまけ 便利なツール集 #####Jenkins 継続的インテグレーションツール。 要は予め準備していた、テスト、ビルド、デプロイなどの処理を自動化、バッチ実行するために使用する。 #####Jmeter パフォーマンステスト用のツール。 個人では使うことは少ないと思うが、仕事などでシステムに同時に多数のアクセスを行った場合の性能テストを行うときに使ったりする。 システム全体のテストというより、負荷の掛かりそうな機能に対して部分的に使用する。 #####djUnit 主にカバレッジ測定のために使用するEclipseプラグイン。 条件分岐などの網羅が正確に行われているかなどの確認が簡単に行える。 #####Checkstyle コーディング規約をチェックするための静的解析ツール。 例えばソースコードにおける中括弧や、空白スペースの記述位置などコーディングスタイルに関する指摘のほか、クラス名、定数、変数、メソッド名などの命名規則がコーディング規約(あるいは命名規約)として定義された規則に従い、正しく記述されているかどうかをチェックする。 FindBugsと比べると、特にコーディングスタイルや命名規約のチェックが優れている。 #####FindBugs 潜在的なバグをチェックすることができる静的解析ツール。 例えばメソッドのパラメーターを、nullチェックを行わずに使用しているため、NullPointerExceptionが発生する可能性などを指摘。 Checkstyleと比べると、より潜在的なバグの検出が得意なツール。 #####Quick JUnit テストコードを書くときに便利なショートカットなどがあるEclipseプラグイン。 >Ctrl + 9 : ソースコード↔テストコード >Ctrl + 0 : 選択しているメソッドの実行 >Ctrl + Shift + 0 : 選択しているメソッドのデバッグ実行 ## 関連書籍 このあたりは、見といたほうがいいかも。 [現場で使えるソフトウェアテスト Java編](http://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%86%E3%82%B9%E3%83%88-Java%E7%B7%A8-%E9%A3%AF%E5%B1%B1-%E6%95%99%E5%8F%B2/dp/4798114634/ref=sr_1_4?ie=UTF8&qid=1380376740&sr=8-4) → [まとめ](http://qiita.com/disc99/items/ebe939f8cc203d59f27e) [経験ゼロでもできるプログラミング現場の単体テスト](http://www.amazon.co.jp/%E7%B5%8C%E9%A8%93%E3%82%BC%E3%83%AD%E3%81%A7%E3%82%82%E3%81%A7%E3%81%8D%E3%82%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E7%8F%BE%E5%A0%B4%E3%81%AE%E5%8D%98%E4%BD%93%E3%83%86%E3%82%B9%E3%83%88-%E7%89%87%E6%A1%90-%E4%B8%80%E5%AE%97/dp/4798118915/ref=sr_1_3?ie=UTF8&qid=1380376740&sr=8-3) [JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus) ](http://www.amazon.co.jp/JUnit%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-~%E4%BD%93%E7%B3%BB%E7%9A%84%E3%81%AB%E5%AD%A6%E3%81%B6%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E6%8A%80%E6%B3%95-WEB-PRESS-plus/dp/477415377X/ref=sr_1_1?ie=UTF8&qid=1380376740&sr=8-1) ##最後に ここに書かれていることは所感でまとめたため、ご意見ご要望、誤りがありましたら訂正しますので、編集リクエスト、コメントをもらえればと思います。 |
|
| 970位 |
|
|||
|
22:04:59 |
|
|
先日 [vim-powerline](https://github.com/Lokaltog/vim-powerline) から [vim-airline](https://github.com/bling/vim-airline) に乗り換えたばかりだったのですが、あっさり [lightline.vim](https://github.com/itchyny/lightline.vim) に乗り換えてしまいました。
理由は、lightline.vim 作者さんの以下記事に共感したからです。 - [lightline.vim作りました - プラグインの直交性について - プログラムモグモグ](http://d.hatena.ne.jp/itchyny/20130824/1377351527) また、設定方法が直感的で分かりやすい点や、カスタマイズしやすい点なども気に入りました。 # インストール ```vim NeoBundle 'itchyny/lightline.vim' ``` 今回のカスタマイズでは、以下のプラグインと連携させているので入れておくと良いです。 (無くてもエラーになったりはしません) ```vim NeoBundle 'tpope/vim-fugitive' NeoBundle 'airblade/vim-gitgutter' ``` NeoBundle 以外のインストール方法は、[README](https://github.com/itchyny/lightline.vim/blob/master/README.md) 読んで下さい。 # カスタマイズ おすすめの設定ですが、[README](https://github.com/itchyny/lightline.vim/blob/master/README.md) を上から順に試していくのも良いですが、作者さんの設定を真似るのが一番手っ取り早いです。 作者さんの設定は、[README](https://github.com/itchyny/lightline.vim/blob/master/README.md) の一番最後に記載されています。 もしくは、直接 [作者さんの .vimrc](https://github.com/itchyny/dotfiles/blob/master/.vimrc) を参考にしましょう。 さらに今回は、カーソル下の文字コード表示と、[vim-gitgutter](https://github.com/airblade/vim-gitgutter)と連携してGitの追加/変更/削除のハンク数も表示させるようにしました。  以下が最終的な設定です。 特に解説はしませんが lightline.vim の設定方法は非常に直感的で分かりやすいので、すぐに理解できると思います。 また、[README](https://github.com/itchyny/lightline.vim/blob/master/README.md) がチュートリアル方式になっているので、もし分からなければ読んでみると良いでしょう。 ```vim " vim-gitgutter let g:gitgutter_sign_added = '✚' let g:gitgutter_sign_modified = '➜' let g:gitgutter_sign_removed = '✘' " lightline.vim let g:lightline = { \ 'colorscheme': 'landscape', \ 'mode_map': {'c': 'NORMAL'}, \ 'active': { \ 'left': [ \ ['mode', 'paste'], \ ['fugitive', 'gitgutter', 'filename'], \ ], \ 'right': [ \ ['lineinfo', 'syntastic'], \ ['percent'], \ ['charcode', 'fileformat', 'fileencoding', 'filetype'], \ ] \ }, \ 'component_function': { \ 'modified': 'MyModified', \ 'readonly': 'MyReadonly', \ 'fugitive': 'MyFugitive', \ 'filename': 'MyFilename', \ 'fileformat': 'MyFileformat', \ 'filetype': 'MyFiletype', \ 'fileencoding': 'MyFileencoding', \ 'mode': 'MyMode', \ 'syntastic': 'SyntasticStatuslineFlag', \ 'charcode': 'MyCharCode', \ 'gitgutter': 'MyGitGutter', \ }, \ 'separator': {'left': '⮀', 'right': '⮂'}, \ 'subseparator': {'left': '⮁', 'right': '⮃'} \ } function! MyModified() return &ft =~ 'help\|vimfiler\|gundo' ? '' : &modified ? '+' : &modifiable ? '' : '-' endfunction function! MyReadonly() return &ft !~? 'help\|vimfiler\|gundo' && &ro ? '⭤' : '' endfunction function! MyFilename() return ('' != MyReadonly() ? MyReadonly() . ' ' : '') . \ (&ft == 'vimfiler' ? vimfiler#get_status_string() : \ &ft == 'unite' ? unite#get_status_string() : \ &ft == 'vimshell' ? substitute(b:vimshell.current_dir,expand('~'),'~','') : \ '' != expand('%:t') ? expand('%:t') : '[No Name]') . \ ('' != MyModified() ? ' ' . MyModified() : '') endfunction function! MyFugitive() try if &ft !~? 'vimfiler\|gundo' && exists('*fugitive#head') let _ = fugitive#head() return strlen(_) ? '⭠ '._ : '' endif catch endtry return '' endfunction function! MyFileformat() return winwidth('.') > 70 ? &fileformat : '' endfunction function! MyFiletype() return winwidth('.') > 70 ? (strlen(&filetype) ? &filetype : 'no ft') : '' endfunction function! MyFileencoding() return winwidth('.') > 70 ? (strlen(&fenc) ? &fenc : &enc) : '' endfunction function! MyMode() return winwidth('.') > 60 ? lightline#mode() : '' endfunction function! MyGitGutter() if ! exists('*GitGutterGetHunkSummary') \ || ! get(g:, 'gitgutter_enabled', 0) \ || winwidth('.') <= 90 return '' endif let symbols = [ \ g:gitgutter_sign_added . ' ', \ g:gitgutter_sign_modified . ' ', \ g:gitgutter_sign_removed . ' ' \ ] let hunks = GitGutterGetHunkSummary() let ret = [] for i in [0, 1, 2] if hunks[i] > 0 call add(ret, symbols[i] . hunks[i]) endif endfor return join(ret, ' ') endfunction " https://github.com/Lokaltog/vim-powerline/blob/develop/autoload/Powerline/Functions.vim function! MyCharCode() if winwidth('.') <= 70 return '' endif " Get the output of :ascii redir => ascii silent! ascii redir END if match(ascii, 'NUL') != -1 return 'NUL' endif " Zero pad hex values let nrformat = '0x%02x' let encoding = (&fenc == '' ? &enc : &fenc) if encoding == 'utf-8' " Zero pad with 4 zeroes in unicode files let nrformat = '0x%04x' endif " Get the character and the numeric value from the return value of :ascii " This matches the two first pieces of the return value, e.g. " "<F> 70" => char: 'F', nr: '70' let [str, char, nr; rest] = matchlist(ascii, '\v\<(.{-1,})\>\s*([0-9]+)') " Format the numeric value let nr = printf(nrformat, nr) return "'". char ."' ". nr endfunction ``` あとは気に入らない部分を自分好みに変えていくと良いかと思います。 - vim-gitgutter の sign は私の好みで変えてるだけなので、別にデフォルトのままでも良いです - `separator` と `subseparator` は [vim-powerline](https://github.com/Lokaltog/vim-powerline) のものなので、対応していない方は設定を削除するか、フォントに[パッチ](https://github.com/Lokaltog/vim-powerline/tree/develop/fontpatcher)を当てて下さい。[powerline](https://github.com/Lokaltog/powerline) を使ってる方はそっちでもおk 私の最新の設定はGithubに置いてあるので、良ければ何かの参考にでも。 - [dotfiles/.vimrc.plugins_setting at master · yonchu/dotfiles](https://github.com/yonchu/dotfiles/blob/master/.vimrc.plugins_setting) ## 追記 ### 2010/08/28 - ファイル名を取得する関数(MyFilename())内の `expand(%t)` を `expand(%:t)` に修正 (←タイポです) `%:t` でファイル名のみの表示になります。 以前のようにパスも含めて表示したい場合は、`expand('%')` として下さい。(`t` は要りません) |
|
| 971位 |
|
|||
|
18:34:02 |
|
|
しばしば三項演算子やdo-while構文が禁止されているコーディング規約に遭遇する。それは今すぐ撤廃すべきという論理武装を考えてみよう。事例はPHPだが、このことはすべての言語にいえる。 ### 例 あるサービスから何らかのデータを取り出し、それをクライアント側で使うという手続きを考える。 ```php <?php $client->useSomeData($service->getSomeData()); ``` ここで、サービス側のAPIとクライアント側のAPIに仕様ギャップがあったとしよう。`$service` は古く、うまく行かなかったとき例外ではなく `null` 的なもの(PHPではもしかすると`false`かもしれない)を返す習慣で作られていた。いっぽう `$client` は `null` を入力される想定がない。何もしないで欲しいときは空の `array` を入力する仕様だった。 以下の実装例を見てほしい。 ```php <?php $data = $service->getSomeData(); if ($data === null || $data === false) { $data = array(); } $client->useSomeData($data); ``` このコードがまずいのは、ひとつの変数を構築するプロセスが3行に分かれている点だ。ここに手続きプログラミングゆえの記述の曖昧さが入る余地がある。後のメンテナが `$data2` も同時に取り扱いたい場合、どう書き足すだろうか。 ```diff <?php $data = $service->getSomeData(); if ($data === null || $data === false) { $data = array(); } + $data2 = $service->getSomeData2(); + if ($data2 === null || $data2 === false) { + $data2 = array(); + } $client->useSomeData($data); + $client->useSomeData($data2); ``` ```diff <?php $data = $service->getSomeData(); + $data2 = $service->getSomeData2(); + if ($data === null || $data === false) { $data = array(); } + if ($data2 === null || $data2 === false) { + $data2 = array(); + } $client->useSomeData($data); + $client->useSomeData($data2); ``` 前者は取り出しと妥当化をワンセットと認識して書いたもの、後者は、取り出しは取り出し、その後妥当化手続きをすると認識して書いたもの。どちらも間違いとは言えない。意図を知らずにこのソースコードのあるがままを読んだのだと仮定すれば… (そして、コードの意図を共有しきれないのもまた、保守フェーズの宿命だ) 当初の目的の意味は、レガシーなサービスが提供するデータ形式はこのシステムでは受け入れられないということだ。とすると、前者はまだいいが、後者では `$data` 変数の値を決定するブロックが分断されている。ここに「文脈を共有しうる手続きが走っている間、妥当化されていない変数が存在する」状態を許しているという問題がある。 では三項演算子に登場してもらおう。三項演算子は手続きを式にすることができる。そして式は文に入れ込むことができる。 ```php <?php $d = $service->getSomeData(); $data = ($d !== null || $d !== false) ? $d : array(); $client->useSomeData($data); ``` こう書くことで、`$data` は条件付きで変化する状態変数ではなく、必ず再定義されるイミュータブルな変数(とみなして扱うことが可能)であることが、機械的に保証できた。このほうが関数型言語でいう変数(Scalaのval)に近い。 後者の保守プログラマーがなぜそれを定義と手続きとに分けて意識してしまうのか。それは if 文によって `$data=` と `$data2=` が不揃いになるせいで、変数の定義に見えなくなるせいだ。定義に見えない変数代入は手続きフェーズに移動させるという癖は、C言語経験があるベテランプログラマーほど強くなる。 もう少しPHPらしく。厳密さは型アノテーションとPHPUnitに任せよう。 ```php <?php $d = $service->getSomeData(); $data = !empty($d) ? $d : array(); $client->useSomeData($data); ``` PHPの `empty` は変数に対してしか使えないので、PHP5.2までは `$d` を消すことができなかった。が、さらにここで、PHP5.3で登場したエルビス演算子で置き換えるとどうなるか。 ```php <?php $data = $service->getSomeData() ?: array(); $client->useSomeData($data); ``` あらゆる変数から、一時的にですら `null` である可能性を完全に排除できた。何より素晴らしいのは、変数の値を決定するのが「1行」になったことだ。変数の妥当化までに他の手続きが介入する余地がいっさいない。 またこうなると、文による記述の制約から開放されるので、機械的なリファクタリングで以下のコードと相互変換できるようになる。 ```php <?php $client->useSomeData( $service->getSomeData() ?: array() ); ``` これを `null` を意識しなかった修正前のコードと比べてみるといい。式は値と相互に置き換えられることがポイントだ。 三項演算子はif文と全く等価ではない。それが「式であること」が重要なのだ。 - ミュータビリティを式の中に封印できる - 変数がイミュータブルになる - 文は一時変数を欲しがる - 文は行を消費する - 複数の行があるところにはコード挿入の余地がある - 後任の保守プログラマーは何を考えているかわからない 動くだけのコードから、動かなくなる余地のないコードへ、そして、プログラマーの心理学へというのを考えてみた。 三項演算子が無条件に可読性を上げるか下げるかではなく、可読性はプログラマーの心理の中にあり、適切に使うか使わないかを決める能力と権限が必要だという考え方になろう。 特定のイデオムを禁止したせいで、後々本当に読めないコードに変化することのほうが、ちょっとした言い回しを知らない人にマニュアルを参照してもらうより、生産性へのダメージが大きいのは明白だ。 |
|
| 972位 |
|
|||
|
23:45:30 |
|
|
##何が問題かというと RailsでJSON APIを定義する時、素のままでやろうとすると コントーラでto_jsonを呼んだり、モデルにas_jsonを定義したりすることになるかと思います。 モデルに書くとAPIによって出力内容を変えたい場合にとても苦労します。 API数が増えれば増えるほどモデルが複雑になっていきます。 APIレスポンスとしてのJSONはコントローラやモデルに書くべきでしょうか? ビューに書いた方が自然ではないでしょうか? ##RABL とはいえ、他の画面用のビューのようにERBでJSONレスポンスを書くというのはないでしょう。 そこで、JSONのAPIレスポンスを表現することに特化したDSLライブラリのRABLが使えます。 http://nesquena.github.com/rabl/ https://github.com/nesquena/rabl http://engineering.gomiso.com/2011/06/27/building-a-platform-api-on-rails/ ##インストール RABLをインストールするにはGemfileに以下の行を追加して、bundle installします。 ```Ruby:Gemfile gem 'rabl' ``` ##APIコントローラ まずはAPI専用のコントローラクラスを用意します。 ```Ruby:app/controllers/api_controller.rb class ApiController < ApplicationController end ``` ##RABLが不要なAPI 追加/変更/削除等のレスポンスが非常にシンプルなAPIであれば、RABLを使う必要はありません。 その場合はコントローラ+モデルだけで書くことができます。 例としてユーザ名を変更するAPI、updateUserNameを定義してみます。 ```Ruby:app/controllers/api_controller.rb class ApiController < ApplicationController def updateUserName current_user.name = params[:name] if current_user.save render json: {result: true} else render json: {error: {code: Error::SAVE_FAILURE}} end end end ``` ユーザ名の変更に成功すればresult:trueを返し、失敗した場合はエラーコードを返すようになっています。 この程度であればビューを括りだす必要はないですね。 ##RABLが便利なAPI 一覧取得や詳細取得などの情報表示に関わるAPIは、レスポンスが複雑になってきますので、 RABLを使ったビューを定義すると奇麗になります。 例としてアイテム一覧を取得するAPIを定義します。 ```Ruby:app/controllers/api_controller.rb class ApiController < ApplicationController def getItems @items = Item.all(:order => "created_at DESC") end end ``` コントローラのアクションはこれだけです! 何をレスポンスに含めるのか、どういった構造で返すのか、 そういった情報はRABLビューで定義します。 RABLビューファイルは「アクション名.json.rabl」という名前にします。 ```Ruby:app/views/api/getItems.json.rabl collection @items, :root => :result, :object_root => false attribute :id, :name node(:photo_url) do |item| item.photo.url end ``` このAPIは次のようなJSONレスポンスを返します。 ```javascript:getItemsレスポンス { "result": [ { "id": 1, "name": "Taro Yamada", "photo_url": "http://.../00001" }, { "id": 2, "name": "Jiro Suzuki", "photo_url": "http://.../00002" } ] } ``` RABLのDSLによってJSONのレスポンスが簡潔に定義できているのがわかるかと思います。 ## もう少し複雑な例 もう1例としてアイテム詳細APIを定義してみます。 ```Ruby:app/controllers/api_controller.rb class ApiController < ApplicationController def getItemDetail @item = Item.find(params[:id]) end end ``` ```Ruby:app/views/getItemDetail.json.rabl object false child(@item => :result) do attribute :id, :name, :memo, :bought_at, :location_name, :price, :user, :likes, :comments, :tags node :photo_url do |item| item.photo.url end child :user do attribute :id, :name node :photo_url do |user| user.photo.url end end child(:tags) do attribute :name end child(:likes) do attribute :user_id end child(:comments) do attribute :user, :body, :created_at child(:user) do attribute :id node :photo_url do |user| user.photo.url end end end end ``` JSON出力結果は書くのが大変なので割愛しますが、 アイテムの中にコメントが複数あって、各コメントには書込んだユーザがいて、ユーザには写真があって、 といった複雑なレスポンスがわかりやすく記述できるようになります。 ## まとめ RABLを使う事で情報表示系のAPIのメンテナンス性が格段に上がりました。 モバイルアプリやフルJavaScriptクライアント用にJSON APIを用意する時にはぜひ、おすすめです。 上記の例で出てくるRABL DSLの詳細については、この辺りをご参照ください。 http://nesquena.github.com/rabl/#usage ```Ruby:keyword object, collection, child, attribute, node ``` |
|
| 973位 |
|
|||
|
09:13:47 |
|
|
# 必要なもの
NeoBundleはPCにGitがインストールされている必要があります。 # インストール ## NeoBundleのインストール 1. .vimフォルダ以下にbundleフォルダを作成 2. 作成したbundleフォルダにneobundle.vimをクローン ```sh $ mkdir -p ~/.vim/bundle $ git clone git://github.com/Shougo/neobundle.vim ~/.vim/bundle/neobundle.vim ``` ## 設定 公式サンプルからとりあえず最低限の設定のみ抜粋 .vimrcに次の内容を書いておけば、最低限利用できます。 ```vim:.vimrc "-------------------------------------------------------------------------- " neobundle set nocompatible " Be iMproved filetype off " Required! if has('vim_starting') set runtimepath+=~/.vim/bundle/neobundle.vim/ endif call neobundle#rc(expand('~/.vim/bundle/')) filetype plugin indent on " Required! " Installation check. if neobundle#exists_not_installed_bundles() echomsg 'Not installed bundles : ' . \ string(neobundle#get_not_installed_bundle_names()) echomsg 'Please execute ":NeoBundleInstall" command.' "finish endif ``` ## プラグインの取得 さらに、次のように.vimrcに追記して、`:NeoBundleInstall`を実行するとプラグインが`.vim/bundle`フォルダ以下に追加され、利用できるようになります。 ```vim:.vimrc "GitHubリポジトリにあるプラグインを利用場合 NeoBundle 'tpope/vim-fugitive' "GitHub以外のGitリポジトリにあるプラグインを利用する場合 NeoBundle 'git://git.wincent.com/command-t.git' "Git以外のリポジトリにあるプラグインをを利用する場合 NeoBundle 'http://svn.macports.org/repository/macports/contrib/mpvim/' NeoBundle 'https://bitbucket.org/ns9tks/vim-fuzzyfinder' ``` 以上を追記してから、vimを立ち上げて下記コマンドを実行 ``` :NeoBundleInstall ``` # 参考情報 * [Shougo/neobundle.vim](https://github.com/Shougo/neobundle.vim) * [NeoBundleの導入](http://qiita.com/items/1c32d3f24cc2919203eb) |
|
| 974位 |
|
|||
|
19:05:02 |
|
|
「顔認識ってサクッとできないのー?」って聞かれたので試してみた。
細かいチューニングとかはできてない。 #インストール Macだと結構簡単。OpenCVをbrewを使ってインストールする。 pythonはbrewで入れたものを使ってる。 numpyも必要みたいだけど、インストール済みだったんで省略。 どうやら、OpenCVをインストールする時にPython用のパッケージcv2がsite-packageにコピーされるみたいなので、インストール順としてはPython→numpy→OpenCVがいいみたい。 ```bash:bash brew tap homebrew/science brew install opencv ``` #Pythonで動かす とりあえず、先にコードを晒す。 試行錯誤に使った時のコメントも、汚いけど残しておくことにした。 パラメータチューニング用の説明まで残しているがwww ```python:recognize.py # -*- coding: utf-8 -*- import cv2 #HAAR分類器の顔検出用の特徴量 #cascade_path = "/usr/local/opt/opencv/share/OpenCV/haarcascades/haarcascade_frontalface_default.xml" cascade_path = "/usr/local/opt/opencv/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml" #cascade_path = "/usr/local/opt/opencv/share/OpenCV/haarcascades/haarcascade_frontalface_alt2.xml" #cascade_path = "/usr/local/opt/opencv/share/OpenCV/haarcascades/haarcascade_frontalface_alt_tree.xml" image_path = "lena.jpg" color = (255, 255, 255) #白 #color = (0, 0, 0) #黒 #ファイル読み込み image = cv2.imread(image_path) #グレースケール変換 image_gray = cv2.cvtColor(image, cv2.cv.CV_BGR2GRAY) #カスケード分類器の特徴量を取得する cascade = cv2.CascadeClassifier(cascade_path) #物体認識(顔認識)の実行 #image – CV_8U 型の行列.ここに格納されている画像中から物体が検出されます #objects – 矩形を要素とするベクトル.それぞれの矩形は,検出した物体を含みます #scaleFactor – 各画像スケールにおける縮小量を表します #minNeighbors – 物体候補となる矩形は,最低でもこの数だけの近傍矩形を含む必要があります #flags – このパラメータは,新しいカスケードでは利用されません.古いカスケードに対しては,cvHaarDetectObjects 関数の場合と同じ意味を持ちます #minSize – 物体が取り得る最小サイズ.これよりも小さい物体は無視されます facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1)) #facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.1, minNeighbors=3, minSize=(10, 10), flags = cv2.cv.CV_HAAR_SCALE_IMAGE) print "face rectangle" print facerect if len(facerect) > 0: #検出した顔を囲む矩形の作成 for rect in facerect: cv2.rectangle(image, tuple(rect[0:2]),tuple(rect[0:2]+rect[2:4]), color, thickness=2) #認識結果の保存 cv2.imwrite("detected.jpg", image) ``` 今回は学習用の画像を用意していないので、OpenCVに一緒に付いているHAAR分類器の特徴量を利用している。HAAR分類器についてはこちらを参照([第10回CV勉強会 OpenCV祭り 物体検出徹底解説!](http://www.slideshare.net/takmin/opecv-object-detectiontakmin opencv))。 有名なレナさんの画像だとどの特徴量でも認識できたが、他の画像で試すとhaarcascade_frontalface_default.xmlはあんまり上手く認識できなかったので、色々試したほうが良さげ。 元の画像  認識後の画像  |
|
| 975位 |
|
|||
|
03:23:07 |
(KLab Inc. 所属) |
|
今年3月にリリース予定の Python 3.4 から、 enum が追加されます。 ([ドキュメント](http://docs.python.org/3.4/library/enum.html)) Python 2.4 から使えるバックポートが開発されており、 `pip install enum34` でインストールできます。 ## enum が欲しい理由 今までも例えばこんな方法で定数を定義していました。 ```python class Spam: FOO = 1 BAR = 2 BAZ = 3 ``` しかし、この方法には以下のような問題点があります。 * デバッグ時など、ただの整数値が表示され、意味が分かりにくい * 値から名前を引く方法を自作しないといけない * 列挙する方法も自作しないといけない アドホックに解決したり、ライブラリがあったりしたんですが、これくらい標準ライブラリに欲しいですよね。で、標準ライブラリに入ったんだから、使いましょう。 ## シンプルな使い方 `namedtuple` と同じく、最初に形名、次に名前の一覧のリスト、あるいは空白区切りの文字列を渡すと、勝手に1から順番に値が割り振られます。 できた型は、属性、呼び出し、添字の3種類の方法で、要素にアクセスできます。 ```python >>> import enum >>> Foo = enum.Enum("Foo", "foo bar baz") >>> Foo.foo <Foo.foo: 1> >>> Foo(1) <Foo.foo: 1> >>> Foo["foo"] <Foo.foo: 1> >>> isinstance(Foo.foo, Foo) True ``` 要素には name と value という属性があります。 ```python >>> Foo.foo.value 1 >>> Foo.foo.name 'foo' ``` 要素自体はその値と区別されます。全ての要素はクラス生成と同時に1つだけ作られる singleton なので、 `==` ではなく `is` で比較できます。 ```python >>> Foo.foo == 1 False >>> Foo.foo is Foo(1) is Foo["foo"] True >>> Foo.foo is Foo(1) True ``` イテレートもできます。 ```python >>> for m in Foo: ... print(m) ... Foo.foo Foo.bar Foo.baz ``` ## 任意の値を利用する `Enum` を呼び出すのではなく継承したクラスを定義することで、1からの連番ではなく任意の値を要素に割り当てることができます。 ```python >>> class Bar(enum.Enum): ... foo = 'x' ... bar = 'y' ... baz = 'z' ... >>> Bar.foo <Bar.foo: 'x'> ``` ### Python 2 での制限 Python 2 では、クラスの要素の定義順を利用する方法が(基本的には)ありません。 for 文で列挙するときに定義順を維持したい場合は、 `__order__` という属性を定義しておきます。これは Python 2 対応のための、 enum34 による拡張であり、 Python 3.4 の enum モジュールでは何もしなくても定義順が保存されます。 ```python >>> class Bar(enum.Enum): ... __order__ = 'foo bar baz' ... foo = 'x' ... bar = 'y' ... baz = 'z' ... >>> list(Bar) [<Bar.foo: 'x'>, <Bar.bar: 'y'>, <Bar.baz: 'z'>] ``` ## 要素を整数として扱う 要素が直接整数として振る舞うと便利なことありますよね。その場合は IntEnum を使えます。 ```python >>> class Foo(enum.IntEnum): ... foo = 1 ... bar = 3 ... baz = 5 ... >>> Foo.foo == 1 True >>> Foo.foo + 2 3 >>> Foo.foo < Foo.bar True ``` 実は、 IntEnum の定義はたったこれだけです。 ```python class IntEnum(int, Enum): """Enum where members are also (and must be) ints""" ``` このように、要素に持たせたい振る舞いを継承で持たせることができます。 ```python >>> class Bar(str, enum.Enum): ... foo = 'x' ... bar = 'y' ... >>> Bar.foo + Bar.bar 'xy' ``` これは便利な半面、動的型付けの Python では、ある変数が持っているのが Enum の要素なのか、純粋な値型なのかわかりにくくなるという危険があります。節度を守って使いましょう。 ## in について IntEnum を使っても、整数値と Enum 型の間に直接 `in` を使ってその整数値に対応する Enum の値があるかどうかはわかりません。 `in` が使えるのはその Enum 型の要素だけです。 ```python >>> class Foo(enum.IntEnum): ... foo = 3 ... bar = 7 ... >>> 3 in Foo False >>> Foo.foo in Foo True ``` EnumMeta を継承してカスタマイズしたり、非公開APIを直接叩けばなんとでもなりますが、そんなお行儀の悪いことはしないで、普通に呼び出して例外でチェックしましょう。 呼び出しで値が見つからなかった時は `ValueError` が投げられます。 名前から要素を引くときは、同じように添字記号と `KeyError` を使うことができます。 ```python >>> Foo(2) ValueError: 2 is not a valid Foo >>> Foo['xyzzy'] KeyError: 'xyzzy' ``` ただし、公開 API として名前をキーに、要素を値にした辞書が `__members__` という名前で用意されているので、名前で引くときは `in` とか `.get()` とか普段の dict 操作も使えます。 ```python >>> Foo.__members__ {'foo': <Foo.foo: 3>, 'bar': <Foo.bar: 7>} >>> Foo.__members__['foo'] <Foo.foo: 3> >>> 'xyzzy' in Foo.__members__ False ``` |
|
| 976位 |
|
|||
|
09:44:09 |
|
|
## このエントリで紹介していること - IntelliJ IDEAプラグインの事前知識 - プラグインの開発方法 - プラグインデバッグ手法、Tips - プラグインの公開の仕方 - 開発にあたっての便利リンク集 ## このエントリの目標 IntelliJ IDEAプラグインでNotificationメッセージに”Hello Plugin”を表示させる [オプション] プロジェクト内に永続変数/クラスを持つ ## IntelliJ IDEA系のプラグインについて IntelliJ IDEA系の製品(PhpStorm/RubyMine他)で使えるプラグインです。どのIDEで利用可能とするかは任意で選択出来ます。 プラグインの公開は無料ですが、JetBrainsアカウントのユーザ登録が必須となります。 プラグインについてはこちらのサイトで公開されています。 http://plugins.jetbrains.com/ ## 事前知識として 以下の項目が必須となります。 - エディタとしてIntelliJ IDEA - 言語知識としてJava(もしくはGroovy / Scala / KotlinなどのJVM言語) IntelliJ系のプラグインの開発にはIntelliJ IDEAが必須となります。 また、開発にあたっては基本的にJavaで書くことになります。上述したように他のJVM言語でも書けますが、今回はJavaでのみ解説します。 IntelliJ IDEAで[php-openapi]なるものがあるのでよく勘違いされる方いますが、PhpStormではプラグイン開発はできません。 ## つくってみる 今回の解説ではIntelliJ IDEA13.0(Build #IU-133.193)を使用します。 ### プロジェクトの作成 以下の手順で新しいプロジェクトを作成します。 - [New Project] - [IntelliJ Platform Plugin] Project locationはどこでも任意の場所で大丈夫です。プロジェクト名は[HelloPlugin]にしてみます。  設定ができたら[Finish]でプロジェクトを作成します。 ### アクションを作成 基本的に[アクション]という単位で動作を作成していきます。 srcディレクトリを右クリックで以下の手順でアクションを作成します。 - [New] - [Action] Action IDはプラグイン内で一意にします。基本的にはクラス名と一緒にすれば良いです。ここでは[HelloPluginAction]にします。 Nameはアクション名として表示されます。ここでは[Say Hello]とします。 Groupsは、作成したアクションがどの処理グループに属すかを設定します。ここでは簡易ツールとしたいので[ToolsMenu (Tools)]を選択します。だいぶ下の方です。 下のKeyboard Shortcutsはそのアクションを実行するショートカットキーを設定出来ます。ここではFirstに[Cmd+Shift+F12]を設定します。Winの場合は[Ctrl+Shift+F12]とかで良いと思います。  とりあえずこんな感じで入れたら[OK]を押しましょう。そうするとHelloPluginActionクラスが作成され、plugin.xmlに<action>が追加されます。 - HelloPluginAction.java  - plugin.xml  実行されるアクションはactionPerformed()に入ってきます。main関数のようなイメージです。基本的な処理はここに書いていきます。 ついでに、plugin.xmlの中身も見てみましょう。 plugin.xmlはプラグイン本体のマニフェスト情報やショートカット情報を管理している設定ファイルです。 上のアクションを作成することで赤枠の中が追加されました。テキストやショートカットキーの変更などは、こちらを直に書き換えてしまっても大丈夫です。 簡単に他の項目も説明しておきます。 上部のid, name, version, vendorはプラグインの情報です。プラグイン名やID、作成者情報を設定出来ます。 その下のdescriptionはプラグインの概要、change-notesは更新履歴を書いていきます。この中ではHTMLタグが利用出来ます。 extensionsは永続化設定やReferenceやCompletionなどを定義します。後半でちょっとだけ触ります。 HelloPluginActionに戻ります。 actionPerformed()の中身を以下のように書き換えてみます。 ```java public void actionPerformed(AnActionEvent e) { Notifications.Bus.notify( new Notification("sample", "Hello Plugin!", "Hello! This is Sample Plugin.", NotificationType.INFORMATION) ); } ``` 試しにこれでデバッグしてみましょう。 ### プラグインを動かしてみる ツールバーの[Run] - [Debug]から実行します。 Edit Configurationの設定を言われる場合は、[+]から[Plugin]を追加して以下のような感じで[Use classpath of module]の欄を設定して、[Debug]を押します。  コンパイル後、IntelliJ IDEAがもう1プロセス立ち上がります。こちらがデバッグ用のエディタです。 立ち上がった方のIntelliJ IDEA(以下デバッグエディタ)はまっさらな状態です。とりあえず設定はそのままで、適当なプロジェクトを開いてみます。 開き終わったら先ほど設定したショートカット(Cmd+Shift+F12)を押してみます。 そうすると、こんな感じで右上にNotification通知がされると思います。  ざっくりな動かし方はこんな感じです。いかがでしたでしょうか。 実質コードはほとんど書いていないので、全然物足りないという方は以下のオプションも読んでみてください。 ### プロジェクト内に永続値を持つ エディタを閉じた後もプロジェクト固有の変数や定数を永続的に保持したい場合は数多くあると思います。それにはPersistStateComponentを使います。 まずはPersistStateComponentを継承したモデルクラスを作成します。 永続化しておきたい変数や定数、メソッドを格納したModelをプロジェクト固有で持つような感じになります。 クラス名は任意ですが、ここでは[PluginConfig]とします。 src配下右クリックから[New] - [Java Class] で、普通にクラスを作ります。 ここではHelloPluginのアクションが実行された回数を保持しておいて、実行されるたびにインクリメントするようにしてみます。 中身はこんな感じにしました。 - HelloPluginAction.java ```java import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; public class HelloPluginAction extends AnAction { public void actionPerformed(AnActionEvent e) { PluginConfig config = PluginConfig.getInstance(e.getProject()); if(config.isEmpty()) { config.init(); } int count = config.getCount(); Notifications.Bus.notify( new Notification("sample", "Hello Plugin!", String.format("This action has been performed %d times", count), NotificationType.INFORMATION) ); config.increment(); } } ``` - PluginConfig.java ```java import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.util.xmlb.XmlSerializerUtil; import org.jetbrains.annotations.Nullable; @State( name = "HelloPluginConfig", reloadable = true, storages = { @Storage(id = "default", file = "$PROJECT_FILE$"), @Storage(id = "dir", file = "$PROJECT_CONFIG_DIR$/hello_plugin.xml", scheme = StorageScheme.DIRECTORY_BASED) } ) public class PluginConfig implements PersistentStateComponent<PluginConfig> { private Integer count; @Nullable @Override public PluginConfig getState() { return this; } @Override public void loadState(PluginConfig config) { XmlSerializerUtil.copyBean(config, this); } @Nullable public static PluginConfig getInstance(Project project) { return ServiceManager.getService(project, PluginConfig.class); } public void setCount(Integer count) { this.count = count; } public Integer getCount() { return count; } public boolean isEmpty() { return count == null; } public void init() { this.count = 0; } public void increment() { this.count++; } } ``` - plugin.xml(一部抜粋) ```xml <extensions defaultExtensionNs="com.intellij"> <projectService serviceInterface="PluginConfig" serviceImplementation="PluginConfig"/> </extensions> ```  これでアクションを実行する度にカウントがインクリメントされるようになりました。 プロジェクトごとの固有にしてあるので、エディタを開きなおしてもこの数字は保持されています。 PluginConfigがPersistentStateComponentを継承しているところがポイントです。 @State()アノテーションの中身でこの設定クラスを保存する保存先を指定しています。ここではプロジェクトディレクトリの設定ディレクトリ(.idea/)にhello_plugin.xmlという名前で保存するようにしています。 オプションや、これ以上の詳しい使い方についてはこちらに書いてあります。 [Persisting State of Components](http://confluence.jetbrains.com/display/IDEADEV/Persisting+State+of+Components) ### プラグインをデバッグする [Debug]として開始していれば、任意の箇所でブレイクポイントを張るだけで止まってくれます。  APIのドキュメントなどがないため、渡されたクラスにどのようなメソッドがあるか、そのメソッドがどのような値を返すかをデバッグしながら開発することは非常に有効です。 とはいえコードを書き換えて毎回デバッグ起動するのではキリがないので、ここではEvaluate Expression(Ctrl+F8)を使用します。  上記のようにブレイクポイントで止めた状態でEvaluate Expressionを押すとウィンドウが開きます。ここで実行したいコードを入力するとその場で動的実行して、その結果を表示してくれます。 例えば、8行目で渡されてきたAnActionEvent eがどのようなメソッドを持っているか試したい場合はこのように  e.getProject().getBasePath()で返される値がどんな感じか試したいときは  入力してEvaluateを実行すると、実行結果がその場で返されます。 特にプラグイン開発においてはこの機能を使いこなせるかどうかで開発効率が桁違いに変わってくるので、是非試してみてください。 ### プラグインを公開する。 上述の通り、プラグインの公開にはJetBrainsアカウントが必要になります。アカウントはプラグイン作成ページからも登録出来ます。 アップロードする前にプラグインのjarファイルを作成する必要があります。 [Build] - [Prepare Plugin Module “Hello Plugin” For Deployment] を実行すると、プロジェクトのディレクトリにjarファイルが作成されます。 http://plugins.jetbrains.com/ jarファイルが用意出来たら、ログインした状態で右上の[Add new plugin]から登録を行います。 アップロードフォームがあるので先ほどのjarを添付し、プラグインのカテゴリを選択します。 次のページでプラグインの概要やライセンス、開発者、プラグインを配布対象のIDEなどを選択します。 最後に保存すると公開準備は完了です。 審査が終わると1日程度で公開され、IntelliJ IDEAのプラグインセンターからダウンロード出来るようになります。 ## ハマったときは? ドキュメントのFAQで解決することも多いので、まずはFAQをじっくり見ます。 [Plugin Development FAQ](http://confluence.jetbrains.com/display/IDEADEV/Plugin+Development+FAQ) そもそもどういう風に動かしているのか分からない場合、同じことをやっているような/やっていそうなプラグインを探してきて、ソースコードを真似るのが一番いいと思います。 ローカルに落としてきてデバッグしながらコードを追うのも有効です。 [intellij-community](https://github.com/JetBrains/intellij-community)のCoreのソースコードを落としてきて類似処理を探すのも有効です。中規模以上のプラグインを作ることになったらお世話になることは多いです。 コードだけで1GB以上あるので追いかけるのはちょっと大変ですが。 どうしても解決しなければフォーラムで質問するのもいいと思います。1〜2日くらいで中の人が答えてくれる場合が多いです。 [Open API and Plugin Development Forum](http://devnet.jetbrains.com/community/idea/open_api_and_plugin_development) [@vexus2](http://twitter.com/vexus2)宛てにTwitterでリプライもらえれば力になれる場合もあるかもしれません。 ## 最後に APIドキュメントがなく、公式ドキュメントもかなり少ない&日本語情報はほぼ皆無なため結構苦労することが多いです。が、ハードルが高いからこそ得られる達成感もあります。 また、自分が好きなエディタを更に自分好みにカスタマイズ出来るというのは非常にメリットかと思います。 ぜひIntelliJ IDEAのプラグイン開発に挑戦してみてはいかがでしょうか。 ## 参考リンク - [IntelliJ Plugin Development](http://confluence.jetbrains.com/display/IDEADEV/PluginDevelopment) - [Open API and Plugin Development Forum](http://devnet.jetbrains.com/community/idea/open_api_and_plugin_development) - [ツイートするIntelliJプラグインを作ったよ](http://hotchemi.hateblo.jp/entry/2013/01/07/013344) - [IntelliJ IDEAプラグインの開発 - 有用な情報とリンク集Add Star](http://d.hatena.ne.jp/fuzzhead/20120729/p1) - [IntelliJ IDEAのプラグイン開発](http://kxbmap.hatenablog.com/entry/20111102/1320169073) |
|
| 977位 |
|
|||
|
12:03:09 |
(GIFMAGAZINE Inc. 所属) |
|
みなさん、Laravelしてますか?
Laravelはここ1年で急激な台頭をみせているPHPのフレームワークです。 githubのスター数は1位のSymfonyに続き第2位です。 以下がGoogleトレンドの結果になりますが、Laravelがいかに近年人気急増中であるかがみてとれると思います。  そんなLaravelですが、実は日本語の書籍がたくさんあることをご存知でしょうか? まだ日本語が原著のLaravel本はでていませんが、海外のLaravel本(電子書籍)を翻訳した日本語版ならたくさんあります。 なんと日本人に優しいことでしょう! 今回は、それらLaravel本の中から厳選して2冊ご紹介したいと思います。 ## Laravel Testing Decoded  [Laravel Testing Decoded](https://leanpub.com/laravel-testing-decoded-japanese)はLaravelでmodel,view,controller,その他DB,IoC,APIのテスト等 Laravelで開発を行うときに遭遇するであろうほとんどすべての状況に対して準備から実践をひとつひとつ丁寧に解説してくれる本です。 LaravelにはIlluminate\Foundation\Testing\TestCaseという標準のライブラリがあり、そこにはPHPUnitをよりわかりやすく便利に実行するためのメソッドがたくさん用意されています。 例えば、ブログの投稿情報をすべて取得してviewを返すコントローラのテストならMockeryを使うことでPHPUnitよりも直感的に以下のように書くことができます。 ``` public function __construct() { $this->mock = Mockery::mock('Eloquent', 'Entry'); } public function tearDown() { mockery::close(); } public function testIndex() { //PHPUnitのデフォルトより直感的で読みやすい $this->mock ->shouldReceive('all') ->once() ->andReturn('foo'); $this->app->instance('Entry', $this->mock); $this->call('GET', 'entries'); $this->assertViewHas('entries'); } ``` 小さなことですが、こうしたヘルパーによって可読性があがるとテストを行うモチベーションも上がります。 ところで、Laravel本を紹介するのにどうして1つ目がテストの本なんだ?と思われるかもしれません。 たしかに私も1つ目ならまずはLaravelの使い方を解説した本とかチュートリアルをまとめた本だとか、 まずはLaravelでアプリ開発をする方法の本を紹介しようと考えていました。 しかし、それでもlaravel Testing Decodedを1番目に紹介したいと思ったのは、この本がLaravelを超えて、PHPでのテスト、しいてはPHPでTDDをする方法について 解説している書籍であるからです。 私が働いている会社ではLaravelを使っているわけではないですが、PHPでのテストについて勉強するためにこの本を読みました。 実際この本に書いてあるコードを引用して紹介できないのが残念ですが、$15.00でこの網羅性は買って損はないなと思います。 - [Laravel Testing Decoded](https://leanpub.com/laravel-testing-decoded-japanese) ## Implementing Laravel  [Implementing Laravel](https://leanpub.com/implementinglaravel-jpn)は、LaravelのIoCコンテナや依存注入(DI)といった便利な仕組みを具体的にアプリに実装するにはどうしたらよいかということを解説してくれる本です。 Laravelを日本語で勉強する人はまず[こちらのドキュメント](http://laravel4.kore1server.com/)を読んだり、[Laravel4、学習の道](http://kore1server.com/174)に沿って実際に手を動かしてみるという流れになると思います。 このフローを踏めば、たいていのアプリはつくることができるしLaravelの良さもわかるはずです。 私もこの学び方で[ShareMemo](https://github.com/YuheiNakasaka/sharememo)というサービスをつくりました(現在はclose中)。 しかし、このアプリではLaravelのIoCコンテナと依存注入という概念を使用していません。 理由は依存注入がなくてもアプリはつくれるし、むしろ慣れていないうちはこの概念を使わずにつくった方が完成が早いからです。 ただ、「LaravelをもっとLaravelらしく使いたい」「Laravelで大規模なサイトを構築したい」と思うのならIoCコンテナと依存注入という概念を避けて通れません。 そんなときに役に立つのがこのimplementing Laravelという本です。 この本を読めば、依存注入をLaravelでどう解決し、リポジトリーパターンをどう具体的なコードに落とし込むかがわかります。 本書はLaravel初心者には向かないかもしれないですが今後Laravelを実運用で使っていきたいなら読むと得られるものが多いと思います。 ## その他(日本語訳あり) ちなみに私は日本語翻訳されたLaravel本はすべて読んでいるので、その他の本に関しても簡単に紹介できればと思います。 - [Laravel4 Cookbook](https://leanpub.com/laravel4cookbook-jp) この本は、その他で紹介するべきでないかもしれません。なぜなら、現状日本語訳されているLaravel4本の中で一番実践的で濃いチュートリアルを含んでいるからです。おそらく、Laravel公式ドキュメントを読んだ後、この本をやればLaravelでのアプリ作成に困ることはないかもしれません。個人的にはlaravel4とBackbone/Ember/Angularを使ってリアルタイムチャットを実装する例が大変ためになりました。上記2冊並におすすめです。 - [From Apprentice To Artisan](https://leanpub.com/laravel-jp) この本はLaravelの開発者であるTayler Otwellが書いた書籍です。それだけです。Implementing Laravelの方が個人的には良いと思います。 - [Laravel4 ドキュメント+](https://leanpub.com/laravel4plus) Laravel4の公式ドキュメントの日本語訳版です。サイトで無料で日本語版が見られるのでわざわざ買う必要はないかもしれません。私は買いました、5ドルなので寄付も兼ねて。 - [Code Happy](https://leanpub.com/codehappy-jp) Laravel3の聖典のような本。英語でよいなら著者のBlogを見るとほぼ同じことが書いてあります。 ## その他(日本語訳なし 2013/12/01現在) - [Code Bright ](https://leanpub.com/codebright-jp) 『Code Happy』のLaravel4版です。まだ日本語訳はありませんが、話を聞く限り公式ドキュメント+αくらいな内容なようです。 - [Laravel4でこなすプログラム術 Getting Stuff Done](https://leanpub.com/gsd-laravel-jp) 最後に紹介するこの本は、いま最も日本語訳版が待ち遠しいLaravel本です。500ページを超える鈍器のようなページ数ですが電子書籍なので安心です。これがおそらくLaravel4本の決定版になるのではないでしょうか。リリースは来年らしいのでとりあえずは英語版を購入して我慢です。 ## 最後に 冒頭でも書きましたが、Laravel4はまだまだ若いフレームワークです。 しかしこれほどたくさんの書籍が世界中で発表され、各地の言語に翻訳されています。 幸いにも日本では[Kawase Hirohisa](https://twitter.com/hirokws)氏がすごいスピードで日本語訳をされているので英語が苦手な方でも無理なく読むことができます。 日本ではFuelPHPやCakePHPが人気ですが、ここで紹介した書籍を片手にLaravelデビューしてみてはいかがでしょうか? 以上、Laravel Advent Calender 1日目の記事でした。 |
|
| 978位 |
|
|||
|
11:11:09 |
|
|
リリース後にバグが発生!!
誰の修正が原因なのか、 見つけた! 「hoge.txt」ファイルの修正が原因っぽい。 でも、そこはAさんと、Bさんの修正が混ざってるけど。 そんなときに以下のコマンドを実行! ```bash git blame ファイル名 ``` すると、 7b64c3aX (Asan 2013-03-08 09:31:18 +0900 51) def hoge 7b64c3aX (Bsan 2013-03-08 09:31:18 +0900 51) p hogehoge 7b64c3aX (Bsan 2013-03-08 09:31:18 +0900 51) p bug 7b64c3aX (Asan 2013-03-08 09:31:18 +0900 58) end 行ごとにコミッターの名前が表示されました。 「あっ、Bさんの修正が原因だ!」 となり、 Bさんに修正依頼をして 無事に解決。 めでたし、めでたし。 |
|
| 979位 |
|
|||
|
00:14:56 |
(株式会社キュリオシティソフトウェア 所属) |
|
このTipsはiOSアプリ開発時に納品物定義をする際のリストです。デザインをお願いする他社のデザイナさんがiOSアプリなどの経験がない場合、どのような納品形態として成果物を納めてもらうかということでよく話題になるのでその項目を残しています(同じ社内にいるメンバーの場合は適当にコミュニケーションをとることやググレカスで解決すると思いますが…)。
ですので、デザインに対する細かい指示というよりもそもそも納品物として何がいるんだっけ、どう指定しておけば面倒が増えないんだっけということを書いています。 前提としてディレクターが作ったワイヤーフレームもしくは画面遷移図があり、そのドキュメントを元に他社のデザイナさんとのやりとりがあった後に納品というフェイズがあることを想定しています。 以下がその本文 --- # 納品物定義 ## 納品物 * 画面全体のデザインとしてのPSDファイル (納品物として管理するために必要となります。ファイル名は日本語名でかまいません) * 画面ごとのデザインが分かるJPEGファイル (画面ごとのデザインの確認用に使用します。ファイル名は日本語名でかまいません) * ボタンや背景画像など各パーツをPNGファイルとして出力したもの (開発側で使用します。ファイル名ルールについては下記参照) ## 備考 * PSDファイルの互換性はPhotoshop CSで開けるファイルでお願いします。 * 画面イメージは横画面サイズを640px(iPhone4, iPhone4S, iPhone5相当)で作成をお願いします。 * 各パーツの縦横サイズはRetina相当のPNGファイルと、非Retina相当のPNGファイルを2つをお願いします * PNGファイルの命名はRetina相当のものについては@2xをつけてください * PNGファイルのファイル名はアルファベットすべて小文字で文字の区切りはアンダースコアでお願いします (例:button_back@2x.png) * Ratian素材の縦横画像サイズは必ず偶数にしてください * ナビゲーションバーのボタン内の画像は、上下左右の余白なくお願いします。 * タブバーの画像については次のサイトを参考にして下さい http://golog.plus.vc/web/5866/ * ActivityIndictorはiOSの標準にあるので素材として必要ありません * セル右側のアクセサリー">"等は標準のものであれば素材として必要ありません ---- このような内容をそのまま納品物定義として送っています。 # お願いする内容について Photoshop CS以降は互換性があるために、CS以降で開けるファイルとお願いすれば良いらしいです。解像度は指定していませんがデザインするキャンバス自体の横サイズを640pxにしてデザインを作成してもらえばRetinaと同じピクセルとなるので、それぞれの素材の解像度が低いということは無い気がします。ファイル名はそのままアプリに組み込みたいので日本語などで作成してもらわず、大文字小文字の区別もしたくないのですべて小文字で作成してもらったほうが楽です。 また、PSDファイルや確認だけできればよいJPEGファイルに独自ルールのアルファベットの名前を付けられることがありますが、これは解りづらいだけです(バージョン管理するなら別かもしれませんが)。 画像のサイズについてRatinaを偶数にする理由は、もし奇数だった場合に非Ratinaのサイズが奇数の半分で小数点以下になってしまうからです。 ナビゲーションバー、ActivityIndicatorやセルなどはなるべく正しい名前を使って指示します。ググってもらえればそれがなにか分かるはずですし、わかりやすくしようと”ぐるぐるのやつ”、"上のバー"などの言葉をつかってしまったりすると逆に混乱を招いたりします。 この他にもこういう勘違いがおこるからこうしたらいいよ!などのご意見があればコメントでお願いします。追記していきます。 |
|
| 980位 |
|
|||
|
18:32:01 |
|
|
# ローカルタイムに関する設定 Rails3系からデータベースにはUTCで保存、取り出して扱う際に必要なタイムゾーンに変更するというのが基本的な考え方の様子。 タイムゾーンの設定はapplication.rbにて行えるが、主に関係する設定が2つある。 * config.active_record.default_timezone * config.time_zone ## config.active_record.default_timezone データベースに保存する際のタイムゾーンを指定するディレクティブ。先ほど書いたようにRails3からはデータベースに時刻を保存する場合は基本的にUTCで保存するようになったみたいで、これをローカルタイムで保存するには以下のように設定すれば良い。 ```ruby:config/application.rb config.active_record.default_timezone = :local ``` ## config.time_zone こちらが色々悩ましそうな感じで今確認出来たところだとActiveSupport::TimeZoneクラスの出力に影響を与える様子。試しに何も設定しない場合とtime_zoneをtokyoにした場合で色々出力してみると ### 設定しない場合 ```ruby: Time.zone # => (GMT+00:00) UTC Time.zone.now # Mon, 24 Sep 2012 09:19:35 UTC +00:00 ``` ### Tokyoにした場合 ```ruby:config/application.rb config.time_zone = 'Tokyo' ``` ```ruby: Time.zone # => (GMT+09:00) Tokyo Time.zone.now #=> Mon, 24 Sep 2012 18:16:43 JST +09:00 ``` とこの様に出力が変更されていることが確認できた。注意が必要なのはActiveSupport::TimeZoneを使うにはTime.zoneで処理を行う必要がありTime.nowのようにTimeで直接処理すると設定に関係なくローカルタイムで表示されてしまう点。 ### Time / Time.zoneの違い ``` Time.class # => Class (常にローカル扱い) Time.zone.class # => ActiveSupport::TimeZone (config.time_zoneの設定に依存) ``` ということでユーザによってタイムゾーンを変える必要があるアプリケーションは * config.time_zone = 'Tokyo' を設定する * Time.zoneを常に使う (追記: ActiveRecordと併用する場合はto_s(:db)でUTCに変換 => DB登録時に更に時刻の差分を補正と2重に処理が走ってしまうので注意) 更に追記、config.time_zoneを変更してしまうとActiveRecordを利用する際に * オブジェクトを生成したタイミングでタイムゾーンをUTCに合わせようとする * Arelなどwhereをする際にカラムの値で指定する場合などでは合わせようとしない という違いが発生してしまう。 ```ruby:オブジェクトを生成した場合の挙動 created_at = "2012-09-01 00:00:00" @example = Example.new(:created_at => created_at) @example.created_at # => 2012-08-31-15:00:00 ``` ```ruby:Arelを利用する場合の挙動 created_at = "2012-09-01 00:00:00" Example.where(:created_at => created_at).to_sql # => SELECT `examples`.* FROM `examples` WHERE `examples`.`created_at` = '2012-09-01 00:00:00 ``` なので、結果的には何も設定せずにユーザ毎に自前で処理するのが良いかもしれないです… 常にローカルタイムで良いアプリケーションは * config.active_record.default_timezone = :localを設定する * config.time_zone = 'Tokyo' って感じが良いのだろうか? # Tips ## データベース用のフォーマット変換する Time.zone.to_s(:db)は使ってしまうと事故が起きそう… ```ruby: Time.zone.now.to_s(:db) # UTCに自動的に変更される Time.zone.now.strftime("%Y-%m-%d %H:%M:%S") # ローカルタイムのまま Time.now.to_s(:db) # ローカルタイムのまま ``` |
|
| 981位 |
|
|||
|
10:24:23 |
(Wantedly 所属) |
|
## コード規約 * https://github.com/wantedly/objective-c-style-guide * NYTimesをベースにraywenderlichからいくつか使えるものを追加し、Storyboard, Category, Directory, Cocoapodsの章を追加した。 * https://github.com/NYTimes/objective-c-style-guide * https://github.com/raywenderlich/objective-c-style-guide ## 既存のプログラム見て確認して欲しいポイント * RestKit * APIの仕様の確認 * ORマッピングについて * モデルへのマッピングをどうやっているか * ObjectManager, ResponseMapping, RequestMapping, Descriptorはどういう役割をしているか * StoryboardとContainer * autolayoutの使い方 * viewController間のデータ受け渡しはどうしているか * containerViewのsize調整はどうやっているか * Extensionsの内容 * それぞれのObjectにどういうカテゴリを定義しているか * モダンなiOSの記法(コード規約) * NSArray, NSDictionaryの定義方法 * NSEnum * property * 編集画面のデータの受け渡し方法 * copyを利用した受け渡し * コントローラーを軽く * modelに移せるものは移す * view移せるものは移す * http://www.objc.io/issue-1/lighter-view-controllers.html * (data sourceとprotocolの独立はしてない) * comunication pattern * delegate, notification, kvo, blockなどの使い分け * http://www.objc.io/issue-7/communication-patterns.html * ライブラリ * どういうものを利用しているか確認してほしい * 定数の使い分け * globalな定数か、そのクラスだけの定数か、他の一部なクラスで使う定数か * enumを使ってコードを整理する ## 進め方 * コード読んで分からない箇所は全部聞く * 実装の意図 * 勉強するのにどこを見ればいいかとか * githubでコミュニケーション * 細かい単位でレビューしやすいcommit * pushして定期的に実装内容や方針の確認 ## ブックマーク ### 全般 * foundationが学びたいとき http://nshipster.com/ * いろいろTipsがある http://www.objc.io * UIのサンプル https://www.cocoacontrols.com/ * iOSの情報取得のbot http://www.iosdevbot.com/ * 動画。割といい http://nsscreencast.com/ * iosの情報ブログ http://cocoadays-info.blogspot.jp/ * apple 日本語ドキュメント https://developer.apple.com/jp/devcenter/ios/library/japanese.html * cocoapodの検索(川崎さんが作った) http://cocoapods.wantedly.com/ * 本家 http://cocoapods.org/ * いろいろcocoapodがまとまってる http://bpoplauschi.wordpress.com/2013/11/06/worthy-ios-libraries/ * gitに慣れてないなら http://qiita.com/reikubonaga/items/60b4f6ee0a86ed06e83b * githubのbrowserの使い方 http://qiita.com/reikubonaga/items/da5992a8360ccfa25106 * cocoaのcoding規約 https://developer.apple.com/library/mac/documentation/cocoa/conceptual/codingguidelines/CodingGuidelines.html ### ツール * dash(これは入れるべき) https://itunes.apple.com/jp/app/dash-docs-snippets/id458034879?mt=12 * iOSのデバックに便利(使ってるけど重い) http://sparkinspector.com/ ### 関連URL * containerを使う概要 http://qiita.com/reikubonaga/items/2e88ddda1ac0868a2729 * autolayoutの使い方 http://qiita.com/reikubonaga/items/06d73aa4b28d0eb3fb9c ### その他 * スタートアップでのコードの磨き方(すごい勉強になる) https://speakerdeck.com/hedjirog/sutatoatupudefalseji-shu-li-falsemo-kifang |
|
| 982位 |
|
|||
|
02:14:22 |
(ZIGExN Co., Ltd. 所属) |
|
###[TDDの導入とエンドツーエンドテスト自動化の実践](http://devlove.doorkeeper.jp/events/8444#description) 2014/02/13に行われた[TDDの導入とエンドツーエンドテスト自動化の実践](http://devlove.doorkeeper.jp/events/8444#description)の内容を簡単にまとめました。 ##「Growing Learning Feedback Loop, Guided by TDD & Patterns」家永英治氏 ### TDDとは? - [テスト駆動開発](http://ja.wikipedia.org/wiki/%E3%83%86%E3%82%B9%E3%83%88%E9%A7%86%E5%8B%95%E9%96%8B%E7%99%BA) - コードを書く前にテストを書く ### なぜTDDをやるのか? #### テストとリファクタリングできれいなコードを保つ  #### 悪循環から好循環のループに持っていく ##### 好循環ループとは   - テストを書くことによって継続的にデモを行うことが出来る ### TDDの落とし穴   - カバレッジ率だけ追っても意味が無い - 大切なことは”リーダブルコードを保つ”ということ - [リーダブルコード](http://www.amazon.co.jp/%E3%83%AA%E3%83%BC%E3%83%80%E3%83%96%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89-%E2%80%95%E3%82%88%E3%82%8A%E8%89%AF%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B8%E3%81%8F%E3%81%9F%E3%82%81%E3%81%AE%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%A7%E5%AE%9F%E8%B7%B5%E7%9A%84%E3%81%AA%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF-Theory-practice-Boswell/dp/4873115655)を読みましょう! - [リーン開発の現場](http://www.amazon.co.jp/%E3%83%AA%E3%83%BC%E3%83%B3%E9%96%8B%E7%99%BA%E3%81%AE%E7%8F%BE%E5%A0%B4-%E3%82%AB%E3%83%B3%E3%83%90%E3%83%B3%E3%81%AB%E3%82%88%E3%82%8B%E5%A4%A7%E8%A6%8F%E6%A8%A1%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E9%81%8B%E5%96%B6-Henrik-Kniberg/dp/427406932X/ref=wl_it_dp_o_pC_nS_nC?ie=UTF8&colid=3L6C62C8PXHWM&coliid=I1MO6VJY3Z84QX) - [レガシーコード改善ガイド](http://www.amazon.co.jp/%E3%83%AC%E3%82%AC%E3%82%B7%E3%83%BC%E3%82%B3%E3%83%BC%E3%83%89%E6%94%B9%E5%96%84%E3%82%AC%E3%82%A4%E3%83%89-Object-Oriented-SELECTION-%E3%83%9E%E3%82%A4%E3%82%B1%E3%83%AB%E3%83%BBC%E3%83%BB%E3%83%95%E3%82%A7%E3%82%B6%E3%83%BC%E3%82%BA/dp/4798116831/ref=sr_1_1?ie=UTF8&qid=1392221363&sr=8-1&keywords=%E3%83%AC%E3%82%AC%E3%82%B7%E3%83%BC%E3%82%B3%E3%83%BC%E3%83%89%E6%94%B9%E5%96%84%E3%82%AC%E3%82%A4%E3%83%89) ### TDDを身につける - [TDDBC](http://devtesting.jp/tddbc) - TDDを実践演習で教えてもらう - 素振り(写経) - [Rails Tutorial](http://railstutorial.jp/)をやる ### TDDをはじめる - 一人でひっそり - 気の合う同僚 - 界王拳(自発的残業) ### TDDを広げる - [アイデアを組織に広めるための48のパターン](http://www.amazon.co.jp/Fearless-Change-%E3%82%A2%E3%82%B8%E3%83%A3%E3%82%A4%E3%83%AB%E3%81%AB%E5%8A%B9%E3%81%8F-%E3%82%A2%E3%82%A4%E3%83%87%E3%82%A2%E3%82%92%E7%B5%84%E7%B9%94%E3%81%AB%E5%BA%83%E3%82%81%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE48%E3%81%AE%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-Manns/dp/462108786X/ref=sr_1_sc_1?ie=UTF8&qid=1392221899&sr=8-1-spell&keywords=fareless+change) - 感謝を伝える - 個人的な接触 ## 「テスト自動化のアプローチ拡張トレンド 〜Excel項目定義手動テストから自動テストへ〜」福井修氏 ### エンドツーエンドテストとは - e2eテスト - システム全体が正しく動作することを確認する ### テスト自動化のアプローチ拡張トレンド <iframe src="http://www.slideshare.net/slideshow/embed_code/31120254" width="427" height="356" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px 1px 0; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="https://www.slideshare.net/FUKUIOsamu/20140212-develove" title="20140212 develove" target="_blank">20140212 develove</a> </strong> from <strong><a href="http://www.slideshare.net/FUKUIOsamu" target="_blank">Fukui Osamu</a></strong> </div> ### Gherkin + Capybara + Turnipによるe2eテストの自動化 - [エンドツーエンドテストの自動化は Cucumber から Turnip へ](http://magazine.rubyist.net/?0042-FromCucumberToTurnip) - ボトムアップのテスト - エンジニア視点 - Unitテストなど - トップダウンのテスト - ユーザ視点 - 仕様書からの掘り下げ - Excelなどでテスト項目を列挙するテスト方案など ### Gherkin - テストが自然言語で書ける ```view01.feature # encoding: utf-8 # language: ja 機能: 1.画面表示確認 シナリオ: 1.トップページ確認 前提 対象ページを表示する ならば 'ようこそ 関西Ruby会議05'が表示されて いること ``` - これがテストコードです - 日本語で書けます - プログラミングが出来なくても書けるかも? - [Cucumber のフィーチャの文法 - Gherkin](http://blog.eiel.info/blog/2013/02/12/gherkin/) ### テスト管理ツールをExcelからWebDBへ - Rtestdeck近日公開 |
|
| 983位 |
|
|||
|
10:26:05 |
|
|
## はじめに 自分がコミットメッセージを書くときに考えていることを書きます。 ただし、絶対にこの書き方をずっと続けるというわけでありません。日が経つにつれ、「そういえばこんなことも思ってた」「こういうのいいなあ」「これないわー」といった心境の変化があると予想するので、その時その時で手を入れていくつもりでいます(入れないかもしれません)。なので生煮えです。たぶんずっと生煮えです。それにかこつけて文章の文体もざっくりしています。 あと、あくまでもオレオレなので他の人の書き方をどうこうする意図はありません。うっかり参考になったらいいなあぐらいです。 最初に概念的な話をしてから後半で実際の書き方に入ります。 TODO: 箇条書き部分をあとでちゃんと文章にするかもしれない(読みやすそうなほうで) ## そもそも何書くの * コミットメッセージはdiffのサマリー * メールでいうところのタイトルを、それよりさらに詳しく書ける感じ * diffにより何が変わったかを端的に伝える * たぶんこれがコミットメッセージで一番大事なこと...だと思ってる * 端的といっても情報が足りないとこまるので、diffを読む前に最低限知っておいてほしいことを不足なく書くイメージ * タイトルが「お知らせ」だけのメールは開きたくないわ〜みたいな感じ(なにがどうなったお知らせじゃー) ## なんでメッセージにそんなこと書くの * そのコミットで何したか他人にわかってほしい * 他人その1:一緒に開発してる人 * 他人その2:未来の自分 * この辺りでなにが変わったのかを目次よろしく伝えるため * なぜわかってほしいか? * どこまで開発が進んでるか・何が修正されたのかを知る * それにより次に何をするべきか・何を開発するべきかを知る * 何ができたか=最終的な成果物まであとどれくらいか * (何ができたか=俺こんなのつくったんだぜすげえだろ(自己満)) * 自分はリリースするときのNEWSのネタ代わり 1. NEWSには、このバージョンではこれこれが変わったよ!を書く 1. さて書こう、ところで前のバージョンから何が変わったんだっけ? 1. ログ見ればわかるよ! ## いつコミットするの * 場合によりけりですが、「1つの作業がおわったとき」 * 機能ができたとき、とはちょっと違う * e.g.) * △:このクラスのテストが全部書けたら... * ○:このクラスのテストが1つ書けたら... * たまに、大量にtypoしてて1息で全部直すのつらいとか実装の手順上全部作りきれない場合もある * その場合は途中であることがコミットメッセージに書く(後述) * 途中でしょうがない時だけ限定で「途中だけどまあいったんコミットしちゃえー」ではない * コミット単位でさかのぼった時に細かい単位にしたい * 1つのコミットをもどったらごっそり機能なくなるとかを避けたい * bisectしたときに、「あ、この変更でテスト壊れたのか」がコミット日から1ヶ月ぐらい経ってもわかる単位 * まだ試行錯誤中 ## どういう開発のしかたでも同じなの * masterにガンガンコミットを積んでく感じの開発方法です * コードレビューのことは考えてないですが、基本的には変わらないはず * Pull Requestしてコードレビュー受けて、という開発フローの場合は、メッセージはともかく、コミットそのものはある程度作業のまとまりごとにまとまってたほうがいいかもです * ちなみにgitを使ってますが他でも変わらないと思います ここから実際の書き方です。 ## フォーマット編 ### 1行目 * 英語 * 日本語だと曖昧でもそれっぽく書けてしまい後から見たらイミフということがありえそう(想像) * 英語で書きにくければ多分色々しすぎていてまとめきれないのだという指針代わり(後で詳しく) * ただし英文として成り立ってるかは微妙 * 頭文字は大文字で動詞の現在形で書き始める * 最大80文字 * でも本当は50字以内がいいらしいですねえ * 固有名詞が入るとつらいときがありますが、そんな長い固有名詞使うなってことなのかもしれない(けどどうしようもないこともままある) * 便利なときは冒頭にカテゴリを付けます * カテゴリがついたら文の頭文字は小文字 * e.g) ```test: fix a typo``` * ライブラリのNEWS書くときに便利です * Redmineと連携してたりGitHubのissueを使ってたら番号も書きます * e.g.) ```refs #1234 Implement Entry#summary``` * 3行目のときもある * 作業途中でコミットする場合は後述 ### 2行目 * 空行 ### 3行目以降 * 補足説明 * 1行目で書ききれなかったけどあったほうが間違いなくdiffの中身が理解しやすくなる情報 * e.g.) バグを直したとき * 1行目でこういうバグ直したって言ったけどこれは実際にはこういうことが起きるバグです! * こうやると再現するよ! * こうなるのが自然だからそんなふうに直したんだからね! * e.g.2) 何かコマンドを実行して生成した場合(TODO: あまりいい例じゃないので後から直す) ``` Implement CRUD for User by scaffold $ rails g scaffold User ``` * e.g.3) ベンチマークとったらその結果(ベンチマーク自体はリポジトリにいれる) * e.g.4) typoの具体的な位置を ```^``` で示す ``` Fix a typo poistion -> position ^^ ``` ## 単語編 ### 使わない単語 情報があまり含まれないから、という理由です。 * Change/Modify * そりゃコミットしたから何かしら変えてるだろう * →具体的に何をどうしてどう変えたのかを書く * Revise * 大体のコミットって修正だろう * →その修正で何をどうしたかったのかを書く ### 時と場合により * Update * 大体のコミットって(ry * ```Update XXX to do…``` なら ```Do … for XXX``` みたいにDoを前に出したい * 大事なのはUpdateよりもDo * ドキュメントの文章を綺麗にしたときは使うかも(句読点の位置替えたとか単語の位置調整したとか) * ドキュメントに具体的に項目を追加した場合はその内容を書く(```doc: add ほげほげ```) * Fix * TODO: 例をちゃんとひろってくる(今のは適当) * 書く:```Fix a bug that incorrect URL options are genearated``` * 書かない:```Fix a UsersController``` * 何直したんかわからん * 何直したかはdiffを見ろ?diffをわかりやすくするためのコミットメッセージなんですう>< * Refactor * 動詞としてよりカテゴリとして使うほうが良さそう * あえて書かないといけないタイミングを見つけていない ### よく使う単語・表現 #### 動詞 * Implement(実装) * Add(追加) * Remove(削除) * Extract(メソッドなどの切り出し) * Integrate(統合・切り出していたメソッドを呼び出し元に展開して元メソッド削除) * Use * 大体なにかの代わりにこれ使うようにしたよ系 * ```Use Symbol instead of String for URL option keys``` #### 形容詞関係 * いらないもの関連 * deprecated(非推奨) * duplicated(重複) * needless(不要・前まで使ってたけど要らなくなったメソッドとか) * unused(実は使ってなかった・needlessと適当に使い分け) * 入れ忘れたもの * missing * よりよいもの * more proper/readable/meaningful #### その他 * instead of * XXXの代わりにYYYにしたよ!とか * trailing space(文末のスペース) * blank line(空行) * Remove needless blank lines #### 言語特有 * Rubyのインスタンスメソッドの頭に```#``` * ```Implement UsersController#index``` * Rubyのクラスメソッドの頭に```.``` * ```Use .find_notes_metadatas instead of deprecated .find_notes``` ## 特殊な場合 ### 途中だけどコミットするよ 1行目は普通に書いて後ろに ```[WIP]``` をつけ、WIPな状況を3行目以降に書く WIPな状況: - ここまでやった - あとこれが残ってる - これからこういうことをしたい(リファクタしたいとか) ``` e.g.) Add the tests for User#summary [WIP] I added the test for User having both last_name and first_name, tests for last_name or first_name only are needed. ``` - コードレビューを受ける場合には、WIPなコミットはあんまり良くなさそう - たぶんレビューしにくい - 後から作業完了したコミットとfixupしたほうがいいかも ## TODO:具体例も書きたい ## 番外:コミットの単位 コミットメッセージが書きにくいということは、きっと2つ以上のことを1つのコミットでしてしまっているんだろう、と思うようにしています。 「2つ以上のことをそのコミットでしていないかどうか」を見つけるためのコミットメッセージの書き方でもありそうです。 1つのコミットでは1つのことをする。1つのことをどう数えるかというと、今のところ「動作を正しい状態で変えるかどうか」もしくは「もしそのコミットでバグが起きた時に戻す場所に困らないかどうか」です。2つを適当に使い分けている感じです。 例えば1つ目で考えると、メソッド名を変えたら呼び出し元も変えてからコミットします(メソッド名の変化で1つの変化だけど、そのままではエラーになるため)。さらにテストも変えます(TODO: この前、間違えて実装だけコミットした) 2つ目はリファクタ関連で使うことが多いかもしれないです。例えば、次の2種類のtypoを見つけたとします。 * position → potision(tとsが逆) * definition → defintion(nの後にiがない) これらtypoを修正するときは、まず片方を直してコミットしてから残りを直してコミットします。 両方をまとめてコミットすると、うっかり間違えたときに困りそうです。たとえば、typoを直すつもりでさらにtypoし(definitonになったとか)そのままコミットしたとします。その結果エラーが起きた場合、どちらかのtypoの修正を間違えたのでしょう。さてどっちだ〜すぐにわからんだろ〜みたいな感じです。1つのファイルぐらいだったらいいですが大量にあったら泣ける気がします。大体にして、バグを潰すときに、2つ調べるより1つ調べるほうが精神的に楽です(状況的な意味で) 何より、1つずつ戻して考えられることは頭への負担が減ります。人間はマルチタスク苦手とかそういうあれです。少なくとも私は苦手です。 2つを1つずつ分けておけば1つのこと(上のtypoなら1単語)に対して注力できますし、そこで間違いがあれば直してコミット、さて次だ!と1つずつ終わらせていくことができます。2つあったら、1つ目はこの単語だから調べて直して、次はこの単語を探して、これでどっちも直ったからコミットしようか、というのは私にはつらそうです。1単語直したからコミットさせてよ!みたいな気分です。 ## おわりに 私とお知り合いの方、私のコミットを見たことある方にツッコまれそうな気もする。目標は大事です。 |
|
| 984位 |
|
|||
|
21:19:16 |
(UX-DESIGN TOKYO 所属) |
|
APIレベル13からActivity.showDialog(int id)は非推奨となってしまいました。 DialogFragmentを使ってダイアログを表示させる方法が推奨されています。 (2013年12月12日現在) 参考 - 公式ドキュメント Dialogs http://developer.android.com/guide/topics/ui/dialogs.html#ShowingADialog - DialogFragmentを利用するときのチェックポイント http://www18.atwiki.jp/bovecrach/pages/74.html ## シンプルなアラートダイアログ  ※ 縦横切替時にTestDialogFragmentのコンストラクタがないと落ちるかもしれません(未確認) ~~~java:HogeActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_top); // ダイアログを表示する DialogFragment newFragment = new TestDialogFragment(); newFragment.show(getFragmentManager(), "test"); } public static class TestDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setMessage("ダイアログ") .setPositiveButton("はい", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // FIRE ZE MISSILES! } }) .setNegativeButton("キャンセル", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // User cancelled the dialog } }); // Create the AlertDialog object and return it return builder.create(); } } ~~~ ## リストを表示するダイアログ iOSのActionSheetの変わりにするならこのダイアログだと思います。  ~~~java:HogeActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_top); // ダイアログを表示する DialogFragment newFragment = new ContactUsDialogFragment(); newFragment.show(getFragmentManager(), "contact_us"); } public static class ContactUsDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { CharSequence[] items = {"使い方", "よくある質問", "メール", "閉じる"}; Activity activity = getActivity(); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { switch (which) { case 0: Toast.makeText(activity, "使い方が押された", Toast.LENGTH_LONG).show(); break; case 1: Toast.makeText(activity, "よくある質問が押された", Toast.LENGTH_LONG).show(); break; case 2: Toast.makeText(activity, "メールが押された", Toast.LENGTH_LONG).show(); break; case 3: Toast.makeText(activity, "閉じる", Toast.LENGTH_LONG).show(); break; default: break; } } }); return builder.create(); } } ~~~ ## カスタムダイアログ  ~~~java:HogeActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_top); // ダイアログを表示する DialogFragment newFragment = new TestDialogFragment(); newFragment.show(getFragmentManager(), "test"); } public static class TestDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); LayoutInflater inflater = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View content = inflater.inflate(R.layout.dialog_setting, null); builder.setView(content); builder.setMessage("設定") .setNegativeButton("閉じる", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // User cancelled the dialog } }); // Create the AlertDialog object and return it return builder.create(); } } ~~~ ~~~java:dialog_setting.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="340dp" android:layout_height="wrap_content" android:orientation="horizontal" android:paddingBottom="20dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="声音量" android:id="@+id/textView" android:layout_gravity="center_vertical" android:layout_weight="1"/> <SeekBar android:layout_width="10dp" android:layout_height="wrap_content" android:id="@+id/seek_bar_voice_volume" android:layout_gravity="center_vertical" android:max="1000" android:layout_weight="4"/> </LinearLayout> <LinearLayout android:layout_width="340dp" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="エコー" android:id="@+id/textView" android:layout_gravity="center_vertical" android:layout_weight="1"/> <SeekBar android:layout_width="10dp" android:layout_height="wrap_content" android:id="@+id/seek_bar_echo" android:layout_gravity="center_vertical" android:max="1000" android:layout_weight="4"/> </LinearLayout> </LinearLayout> ~~~ |
|
| 985位 |
|
|||
|
00:41:53 |
|
|
Mecabで遊んでて、いいのないかなって思ったら見つけたのでメモ。
テキストでもCSVでも何でも良いですが、重複の存在するようなリストで、各要素の出現頻度を数えるようなコードを書きたいことはまれによくあると思います。 辞書を使って素直に実装すると ```python data = ['aaa', 'bbb', 'ccc', 'aaa', 'ddd'] word_and_counts = {} for word in data: if word_and_counts.has_key(word): word_and_counts[word] += 1 else: word_and_counts[word] = 1 for w, c in sorted(word_and_counts.iteritems(), key=lambda x: x[1], reverse=True): print w, c # => # aaa 2 # bbb 1 # ccc 1 # ddd 1 ``` とかそんな感じの雰囲気になると思います。 こういうとき[collectionsモジュール](http://docs.python.jp/2/library/collections.html)が便利なんですよ。 つうわけでcollections.Counterを使って実装しなおします。 ```python from collections import Counter data = ['aaa', 'bbb', 'ccc', 'aaa', 'ddd'] counter = Counter(data) for word, cnt in counter.most_common(): print word, cnt # => # aaa 2 # bbb 1 # ccc 1 # ddd 1 ``` なんか簡潔に実装出来ました。しかも組み込みなので早そうです。 その上、Counterは他にも各種演算子や便利メソッドをそなえています。 ```python from collections import Counter dataA = ['aaa', 'bbb', 'ccc', 'aaa', 'ddd'] dataB = ['aaa', 'bbb', 'bbb', 'bbb', 'abc'] counterA = Counter(dataA) counterB = Counter(dataB) counter = counterA + counterB # 頻度を足し合わせられる counterA.subtract(counterB) # 要素の差をとる(破壊的メソッド) counter.most_common(3) # 上位3要素の取得(上記の例のように、引数nの省略を省略すればすべての要素を降順で取得) # 他にもいくつか ``` ハッシュ可能なオブジェクトであれば良いということなので、他にもなんかいい感じの使い道があるかもですね? 他にも、collectionsモジュールはいい感じに便利なクラスがあったりするので一度目を通しておくとたまに役に立つ気がします。 最終的に、Counterを使って、ダウンロードしてきたツイッターのツイート履歴でMecabってみたコードが以下の感じになります。 ```python # -*- coding: utf-8 -*- from collections import Counter import codecs import json import MeCab # バッドノウハウ感あるけど、出力結果をリダイレクトしたいし import sys reload(sys) sys.setdefaultencoding("utf-8") # codecsはunicodeを返す # 一行目に余計な記述があってだるいしテストコードだし面倒なので事前に消しておこう _tweetfile = codecs.open('./data/js/tweets/2013_09.js', 'r', 'sjis') tweets = json.load(_tweetfile) # Mecabはstr型しか受け付けないのでエンコード texts = (tw['text'].encode('utf-8') for tw in tweets) tagger = MeCab.Tagger('-Ochasen') counter = Counter() for text in texts: nodes = tagger.parseToNode(text) while nodes: if nodes.feature.split(',')[0] == '名詞': word = nodes.surface.decode('utf-8') counter[word] += 1 nodes = nodes.next for word, cnt in counter.most_common(): print word, cnt ``` 名詞かどうかを判別する部分がダサかったり、記号が入り込んだりしますがひとまずいい感じに動きました。めでたしめでたし。 ------ こういう小技っぽいものをまとめてみたのでよろしければどうぞ ([覚えるだけでPythonのコードが少し綺麗になる頻出イディオム](http://hachibeechan.hateblo.jp/entry/Python-idiom-101)) |
|
| 986位 |
|
|||
|
12:06:07 |
(フリーランス 所属) |
|
## やってみた
MySQLの照合順序、UTF-8の日本語で使える照合順序はいくつかあるけど、 実際にどんなマッチの仕方をするのかわからなかったので、ちょっとやってみた。 ちなみに、MySQLのUTF-8で日本語を扱う場合に使用可能な照合順序はしたの3つ。 * utf8_bin * utf8_general_ci * utf8_unicode_ci 確認したかったのは、半角・全角、小文字・大文字がどんな感じでマッチするのかという事。 ## ながーいので、結果を * utf8_bin キャラクターコードが完全に一致するもののみマッチする。 * utf8_general_ci アルファベットの大文字・小文字は区別せずにマッチする。 ただし、区別しない文字は、半角は半角の大文字・小文字、全角は全角の大文字・小文字のみ。 半角小文字と全角小文字同士はマッチしない。 * utf8_unicode_ci 下記がマッチする。 * アルファベットの大文字・小文字(全半角混合) * ひらがなの大文字・小文字とカタカナの大文字・小文字 (例えば、'あ'で検索すると、'ぁ', 'ア', 'ァ'もマッチする) ## テーブル定義 テーブル定義はこんな感じ。 ```sql:ddl.sql drop table if exists `utf8_bin`; create table `utf8_bin` ( `id` int(11) NOT NULL auto_increment, `str` varchar(255), `num` int(11), primary key (`id`) ) engine=InnoDB default character set utf8 collate utf8_bin ; drop table if exists `utf8_general_ci`; create table `utf8_general_ci` ( `id` int(11) NOT NULL auto_increment, `str` varchar(255), `num` int(11), primary key (`id`) ) engine=InnoDB default character set utf8 collate utf8_general_ci ; drop table if exists `utf8_unicode_ci`; create table `utf8_unicode_ci` ( `id` int(11) NOT NULL auto_increment, `str` varchar(255), `num` int(11), primary key (`id`) ) engine=InnoDB default character set utf8 collate utf8_unicode_ci ; ``` ## テストデータ テストデータはこんな感じ。 とりあえず、気になってた文字の比較。 ```sql:dml_testdata.sql insert into `utf8_bin`(`str`, `num`) values ('a', null), ('a', null), ('A', null), ('A', null), ('あ', null), ('ぁ', null), ('ア', null), ('ァ', null), ('高橋', null), ('髙橋', null), ('-', null), ('−', null), ('+', null), ('+', null), ('0', null), ('0', null), ('1', null), ('1', null), (null, 0), (null, 1), (null, 9), (null, 100) ; insert into `utf8_general_ci`(`str`, `num`) select `str`, `num` from `utf8_bin`; insert into `utf8_unicode_ci`(`str`, `num`) select `str`, `num` from `utf8_bin`; ``` ## 確認してみた これ、途中で気づいたんだけど、プロシージャで書けばよかった… 思いつきで書き始めたけど、なんか長くなってしまった。 ```sql:dml_test.sql select '## str = a(半角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = 'a' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = 'a' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = 'a' ; show warnings; select '## str = A(半角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = 'A' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = 'A' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = 'A' ; show warnings; select '## str = a(全角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = 'a' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = 'a' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = 'a' ; show warnings; select '## str = A(全角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = 'A' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = 'A' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = 'A' ; show warnings; select '## str = あ(大文字)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = 'あ' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = 'あ' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = 'あ' ; show warnings; select '## str = ぁ(小文字)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = 'ぁ' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = 'ぁ' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = 'ぁ' ; show warnings; select '## str = ア(大文字)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = 'ア' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = 'ア' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = 'ア' ; show warnings; select '## str = ァ(小文字)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = 'ァ' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = 'ァ' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = 'ァ' ; show warnings; select '## str = 高橋' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = '高橋' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = '高橋' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = '高橋' ; show warnings; select '## str = 髙橋' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = '髙橋' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = '髙橋' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = '髙橋' ; show warnings; select '## str = -(半角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = '-' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = '-' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = '-' ; show warnings; select '## str = −(全角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = '−' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = '−' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = '−' ; show warnings; select '## str = +(半角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = '+' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = '+' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = '+' ; show warnings; select '## str = +(全角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = '+' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = '+' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = '+' ; show warnings; select '## str = 0(半角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = '0' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = '0' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = '0' ; show warnings; select '## str = 0(全角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = '0' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = '0' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = '0' ; show warnings; select '## str = 1(半角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = '1' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = '1' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = '1' ; show warnings; select '## str = 1(全角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` = '1' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` = '1' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` = '1' ; show warnings; select '## str like \'高%\'' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` like '高%' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` like '高%' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` like '高%' ; show warnings; select '## str like \'髙%\'' as '実行内容'; select 'utf8_bin' as 'table', `id`, `str` from `utf8_bin` where `str` like '髙%' union select 'utf8_general_ci' as 'table', `id`, `str` from `utf8_general_ci` where `str` like '髙%' union select 'utf8_unicode_ci' as 'table', `id`, `str` from `utf8_unicode_ci` where `str` like '髙%' ; show warnings; select '## num = 0' as '実行内容'; select 'utf8_bin' as 'table', `id`, `num` from `utf8_bin` where `num` = 0 union select 'utf8_general_ci' as 'table', `id`, `num` from `utf8_general_ci` where `num` = 0 union select 'utf8_unicode_ci' as 'table', `id`, `num` from `utf8_unicode_ci` where `num` = 0 ; show warnings; select '## num = a(半角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `num` from `utf8_bin` where `num` = 'a' union select 'utf8_general_ci' as 'table', `id`, `num` from `utf8_general_ci` where `num` = 'a' union select 'utf8_unicode_ci' as 'table', `id`, `num` from `utf8_unicode_ci` where `num` = 'a' ; show warnings; select '## num = A(半角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `num` from `utf8_bin` where `num` = 'A' union select 'utf8_general_ci' as 'table', `id`, `num` from `utf8_general_ci` where `num` = 'A' union select 'utf8_unicode_ci' as 'table', `id`, `num` from `utf8_unicode_ci` where `num` = 'A' ; show warnings; select '## num = AAA(半角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `num` from `utf8_bin` where `num` = 'AAA' union select 'utf8_general_ci' as 'table', `id`, `num` from `utf8_general_ci` where `num` = 'AAA' union select 'utf8_unicode_ci' as 'table', `id`, `num` from `utf8_unicode_ci` where `num` = 'AAA' ; show warnings; select '## num = a(全角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `num` from `utf8_bin` where `num` = 'a' union select 'utf8_general_ci' as 'table', `id`, `num` from `utf8_general_ci` where `num` = 'a' union select 'utf8_unicode_ci' as 'table', `id`, `num` from `utf8_unicode_ci` where `num` = 'a' ; show warnings; select '## num = A(全角)' as '実行内容'; select 'utf8_bin' as 'table', `id`, `num` from `utf8_bin` where `num` = 'A' union select 'utf8_general_ci' as 'table', `id`, `num` from `utf8_general_ci` where `num` = 'A' union select 'utf8_unicode_ci' as 'table', `id`, `num` from `utf8_unicode_ci` where `num` = 'A' ; show warnings; select '## num like \'1%\'' as '実行内容'; select 'utf8_bin' as 'table', `id`, `num` from `utf8_bin` where `num` like '1%' union select 'utf8_general_ci' as 'table', `id`, `num` from `utf8_general_ci` where `num` like '1%' union select 'utf8_unicode_ci' as 'table', `id`, `num` from `utf8_unicode_ci` where `num` like '1%' ; show warnings; select '## num like \'a%\'' as '実行内容'; select 'utf8_bin' as 'table', `id`, `num` from `utf8_bin` where `num` like 'a%' union select 'utf8_general_ci' as 'table', `id`, `num` from `utf8_general_ci` where `num` like 'a%' union select 'utf8_unicode_ci' as 'table', `id`, `num` from `utf8_unicode_ci` where `num` like 'a%' ; show warnings; ``` ## テスト結果 これもまただらだらいきます(;´∀`) ### str = a(半角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 1 | a | | utf8_general_ci | 1 | a | | utf8_general_ci | 3 | A | | utf8_unicode_ci | 1 | a | | utf8_unicode_ci | 2 | a | | utf8_unicode_ci | 3 | A | | utf8_unicode_ci | 4 | A | * エラー無し ### str = A(半角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 3 | A | | utf8_general_ci | 1 | a | | utf8_general_ci | 3 | A | | utf8_unicode_ci | 1 | a | | utf8_unicode_ci | 2 | a | | utf8_unicode_ci | 3 | A | | utf8_unicode_ci | 4 | A | * エラー無し ### str = a(全角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 2 | a | | utf8_general_ci | 2 | a | | utf8_general_ci | 4 | A | | utf8_unicode_ci | 1 | a | | utf8_unicode_ci | 2 | a | | utf8_unicode_ci | 3 | A | | utf8_unicode_ci | 4 | A | * エラー無し ### str = A(全角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 4 | A | | utf8_general_ci | 2 | a | | utf8_general_ci | 4 | A | | utf8_unicode_ci | 1 | a | | utf8_unicode_ci | 2 | a | | utf8_unicode_ci | 3 | A | | utf8_unicode_ci | 4 | A | * エラー無し ### str = あ(大文字) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 5 | あ | | utf8_general_ci | 5 | あ | | utf8_unicode_ci | 5 | あ | | utf8_unicode_ci | 6 | ぁ | | utf8_unicode_ci | 7 | ア | | utf8_unicode_ci | 8 | ァ | * エラー無し ### str = ぁ(小文字) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 6 | ぁ | | utf8_general_ci | 6 | ぁ | | utf8_unicode_ci | 5 | あ | | utf8_unicode_ci | 6 | ぁ | | utf8_unicode_ci | 7 | ア | | utf8_unicode_ci | 8 | ァ | * エラー無し ### str = ア(大文字) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 7 | ア | | utf8_general_ci | 7 | ア | | utf8_unicode_ci | 5 | あ | | utf8_unicode_ci | 6 | ぁ | | utf8_unicode_ci | 7 | ア | | utf8_unicode_ci | 8 | ァ | * エラー無し ### str = ァ(小文字) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 8 | ァ | | utf8_general_ci | 8 | ァ | | utf8_unicode_ci | 5 | あ | | utf8_unicode_ci | 6 | ぁ | | utf8_unicode_ci | 7 | ア | | utf8_unicode_ci | 8 | ァ | * エラー無し ### str = 高橋 | table | id | str | |:----------------|:---|:-------| | utf8_bin | 9 | 高橋 | | utf8_general_ci | 9 | 高橋 | | utf8_unicode_ci | 9 | 高橋 | * エラー無し ### str = 髙橋 | table | id | str | |:----------------|:---|:-------| | utf8_bin | 10 | 髙橋 | | utf8_general_ci | 10 | 髙橋 | | utf8_unicode_ci | 10 | 髙橋 | * エラー無し ### str = -(半角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 11 | - | | utf8_general_ci | 11 | - | | utf8_unicode_ci | 11 | - | * エラー無し ### str = −(全角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 12 | − | | utf8_general_ci | 12 | − | | utf8_unicode_ci | 12 | − | * エラー無し ### str = +(半角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 13 | + | | utf8_general_ci | 13 | + | | utf8_unicode_ci | 13 | + | | utf8_unicode_ci | 14 | + | * エラー無し ### str = +(全角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 14 | + | | utf8_general_ci | 14 | + | | utf8_unicode_ci | 13 | + | | utf8_unicode_ci | 14 | + | * エラー無し ### str = 0(半角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 15 | 0 | | utf8_general_ci | 15 | 0 | | utf8_unicode_ci | 15 | 0 | | utf8_unicode_ci | 16 | 0 | * エラー無し ### str = 0(全角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 16 | 0 | | utf8_general_ci | 16 | 0 | | utf8_unicode_ci | 15 | 0 | | utf8_unicode_ci | 16 | 0 | * エラー無し ### str = 1(半角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 17 | 1 | | utf8_general_ci | 17 | 1 | | utf8_unicode_ci | 17 | 1 | | utf8_unicode_ci | 18 | 1 | * エラー無し ### str = 1(全角) | table | id | str | |:----------------|:---|:-----| | utf8_bin | 18 | 1 | | utf8_general_ci | 18 | 1 | | utf8_unicode_ci | 17 | 1 | | utf8_unicode_ci | 18 | 1 | * エラー無し ### str like '高%' | table | id | str | |:----------------|:---|:-------| | utf8_bin | 9 | 高橋 | | utf8_general_ci | 9 | 高橋 | | utf8_unicode_ci | 9 | 高橋 | * エラー無し ### str like '髙%' | table | id | str | |:----------------|:---|:-------| | utf8_bin | 10 | 髙橋 | | utf8_general_ci | 10 | 髙橋 | | utf8_unicode_ci | 10 | 髙橋 | * エラー無し ### num = 0 | table | id | num | |:----------------|:---|:-----| | utf8_bin | 19 | 0 | | utf8_general_ci | 19 | 0 | | utf8_unicode_ci | 19 | 0 | * エラー無し ### num = a(半角) | table | id | num | |:----------------|:---|:-----| | utf8_bin | 19 | 0 | | utf8_general_ci | 19 | 0 | | utf8_unicode_ci | 19 | 0 | `show warnings;` | Level | Code | Message | |:--------|-----:|:--------------------------------------| | Warning | 1292 | Truncated incorrect DOUBLE value: 'a' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'a' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'a' | ### num = A(半角) | table | id | num | |:----------------|:---|:-----| | utf8_bin | 19 | 0 | | utf8_general_ci | 19 | 0 | | utf8_unicode_ci | 19 | 0 | `show warnings;` | Level | Code | Message | |:--------|-----:|:--------------------------------------| | Warning | 1292 | Truncated incorrect DOUBLE value: 'A' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'A' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'A' | ### num = AAA(半角) | table | id | num | |:----------------|:---|:-----| | utf8_bin | 19 | 0 | | utf8_general_ci | 19 | 0 | | utf8_unicode_ci | 19 | 0 | `show warnings;` | Level | Code | Message --| |:--------|-----:|:----------------------------------------| | Warning | 1292 | Truncated incorrect DOUBLE value: 'AAA' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'AAA' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'AAA' | ### num = a(全角) | table | id | num | |:----------------|:---|:-----| | utf8_bin | 19 | 0 | | utf8_general_ci | 19 | 0 | | utf8_unicode_ci | 19 | 0 | `show warnings;` | Level | Code | Message --| |:--------|-----:|:----------------------------------------| | Warning | 1292 | Truncated incorrect DOUBLE value: 'a' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'a' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'a' | ### num = A(全角) | table | id | num | |:----------------|:---|:-----| | utf8_bin | 19 | 0 | | utf8_general_ci | 19 | 0 | | utf8_unicode_ci | 19 | 0 | `show warnings;` | Level | Code | Message --| |:--------|-----:|:----------------------------------------| | Warning | 1292 | Truncated incorrect DOUBLE value: 'A' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'A' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'A' | ### num like '1%' | table | id | num | |:----------------|:---|:-----| | utf8_bin | 20 | 1 | | utf8_bin | 22 | 100 | | utf8_general_ci | 20 | 1 | | utf8_general_ci | 22 | 100 | | utf8_unicode_ci | 20 | 1 | | utf8_unicode_ci | 22 | 100 | * エラー無し ### num like 'a%' * マッチせず * エラー無し ## 思ったこと utf8_bin / utf8_general_ciはそんなに違いは無いのね。 大文字・小文字を判別するかしないか、くらいっぽい。 utf8_unicode_ciはヒトが考えるマッチ条件に似ているかな。 全半角をゴチャッと検索できるのはうれしいかも。システム的には厳密じゃないけど。 理解して使い分けて行かないと思わぬバグを生産しそうだけど、 とっても便利だという事がわかりました。 |
|
| 987位 |
|
|||
|
00:26:09 |
|
|
投稿テストも兼ねたメモ
# 配列の連結 一般的に配列の連結はconcatを用いるが、配列に配列を継ぎ足していくような処理だとconcatでは毎回配列を生成しているのがきになり、他の手段を調べてみるとArray.prototype.push.applyというものがあったためメモ ## 実行ファイル ```js:test1.js var a = [0, 1, 2, 3], b = [4, 5, 6, 7], i, count = 10000; console.time(); for (i = 0; i < count; i++) { a = a.concat(b); } console.timeEnd(); ``` ```js:test2.js var a = [0, 1, 2, 3], b = [4, 5, 6, 7], i, count = 10000; console.time(); for (i = 0; i < count; i++) { Array.prototype.push.apply(a, b); } console.timeEnd(); ``` ## 方法 上記のファイルをそれぞれ5回ずつ実行して計測しました。(node v0.10.18) ## 結果 | | 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | |:--|:--:|:--:|:--:|:--:|:--:| | concat | 200ms | 200ms | 201ms | 202ms | 201ms | | Array.prototype.push.apply | 3ms | 3ms | 2ms | 2ms | 3ms | ## 結論 大量に配列を連続して連結する必要がある場合にはとても有効かと思います。 しかし、http://www.kanasansoft.com/weblab/2009/04/javascriptconcat.html のブログにもあるように、 単純に1回だけなどではconcatの方が早かったりするため、上記のような条件以外では検証が必要です。 ### 補足 #### 配列の連結が1回だけの場合 ```js:test3.js var i, count = 100000; console.time(); for (i = 0; i < count; i++) { var a = [0, 1, 2, 3], b = [4, 5, 6, 7]; a = a.concat(b); } console.timeEnd(); ``` ```js:test4.js var i, count = 100000; console.time(); for (i = 0; i < count; i++) { var a = [0, 1, 2, 3], b = [4, 5, 6, 7]; Array.prototype.push.apply(a, b); } console.timeEnd(); ``` ## 結果 | | 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | |:--|:--:|:--:|:--:|:--:|:--:| | concat | 10ms | 9ms | 10ms | 10ms | 9ms | | Array.prototype.push.apply | 15ms | 16ms | 15ms | 15ms | 15ms | |
|
| 988位 |
|
|||
|
15:45:29 |
|
|
この投稿は自分のブログ記事からRedis Sentinel関連の項目だけ抜粋したものです。追記事項があればブログの方に書いていきます。
__「Redisの監視/分析系ツールまとめ」__ http://rest-term.com/archives/3045/ # [Redis Sentinel](http://redis.io/topics/sentinel) Redis本家プロジェクトで開発されている、Redisサーバの死活監視/通知および自動フェイルオーバー機能を提供する管理サーバ(redis-sentinel)です。v2.4.16または2.6.0-rc6以降のバージョンから利用可能になりました。公式ドキュメントを参考にしつつ動作確認をします。 - 環境: CentOS 5.9 (x86_64), Redis 2.6.10 ## 構成 ここでは2つのホストでSlaveを2プロセス、Sentinelを3プロセスの構成で試します。 - Master db0:6379 - Slave db0:6380, db1:6379 - Sentinel db0:26379, db0:23680, db1:26379 ## 設定 ```py:/etc/redis/sentinel.conf # port <sentinel-port> port 26379 # sentinel monitor <master-name> <ip> <redis-port> <quorum> sentinel monitor mymaster db0 6379 2 # sentinel down-after-milliseconds <master-name> <milliseconds> sentinel down-after-milliseconds mymaster 5000 # sentinel failover-timeout <master-name> <milliseconds> sentinel failover-timeout mymaster 900000 # sentinel can-failover <master-name> <yes|no> sentinel can-failover mymaster yes # sentinel parallel-syncs <master-name> <numslaves> sentinel parallel-syncs mymaster 1 ``` 各設定項目について表にしています。Sentinelによるクラスタ監視はいわゆるQuorumベース投票の方式を採ります。複数のSentinelがMasterを監視していて、その内しきい値数以上のSentinelがMasterのダウンを検知したらフェイルオーバー処理を開始します。 | 設定項目 | 概要 | |:---|:---| | monitor | Masterのホストとポートおよび状態が**ODOWN(objectively down)**に移行するための定足数(quorum) | | down-after-milliseconds | Master/Slaveのダウン検知後、状態が**SDOWN(subjectively down)**に移行するまでの時間(ms) | | failover-timeout | フェイルオーバー処理のタイムアウト(ms) | | can-failover | フェイルオーバー処理が実行可能か(yes/no) | | parallel-syncs | SlaveをMasterに昇格させた後、いくつのSlaveと同期させるか | SentinelはMasterとSlave両方の死活監視をしてくれますが、状態がODOWNに遷移するのはMasterのみなので注意してください。can-failover を no にして起動したSentinelプロセスはフェイルオーバー処理自体は行わず(他のSentinelに任せる)、自身はダウン検知と投票処理のみを行います。 ## 起動 Sentinel起動前にレプリケーションが動作していることを確認しておきます。 ```py:redis-cli redis db0:6379> info replication # Replication role:master connected_slaves:1 slave0:db0,6380,online slave1:db1,6379,online ``` 続けて3つのSentinelプロセスを立ち上げます。 ```py:redis-sentinel $ redis-sentinel /etc/redis/sentinel.conf ## または redis-server をsentinelモードで起動する $ redis-server /etc/redis/sentinel.conf --sentinel ``` ログにて各SlaveおよびSentinelと接続したことを確認できます。 ``` [19069] 14 May 16:30:31.909 * +slave slave db1:6379 db1 6379 @ mymaster db0 6379 [19069] 14 May 16:30:31.909 * +slave slave db0:6380 db0 6380 @ mymaster db0 6379 [19069] 14 May 16:30:38.320 * +sentinel sentinel db0:26380 db0 26380 @ mymaster db0 6379 [19069] 14 May 16:30:39.655 * +sentinel sentinel db1:26379 db1 26379 @ mymaster db0 6379 ``` また、Sentinel起動後はINFOコマンドでSentinel関連の情報を確認ができます。 ```py:redis-cli redis db0:26379> info sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 master0:name=mymaster,status=ok,address=db0:6379,slaves=2,sentinels=3 ``` Sentinelプロセス自体の監視は一番下の項目を見るようにすれば良いかと思います。 フェイルオーバー処理の動作確認の為、ここでMasterを落とします。 ```py:redis-cli redis db0:6379> shutdown ``` ログにてフェイルオーバー処理の進捗を確認できます。 ``` [19069] 14 May 16:30:51.170 # +sdown master mymaster db0 6379 [19069] 14 May 16:30:52.386 # +odown master mymaster db0 6379 #quorum 2/2 [19069] 14 May 16:30:52.386 # +failover-triggered master mymaster db0 6379 [19069] 14 May 16:30:52.386 # +failover-state-wait-start master mymaster db0 6379 #starting in 13910 milliseconds [19069] 14 May 16:31:06.379 # +failover-state-select-slave master mymaster db0 6379 [19069] 14 May 16:31:06.480 # +selected-slave slave db1:6379 db1 6379 @ mymaster db0 6379 [19069] 14 May 16:31:06.480 * +failover-state-send-slaveof-noone slave db1:6379 db1 6379 @ mymaster db0 6379 [19069] 14 May 16:31:06.583 * +failover-state-wait-promotion slave db1:6379 db1 6379 @ mymaster db0 6379 [19069] 14 May 16:31:06.901 # +promoted-slave slave db1:6379 db1 6379 @ mymaster db0 6379 [19069] 14 May 16:31:06.902 # +failover-state-reconf-slaves master mymaster db0 6379 [19069] 14 May 16:31:06.991 * +slave-reconf-sent slave db0:6380 db0 6380 @ mymaster db0 6379 [19069] 14 May 16:31:07.294 * +slave-reconf-inprog slave db0:6380 db0 6380 @ mymaster db0 6379 [19069] 14 May 16:31:08.316 * +slave-reconf-done slave db0:6380 db0 6380 @ mymaster db0 6379 [19069] 14 May 16:31:08.417 # +failover-end master mymaster db0 6379 [19069] 14 May 16:31:08.417 # +switch-master mymaster db0 6379 db1 6379 [19069] 14 May 16:31:08.548 * +slave slave db0:6380 db0 6380 @ mymaster db1 6379 [19069] 14 May 16:31:09.068 * +sentinel sentinel db0:26380 db0 26380 @ mymaster db1 6379 [19069] 14 May 16:31:12.258 * +sentinel sentinel db1:26379 db1 26379 @ mymaster db1 6379 ``` SentinelがMasterのダウンを検知すると状態がSDOWNに、quorumパラメータで指定した数のSDOWNが揃うと次はODOWNへと遷移、その後フェイルオーバー処理が開始されます。 最後に新しいMasterとSlaveで正常稼働していることを確認します。 ```py:redis-cli redis db1:6379> info replication # Replication role:master connected_slaves:1 slave0:db0,6380,online ``` 以上、Sentinelによるフェイルオーバー機能の動作確認まで試してみました。 ## Sentinel API SentinelはいくつかのAPIを提供しているので簡単に紹介したいと思います。 ```py:redis-cli ## SENTINEL masters # 監視対象のMasterに関する情報を確認 redis 127.0.0.1:26379> sentinel masters 1) 1) "name" 2) "mymaster" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6379" 7) "runid" 8) "b3b31e6c6d1fbb0cec5d179fd666ce00ea103746" 9) "flags" 10) "master" 11) "pending-commands" 12) "0" 13) "last-ok-ping-reply" 14) "568" 15) "last-ping-reply" 16) "568" 17) "info-refresh" 18) "9745" 19) "num-slaves" 20) "1" 21) "num-other-sentinels" 22) "1" 23) "quorum" 24) "2" ## SENTINEL slaves <master name> # Slaveに関する情報を確認 redis 127.0.0.1:26379> sentinel slaves mymaster 1) 1) "name" 2) "127.0.0.1:6380" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6380" 7) "runid" 8) "17c0f7e7e4d2af0f5f6140ea0ceb0d82d0010aed" 9) "flags" 10) "s_down,slave,disconnected" 11) "pending-commands" 12) "0" 13) "last-ok-ping-reply" 14) "709377" 15) "last-ping-reply" 16) "709377" 17) "s-down-time" 18) "704333" 19) "info-refresh" 20) "712808" 21) "master-link-down-time" 22) "0" 23) "master-link-status" 24) "ok" 25) "master-host" 26) "127.0.0.1" 27) "master-port" 28) "6379" 29) "slave-priority" 30) "100" ## SENTINEL is-master-down-by-addr <ip> <port> # Masterの死活確認 redis 127.0.0.1:26379> sentinel is-master-down-by-addr 127.0.0.1 6379 1) (integer) 0 ## 0:UP, 1:DOWN 2) "397dec99760d5d30043941fe4c1c35ba99e99ebf" ## Sentinel(subjective leader)のrun_id ## SENTINEL get-master-addr-by-name <master name> # MasterのIP, Portを確認 redis 127.0.0.1:26379> sentinel get-master-addr-by-name mymaster 1) "127.0.0.1" 2) "6379" ## SENTINEL reset # Sentinelの現在の状態をリセット(フェイルオーバー処理中であっても) redis 127.0.0.1:26379> sentinel reset mymaster (integer) 1 ``` Sentinel関連の管理ツールを作る際にはこのSentinel APIをサポートしたクライアントライブラリを使うと楽かと思います。 以下、フェイルオーバー処理の挙動に関する部分をピックアップして紹介します。 ## 昇格させるSlaveの選択 当然ですがSlaveとして正常に稼働しているプロセスが対象となります。細かい条件を省いてざっくり言うと、Masterとの接続が安定していて(ダウンタイムが少ない)、SentinelからのPING/INFOコマンドに直近5000ms以内に応答しているSlaveが対象になります(詳細は公式ドキュメントと src/sentinel.c を参照)。 対象となるSlaveが複数ある場合は **slave_priority** の値が小さいSlaveを選択します。 ```py:redis-cli redis db0:6380> info replication # Replication role:slave master_host:127.0.0.1 master_port:6379 master_link_status:up master_last_io_seconds_ago:0 master_sync_in_progress:0 slave_priority:100 ## <- この値 slave_read_only:1 connected_slaves:0 ``` 同じ slave_priority のSlaveが存在する場合は **run_id** が小さいSlaveを選択します。 ```py:redis-cli redis db0:6380> info # Server redis_version:2.6.10 redis_git_sha1:00000000 redis_git_dirty:0 redis_mode:standalone os:Linux 2.6.18-194.26.1.el5 x86_64 arch_bits:64 multiplexing_api:epoll gcc_version:4.1.2 process_id:17468 run_id:17c0f7e7e4d2af0f5f6140ea0ceb0d82d0010aed ## <- この値 tcp_port:6380 uptime_in_seconds:1738 uptime_in_days:0 lru_clock:538965 ``` 2013/06現在は slave_priority の変更を行うAPIは公開されていないようですので、単に run_id が若いSlaveが選択されることになります。Slave選択のアルゴリズムは今後変更されることもあるでしょう。 ## Sentinels/Slavesの自動検出 Sentinelの設定にSlaveのリストを持たせる必要がないのは、SentinelがPub/Subによる情報共有を行っているからです。途中で別のSentinelやSlaveをオンラインで追加しても他のSentinelにその情報は伝わります。 ```py:redis-cli ## Sentinelは "__sentinel__:hello" チャンネルを利用している redis db0:6379> subscribe __sentinel__:hello Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "__sentinel__:hello" 3) (integer) 1 1) "message" 2) "__sentinel__:hello" 3) "127.0.0.1:26380:77887a367e1c76ee9dc15f67a2fb3dc49bb00bf4:1" 1) "message" 2) "__sentinel__:hello" 3) "127.0.0.1:26379:3a92da7269b69fa1dcce5915068c4af6e8caf161:1" 1) "message" 2) "__sentinel__:hello" 3) "127.0.0.1:26380:77887a367e1c76ee9dc15f67a2fb3dc49bb00bf4:1" 1) "message" 2) "__sentinel__:hello" 3) "127.0.0.1:26379:3a92da7269b69fa1dcce5915068c4af6e8caf161:1" ``` メッセージの内容は **host:port:run_id:can-failover** となっていて、\_\_sentinel\_\_:hello チャンネルに5秒毎にpublish、全てのSentinelはこのチャンネルをsubscribeして情報共有しています。 ## レプリケーションの注意点 RedisのレプリケーションはMySQLなどのRDBMSのレプリケーションとは異なる部分が多いです。Redisはレプリケーション開始時に全てのデータをディスクに書き出してからSlaveに転送するのでI/Oやネットワーク帯域には注意してください。ただし、Redis v2.8ではPSYNC(Partial Resynchronization)が実装されるとのことなのでこの問題は解消されると思われます。 引き続きSentinelの検証を続けていきたいと思います。 |
|
| 989位 |
|
|||
|
13:49:10 |
|
|
プログラムは動かさないと始まらない!最初は動かなくて嵌るかもしれない。
でも自分の手を動かして書いたソースが動き始めるともっと書きたくなってくるはず! 手を動かすことに喜びを感じたら、プログラミングの才能があるね! 最初はうまく書けなくても良いんだよ。 とにかくソースを書いて動かしてみようぜ!! # 事前準備 まずは動作環境が必要だ。Windows7しか想定してない。すまん。 ## JavaとScalaのインストール・ダウンロード 以下のバージョンでダウンロードしインストールする。 * Java SE 7u21 * http://www.oracle.com/technetwork/java/javase/downloads/index.html * scala-2.10.1 * http://www.scala-lang.org/downloads ## 環境変数の設定 * JAVA_HOME * JAVA_HOME=C:\Program Files\Java\jdk1.7.0_21 * SCALA_HOME * SCALA_HOME=C:\Program Files\scala 以上! # Hello Worldを出力してみる 新しい言語を始める際にまずやることは"Hello World"の出力。 もちろんここでも基本に忠実に"Hello World"をやるよ。 今回の全てのソースのInputとOutput。 * Input * なし * Output * コンソールに"Hello World"と出力 ## ソース ```scala:HelloWorld.scala object HelloWorld{ def main(args: Array[String]){ if(args.size == 0) { val strs = Array("Hello", "World") output(strs) } else { printf("Invalid args.") } } def output(strs: Array[String]):Unit = { for(str <- strs) { printf("%s ", str) } } } ``` 上記を"HelloWorld.scala"というファイル名で保存だ。 そしてコンソールで`scala HelloWorld.scala`とコマンドを叩こう。 "Hello World"が出力されるよ! ```scala: $ scala HelloWorld.scala Hello World ``` ## ソースの説明 動いたらどうなっているか気になってこないかな? ここから説明するよ!! ### objectキーワード シングルトンオブジェクトの定義。Javaとは違い静的(static)な扱いがない。 Scalaの世界では全てがオブジェクトとなる。 複数のインスタンスを必要としない場合に使用する。 シングルトンオブジェクトとは、インスタンスが1つしか存在しない オブジェクトのことである。 例えば今回のソースのように、実行の起点となるmainメソッドには 複数のインスタンスは必要ない。そのため`object HelloWorld`と 宣言することにより、HelloWorldシングルトンオブジェクトの mainメソッドという形にしている。 #### 代表的なキーワード * case * class * match * return * trait ### mainメソッド objectキーワードで記述したが、実行の起点となるメソッド。 scalaのソースを動かしたい場合に使用する。通常は以下の構文となる。 ```scala: object オブジェクト{ def main(args: Array[String]): Unit = { 処理 } } ``` `: Unit =`は省略可能である。Unitとは戻り値がない場合の宣言。 Javaではvoidにあたる。 ### オブジェクトとクラス、インスタンス、フィールド、メソッド オブジェクト指向の核となる概念。Javaの世界では以下のように考える。 * クラス = 型。class宣言する。 * インスタンス = 型を具現化したモノ。newする。newする毎にメモリが確保される。 * オブジェクト = クラスでありインスタンスでもある。ソース上ではObjectクラス以外、明示的なオブジェクトは現れない。 * フィールド = 状態を表す。インスタンス毎に値が変わることが多い。 * メソッド = 処理。何をするかを記述する。  言葉だけじゃわかりにくいから図を作ってみた。 車で考えてみるよ。 クラスは __車__ だ。車には色んな属性があるけど、今回は __車種__ だけとする。 これがフィールドだ。 では車種を決めてみよ。例えば __バス__、 __スポーツカー__ にしてみるよ。 するとインスタンスができるんだ。 車ができること、例えば __走る__ だ。これがメソッド。 インスタンス化したバス・スポーツカーももちろん __走る__ ことができる。 違いはないかもしれないし、速さに違いがあるかもしれない。 どうわかったかな? ところでオブジェクトはって? オブジェクトは __車__ であり、 __バス__ であり __スポーツカー__ であるんだよ。 ### if文 分岐を表す。条件はtrue, falseで評価する。 ```scala: if(args.size == 0) { val strs = Array("Hello", "World") output(strs) } else { printf("Invalid args.") } ``` 上記では、条件`args.size == 0`を評価し、trueの場合は直後の{}内の処理を実行する。 falseの場合はelse直後の{}内の処理を実施する。 ここで`args.size`は配列argsのサイズである。 ### defキーワード、for式、printf関数 ```scala: def output(strs: Array[String]):Unit = { for(str <- strs) { printf("%s ", str) } } ``` #### defキーワード メソッド宣言。下記の構文が通常の形となる。 ```scala: def メソッド名(引数リスト): メソッドの戻り値の型 = {} ``` #### for式 繰り返しを表す。 このfor式は、文字列配列から1要素ずつ順番に抜き出し、 `printf`の引数としている。`<-`はジェネレータと呼ぶ。 #### printf関数 フォーマットを指定し文字列を出力する。第1引数の`%s `の`%s`を、 第2引数の値で置換して出力する。 ### valキーワード イミュータブル(値を変えることができない)変数を定義する。 ```scala: val strs = Array("Hello", "World") ``` 上記のように変数`strs`に一度値(配列)を設定すると、 `strs`に別の値を入れ直すことはできない。Javaの`final`と同等。 # Scalaらしくリファクタリング HelloWorld.scalaはJavaのソースとほとんど変わらないので、 `output`メソッドをもう少しScalaらしくしてみたから確認してみよう! これでもOutputは変わらないでしょ? ## リファクタリング後のソース ```scala:HelloWorld1.scala object HelloWorld{ def main(args: Array[String]){ if(args.size == 0) { val strs = Array("Hello", "World") output(strs) } else { printf("Invalid args.") } } def output(strs: Array[String]){ strs.foreach(printf("%s ", _)) } } ``` ## 変更点の説明 細かい変更は自分で確認してね!大きな変更点だけ書くよ。 ### foreachキーワード これも繰り返しを表す。引数に関数を渡すことができる。 ```scala: def output(strs: Array[String]){ strs.foreach(printf("%s ", _)) } ``` ここでは、`printf("%s ", _)`という関数を引数として渡している。 プレイスホルダー`_`が配列strsの要素を表す。 配列strsから1要素ずつ取り出すたびにプレイスホルダー`_`に 要素の値を設定し、引数の関数を呼び出す。 # まとめ 基本のHelloWorldだけど、キーワードを何個か入れ込んだから、 盛り沢山だったかな。このソースをいじって色々試してみようぜ! 今回のサンプルソースを通しScalaのソースを実際に動かすことで、 __体で感じてくれたかな?__ |
|
| 990位 |
|
|||
|
16:03:46 |
|
|
# 概要
Linux(CentOS6.5)上でJavaからCの処理を呼び出す方法を調べた際のメモです。簡単な例として以下のようなCの関数をJNI/JNA/SWIGを使ってJavaから使う方法を示します。 ```c:hello.c #include <stdio.h> void hello (){ printf("Hello World!\n"); } ``` 最初に言っておくと、JNA > swig > JNIの順で簡単だと思います。 多分今後はJNAしか使わないと思いますが、複雑なことをして色々留意点等が出てきたりしたら追記・更新する予定です。 # JNI JNIではCのソースに直接手を加える必要があります。 ということで、hello.cを以下のように書き換えます。 ```c:hello.c #include "HelloJNI.h" /* Java_HelloJNI_helloのHelloJNIはあとで作るJavaのクラス名、helloはあとで作るnativeメソッド名です。 */ JNIEXPORT void JNICALL Java_HelloJNI_hello (JNIEnv *env, jobject obj) { printf("Hello World!\n"); } ``` 続いてJavaのソースを書きます。 ```java:HelloJNI.java public class HelloJNI { static { // ライブラリのロード。あとで作るlib***.soの***と一致させます。 System.loadLibrary("hello"); } // nativeメソッドの宣言 public native void hello(); public static void main(String[] args) { HelloJNI hello = new HelloJNI(); hello.hello(); } } ``` javacしてからjavahします。 ```bash:javacしてjavahする javac HelloJNI.java javah HelloJNI ``` javacによりHelloJNI.classが、javahによりHelloJNI.hが生成されます。 次にgccコマンドで共有ライブラリファイル(.soファイル)を作成します。 ```bash:gccで共有ライブラリを作る gcc -fPIC -shared hello.c -I /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.51.x86_64/include -I /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.51.x86_64/include/linux/ -o libhello.so ``` パスは適当に書き換えて下さい。 最後にjavaコマンドを-Djava.library.pathオプションで作成した共有ライブラリファイルが置かれたディレクトリ(今回はカレントディレクトリ)を指定して実行します。 ```java:HelloJNIを実行 java -Djava.library.path=. HelloJNI Hello World! ``` Cを書き換えないといけないので中々大変なのが分かるかと思います。 # JNA JNAではCのソースに手を加える必要がありません。 まず、元のCのソースをコンパイルして共有ライブラリを作成します。 ```bash:gccで共有ライブラリを作成する。 gcc -fPIC -shared -o libhello.so hello.c ``` 次に[公式サイト](https://github.com/twall/jna)から最新版のjna.jarを落としてきます。 今回はjna-4.1.0.jarを使いました。 続いて以下のようなJavaのソースを書きます。 ```java:HelloJNA.java import com.sun.jna.Library; import com.sun.jna.Native; interface HelloLib extends Library { // loadLibraryの第一引数はあとで作成するlib***.soの***と一致させる。 HelloLib INSTANCE = (HelloLib) Native.loadLibrary("hello", HelloLib.class); // Cの関数名と一致させる void hello(); } public class HelloJNA { public static void main(String[] args){ HelloLib hello = HelloLib.INSTANCE; hello.hello(); } } ``` あとはjna.jarにクラスパスを通してコンパイル・実行するだけです。 ```bash:HelloJNAのコンパイルと実行 javac -cp jna-4.1.0.jar HelloJNA.java java -cp .:jna-4.1.0.jar HelloJNA ``` 簡単ですね。 # SWIG [SWIG](http://www.swig.org/)はC/C++の処理をJavaに限らず様々な言語から呼び出せる仕組みです。インストールされていなかったらyum等で入れて下さい。 SWIGでは専用の.iファイルを作成する必要があります。今回作るhello.iファイルの中身は以下の通りです。結構シンプルだと思いますが、文法を覚えないといけないので敷居が高いかもしれません。 ```:hello.i %module hello %{ #include "stdio.h" %} void hello(); ``` 続いてswigコマンドを実行します。 ```bash:swigコマンド実行 swig -java hello.i ``` 実行するとhello.java、helloJNI.java、hello_wrap.cというファイルが出来上がります。hello_wrap.cは長い&可読性が低いので割愛しますが、hello.javaとhelloJNI.javaは以下の様なシンプルなファイルです。 ```java:hello.java public class hello { public static void hello() { helloJNI.hello(); } } ``` ```java:helloJNI.java class helloJNI { public final static native void hello(); } ``` 今回は上記を呼び出すために以下の様なMain.javaファイルを作成します。 ```java:Main.java public class Main { static { System.loadLibrary("hello"); } public static void main(String[] args) { hello.hello(); } } ``` javacコマンドでjavaファイルをコンパイルします。 ```bash:javaファイルのコンパイル javac *.java ``` gccコマンドで共有ライブラリを作成します。 ```bash:Cファイルから共有ライブラリを作成 gcc -fPIC -I /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.51.x86_64/include -I /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.51.x86_64/include/linux/ -shared -o libhello.so hello.c hello_wrap.c ``` 最後に以下のように実行します。 ``` java -Djava.library.path=. Main Hello World! ``` # おまけ `ProcessBuilder`とか`Runtime#getRuntime()#exec`等でもプロセス実行できるので、以下のようなファイルを作っておいて、 ```c:main.c void main (){ hello(); } ``` コンパイル(`gcc -o hello *.c`)してJavaから呼ぶ手もあります。 該当箇所をエラーハンドリングとか抜かして書くと↓な感じです。 ```java:ProcessBuilderで実行 ProcessBuilder pb = new ProcessBuilder("/path/to/hello"); pb.redirectErrorStream(true); Process process = pb.start(); BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); br.lines().forEach(System.out::println); System.out.println(process.waitFor()); br.close(); ``` # 参考にさせて頂いたページ - JNI/JNA - http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html - http://www.atmarkit.co.jp/fjava/special/jna/jna_1.html - http://blue-red.ddo.jp/~ao/wiki/wiki.cgi?page=JNI%A1%A2JNA%A4%CE%BB%C8%A4%A4%CA%FD - SWIG - http://www.swig.org/ - http://d.hatena.ne.jp/gymno/20110728/1311869632 - gcc option - http://gcc.gnu.org/onlinedocs/gcc/Option-Index.html - http://linuxjm.sourceforge.jp/html/GNU_gcc/man1/gcc.1.html |
|
| 991位 |
|
|||
|
01:20:12 |
|
|
#### 2015/2/3 更新
DNSに設定するAレコードが変更になったので、更新しました。 以前の`204.232.175.78`で登録している方はGitHubからメールが届いてると思うので変更しましょう。 --- せっかくドメインを取ったのに何もせず放置してたからそろそろ使ってみようと思って。。 でもサーバー立てる程でもないしと思っていたらGitHub Pagesを見つけました。 リポジトリ単位でしか設定出来ないと思ってたけど、これ自分のアカウント名でPage作れるみたい! サーバー立てる必要もないしせっかくなので使ってみた。 今回作ったページ [mofumofu3n.com](http://mofumofu3n.com) ## 必要なもの * GitHubアカウント * 独自ドメイン(お名前.comで取りました) * 設定いじれるDNSサーバ(お名前.comのやつ使った) ## リポジトリの作成 GitHub上で下記のリポジトリを作成する ``` リポジトリ名:<アカウント名>.github.io ``` これで自分のアカウントでGitHub Pagesが作成できた。 こんな感じでアクセスできる。 [mofumofu3n.github.io](http://mofumofu3n.github.io) ## DNSの設定 今回は`mofumofu3n.com`を使います 1. お名前.comにログインしタブから"ドメイン設定"を開く 2. "DNS関連機能の設定"を開く 3. "DNSレコード設定を利用する"の設定を開き、自分の設定するドメインを選択 4. Aレコードに`192.30.252.153`と`192.30.252.154`を追加する 5. 設定を保存する DNSの設定はこれで終わり ## CNAMEファイルを作成 1. 作成したリポジトリをcloneして、以下のようにCNAMEファイルを作成しリポジトリにプッシュする ``` $ echo mofumofu3n.com > CNAME ドメインは自分のドメインにしてください ``` あとはDNSの設定が完了すれば自動的にリダイレクトされる。 ## おわりに GitHub Pagesを使えば簡単に静的ページが作成できます。 ブログをやりたい場合は[jekyll](http://jekyllrb.com/)を使えば出来るらしいです。 しかもMarkdownで書けるみたいだからエンジニアには嬉しいね! |
|
| 992位 |
|
|||
|
00:24:53 |
|
|
## はじめに gem を使わずに Rails の機能だけで、ファイルのアップロードを試してみます。 バージョン + ruby 2.0.0p247 + Rails 4.0.0 今回は、アップロード可能な形式やファイルサイズの制限はしてません。 ## Model ``` ruby class CreateContents < ActiveRecord::Migration def change create_table :contents do |t| t.string :upload_file_name t.binary :upload_file t.timestamps end end end ``` モデル名は Content としています。 アップロードされたファイルを保存する upload_file をbinary、ファイル名を保存する upload_file_name を string で定義しています。 ## View ``` index.html.erb <%= form_for(@content) do |f| %> <div class="field"> <%= f.file_field :upload_file %> </div> <div class="actions"> <%= f.submit "Upload" %> </div> <% end %> ``` file_field を配置して、アップロードする form を作成する。 ## Controller ``` ruby:contents_controller.rb def create upload_file = content_params[:upload_file] content = {} if upload_file != nil content[:upload_file] = upload_file.read content[:upload_file_name] = upload_file.original_filename end content[:password] = content_params[:password] @content = Content.new(content) respond_to do |format| if @content.save format.html { redirect_to @content, notice: 'Upload success' } format.json { render action: 'show', status: :created, location: @content } else @contents = Content.all format.html { render action: 'index' } format.json { render json: @content.errors, status: :unprocessable_entity } end end end ``` create アクションに処理を書いていきます。 アップロードされたファイルの取得方法については、[railsdoc](http://railsdoc.com/controller) の「アップロードされたファイルを取得」に詳しく載ってます。 content_params は strong_parameter です。 注意しなければいけないのは、アップロードされたファイルを直接DBに保存しようとするとエラーになります。 アップロードされたファイルは、 ActionDispatch::Http::UploadedFile クラスのオブジェクトであ り、以下のようなインスタンスになっています。 ``` ruby #<ActionDispatch::Http::UploadedFile:0x007fe7ad501990 @content_type="text/plain", @headers= "Content-Disposition: form-data; name=\"content[upload_file]\"; filename=\"test.txt\"\r\nContent-Type: text/plain\r\nContent-Length: 15\r\n", @original_filename="test.txt", @tempfile= #<File:/var/folders/bg/3rk9r1y94_126k3231h0hvn40000gn/T/RackMultipart20131129-17450-zqlkd1>> ``` ファイル本体を取得するためには、@tempfile を read する必要があるんですが、UploadedFile クラスの read メソッドを利用することでことで出来ます。 ``` ruby:ActionDispatch::Http::UploadedFile def read(length=nil, buffer=nil) @tempfile.read(length, buffer) end ``` そして、取得したバイナリデータを保存します。 ファイル名は @original_filename から取得しています。 以上で、ファイルのアップロードが出来るようになりました。 ActionDispatch::Http::UploadedFile クラスについては、以下の記事を参考にさせていただきました。ありがとうございました。 [ActionDispatch::Http::UploadedFileを読む (デバックメモ)](http://qiita.com/selmertsx@github/items/2beb0d7ec0774cbbf050) |
|
| 993位 |
|
|||
|
13:38:59 |
(Money Forward, Inc. 所属) |
|

アプリで結構見かける?TableViewの最下部までスクロールしたら次の○件を自動で取得して表示するやつです。 次の○件を読み込むタイミングは一番下までスクロールしたかどうか判定すれば良いです。 これはUITableViewに内包されているUIScrollViewのデリゲートで判定します。 #実装 ```objc:: - (void)scrollViewDidScroll:(UIScrollView *)scrollView { //一番下までスクロールしたかどうか if(self.tableView.contentOffset.y >= (self.tableView.contentSize.height - self.tableView.bounds.size.height)) { //まだ表示するコンテンツが存在するか判定し存在するなら○件分を取得して表示更新する } } ``` #サンプルコード [https://github.com/hachinobu/LoadMoreTableView](https://github.com/hachinobu/LoadMoreTableView "https://github.com/hachinobu/LoadMoreTableView") |
|
| 994位 |
|
|||
|
00:17:14 |
|
|
Static Libraryをアプリケーションで利用するための初期設定は慣れるまで多少面倒ですね。手順だけ暗記しても身に付かないのでできる限りそれがどういう意味を持っているのかも併せて説明していこうと思います。
##はじめに ライブラリはリンクの仕方によって,Static LibraryとDynamic Libraryに分類されます。ちなみにOS X向けには両方サポートされていて,iOSに対してはStatic Libraryのみサポートされているようです。 Static Libraryはアプリケーションのコンパイル時に組み込まれる形で(静的に)リンクするため,その分アプリケーション自体のサイズが大きくなります。一方でDynamic Libraryはアプリケーションの実行時にローダがライブラリを検索し,(動的に)リンクします。 以下の手順も基本的にStatic Library(lib[name].aファイル)と,そのヘッダファイルをアプリケーションバンドル内に置くための操作となります。 ##Static LibraryのProjectの設定 Xcodeの左側のファイルリストの最上段にあるProjectボタンをクリックすると,右隣にプロジェクトエディタが開きます。エディタ左のProjectリストの下にあるTargetリストの中から今回利用したい(パルテノン神殿みたいなアイコンのついたやつ)Static Libraryを選択します。それからエディタ上段のタブから『Build Phases』を選択します。すると以下の画面が開きます。  すでに丸で囲っていますが,ここの"Compile Sources","Copy Files"の2つの項目を設定していきます。もしどちらかが存在していなければ,エディタ右下の『Add Build Phase』ボタンを押して追加できます。 * "Compile Sources"の項目に使用したい実装ファイル(.m)を追加する。 * "Copy Files"の項目のDestinationポップアップボタンから"Procducts Directory"を選 択。"Subpath"の値は`include/${PRODUCT_NAME}`とします。使用したいクラスの宣言ファイル(.h)を追加。これによりアプリケーションバンドル内のincludeディレクトリ内にStatic Libraryのヘッダファイルを置いてくれます。 次に,エディタ上段のタブの中から"Build Settings"を選択します。そして多数の項目の中から以下の2つを探します。 * "Installation Directory"欄の値を`${BUILT_PRODUCTS_DIR}` に変更。 * "Public Headers Folder Path"欄の値を`${TARGET_NAME}` に変更します。 以上でStatic Library側の設定は完了です。つぎにそれを使う側の設定に移ります。 ##Static Libraryを利用するアプリケーション側のProjectの設定 まず,アプリケーション側にStatic Library Projectを取り込みたいと思います。Xcodeのメインメニューから"File"->"Add Files to \<project name\>..."から目的のStatic LibraryのXcode projectファイルを選択します。  上の画像のような設定で右下の『Add』ボタンをクリックします。これでProjectのファイルリストにStatic Library Projectが追加されました。今度はそれを使う側,つまりアプリケーションのプロジェクトエディタを開きます。エディタ上段のタブから"Build Phases"を選択。"Link Binary With Libraries"の項目にさきほどのStatic Libraryを追加します。 アプリケーションプロジェクトの"Build Settings"を開き,下図のように"Other Linker Flags"の値に-ObjCを加えます。  この引数はLinkerにStatic Libraryからすべてのクラスとカテゴリをリンクすることを指示します。Static Libraryは基本的に,コンパイル時にライブラリ検索する際にローダが「未定義」と認識しているシンボルだけをスタティックリンクする仕様なので,Objective-Cのような動的言語ではコンパイル時にすべてがわかるわけではないので,あらかじめ引数を指定してすべて読み込んでしまうようです。 最後に,Xcode上段左の"Scheme"ポップアップボタンを押して"Edit Scheme…"をクリックすると,下のようなシートが降りてきます。そのシート左側のリストからBuildボタンをクリックします。するとちょうど下図のような画面が表示されます。  この真ん中のテーブルの左下の+ボタンをクリックしてStatic Libraryを選ぶと,以下の画像のようにStatic Libraryが追加されます。  こうすることでアプリケーションをビルド,クリーンする際,Static Libraryも同時にビルド,クリーンしてくれるので便利です。 ##インポートヘッダの書き方 通常は\#import<フレームワーク名/ヘッダファイル名>となるわけですが,自作ライブラリの場合,ライブラリ側のプロジェクトで設定した"Copy Files"の"Subpath"項目に依存します。今回,`include/${PRODUCT_NAME}`と設定しました。ここで"アプリケーションバンドル/include"は標準でヘッダ探索パスに設定されています。つまり,この変数`${PRODUCT_NAME}`の値を"Build Settings"の"product name"欄から確認し,`#import "ProductName/HeaderName.h"`と記述すればよいわけです。ちなみにこのように本来,括弧はダブルクォーテーションでよいのですが,Xcode4.6ではなぜか山括弧<>でないとコード補完がききません。 以上できちんとアプリケーション側でStatic Libraryを利用できるようになりました。ちょっと長かったですね,お疲れさまでした。 ##補足 ###Static Library側で定義したカテゴリが使えない場合。 稀にあるそうです。"Other Linker Flags"欄に-all_loadを追加すると改善するそうです。  ###リンカエラーが出る。 いわゆるわかりにくい類のやつです↓  Static Library側で必要となるフレームワーク群はすべてアプリケーション側の"Link Binary With Libraries"に含まれている必要があります。 |
|
| 995位 |
|
|||
|
19:52:47 |
|
|
# GRUNT入門
## なぜなにGrunt ### Gruntとは * 自動化ツールです ### Gruntのメリット * htmlバリデートして、scssビルドして、jslintで監視しながらjs書いて、js結合して、jsミニファイして…などを毎回手動でやらなくてよくなります * ```peter$ grunt build``` で全部gruntちゃんがやってくれます ## 事前準備 ### node.js * version >= 0.8.0 (grunt 0.4.x+) * node.js バージョン管理ツール * Windows * nvmw * Mac * nodebrew ### Grunt * version >= 0.4.x ``` peter$ npm install -g grunt-cli ``` ## Grunt ### 新規プロジェクトにGruntを導入する #### 必要なファイル ##### package.json * npmのメタデータの設定ファイル * これから必要なモジュールを自動で設定できたりする(要grunt-init) ##### Gruntfile.js * 自動化したいコマンド群の設定ファイル #### 導入手順 ##### package.jsonをつくる 1. プロジェクトフォルダのルートに移動 1. ターミナル ``` npm init ``` 1. ターミナル上で対話式にpackage.jsonを設定する ``` { "name": "sample", "version": "0.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": "", "author": "", "license": "BSD" } ``` 上記のようなpackage.jsonが出力される ``` { "name": "sample", "version": "0.0.0" } ``` でもとくに必要ないので消しました ##### gruntモジュールを追加 ``` npm install grunt --save-dev ``` * このコマンドは先ほどのnpm installと違い-g(グローバル)オプションがありません * ですので、このモジュールはカレントフォルダにnode_modulesというフォルダが作られ、そこにインストールされます。 * また--save-devオプションはモジュール追加と同時にpackage.jsonにモジュール名を追加してくれる便利なオプションです ``` { "name": "sample", "version": "0.0.0", "devDependencies": { "grunt": "~0.4.1" } } ``` コマンド実行後のpackage.json もし--save-devオプションを使ってもモジュール名が追加されない場合は package.jsonのパーミッションが悪さをしてる場合があります ##### Gruntfile.jsをつくる まずgruntモジュールを作成します ``` module.exports = function (grunt) { … } ``` 読み込むモジュールは ```npm install grunt-contrib-requirejs --save-dev``` で取得したものです ``` grunt.loadNpmTasks('grunt-contrib-compass'); grunt.loadNpmTasks('grunt-contrib-requirejs'); ``` initConfigで各タスクを設定します ``` grunt.initConfig({ pkg:grunt.file.readJSON('package.json'), //compassでscssをビルド compass: { dist: { options: { sassDir:'src/scss', cssDir:'src/css' } } }, //requirejsを使ってjsを出力する requirejs: { compile: { options: { baseUrl: "src/js/", name:"app", mainConfigFile: "src/js/app.js", out: "dist/js/app.js" } } } }); ``` タスクを登録します * defaultの場合は``` peter$ grunt ```で実行できます * タスク名がhogeの場合は``` peter$ grunt hoge ```で実行します ``` grunt.registerTask('default', ['compass','requirejs']); ``` ##### 完成 上記をまとめるとこんな感じになります。 ``` module.exports = function (grunt) { grunt.loadNpmTasks('grunt-contrib-compass'); grunt.loadNpmTasks('grunt-contrib-requirejs'); grunt.initConfig({ pkg:grunt.file.readJSON('package.json'), compass: { dist: { options: { sassDir:'src/scss', cssDir:'src/css' } } }, requirejs: { compile: { options: { baseUrl: "src/js/", name:"app", mainConfigFile: "src/js/app.js", out: "dist/js/app.js" } } } }); grunt.registerTask('default', ['compass','requirejs']); }; ``` これが実行されると 1. src/scssにあるscssをcomapssがビルドしてsrc/cssに出力されます 2. 次にプロジェクトフォルダのsrc/js/app.jsに記載してある設定に基づき、jsファイルが結合されてdist/js/app.jsに出力されます ### 既存プロジェクトへ導入 したい方は[こちらへ](http://gruntjs.com/getting-started#working-with-an-existing-grunt-project) ### まとめ 今回紹介したのはcompassとrequirejsだけですが、 gruntのモジュールはgithubを探せばたくさんありますので、 自動化できるところは自動化して作業効率化していきましょう。 ### おまけ * grunt * 今回紹介したやつ * [bower](http://bower.io/) * jqueryなどのパッケージマネジャー * [yeoman](http://yeoman.io/) * プロジェクトテンプレートジェネレーター をつかうとさらに便利らしい |
|
| 996位 |
|
|||
|
17:05:12 |
(Increments Inc. 所属) |
|
偶然pythonを使って簡単なweb applicationを作ることになったので使ってみた。
求めるものとしては - 導入が簡単 - routingなどの最低限の機能 - template があればよかった。ちょうど探してみるとBottleというのが良さげだったのでメモ代わりに。 とりあえずpython自体も初めて使うのでpythonのインストールから python2系か3系か、どっちを使えばいいのかすら分からなかったのでとりあえず複数の環境を持てるpyenvを使うことに。 pyenvのinstall ``` Bash cd ~ git clone git://github.com/yyuu/pyenv.git .pyenv echo 'export PYENV_ROOT="${HOME}/.pyenv"' >> ~/.zshrc echo 'eval "$(plenv init -)"' >> ~/.zshrc exec $SHELL -l ``` とりあえず2.7系を入れる pip(gemみたいな?)も一緒に入ってくれるので便利 ``` Bash pyenv install 2.7.5 ``` そしたらreadlineがないって言われたのでhomebrewでinstall ``` Bash brew install readline brew link readline ``` common problemらしく、[ここ](https://github.com/yyuu/pyenv/wiki/Common-build-problems)に他にもいろいろ書いてある とりあえず入ったのでBottleを使う ``` Bash pip install bottle ``` で入る。 bottleは`bottle.py`という1ファイルがすべて。そこから必要なものをimportしながら使っていくらしい。 とりあえずhello world(sampleそのまま) ``` python:hello.py from bottle import route, run @route('/hello/:name') def hello(name): return '<h1>Hello %s!</h1>' % name run(host='localhost', port=8080) ``` これでコマンドライン上で ``` Bash python hello.py ``` これで [locahost:8080/hello/tak0303](locahost:8080/hello/tak0303) にアクセスすると、 `Hello tak0303!` となる。簡単。 htmlでtemplateを使いたいときは同じディレクトリに`views`というフォルダを作って、そこに入れる。 ```html:show.tpl(拡張子はなんでもいいらしい) <h1>{{name}}</h1> ``` ``` python:hello.py from bottle import route, run, template @route('/hello/<name>') def hello(name='unknown'): return template('show', name=name) run(host='localhost', port=8080) ``` こんな感じでできる あと、最初の状態だと、ファイルの変更時にサーバを再起動しなければならず、また、デバッグしづらく、かなり不便なので ``` Python: run(host='localhost', port=8080, debug=True, reloader=True) ``` こうすることでブラウザ上にエラーが表示されるのと、ファイルの変更時に自動的にサーバを再起動してくれる。 参考: [http://bottlepy.org/docs/dev/index.html](http://bottlepy.org/docs/dev/index.html) |
|
| 997位 |
|
|||
|
15:49:11 |
|
|
以下はhttp://docs.angularjs.org/guide/directive のドキュメントをだいたい和訳した感じの内容になってます。
## 概要 DOMをいじるコードを綺麗にみせるための仕組み。 ## directiveの処理の流れ http://docs.angularjs.org/guide/directive の Compilation process, and directive matchingの項より 1. parse: HTML文字列をDOMに変換する。 2. compile: 1で変換したDOMを探索してdirectiveが必要な要素を見つけたらdirectiveのcompileを実行する。もし複数のdirectiveが必要な場合優先順位付けが行われる。 3. link: scopeとtemplateを結びつける。ここで要素に対するリスナが登録されたりscopeのwatchesがセットされたりする。 ```javascript:sample.js var $compile = '...'; // injected into your code var scope = '...'; var html = '<div ng-bind="exp"></div>'; // Step 1: parse HTML into DOM element var template = angular.element(html); // Step 2: compile the template var linkFn = $compile(template); // Step 3: link the compiled template with the scope. linkFn(scope); ``` ## directiveを書く directiveはオブジェクトを返すように定義する。 それぞれの値の意味は下で説明する。 ```javascript:sample.js var myModule = angular.module('...'); myModule.directive('directiveName', function factory(injectables) { var directiveDefinitionObject = { priority: 0, template: '<div></div>', templateUrl: 'directive.html', replace: false, transclude: false, restrict: 'A', scope: false, compile: function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { ... }, post: function postLink(scope, iElement, iAttrs, controller) { ... } } }, link: function postLink(scope, iElement, iAttrs) { ... } }; return directiveDefinitionObject; }); ``` ## directiveの省略形 directive は 最低限 link function に当たるものを返せばよい。 ```javascript:sample.js var myModule = angular.module('...'); myModule.directive('directiveName', function factory(injectables) { return function(scope, element, attars) { } } ``` ## directiveで指定する値の意味 * priority: 同じDOM要素で複数のdirectiveが定義されている場合に用いる。値が大きいほうから順番にdirectiveが実行される。 * terminal: priorityとともに用いる。terminalをtrueにしたdirectiveよりpriorityが小さいdirectiveは実行されない。 * scope: true, false, {} の3つの値が設定でき、{} の中には更に @ or @attr, = or =attr, & or &attr の4つの値が設定できる。 * true: 現在のdirectiveのための新しいscopeが作られる。もし同じ要素で複数のdirectiveが新しいscopeを作ろうとした場合1つしか作られない。 * {}: 親scopeを継承しないscopeを生成する。 * @ or @attr: ローカル(このdirectiveの)scopeの値をDOMに追加する。 * = or =attr: 指定した値を親scopeにコピーする。どちらかの値が変更された場合はもう片方も変更される。 * & or &attr: 指定した値を親scopeのcontextで実行する。 * controller: コントローラのコンストラクタ関数を定義する。 ここで指定した関数によりpre-linkの前のタイミングでコントローラが生成される。生成されたコントローラは他のdirectiveと共有できる(requireで指定する) * require: 現在のdirectiveのlinkに渡された他のコントローラをその名前で指定できる。もし存在しない場合エラーが発生する。先頭に?をつけることでoptionalにできる(存在しなくてもエラーが発生しない)。また、先頭に^をつけることで親要素もコントローラの検索範囲になる 。 * restrict: directiveの指定方法を定義する。EACMの4つの値がある。1つを指定したら他の方法ではdirectiveは実行されない。複数指定するときはつなげて書く("EAC"や"EM"など) * E: \<my-directive></my-directive> * A: \<div my-directive="exp"> </div> * C: \<div class="my-directive: exp;"></div> * M: <!-- directive: my-directive exp --> * template: テンプレート * templateUrl: templateをURLで指定できる。URLの内容が読み込まれるまでcompileやlinkの処理は始まらない。 * replace: trueにするとtemplateで指定した値で現在の要素が置換される(追加でなく) * transclude: [ngTransclude](http://docs.angularjs.org/api/ng.directive:ngTransclude)とセットで使われる。 * compile: 上で説明したdirectiveの処理の流れの2で実行される関数 * link: 同3で実行される関数 ## 参考資料 [Egghead](http://egghead.io/): directiveだけでなく他のAngularJSの機能についても解説した豊富なビデオがあります。 |
|
| 998位 |
|
|||
|
01:30:34 |
(Picos LLC. 所属) |
|
NSUserDefaultsでなく/tmpや/Docmentsに保存したりファイル操作をしたい場合に役立ちそうなメソッドです。 これを元にいろいろなカスタマイズも出来るかと思います。 ```objectivec:FileHelper.h /* tmp */ - (NSString*)temporaryDirectory; /* /tmp/fileName */ - (NSString*)temporaryDirectoryWithFileName:(NSString*)fileName; /* /Documents */ - (NSString*)documentDirectory; /* /Documents/fileName */ - (NSString*)documentDirectoryWithFileName:(NSString*)fileName; /* pathのファイルが存在しているか */ - (BOOL)fileExistsAtPath:(NSString*)path; /* pathのファイルがelapsedTimeを超えているか */ - (BOOL)isElapsedFileModificationDateWithPath:(NSString*)path elapsedTimeInterval:(NSTimeInterval)elapsedTime; /* directoryPath内のextension(拡張子)と一致する全てのファイル名 */ - (NSArray*)fileNamesAtDirectoryPath:(NSString*)directoryPath extension:(NSString*)extension; /* pathのファイルを削除 */ - (BOOL)removeFilePath:(NSString*)path; ``` ```objectivec:FileHelper.m - (NSString*)temporaryDirectory { return NSTemporaryDirectory(); } - (NSString*)temporaryDirectoryWithFileName:(NSString*)fileName { return [[self temporaryDirectory] stringByAppendingPathComponent:fileName]; } - (NSString*)documentDirectory { NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES); return [paths objectAtIndex:0]; } - (NSString*)documentDirectoryWithFileName:(NSString*)fileName { return [[self documentDirectory] stringByAppendingPathComponent:fileName]; } - (BOOL)fileExistsAtPath:(NSString*)path { NSFileManager* fileManager = [[NSFileManager alloc] init]; /* ファイルが存在するか */ if ([fileManager fileExistsAtPath:path]) { return YES; } else { return NO; } } - (BOOL)isElapsedFileModificationDateWithPath:(NSString*)path elapsedTimeInterval:(NSTimeInterval)elapsedTime { if ([self fileExistsAtPath:path]) { NSError *error = nil; NSDictionary* dicFileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error]; if (error) { return NO; } /* 現在時間とファイルの最終更新日時との差を取得 */ NSTimeInterval diff = [[NSDate dateWithTimeIntervalSinceNow:0.0] timeIntervalSinceDate:dicFileAttributes.fileModificationDate]; if(elapsedTime < diff){ /* ファイルの最終更新日時からelapseTime以上経っている */ return YES; } else { return NO; } } return NO; } - (NSArray*)fileNamesAtDirectoryPath:(NSString*)directoryPath extension:(NSString*)extension { NSFileManager *fileManager=[[NSFileManager alloc] init]; NSError *error = nil; /* 全てのファイル名 */ NSArray *allFileName = [fileManager contentsOfDirectoryAtPath:directoryPath error:&error]; if (error) return nil; NSMutableArray *hitFileNames = [[NSMutableArray alloc] init]; for (NSString *fileName in allFileName) { /* 拡張子が一致するか */ if ([[fileName pathExtension] isEqualToString:extension]) { [hitFileNames addObject:fileName]; } } return hitFileNames; } - (BOOL)removeFilePath:(NSString*)path { NSFileManager *fileManager = [[NSFileManager alloc] init]; return [fileManager removeItemAtPath:path error:NULL]; } ``` ```objectivec:使用例 /* /tmp内のpngファイルで、最終更新日から1時間以上経過したファイルを全て削除する */ /* /tmp内の全てのpngファイル名を取得 */ NSArray *imgFileNames = [self fileNamesAtDirectoryPath:[self temporaryDirectory] extension:@"png"]; for (NSString *fileName in imgFileNames) { NSString *filePath = [self temporaryDirectoryWithFileName:fileName]; /* 最終更新日から1時間経過しているか */ if ([self isElapsedFileModificationDateWithPath:filePath elapsedTimeInterval:60.0 * 60.0]) { /* 削除 */ [self removeFilePath:filePath]; } } ``` |
|
| 999位 |
|
|||
|
02:35:21 |
(Wantedly, Inc. 所属) |
|
こんにちは、[awakia](http://qiita.com/users/awakia)です。今回のアドベントカレンダー、結構、機械学習ガチなメンツが揃ったみたいなので、俺も対抗してやる!!とも思ったのですが、研究を離れて2年が経とうとしているので、真っ向勝負とか今更無理なことに気づきました...w なので、開発者の皆も知っておくと便利なデータサイエンスの話をすることにします。 ## ABテストと検定の必要性 [Webサービスを運営](https://www.wantedly.com/)していると、見た目の問題だけでも結構悩みます。ボタンの色や文言などの小さなところから、トップページに盛り込む内容をどうするかまで、いろいろです。 今回、「ABテスト」と呼ぶものは、画面に占める大きさ等にかかわらず、パターンAとパターンBを作って、そのどちらがいいかを判断するための実験と定義することにします。 なお、ABテストの呼び名には結構流派があるので別の名前で聞いたことがあるかもしれません。例えば、Googleのマット・カッツ先生は、Googleが行なっているという少数のユーザーグループに対するテストを「バケットテスト」「クッキーテスト」と呼んでいます。(http://www.suzukikenichi.com/blog/are-google-serps-the-same-on-all-browsers/) それでは、ABテストを行う際の具体例として、ユーザー登録のボタンの色を赤/青どっちにした方がいいのか迷っているという状況を考えてみます。これを判断するために、半分のユーザーには赤を、もう半分のユーザーには青を見せるという実験をしてみた結果、 > 赤:120人 vs 青:100人 というデータが得られたとしましょう。 この瞬間、あ、「赤」の方がいいのね。「赤」に決定! データ主義バンザイ!! となったあなたは幸せです。もう赤いボタンにするといいと思います。 実際、赤いボタンの方がいい可能性は、統計的にどう解釈しても高いです。 でも、もし - 自分もしくはチームの誰かが統計的に意味のあるデータしか信じない - 実は自分は青の方が好きで、出来れば赤にしたくない といった状況に置かれてしまった場合、もう少し粘って考えてみましょう。 ちなみに裏話として、こういったデータ主義は、とてもデザイナー泣かせです。一生懸命、全体のバランスを考えてサイトデザインをし、色も選んでいるのに、「データが他の色がいいことを示している( ー`дー´)キリッ」などと言われて、センスの無いエンジニアに色を変えられたのでは、プライドもズタボロでしょう。 でも、実際、この場合エンジニアの方が正しくない?と言う意見もありですが、例えば、楽天市場のホームページってなんかごちゃごちゃ(ごめんなさい、ごめんなさい、悪気は無いんです)していますが、実は、あれ、すっきりしたデザインに変えてみたら急激にクリック率、購入率が下がったので、ごちゃごちゃしたのに戻したという、まことしやかな都市伝説が流れています。 ## 統計的に検証してみる さて、話を戻しましょう。 先ほど得られた、赤が120人、青が100人という結果を用いて、赤いボタンの方が良いと結論付けるためには、この結果が「たまたま起こったわけじゃない!」と言い切る必要があります。 これを言い切れるようにしてくれる方法が、統計学の(有意性)検定と呼ばれもので、これが分かれば「結果に有意差がある/はない!」などと言うことが出来るようになります。 じゃぁ、実際に検定をやってみようと思っても、統計検定のWikipediaカテゴリには、網羅率の低い[Wikipedia日本語版](http://ja.wikipedia.org/wiki/Category:%E7%B5%B1%E8%A8%88%E6%A4%9C%E5%AE%9A)でも28項目(すべてが統計検定の手法なわけではないですが)、[Wikipedia英語版](http://en.wikipedia.org/wiki/Category:Statistical_tests)では約100項目と、正直どれ使えばいいんだ!!状態になっています。 有名なものをとってみてもT,Z,F,U,G検定という英語一文字のものや、χ2検定などというギリシャ文字を使ったものまであり、もう世の中の人を惑わすために名付けたとしか思えない惨状を目のあたりにするはずです。 でも大丈夫! ABテストの結果の検証するためには、**`符号検定`**だけ知っていればOKです。 符号検定は、http://aoki2.si.gunma-u.ac.jp/lecture/Average/sign-test.html に > 対応のある 2変数の組について,母代表値に差があるか検定する。 > 2変数の組で単に,いずれが優れているか劣っているかあるいは同等であるかしかわからないときに適用する と書かれているように、ABテストにピッタリで、パターンAの方が良かったら+、悪かったら-という符号を付けてカウントし、それぞれの個数の差に意味があるかを検証する検定法なんだなと考えれば、"符号"検定というのも思い出しやすいのではないかと思います。 ### 蛇足:検定の一般論 一般に検定を行うためには、以下の2つを定める必要があります。 1. 検証したい仮説がどんな分布に従うか 2. 確率何%をあり得ないかとみなすか 1.については、例えば、PCのハードディスクが壊れてしまう現象は[ポアソン分布](http://ja.wikipedia.org/wiki/%E3%83%9D%E3%82%A2%E3%82%BD%E3%83%B3%E5%88%86%E5%B8%83)に従うと言われています。 今回のABテストに関しては、パターンAとパターンBに違いが無いとみなした場合、コインを投げて表と裏が出る回数を記録していった時の分布と同じとみなせるので、それは(成功確率`p=0.5`の)[二項分布](http://ja.wikipedia.org/wiki/%E4%BA%8C%E9%A0%85%E5%88%86%E5%B8%83)と呼ばれる分布です。 2.は有意水準と呼ばれ、慣習として`5%`か`1%`が使われます。あくまで慣習で、科学的根拠はありません。更にABテストでは、間違った結論を出しても大して困らないので、`1%`を使うことはあまりなく、`5%`の方が使われることが多い気がしています。 なので、「検定結果に有意差があります」と言われたら、「パターンAとパターンBは同等だとみなしたら、5%以下の確率でしか起きないことが起きてしまってるんだよね。これってもうパターンAとパターンBに明確な違いがあるって言ったほうが自然じゃない?」と言われたと思えばいいことになります。 ## 符号検定をしてみる さて、 > 赤:120人 vs 青:100人 という結果で、赤ボタンの方が本当に良いと言えるか検定してみましょう。それには[R言語](http://www.r-project.org/)の`pbinom`を使って、以下のように計算します。 ```r:R > pbinom(120-1, 120+100, 0.5) [1] 0.9 ``` この`0.9`と言う数字は、青ボタンでも赤ボタンでもユーザー登録率は同じだとした時に、赤ボタンを押される回数が220回中、120未満(0-119回)でありえる確率を表しています。 この時、有意水準を5%としても、`1 - 0.05 = 0.95`に達していないので、有意に赤ボタンの方が良いとは言えないということになります。残念でした。 ちなみに、220人中何人が赤ボタンをクリックしていたら有意と言えたかというのは、`qbinom`を使うとわかって ```r:R > qbinom(0.95, 120+100, 0.5) [1] 122 ``` `122+1`の123人であれば有意であったということになります。 このRの結果は、220回中、赤ボタンが0回押される確率から122回押される確率まで全て足し合わせると全体の95%に到達するということを表します。なので赤ボタンが123回以上押される確率は、5%に満たない(=ありえない)事が言えます。ありえないことが起こったら、赤と青の効果が等しいという前提が間違ってたということなので、123回であれば有意だったんですね。 実際に、足し合わせる関数である`pbinom`を用いて確かめると、 ```r:R > pbinom(121, 120+100, 0.5) [1] 0.9396072 > pbinom(122, 120+100, 0.5) [1] 0.9541673 ``` なので、`122`ではじめて0.95を超えてますね。 有意水準1%だとどうなるかというと、 ```r:R > qbinom(0.99, 120+100, 0.5) [1] 127 ``` 128人必要です。 更に、この二項分布がどういう形をしているのか見てみるには、`plot`と`dbinom`を使って ```r:R > plot(dbinom(0:220, 220, 0.5), type="h", xlim=c(70,150)) ``` と入力すれば、  と表示され、120のあたりがどのくらいありえそうか可視化することができます。 Rに関して話をすれば、検定を直接行なってくれる関数`binom.test`も存在して、以下のように使うことができます。 ```r:R > binom.test(120, 220, 0.5, "greater") Exact binomial test data: 120 and 220 number of successes = 120, number of trials = 220, p-value = 0.1 alternative hypothesis: true probability of success is greater than 0.5 95 percent confidence interval: 0.4878 1.0000 sample estimates: probability of success 0.5455 ``` `p-value = 0.1`と言う部分が、先ほどの`0.9`と言う値を、全体の`1`から引いた値に対応していますね。 ## まとめ ABテストでの有意性検定には**符号検定** (半々で実験しない場合は、より一般に**二項検定**と呼ぶ)を用いるとよい。 具体的には、以下の(クリック、ユーザー登録等の)アクションがあったとき、 ||アクション数(回)|実験対象の割合| |:-:|:-:|:-:| | **パターンA**| a | p (0.5, 0.01等) | **パターンB**| b | 1-p 二項分布を用い、以下のように検定する。 ```r:R significance = 1 - pbinom(a-1, a+b, p) >= 0.95 ``` 注) `a+b`が大きすぎる場合、教科書的には、二項分布だとnCkの計算が必要なため計算できなくなる&正規分布に高精度で近似できるので、正規分布を用い検定するとあるが、Rを用いると、別に二項分布でもかなりの大きい数に対して計算が可能なので常に二項分布を使っておけば良い。 ```r:R > pbinom(110000020, 220000000, 0.5) [1] 0.5011028 ``` ## 最後に ABテストはとても実用的で強力なツールです。でも、ABテストに頼りすぎて、データに踊らされることのないように注意して欲しいと思います。 - ユーザー獲得は決してコインの裏表の回数と同一ではありません。仮にあるデザインの方がユーザー数は獲得できるとしても、もう一方のデザインの方が質の良いユーザーを獲得出来る可能性があることを忘れてはいけません。 - 統計的に有意などと言っても、結局は仮定と解釈の産物です。都合のいいデータが出た時に使って、自分の主張を有利にしましょう。 |
|
| 1000位 |
|
|||
|
09:57:26 |
(Akatsuki Inc. 所属) |
|
## 概要 コピペコードが増えがちなサンプルアプリケーションの設計を例にとって、 STI(単一テーブル継承)とメタプログラミングでDRY(重複排除)してみる。 ## 題材 ユーザが保持している楽曲をジャンルごとに管理するようなアプリケーション。 ユーザページでは、ジャンル別に登録曲を一覧(もっと言うとCRUD)できる。 こんなイメージですね。 ``` kidachi_さん あなたの登録曲一覧 Rock ほげRock ふがRock Pops 未登録です。 Jazz ふーJazz ばーJazz ``` 何も考えないで作ると、rock/pops/jazzそれぞれのモデル、ビュー、コントローラに 似たような記述・コピペが増えそうな予感を感じて頂けたでしょうか。 では、それを防ぐために、まずはSTIから。 (※追記) 実は上記だけの要件であれば userテーブル、musicテーブル、genreテーブルのみを用意して `user has_many genres through musics`のassosiationでも実現可能 (そもそもrock/pops/jazzモデルを用意する必要がない)だったりします。 実際は「今後それぞれのgenreごとに特有な処理を複数追加していきたい」というケースを想定して、 各genre個別のモデルを用意することを前提にしています。 ちょっと要件の例がいまいちだったかもしれず、申し訳ありません。。 ## STI(Single Table Inheritance/単一テーブル継承)とは 「継承」と言えば、各クラスの共通項目が括りだされた スーパークラスと、非共通項目のサブクラスを用いることで コードの重複を防ぐ仕組みですが、それを <strong>db(テーブル)設計にも適用しよう</strong>というのがSTIです。 (単一テーブル継承、という名前そのままですね。) ## 具体例 先ほどの楽曲管理アプリのテーブルをざっくり設計してみると、 以下のようなイメージになると思います。  悪くはないのですが、モデル/テーブルに重複情報が 増えてしまいそうですね。 ## そこで単一テーブル継承 こうなります。  スーパークラスを用意して継承の仕組みを使うのと概念は全く一緒です。 一見どうということも無い様に見えますが、実は - <strong>rock、pops、jazz(etc)テーブルは実在しない</strong> - <strong>全てのデータは、musicテーブルに保管される</strong> という特徴を持ちます。 さりげなくmusicテーブルに<strong>「type」</strong>というカラムが追加されていますが、 <strong>ここにrock/pops/jazzといった子の情報が入る</strong>事になります。 また、もしrockテーブルにしか持たないような情報が欲しい場合は、 事前にそれはmusicに定義しておきます。 ## Modelの実装 STIを使っても、モデルは通常の継承の形で実装するだけです。 ```ruby:music.rb class Music < ActiveRecord::Base belongs_to :user validates :name, presence: true validates :artist, presence: true end ``` ```ruby:rock.rb class Rock < Music end ``` 気をつけるのは、 子が継承するのはActiveRecord::Baseでなく親であるMusic というところくらいでしょうか。 ちなみに、親で設定したリレーションやvalidatesは全て 子クラスにも引き継がれます。 STIについてはここまで。 結論、STIを利用するには、db生成時に - 親テーブル(今回はmusic)にtypeカラムを持たせる。 - 子の一つに特有なカラムが必要であれば、それも親に持たせておく - (あとは普通に継承の形でモデルを作る) だけで良いことなります。 ## Cntrollerの実装とメタプログラミング users/showで該当ユーザの楽曲一覧を表示したい、という仕様でしたね。 つまりshowで@rock/@pops/@jazzの3つをセットしておきたいということ。 ここでメタプログラミングを利用してみます。 ```ruby:users_controller.rb class UsersController < ApplicationController GENRE = [ 'rock', 'pops', 'jazz' ] def show @user = User.find(current_user.id) set_music_by_genre end private def set_music_by_genre @music_list = Array.new GENRE.each do |music| key = "@#{music}" if @user.send(music).nil? # 1 music = music.gsub(/\b\w/) { |s| s.upcase } # 2 value = self.class.const_get(music).new # 3 else value = @user.send(music) # 4 end instance_variable_set(key, value) # 5 @music_list << instance_variable_get(key) # 6 end end end ``` ### set_music_by_genreの説明 ####1. if @user.send(music).nil? sendを用いることで、@userに対するメッセージングを動的に行っています。 つまり、@user.rock/@user.pops/@user.jazzを動的に生成しているということ。 ここでは、@userがrock/pops/jazzそれぞれのデータを持っているかどうか を判別し、既存データを保持している場合はそれを呼び出し(#2,3)、 保持していなければ新規オブジェクトを生成(#4)します。 ####2. music.gsub(/\b\w/) { |s| s.upcase } "Rock"/"Pops"/"Jazz"という文字列を動的に生成。 \#3で使います。 ####3. self.class.const_get(music).new self.class.const_get(str)で、文字列(str)から クラスやモジュールの定数を取得できます。 つまり、\#2で生成した"Rock"/"Pops"/"Jazz"を クラス(Rock/Pops/Jazz)に変換し、それぞれnewしています。 ####4. @user.send(music) \#1と同様で、@userに対して動的にメッセージを送っています。 ####5. instance_variable_set(key, value) 動的にインスタンス変数を生成。 @rock/@pops/@jazzそれぞれに、既存オブジェクトもしくは新規オブジェクトをセットします。 ####6. @media_list << instance_variable_get(key) viewで用いるために、 ```ruby [ #<Rock id: nil, type: "Rock", created_at: nil, updated_at: nil>, #<Pops id: nil, type: "Pops", created_at: nil, updated_at: nil>, #<Jazz id: nil, type: "Jazz", created_at: nil, updated_at: nil> ] ``` このようなオブジェクトがセットされた配列を用意します。 ## View ```ruby:views/users/show.html.erb <% @music_list.each do |music| %> <% if music.try(:id).nil? %> <!-- 新規登録フォーム --> <% else %> <!-- 登録済み情報の表示 --> <% end %> <% end %> ``` showでセットした@music_listをもとに、instance_variable_get()で 動的にオブジェクトを取得し、それに応じて表示処理を行います。 ## Controller 新規登録フォームから投げられたデータをもとにcreateする箇所。 ここでもself.class.const_get()でサブクラスに応じた クラス(Rock/Pops/Jazz)を動的にセットし、保存しています。 ```ruby:music_controller.rb class MusicController < ApplicationController before_action :set_music_info before_action :music_params def create const_name = @music_name.gsub(/\b\w/) { |s| s.upcase } # サブクラスごとのオブジェクトを初期化 @music = self.class.const_get(const_name) @music.new(music_params) respond_to do |format| if @music.save! ~ end end end private # strong_parameters def music_params params.require(@music_name).permit(:user_id, :name, :email, :password) end end ``` ```rock_controller.rb class RockController < MusicController private set_music_info @music_name = "rock" end end ``` STIを用いてmusicテーブルが用意されているためか、music_controllerで 各サブクラス(rock/pops/jazz)のsave処理を行うのもごく直感的に行えます。 ※個人的には、STIは特別な仕組みというより #### 継承関係を持たせたいmodelに対して(直感的に)適合するテーブルのインターフェース なんじゃないかな、と思っています。 ## 完成 さて、これでshowページでの楽曲一覧と、楽曲新規登録のロジックができました。 このようなメタな骨組みを用意することで、musicのジャンルを増やす際は hoge_controller.rbとGENREリストに新たなジャンルを追加するだけで良いことになります。 (もちろんcontroller/modelファイルを用意したりroutes.rb追記したり、は別途必要ですが) ```classic_controller.rb class ClassicController < MusicController private set_music_info @music_name = "classic" end end ``` ```ruby:users_controller.rb class UsersController < ApplicationController GENRE = [ 'rock', 'pops', 'jazz', 'classic' ] ``` ※GENRE配列は設定ファイルとして外部化した方が良いですね こんな感じ。 長くなるのでここまでにしますが、メタプログラミングを使うことで 他のロジック部分も同様にコピペコードを撲滅できそうです。 ## 参考 Railsで単一テーブル継承(Single Table Inheritance) http://blog.matake.jp/archives/railssingle_table_inherit const_get (Module) http://ref.xaio.jp/ruby/classes/module/const_get instance_variable_set/get (Object) http://ref.xaio.jp/ruby/classes/object/instance_variable_set http://ref.xaio.jp/ruby/classes/object/instance_variable_get ## 最後に もっと効率の良い設計/書き方があれば、ぜひご教示よろしくお願い致します! |
|